From 90f63d9df1123397a893a242dee4e1bd4bc67350 Mon Sep 17 00:00:00 2001 From: ikmkj <1@qq,com> Date: Mon, 26 Jan 2026 08:49:10 +0800 Subject: [PATCH] =?UTF-8?q?feat(api):=20=E5=88=9D=E5=A7=8B=E5=8C=96?= =?UTF-8?q?=E7=AC=94=E8=AE=B0=E5=BA=94=E7=94=A8=E5=90=8E=E7=AB=AF=E5=9F=BA?= =?UTF-8?q?=E7=A1=80=E6=9E=B6=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加环境配置文件 .env.example 包含数据库、JWT、CORS等配置 - 创建 .gitignore 文件忽略敏感文件和临时文件 - 配置 Apache 重写规则支持路由转发 - 实现 JWT 认证中间件提供用户身份验证功能 - 添加 MySQL 数据库初始化脚本包含分组、图片、笔记表结构 --- .spec-workflow/templates/design-template.md | 96 ++++++++++ .spec-workflow/templates/product-template.md | 51 ++++++ .../templates/requirements-template.md | 50 +++++ .../templates/structure-template.md | 145 +++++++++++++++ .spec-workflow/templates/tasks-template.md | 139 ++++++++++++++ .spec-workflow/templates/tech-template.md | 99 ++++++++++ .spec-workflow/user-templates/README.md | 64 +++++++ biji-php/.env.example | 18 ++ biji-php/.gitignore | 8 + biji-php/README.md | 130 +++++++++++++ biji-php/composer.json | 23 +++ biji-php/config/database.php | 15 ++ biji-php/config/routes.php | 62 +++++++ biji-php/public/.htaccess | 4 + biji-php/public/index.php | 37 ++++ .../src/Controllers/GroupingController.php | 42 +++++ .../src/Controllers/MarkdownController.php | 76 ++++++++ biji-php/src/Controllers/UserController.php | 137 ++++++++++++++ biji-php/src/Middleware/AuthMiddleware.php | 47 +++++ biji-php/src/Middleware/CorsMiddleware.php | 23 +++ biji-php/src/Models/Grouping.php | 57 ++++++ biji-php/src/Models/MarkdownFile.php | 122 +++++++++++++ biji-php/src/Models/RegistrationCode.php | 38 ++++ biji-php/src/Models/SystemSetting.php | 44 +++++ biji-php/src/Models/User.php | 64 +++++++ biji-php/src/Utils/Database.php | 49 +++++ biji-php/src/Utils/JWTUtil.php | 45 +++++ biji-php/src/Utils/Response.php | 41 +++++ biji-php/uploads/.gitkeep | 0 sql/mysql/biji_db.sql | 172 ++++++++++++++++++ .../1fc870d6-d162-4ac3-812a-d862b930576c.png | Bin 0 -> 9733 bytes 31 files changed, 1898 insertions(+) create mode 100644 .spec-workflow/templates/design-template.md create mode 100644 .spec-workflow/templates/product-template.md create mode 100644 .spec-workflow/templates/requirements-template.md create mode 100644 .spec-workflow/templates/structure-template.md create mode 100644 .spec-workflow/templates/tasks-template.md create mode 100644 .spec-workflow/templates/tech-template.md create mode 100644 .spec-workflow/user-templates/README.md create mode 100644 biji-php/.env.example create mode 100644 biji-php/.gitignore create mode 100644 biji-php/README.md create mode 100644 biji-php/composer.json create mode 100644 biji-php/config/database.php create mode 100644 biji-php/config/routes.php create mode 100644 biji-php/public/.htaccess create mode 100644 biji-php/public/index.php create mode 100644 biji-php/src/Controllers/GroupingController.php create mode 100644 biji-php/src/Controllers/MarkdownController.php create mode 100644 biji-php/src/Controllers/UserController.php create mode 100644 biji-php/src/Middleware/AuthMiddleware.php create mode 100644 biji-php/src/Middleware/CorsMiddleware.php create mode 100644 biji-php/src/Models/Grouping.php create mode 100644 biji-php/src/Models/MarkdownFile.php create mode 100644 biji-php/src/Models/RegistrationCode.php create mode 100644 biji-php/src/Models/SystemSetting.php create mode 100644 biji-php/src/Models/User.php create mode 100644 biji-php/src/Utils/Database.php create mode 100644 biji-php/src/Utils/JWTUtil.php create mode 100644 biji-php/src/Utils/Response.php create mode 100644 biji-php/uploads/.gitkeep create mode 100644 sql/mysql/biji_db.sql create mode 100644 uploads/1fc870d6-d162-4ac3-812a-d862b930576c.png diff --git a/.spec-workflow/templates/design-template.md b/.spec-workflow/templates/design-template.md new file mode 100644 index 0000000..1295d7b --- /dev/null +++ b/.spec-workflow/templates/design-template.md @@ -0,0 +1,96 @@ +# Design Document + +## Overview + +[High-level description of the feature and its place in the overall system] + +## Steering Document Alignment + +### Technical Standards (tech.md) +[How the design follows documented technical patterns and standards] + +### Project Structure (structure.md) +[How the implementation will follow project organization conventions] + +## Code Reuse Analysis +[What existing code will be leveraged, extended, or integrated with this feature] + +### Existing Components to Leverage +- **[Component/Utility Name]**: [How it will be used] +- **[Service/Helper Name]**: [How it will be extended] + +### Integration Points +- **[Existing System/API]**: [How the new feature will integrate] +- **[Database/Storage]**: [How data will connect to existing schemas] + +## Architecture + +[Describe the overall architecture and design patterns used] + +### Modular Design Principles +- **Single File Responsibility**: Each file should handle one specific concern or domain +- **Component Isolation**: Create small, focused components rather than large monolithic files +- **Service Layer Separation**: Separate data access, business logic, and presentation layers +- **Utility Modularity**: Break utilities into focused, single-purpose modules + +```mermaid +graph TD + A[Component A] --> B[Component B] + B --> C[Component C] +``` + +## Components and Interfaces + +### Component 1 +- **Purpose:** [What this component does] +- **Interfaces:** [Public methods/APIs] +- **Dependencies:** [What it depends on] +- **Reuses:** [Existing components/utilities it builds upon] + +### Component 2 +- **Purpose:** [What this component does] +- **Interfaces:** [Public methods/APIs] +- **Dependencies:** [What it depends on] +- **Reuses:** [Existing components/utilities it builds upon] + +## Data Models + +### Model 1 +``` +[Define the structure of Model1 in your language] +- id: [unique identifier type] +- name: [string/text type] +- [Additional properties as needed] +``` + +### Model 2 +``` +[Define the structure of Model2 in your language] +- id: [unique identifier type] +- [Additional properties as needed] +``` + +## Error Handling + +### Error Scenarios +1. **Scenario 1:** [Description] + - **Handling:** [How to handle] + - **User Impact:** [What user sees] + +2. **Scenario 2:** [Description] + - **Handling:** [How to handle] + - **User Impact:** [What user sees] + +## Testing Strategy + +### Unit Testing +- [Unit testing approach] +- [Key components to test] + +### Integration Testing +- [Integration testing approach] +- [Key flows to test] + +### End-to-End Testing +- [E2E testing approach] +- [User scenarios to test] diff --git a/.spec-workflow/templates/product-template.md b/.spec-workflow/templates/product-template.md new file mode 100644 index 0000000..82e60de --- /dev/null +++ b/.spec-workflow/templates/product-template.md @@ -0,0 +1,51 @@ +# Product Overview + +## Product Purpose +[Describe the core purpose of this product/project. What problem does it solve?] + +## Target Users +[Who are the primary users of this product? What are their needs and pain points?] + +## Key Features +[List the main features that deliver value to users] + +1. **Feature 1**: [Description] +2. **Feature 2**: [Description] +3. **Feature 3**: [Description] + +## Business Objectives +[What are the business goals this product aims to achieve?] + +- [Objective 1] +- [Objective 2] +- [Objective 3] + +## Success Metrics +[How will we measure the success of this product?] + +- [Metric 1]: [Target] +- [Metric 2]: [Target] +- [Metric 3]: [Target] + +## Product Principles +[Core principles that guide product decisions] + +1. **[Principle 1]**: [Explanation] +2. **[Principle 2]**: [Explanation] +3. **[Principle 3]**: [Explanation] + +## Monitoring & Visibility (if applicable) +[How do users track progress and monitor the system?] + +- **Dashboard Type**: [e.g., Web-based, CLI, Desktop app] +- **Real-time Updates**: [e.g., WebSocket, polling, push notifications] +- **Key Metrics Displayed**: [What information is most important to surface] +- **Sharing Capabilities**: [e.g., read-only links, exports, reports] + +## Future Vision +[Where do we see this product evolving in the future?] + +### Potential Enhancements +- **Remote Access**: [e.g., Tunnel features for sharing dashboards with stakeholders] +- **Analytics**: [e.g., Historical trends, performance metrics] +- **Collaboration**: [e.g., Multi-user support, commenting] diff --git a/.spec-workflow/templates/requirements-template.md b/.spec-workflow/templates/requirements-template.md new file mode 100644 index 0000000..1c80ca0 --- /dev/null +++ b/.spec-workflow/templates/requirements-template.md @@ -0,0 +1,50 @@ +# Requirements Document + +## Introduction + +[Provide a brief overview of the feature, its purpose, and its value to users] + +## Alignment with Product Vision + +[Explain how this feature supports the goals outlined in product.md] + +## Requirements + +### Requirement 1 + +**User Story:** As a [role], I want [feature], so that [benefit] + +#### Acceptance Criteria + +1. WHEN [event] THEN [system] SHALL [response] +2. IF [precondition] THEN [system] SHALL [response] +3. WHEN [event] AND [condition] THEN [system] SHALL [response] + +### Requirement 2 + +**User Story:** As a [role], I want [feature], so that [benefit] + +#### Acceptance Criteria + +1. WHEN [event] THEN [system] SHALL [response] +2. IF [precondition] THEN [system] SHALL [response] + +## Non-Functional Requirements + +### Code Architecture and Modularity +- **Single Responsibility Principle**: Each file should have a single, well-defined purpose +- **Modular Design**: Components, utilities, and services should be isolated and reusable +- **Dependency Management**: Minimize interdependencies between modules +- **Clear Interfaces**: Define clean contracts between components and layers + +### Performance +- [Performance requirements] + +### Security +- [Security requirements] + +### Reliability +- [Reliability requirements] + +### Usability +- [Usability requirements] diff --git a/.spec-workflow/templates/structure-template.md b/.spec-workflow/templates/structure-template.md new file mode 100644 index 0000000..1ab1fbc --- /dev/null +++ b/.spec-workflow/templates/structure-template.md @@ -0,0 +1,145 @@ +# Project Structure + +## Directory Organization + +``` +[Define your project's directory structure. Examples below - adapt to your project type] + +Example for a library/package: +project-root/ +├── src/ # Source code +├── tests/ # Test files +├── docs/ # Documentation +├── examples/ # Usage examples +└── [build/dist/out] # Build output + +Example for an application: +project-root/ +├── [src/app/lib] # Main source code +├── [assets/resources] # Static resources +├── [config/settings] # Configuration +├── [scripts/tools] # Build/utility scripts +└── [tests/spec] # Test files + +Common patterns: +- Group by feature/module +- Group by layer (UI, business logic, data) +- Group by type (models, controllers, views) +- Flat structure for simple projects +``` + +## Naming Conventions + +### Files +- **Components/Modules**: [e.g., `PascalCase`, `snake_case`, `kebab-case`] +- **Services/Handlers**: [e.g., `UserService`, `user_service`, `user-service`] +- **Utilities/Helpers**: [e.g., `dateUtils`, `date_utils`, `date-utils`] +- **Tests**: [e.g., `[filename]_test`, `[filename].test`, `[filename]Test`] + +### Code +- **Classes/Types**: [e.g., `PascalCase`, `CamelCase`, `snake_case`] +- **Functions/Methods**: [e.g., `camelCase`, `snake_case`, `PascalCase`] +- **Constants**: [e.g., `UPPER_SNAKE_CASE`, `SCREAMING_CASE`, `PascalCase`] +- **Variables**: [e.g., `camelCase`, `snake_case`, `lowercase`] + +## Import Patterns + +### Import Order +1. External dependencies +2. Internal modules +3. Relative imports +4. Style imports + +### Module/Package Organization +``` +[Describe your project's import/include patterns] +Examples: +- Absolute imports from project root +- Relative imports within modules +- Package/namespace organization +- Dependency management approach +``` + +## Code Structure Patterns + +[Define common patterns for organizing code within files. Below are examples - choose what applies to your project] + +### Module/Class Organization +``` +Example patterns: +1. Imports/includes/dependencies +2. Constants and configuration +3. Type/interface definitions +4. Main implementation +5. Helper/utility functions +6. Exports/public API +``` + +### Function/Method Organization +``` +Example patterns: +- Input validation first +- Core logic in the middle +- Error handling throughout +- Clear return points +``` + +### File Organization Principles +``` +Choose what works for your project: +- One class/module per file +- Related functionality grouped together +- Public API at the top/bottom +- Implementation details hidden +``` + +## Code Organization Principles + +1. **Single Responsibility**: Each file should have one clear purpose +2. **Modularity**: Code should be organized into reusable modules +3. **Testability**: Structure code to be easily testable +4. **Consistency**: Follow patterns established in the codebase + +## Module Boundaries +[Define how different parts of your project interact and maintain separation of concerns] + +Examples of boundary patterns: +- **Core vs Plugins**: Core functionality vs extensible plugins +- **Public API vs Internal**: What's exposed vs implementation details +- **Platform-specific vs Cross-platform**: OS-specific code isolation +- **Stable vs Experimental**: Production code vs experimental features +- **Dependencies direction**: Which modules can depend on which + +## Code Size Guidelines +[Define your project's guidelines for file and function sizes] + +Suggested guidelines: +- **File size**: [Define maximum lines per file] +- **Function/Method size**: [Define maximum lines per function] +- **Class/Module complexity**: [Define complexity limits] +- **Nesting depth**: [Maximum nesting levels] + +## Dashboard/Monitoring Structure (if applicable) +[How dashboard or monitoring components are organized] + +### Example Structure: +``` +src/ +└── dashboard/ # Self-contained dashboard subsystem + ├── server/ # Backend server components + ├── client/ # Frontend assets + ├── shared/ # Shared types/utilities + └── public/ # Static assets +``` + +### Separation of Concerns +- Dashboard isolated from core business logic +- Own CLI entry point for independent operation +- Minimal dependencies on main application +- Can be disabled without affecting core functionality + +## Documentation Standards +- All public APIs must have documentation +- Complex logic should include inline comments +- README files for major modules +- Follow language-specific documentation conventions diff --git a/.spec-workflow/templates/tasks-template.md b/.spec-workflow/templates/tasks-template.md new file mode 100644 index 0000000..be461de --- /dev/null +++ b/.spec-workflow/templates/tasks-template.md @@ -0,0 +1,139 @@ +# Tasks Document + +- [ ] 1. Create core interfaces in src/types/feature.ts + - File: src/types/feature.ts + - Define TypeScript interfaces for feature data structures + - Extend existing base interfaces from base.ts + - Purpose: Establish type safety for feature implementation + - _Leverage: src/types/base.ts_ + - _Requirements: 1.1_ + - _Prompt: Role: TypeScript Developer specializing in type systems and interfaces | Task: Create comprehensive TypeScript interfaces for the feature data structures following requirements 1.1, extending existing base interfaces from src/types/base.ts | Restrictions: Do not modify existing base interfaces, maintain backward compatibility, follow project naming conventions | Success: All interfaces compile without errors, proper inheritance from base types, full type coverage for feature requirements_ + +- [ ] 2. Create base model class in src/models/FeatureModel.ts + - File: src/models/FeatureModel.ts + - Implement base model extending BaseModel class + - Add validation methods using existing validation utilities + - Purpose: Provide data layer foundation for feature + - _Leverage: src/models/BaseModel.ts, src/utils/validation.ts_ + - _Requirements: 2.1_ + - _Prompt: Role: Backend Developer with expertise in Node.js and data modeling | Task: Create a base model class extending BaseModel and implementing validation following requirement 2.1, leveraging existing patterns from src/models/BaseModel.ts and src/utils/validation.ts | Restrictions: Must follow existing model patterns, do not bypass validation utilities, maintain consistent error handling | Success: Model extends BaseModel correctly, validation methods implemented and tested, follows project architecture patterns_ + +- [ ] 3. Add specific model methods to FeatureModel.ts + - File: src/models/FeatureModel.ts (continue from task 2) + - Implement create, update, delete methods + - Add relationship handling for foreign keys + - Purpose: Complete model functionality for CRUD operations + - _Leverage: src/models/BaseModel.ts_ + - _Requirements: 2.2, 2.3_ + - _Prompt: Role: Backend Developer with expertise in ORM and database operations | Task: Implement CRUD methods and relationship handling in FeatureModel.ts following requirements 2.2 and 2.3, extending patterns from src/models/BaseModel.ts | Restrictions: Must maintain transaction integrity, follow existing relationship patterns, do not duplicate base model functionality | Success: All CRUD operations work correctly, relationships are properly handled, database operations are atomic and efficient_ + +- [ ] 4. Create model unit tests in tests/models/FeatureModel.test.ts + - File: tests/models/FeatureModel.test.ts + - Write tests for model validation and CRUD methods + - Use existing test utilities and fixtures + - Purpose: Ensure model reliability and catch regressions + - _Leverage: tests/helpers/testUtils.ts, tests/fixtures/data.ts_ + - _Requirements: 2.1, 2.2_ + - _Prompt: Role: QA Engineer with expertise in unit testing and Jest/Mocha frameworks | Task: Create comprehensive unit tests for FeatureModel validation and CRUD methods covering requirements 2.1 and 2.2, using existing test utilities from tests/helpers/testUtils.ts and fixtures from tests/fixtures/data.ts | Restrictions: Must test both success and failure scenarios, do not test external dependencies directly, maintain test isolation | Success: All model methods are tested with good coverage, edge cases covered, tests run independently and consistently_ + +- [ ] 5. Create service interface in src/services/IFeatureService.ts + - File: src/services/IFeatureService.ts + - Define service contract with method signatures + - Extend base service interface patterns + - Purpose: Establish service layer contract for dependency injection + - _Leverage: src/services/IBaseService.ts_ + - _Requirements: 3.1_ + - _Prompt: Role: Software Architect specializing in service-oriented architecture and TypeScript interfaces | Task: Design service interface contract following requirement 3.1, extending base service patterns from src/services/IBaseService.ts for dependency injection | Restrictions: Must maintain interface segregation principle, do not expose internal implementation details, ensure contract compatibility with DI container | Success: Interface is well-defined with clear method signatures, extends base service appropriately, supports all required service operations_ + +- [ ] 6. Implement feature service in src/services/FeatureService.ts + - File: src/services/FeatureService.ts + - Create concrete service implementation using FeatureModel + - Add error handling with existing error utilities + - Purpose: Provide business logic layer for feature operations + - _Leverage: src/services/BaseService.ts, src/utils/errorHandler.ts, src/models/FeatureModel.ts_ + - _Requirements: 3.2_ + - _Prompt: Role: Backend Developer with expertise in service layer architecture and business logic | Task: Implement concrete FeatureService following requirement 3.2, using FeatureModel and extending BaseService patterns with proper error handling from src/utils/errorHandler.ts | Restrictions: Must implement interface contract exactly, do not bypass model validation, maintain separation of concerns from data layer | Success: Service implements all interface methods correctly, robust error handling implemented, business logic is well-encapsulated and testable_ + +- [ ] 7. Add service dependency injection in src/utils/di.ts + - File: src/utils/di.ts (modify existing) + - Register FeatureService in dependency injection container + - Configure service lifetime and dependencies + - Purpose: Enable service injection throughout application + - _Leverage: existing DI configuration in src/utils/di.ts_ + - _Requirements: 3.1_ + - _Prompt: Role: DevOps Engineer with expertise in dependency injection and IoC containers | Task: Register FeatureService in DI container following requirement 3.1, configuring appropriate lifetime and dependencies using existing patterns from src/utils/di.ts | Restrictions: Must follow existing DI container patterns, do not create circular dependencies, maintain service resolution efficiency | Success: FeatureService is properly registered and resolvable, dependencies are correctly configured, service lifetime is appropriate for use case_ + +- [ ] 8. Create service unit tests in tests/services/FeatureService.test.ts + - File: tests/services/FeatureService.test.ts + - Write tests for service methods with mocked dependencies + - Test error handling scenarios + - Purpose: Ensure service reliability and proper error handling + - _Leverage: tests/helpers/testUtils.ts, tests/mocks/modelMocks.ts_ + - _Requirements: 3.2, 3.3_ + - _Prompt: Role: QA Engineer with expertise in service testing and mocking frameworks | Task: Create comprehensive unit tests for FeatureService methods covering requirements 3.2 and 3.3, using mocked dependencies from tests/mocks/modelMocks.ts and test utilities | Restrictions: Must mock all external dependencies, test business logic in isolation, do not test framework code | Success: All service methods tested with proper mocking, error scenarios covered, tests verify business logic correctness and error handling_ + +- [ ] 4. Create API endpoints + - Design API structure + - _Leverage: src/api/baseApi.ts, src/utils/apiUtils.ts_ + - _Requirements: 4.0_ + - _Prompt: Role: API Architect specializing in RESTful design and Express.js | Task: Design comprehensive API structure following requirement 4.0, leveraging existing patterns from src/api/baseApi.ts and utilities from src/utils/apiUtils.ts | Restrictions: Must follow REST conventions, maintain API versioning compatibility, do not expose internal data structures directly | Success: API structure is well-designed and documented, follows existing patterns, supports all required operations with proper HTTP methods and status codes_ + +- [ ] 4.1 Set up routing and middleware + - Configure application routes + - Add authentication middleware + - Set up error handling middleware + - _Leverage: src/middleware/auth.ts, src/middleware/errorHandler.ts_ + - _Requirements: 4.1_ + - _Prompt: Role: Backend Developer with expertise in Express.js middleware and routing | Task: Configure application routes and middleware following requirement 4.1, integrating authentication from src/middleware/auth.ts and error handling from src/middleware/errorHandler.ts | Restrictions: Must maintain middleware order, do not bypass security middleware, ensure proper error propagation | Success: Routes are properly configured with correct middleware chain, authentication works correctly, errors are handled gracefully throughout the request lifecycle_ + +- [ ] 4.2 Implement CRUD endpoints + - Create API endpoints + - Add request validation + - Write API integration tests + - _Leverage: src/controllers/BaseController.ts, src/utils/validation.ts_ + - _Requirements: 4.2, 4.3_ + - _Prompt: Role: Full-stack Developer with expertise in API development and validation | Task: Implement CRUD endpoints following requirements 4.2 and 4.3, extending BaseController patterns and using validation utilities from src/utils/validation.ts | Restrictions: Must validate all inputs, follow existing controller patterns, ensure proper HTTP status codes and responses | Success: All CRUD operations work correctly, request validation prevents invalid data, integration tests pass and cover all endpoints_ + +- [ ] 5. Add frontend components + - Plan component architecture + - _Leverage: src/components/BaseComponent.tsx, src/styles/theme.ts_ + - _Requirements: 5.0_ + - _Prompt: Role: Frontend Architect with expertise in React component design and architecture | Task: Plan comprehensive component architecture following requirement 5.0, leveraging base patterns from src/components/BaseComponent.tsx and theme system from src/styles/theme.ts | Restrictions: Must follow existing component patterns, maintain design system consistency, ensure component reusability | Success: Architecture is well-planned and documented, components are properly organized, follows existing patterns and theme system_ + +- [ ] 5.1 Create base UI components + - Set up component structure + - Implement reusable components + - Add styling and theming + - _Leverage: src/components/BaseComponent.tsx, src/styles/theme.ts_ + - _Requirements: 5.1_ + - _Prompt: Role: Frontend Developer specializing in React and component architecture | Task: Create reusable UI components following requirement 5.1, extending BaseComponent patterns and using existing theme system from src/styles/theme.ts | Restrictions: Must use existing theme variables, follow component composition patterns, ensure accessibility compliance | Success: Components are reusable and properly themed, follow existing architecture, accessible and responsive_ + +- [ ] 5.2 Implement feature-specific components + - Create feature components + - Add state management + - Connect to API endpoints + - _Leverage: src/hooks/useApi.ts, src/components/BaseComponent.tsx_ + - _Requirements: 5.2, 5.3_ + - _Prompt: Role: React Developer with expertise in state management and API integration | Task: Implement feature-specific components following requirements 5.2 and 5.3, using API hooks from src/hooks/useApi.ts and extending BaseComponent patterns | Restrictions: Must use existing state management patterns, handle loading and error states properly, maintain component performance | Success: Components are fully functional with proper state management, API integration works smoothly, user experience is responsive and intuitive_ + +- [ ] 6. Integration and testing + - Plan integration approach + - _Leverage: src/utils/integrationUtils.ts, tests/helpers/testUtils.ts_ + - _Requirements: 6.0_ + - _Prompt: Role: Integration Engineer with expertise in system integration and testing strategies | Task: Plan comprehensive integration approach following requirement 6.0, leveraging integration utilities from src/utils/integrationUtils.ts and test helpers | Restrictions: Must consider all system components, ensure proper test coverage, maintain integration test reliability | Success: Integration plan is comprehensive and feasible, all system components work together correctly, integration points are well-tested_ + +- [ ] 6.1 Write end-to-end tests + - Set up E2E testing framework + - Write user journey tests + - Add test automation + - _Leverage: tests/helpers/testUtils.ts, tests/fixtures/data.ts_ + - _Requirements: All_ + - _Prompt: Role: QA Automation Engineer with expertise in E2E testing and test frameworks like Cypress or Playwright | Task: Implement comprehensive end-to-end tests covering all requirements, setting up testing framework and user journey tests using test utilities and fixtures | Restrictions: Must test real user workflows, ensure tests are maintainable and reliable, do not test implementation details | Success: E2E tests cover all critical user journeys, tests run reliably in CI/CD pipeline, user experience is validated from end-to-end_ + +- [ ] 6.2 Final integration and cleanup + - Integrate all components + - Fix any integration issues + - Clean up code and documentation + - _Leverage: src/utils/cleanup.ts, docs/templates/_ + - _Requirements: All_ + - _Prompt: Role: Senior Developer with expertise in code quality and system integration | Task: Complete final integration of all components and perform comprehensive cleanup covering all requirements, using cleanup utilities and documentation templates | Restrictions: Must not break existing functionality, ensure code quality standards are met, maintain documentation consistency | Success: All components are fully integrated and working together, code is clean and well-documented, system meets all requirements and quality standards_ diff --git a/.spec-workflow/templates/tech-template.md b/.spec-workflow/templates/tech-template.md new file mode 100644 index 0000000..57cd538 --- /dev/null +++ b/.spec-workflow/templates/tech-template.md @@ -0,0 +1,99 @@ +# Technology Stack + +## Project Type +[Describe what kind of project this is: web application, CLI tool, desktop application, mobile app, library, API service, embedded system, game, etc.] + +## Core Technologies + +### Primary Language(s) +- **Language**: [e.g., Python 3.11, Go 1.21, TypeScript, Rust, C++] +- **Runtime/Compiler**: [if applicable] +- **Language-specific tools**: [package managers, build tools, etc.] + +### Key Dependencies/Libraries +[List the main libraries and frameworks your project depends on] +- **[Library/Framework name]**: [Purpose and version] +- **[Library/Framework name]**: [Purpose and version] + +### Application Architecture +[Describe how your application is structured - this could be MVC, event-driven, plugin-based, client-server, standalone, microservices, monolithic, etc.] + +### Data Storage (if applicable) +- **Primary storage**: [e.g., PostgreSQL, files, in-memory, cloud storage] +- **Caching**: [e.g., Redis, in-memory, disk cache] +- **Data formats**: [e.g., JSON, Protocol Buffers, XML, binary] + +### External Integrations (if applicable) +- **APIs**: [External services you integrate with] +- **Protocols**: [e.g., HTTP/REST, gRPC, WebSocket, TCP/IP] +- **Authentication**: [e.g., OAuth, API keys, certificates] + +### Monitoring & Dashboard Technologies (if applicable) +- **Dashboard Framework**: [e.g., React, Vue, vanilla JS, terminal UI] +- **Real-time Communication**: [e.g., WebSocket, Server-Sent Events, polling] +- **Visualization Libraries**: [e.g., Chart.js, D3, terminal graphs] +- **State Management**: [e.g., Redux, Vuex, file system as source of truth] + +## Development Environment + +### Build & Development Tools +- **Build System**: [e.g., Make, CMake, Gradle, npm scripts, cargo] +- **Package Management**: [e.g., pip, npm, cargo, go mod, apt, brew] +- **Development workflow**: [e.g., hot reload, watch mode, REPL] + +### Code Quality Tools +- **Static Analysis**: [Tools for code quality and correctness] +- **Formatting**: [Code style enforcement tools] +- **Testing Framework**: [Unit, integration, and/or end-to-end testing tools] +- **Documentation**: [Documentation generation tools] + +### Version Control & Collaboration +- **VCS**: [e.g., Git, Mercurial, SVN] +- **Branching Strategy**: [e.g., Git Flow, GitHub Flow, trunk-based] +- **Code Review Process**: [How code reviews are conducted] + +### Dashboard Development (if applicable) +- **Live Reload**: [e.g., Hot module replacement, file watchers] +- **Port Management**: [e.g., Dynamic allocation, configurable ports] +- **Multi-Instance Support**: [e.g., Running multiple dashboards simultaneously] + +## Deployment & Distribution (if applicable) +- **Target Platform(s)**: [Where/how the project runs: cloud, on-premise, desktop, mobile, embedded] +- **Distribution Method**: [How users get your software: download, package manager, app store, SaaS] +- **Installation Requirements**: [Prerequisites, system requirements] +- **Update Mechanism**: [How updates are delivered] + +## Technical Requirements & Constraints + +### Performance Requirements +- [e.g., response time, throughput, memory usage, startup time] +- [Specific benchmarks or targets] + +### Compatibility Requirements +- **Platform Support**: [Operating systems, architectures, versions] +- **Dependency Versions**: [Minimum/maximum versions of dependencies] +- **Standards Compliance**: [Industry standards, protocols, specifications] + +### Security & Compliance +- **Security Requirements**: [Authentication, encryption, data protection] +- **Compliance Standards**: [GDPR, HIPAA, SOC2, etc. if applicable] +- **Threat Model**: [Key security considerations] + +### Scalability & Reliability +- **Expected Load**: [Users, requests, data volume] +- **Availability Requirements**: [Uptime targets, disaster recovery] +- **Growth Projections**: [How the system needs to scale] + +## Technical Decisions & Rationale +[Document key architectural and technology choices] + +### Decision Log +1. **[Technology/Pattern Choice]**: [Why this was chosen, alternatives considered] +2. **[Architecture Decision]**: [Rationale, trade-offs accepted] +3. **[Tool/Library Selection]**: [Reasoning, evaluation criteria] + +## Known Limitations +[Document any technical debt, limitations, or areas for improvement] + +- [Limitation 1]: [Impact and potential future solutions] +- [Limitation 2]: [Why it exists and when it might be addressed] diff --git a/.spec-workflow/user-templates/README.md b/.spec-workflow/user-templates/README.md new file mode 100644 index 0000000..ad36a48 --- /dev/null +++ b/.spec-workflow/user-templates/README.md @@ -0,0 +1,64 @@ +# User Templates + +This directory allows you to create custom templates that override the default Spec Workflow templates. + +## How to Use Custom Templates + +1. **Create your custom template file** in this directory with the exact same name as the default template you want to override: + - `requirements-template.md` - Override requirements document template + - `design-template.md` - Override design document template + - `tasks-template.md` - Override tasks document template + - `product-template.md` - Override product steering template + - `tech-template.md` - Override tech steering template + - `structure-template.md` - Override structure steering template + +2. **Template Loading Priority**: + - The system first checks this `user-templates/` directory + - If a matching template is found here, it will be used + - Otherwise, the default template from `templates/` will be used + +## Example Custom Template + +To create a custom requirements template: + +1. Create a file named `requirements-template.md` in this directory +2. Add your custom structure, for example: + +```markdown +# Requirements Document + +## Executive Summary +[Your custom section] + +## Business Requirements +[Your custom structure] + +## Technical Requirements +[Your custom fields] + +## Custom Sections +[Add any sections specific to your workflow] +``` + +## Template Variables + +Templates can include placeholders that will be replaced when documents are created: +- `{{projectName}}` - The name of your project +- `{{featureName}}` - The name of the feature being specified +- `{{date}}` - The current date +- `{{author}}` - The document author + +## Best Practices + +1. **Start from defaults**: Copy a default template from `../templates/` as a starting point +2. **Keep structure consistent**: Maintain similar section headers for tool compatibility +3. **Document changes**: Add comments explaining why sections were added/modified +4. **Version control**: Track your custom templates in version control +5. **Test thoroughly**: Ensure custom templates work with the spec workflow tools + +## Notes + +- Custom templates are project-specific and not included in the package distribution +- The `templates/` directory contains the default templates which are updated with each version +- Your custom templates in this directory are preserved during updates +- If a custom template has errors, the system will fall back to the default template diff --git a/biji-php/.env.example b/biji-php/.env.example new file mode 100644 index 0000000..b91c158 --- /dev/null +++ b/biji-php/.env.example @@ -0,0 +1,18 @@ +# 数据库配置 +DB_HOST=localhost +DB_PORT=3306 +DB_NAME=biji_db +DB_USER=root +DB_PASS= + +# JWT 配置 +JWT_SECRET=your-secret-key-change-this +JWT_EXPIRE=86400 + +# 应用配置 +APP_ENV=development +APP_DEBUG=true +UPLOAD_DIR=../uploads + +# CORS 配置 +CORS_ORIGIN=http://localhost:5173 diff --git a/biji-php/.gitignore b/biji-php/.gitignore new file mode 100644 index 0000000..9f11762 --- /dev/null +++ b/biji-php/.gitignore @@ -0,0 +1,8 @@ +/vendor/ +/.env +/uploads/* +!/uploads/.gitkeep +.DS_Store +.idea/ +*.log +composer.lock diff --git a/biji-php/README.md b/biji-php/README.md new file mode 100644 index 0000000..3ac309b --- /dev/null +++ b/biji-php/README.md @@ -0,0 +1,130 @@ +# 笔记管理系统 - PHP 后端 + +基于 PHP 7.4 的笔记管理系统后端,使用 Slim Framework 4 构建 RESTful API。 + +## 环境要求 + +- PHP 7.4(线程安全版) +- MySQL 5.7+ +- Apache/Nginx +- Composer + +## 安装步骤 + +### 1. 安装依赖 + +```bash +cd biji-php +composer install +``` + +### 2. 配置环境变量 + +复制 `.env.example` 为 `.env` 并修改配置: + +```bash +cp .env.example .env +``` + +编辑 `.env` 文件,配置数据库连接信息: + +``` +DB_HOST=localhost +DB_PORT=3306 +DB_NAME=biji_db +DB_USER=root +DB_PASS=your_password + +JWT_SECRET=your-secret-key-change-this +JWT_EXPIRE=86400 + +CORS_ORIGIN=http://localhost:5173 +``` + +### 3. 导入数据库 + +使用 `sql/mysql/all.sql` 文件创建数据库表结构。 + +```bash +mysql -u root -p biji_db < ../sql/mysql/all.sql +``` + +### 4. 配置 Web 服务器 + +#### Apache 配置 + +确保启用了 `mod_rewrite` 模块,并将网站根目录指向 `public` 目录。 + +#### Nginx 配置示例 + +```nginx +server { + listen 80; + server_name your-domain.com; + root /path/to/biji-php/public; + index index.php; + + location / { + try_files $uri $uri/ /index.php?$query_string; + } + + location ~ \.php$ { + fastcgi_pass unix:/var/run/php/php7.4-fpm.sock; + fastcgi_index index.php; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + include fastcgi_params; + } +} +``` + +## API 接口文档 + +### 用户接口 (`/api/user`) + +- `POST /register` - 用户注册 +- `POST /login` - 用户登录 +- `POST /validate-token` - 验证 Token(需认证) +- `PUT /password` - 更新密码(需认证) +- `DELETE /deleteUser` - 删除用户(需认证) + +### Markdown 接口 (`/api/markdown`) + +- `GET /{id}` - 获取笔记内容 +- `GET /` - 获取所有笔记 +- `POST /updateMarkdown` - 更新笔记(需认证) +- `DELETE /{id}` - 删除笔记(需认证) + +### 分组接口 (`/api/groupings`) + +- `GET /` - 获取所有分组(需认证) +- `POST /` - 创建分组(需认证) + +## 项目结构 + +``` +biji-php/ +├── public/ # Web 根目录 +│ ├── index.php # 入口文件 +│ └── .htaccess # Apache 重写规则 +├── src/ # 源代码 +│ ├── Controllers/ # 控制器 +│ ├── Models/ # 模型 +│ ├── Middleware/ # 中间件 +│ └── Utils/ # 工具类 +├── config/ # 配置文件 +├── uploads/ # 文件上传目录 +├── vendor/ # Composer 依赖 +└── composer.json # Composer 配置 +``` + +## 开发说明 + +本项目遵循以下原则: +- PSR-4 自动加载 +- RESTful API 设计 +- JWT Token 认证 +- MVC 架构模式 + +## 许可证 + +MIT License diff --git a/biji-php/composer.json b/biji-php/composer.json new file mode 100644 index 0000000..5fdcc0c --- /dev/null +++ b/biji-php/composer.json @@ -0,0 +1,23 @@ +{ + "name": "biji/php-backend", + "description": "笔记管理系统 PHP 后端", + "type": "project", + "require": { + "php": "^7.4", + "slim/slim": "^4.10", + "slim/psr7": "^1.6", + "php-di/php-di": "^6.4", + "firebase/php-jwt": "^6.3", + "vlucas/phpdotenv": "^5.5" + }, + "autoload": { + "psr-4": { + "App\\": "src/" + } + }, + "config": { + "platform": { + "php": "7.4" + } + } +} diff --git a/biji-php/config/database.php b/biji-php/config/database.php new file mode 100644 index 0000000..1c64de4 --- /dev/null +++ b/biji-php/config/database.php @@ -0,0 +1,15 @@ + $_ENV['DB_HOST'] ?? 'localhost', + 'port' => $_ENV['DB_PORT'] ?? '3306', + 'database' => $_ENV['DB_NAME'] ?? 'biji_db', + 'username' => $_ENV['DB_USER'] ?? 'root', + 'password' => $_ENV['DB_PASS'] ?? '', + 'charset' => 'utf8mb4', + 'options' => [ + PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, + PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, + PDO::ATTR_EMULATE_PREPARES => false, + ] +]; diff --git a/biji-php/config/routes.php b/biji-php/config/routes.php new file mode 100644 index 0000000..64258f2 --- /dev/null +++ b/biji-php/config/routes.php @@ -0,0 +1,62 @@ +get('/', function ($request, $response) { + $data = [ + 'status' => 'success', + 'message' => 'Biji PHP API is running', + 'version' => '1.0.0', + 'endpoints' => [ + 'POST /api/user/register' => '用户注册', + 'POST /api/user/login' => '用户登录', + 'GET /api/markdown' => '获取所有笔记', + 'GET /api/markdown/{id}' => '获取单个笔记', + 'GET /api/groupings' => '获取分组列表' + ] + ]; + $response->getBody()->write(json_encode($data, JSON_UNESCAPED_UNICODE)); + return $response->withHeader('Content-Type', 'application/json'); +}); + +// 用户相关路由(无需认证) +$app->group('/api/user', function (RouteCollectorProxy $group) { + $group->post('/register', [UserController::class, 'register']); + $group->post('/login', [UserController::class, 'login']); +}); + +// 用户相关路由(需要认证) +$app->group('/api/user', function (RouteCollectorProxy $group) { + $group->post('/validate-token', [UserController::class, 'validateToken']); + $group->put('/password', [UserController::class, 'updatePassword']); + $group->delete('/deleteUser', [UserController::class, 'deleteUser']); +})->add(new AuthMiddleware()); + +// Markdown 相关路由 +$app->group('/api/markdown', function (RouteCollectorProxy $group) { + $group->get('/{id}', [MarkdownController::class, 'getById']); + $group->get('', [MarkdownController::class, 'getAll']); +}); + +// Markdown 相关路由(需要认证) +$app->group('/api/markdown', function (RouteCollectorProxy $group) { + $group->post('/updateMarkdown', [MarkdownController::class, 'update']); + $group->delete('/{id}', [MarkdownController::class, 'delete']); +})->add(new AuthMiddleware()); + +// 分组相关路由(无需认证) +$app->group('/api/groupings', function (RouteCollectorProxy $group) { + $group->get('', [GroupingController::class, 'getAll']); +}); + +// 分组相关路由(需要认证) +$app->group('/api/groupings', function (RouteCollectorProxy $group) { + $group->post('', [GroupingController::class, 'create']); + $group->put('/{id}', [GroupingController::class, 'update']); + $group->delete('/{id}', [GroupingController::class, 'delete']); +})->add(new AuthMiddleware()); diff --git a/biji-php/public/.htaccess b/biji-php/public/.htaccess new file mode 100644 index 0000000..66ef8f6 --- /dev/null +++ b/biji-php/public/.htaccess @@ -0,0 +1,4 @@ +RewriteEngine On +RewriteCond %{REQUEST_FILENAME} !-f +RewriteCond %{REQUEST_FILENAME} !-d +RewriteRule ^ index.php [QSA,L] diff --git a/biji-php/public/index.php b/biji-php/public/index.php new file mode 100644 index 0000000..5782d89 --- /dev/null +++ b/biji-php/public/index.php @@ -0,0 +1,37 @@ +load(); + +// 创建容器 +$container = new Container(); +AppFactory::setContainer($container); + +// 创建应用 +$app = AppFactory::create(); + +// 添加路由中间件(必须在路由定义之前) +$app->addRoutingMiddleware(); + +// 添加错误处理 +$app->addErrorMiddleware(true, true, true); + +// 添加 CORS 中间件 +$app->add(new \App\Middleware\CorsMiddleware()); + +// 处理 OPTIONS 请求 +$app->options('/{routes:.+}', function ($request, $response) { + return $response; +}); + +// 加载路由 +require __DIR__ . '/../config/routes.php'; + +$app->run(); diff --git a/biji-php/src/Controllers/GroupingController.php b/biji-php/src/Controllers/GroupingController.php new file mode 100644 index 0000000..a37b285 --- /dev/null +++ b/biji-php/src/Controllers/GroupingController.php @@ -0,0 +1,42 @@ +getQueryParams(); + $parentId = $params['parentId'] ?? null; + + $model = new Grouping(); + $groupings = $model->getAll($parentId); + + $response->getBody()->write(json_encode(ApiResponse::success($groupings), JSON_UNESCAPED_UNICODE)); + return $response->withHeader('Content-Type', 'application/json; charset=utf-8'); + } + + public function create(Request $request, Response $response) + { + $data = $request->getParsedBody(); + $grouping = $data['grouping'] ?? ''; + $parentId = $data['parentId'] ?? 0; + + if (empty($grouping)) { + $response->getBody()->write(json_encode(ApiResponse::fail('分组名称不能为空'), JSON_UNESCAPED_UNICODE)); + return $response->withHeader('Content-Type', 'application/json; charset=utf-8')->withStatus(400); + } + + $model = new Grouping(); + $id = $model->create($grouping, $parentId); + $created = $model->findById($id); + + $response->getBody()->write(json_encode(ApiResponse::success($created), JSON_UNESCAPED_UNICODE)); + return $response->withHeader('Content-Type', 'application/json; charset=utf-8'); + } +} diff --git a/biji-php/src/Controllers/MarkdownController.php b/biji-php/src/Controllers/MarkdownController.php new file mode 100644 index 0000000..92d1389 --- /dev/null +++ b/biji-php/src/Controllers/MarkdownController.php @@ -0,0 +1,76 @@ +findById($id); + + if (!$file) { + $response->getBody()->write(ApiResponse::json(ApiResponse::fail('文件不存在'))); + return $response->withHeader('Content-Type', 'application/json; charset=utf-8')->withStatus(404); + } + + // 检查是否为私密笔记 + if ($file['is_private'] == 1) { + // 检查是否已认证 + $userId = $request->getAttribute('userId'); + if (!$userId) { + // 未认证,返回空内容 + $response->getBody()->write(ApiResponse::json(ApiResponse::success(''))); + return $response->withHeader('Content-Type', 'application/json; charset=utf-8'); + } + } + + $response->getBody()->write(ApiResponse::json(ApiResponse::success($file['content']))); + return $response->withHeader('Content-Type', 'application/json; charset=utf-8'); + } + + public function getAll(Request $request, Response $response) + { + $model = new MarkdownFile(); + $files = $model->getAll(); + + $response->getBody()->write(ApiResponse::json(ApiResponse::success($files))); + return $response->withHeader('Content-Type', 'application/json; charset=utf-8'); + } + + public function update(Request $request, Response $response) + { + $data = $request->getParsedBody(); + $id = $data['id'] ?? null; + + if (!$id) { + $response->getBody()->write(ApiResponse::json(ApiResponse::fail('缺少文件ID'))); + return $response->withHeader('Content-Type', 'application/json; charset=utf-8')->withStatus(400); + } + + $model = new MarkdownFile(); + $model->update($id, $data); + + $file = $model->findById($id); + $response->getBody()->write(ApiResponse::json(ApiResponse::success($file))); + return $response->withHeader('Content-Type', 'application/json; charset=utf-8'); + } + + public function delete(Request $request, Response $response, $args) + { + $id = $args['id']; + $userId = $request->getAttribute('userId'); + + $model = new MarkdownFile(); + $model->softDelete($id, $userId); + + $response->getBody()->write(ApiResponse::json(ApiResponse::success(null, '删除成功'))); + return $response->withHeader('Content-Type', 'application/json; charset=utf-8'); + } +} diff --git a/biji-php/src/Controllers/UserController.php b/biji-php/src/Controllers/UserController.php new file mode 100644 index 0000000..e4ef469 --- /dev/null +++ b/biji-php/src/Controllers/UserController.php @@ -0,0 +1,137 @@ +getParsedBody(); + $username = $data['username'] ?? ''; + $password = $data['password'] ?? ''; + $email = $data['email'] ?? ''; + $registrationCode = $data['registrationCode'] ?? ''; + + // 验证输入 + if (empty($username) || empty($password) || empty($email)) { + $response->getBody()->write(ApiResponse::json(ApiResponse::fail('用户名、密码和邮箱不能为空'))); + return $response->withHeader('Content-Type', 'application/json; charset=utf-8')->withStatus(400); + } + + // 检查注册功能是否开启 + $systemSetting = new SystemSetting(); + if (!$systemSetting->isRegistrationEnabled()) { + $response->getBody()->write(ApiResponse::json(ApiResponse::fail('注册功能已关闭'))); + return $response->withHeader('Content-Type', 'application/json; charset=utf-8')->withStatus(403); + } + + // 验证注册码 + $regCodeModel = new RegistrationCode(); + if (!$regCodeModel->validateCode($registrationCode)) { + $response->getBody()->write(ApiResponse::json(ApiResponse::fail('无效或已过期的注册码'))); + return $response->withHeader('Content-Type', 'application/json; charset=utf-8')->withStatus(400); + } + + $userModel = new User(); + + // 检查用户名是否已存在 + if ($userModel->findByUsername($username)) { + $response->getBody()->write(ApiResponse::json(ApiResponse::fail('用户名已存在'))); + return $response->withHeader('Content-Type', 'application/json; charset=utf-8')->withStatus(400); + } + + try { + $userId = $userModel->create($username, $password, $email); + $user = $userModel->findById($userId); + unset($user['password']); // 不返回密码 + + $response->getBody()->write(ApiResponse::json(ApiResponse::success($user, '注册成功'))); + return $response->withHeader('Content-Type', 'application/json; charset=utf-8'); + } catch (\Exception $e) { + $response->getBody()->write(ApiResponse::json(ApiResponse::error('注册失败: ' . $e->getMessage()))); + return $response->withHeader('Content-Type', 'application/json; charset=utf-8')->withStatus(500); + } + } + + public function login(Request $request, Response $response) + { + $data = $request->getParsedBody(); + $username = $data['username'] ?? ''; + $password = $data['password'] ?? ''; + + if (empty($username) || empty($password)) { + $response->getBody()->write(ApiResponse::json(ApiResponse::fail('用户名和密码不能为空'))); + return $response->withHeader('Content-Type', 'application/json; charset=utf-8')->withStatus(400); + } + + $userModel = new User(); + $user = $userModel->findByUsername($username); + + if (!$user || !$userModel->verifyPassword($password, $user['password'])) { + $response->getBody()->write(ApiResponse::json(ApiResponse::fail('用户名或密码错误'))); + return $response->withHeader('Content-Type', 'application/json; charset=utf-8')->withStatus(401); + } + + // 生成 JWT token + $token = JWTUtil::encode($user['id'], $user['username']); + $expireTime = date('Y-m-d H:i:s', time() + ($_ENV['JWT_EXPIRE'] ?? 86400)); + + // 更新数据库中的 token + $userModel->updateToken($user['id'], $token, $expireTime); + + $response->getBody()->write(ApiResponse::json(ApiResponse::success(['token' => $token], '登录成功'))); + return $response->withHeader('Content-Type', 'application/json; charset=utf-8'); + } + + public function validateToken(Request $request, Response $response) + { + // 如果能到达这里,说明 token 已经通过中间件验证 + $response->getBody()->write(ApiResponse::json(ApiResponse::success(null, 'Token is valid'))); + return $response->withHeader('Content-Type', 'application/json; charset=utf-8'); + } + + public function updatePassword(Request $request, Response $response) + { + $userId = $request->getAttribute('userId'); + $data = $request->getParsedBody(); + $oldPassword = $data['oldPassword'] ?? ''; + $newPassword = $data['newPassword'] ?? ''; + + if (empty($oldPassword) || empty($newPassword)) { + $response->getBody()->write(ApiResponse::json(ApiResponse::fail('旧密码和新密码不能为空'))); + return $response->withHeader('Content-Type', 'application/json; charset=utf-8')->withStatus(400); + } + + $userModel = new User(); + $user = $userModel->findById($userId); + + if (!$userModel->verifyPassword($oldPassword, $user['password'])) { + $response->getBody()->write(ApiResponse::json(ApiResponse::fail('旧密码错误'))); + return $response->withHeader('Content-Type', 'application/json; charset=utf-8')->withStatus(400); + } + + $userModel->updatePassword($userId, $newPassword); + + $response->getBody()->write(ApiResponse::json(ApiResponse::success(null, '密码更新成功'))); + return $response->withHeader('Content-Type', 'application/json; charset=utf-8'); + } + + public function deleteUser(Request $request, Response $response) + { + $userId = $request->getAttribute('userId'); + + $userModel = new User(); + $userModel->delete($userId); + + $response->getBody()->write(ApiResponse::json(ApiResponse::success(null, '用户删除成功'))); + return $response->withHeader('Content-Type', 'application/json; charset=utf-8'); + } +} diff --git a/biji-php/src/Middleware/AuthMiddleware.php b/biji-php/src/Middleware/AuthMiddleware.php new file mode 100644 index 0000000..90cfad5 --- /dev/null +++ b/biji-php/src/Middleware/AuthMiddleware.php @@ -0,0 +1,47 @@ +getHeaderLine('Authorization'); + + if (empty($authHeader)) { + $response = new Response(); + $response->getBody()->write(json_encode([ + 'code' => 401, + 'message' => '未提供认证令牌', + 'data' => null + ])); + return $response->withHeader('Content-Type', 'application/json')->withStatus(401); + } + + // 提取 token (Bearer token) + $token = str_replace('Bearer ', '', $authHeader); + + $decoded = JWTUtil::decode($token); + + if ($decoded === null) { + $response = new Response(); + $response->getBody()->write(json_encode([ + 'code' => 401, + 'message' => '无效或过期的令牌', + 'data' => null + ])); + return $response->withHeader('Content-Type', 'application/json')->withStatus(401); + } + + // 将用户信息添加到请求属性中 + $request = $request->withAttribute('userId', $decoded['userId']); + $request = $request->withAttribute('username', $decoded['username']); + + return $handler->handle($request); + } +} diff --git a/biji-php/src/Middleware/CorsMiddleware.php b/biji-php/src/Middleware/CorsMiddleware.php new file mode 100644 index 0000000..a6c5cd9 --- /dev/null +++ b/biji-php/src/Middleware/CorsMiddleware.php @@ -0,0 +1,23 @@ +handle($request); + + $origin = $_ENV['CORS_ORIGIN'] ?? '*'; + + return $response + ->withHeader('Access-Control-Allow-Origin', $origin) + ->withHeader('Access-Control-Allow-Headers', 'X-Requested-With, Content-Type, Accept, Origin, Authorization') + ->withHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, PATCH, OPTIONS') + ->withHeader('Access-Control-Allow-Credentials', 'true'); + } +} diff --git a/biji-php/src/Models/Grouping.php b/biji-php/src/Models/Grouping.php new file mode 100644 index 0000000..c3b36f4 --- /dev/null +++ b/biji-php/src/Models/Grouping.php @@ -0,0 +1,57 @@ +db = Database::getInstance()->getConnection(); + } + + public function getAll($parentId = null) + { + if ($parentId === null) { + $stmt = $this->db->prepare("SELECT * FROM `grouping` WHERE is_deleted = 0 ORDER BY id"); + } else { + $stmt = $this->db->prepare("SELECT * FROM `grouping` WHERE parentId = ? AND is_deleted = 0 ORDER BY id"); + $stmt->execute([$parentId]); + return $stmt->fetchAll(); + } + $stmt->execute(); + return $stmt->fetchAll(); + } + + public function findById($id) + { + $stmt = $this->db->prepare("SELECT * FROM `grouping` WHERE id = ? AND is_deleted = 0"); + $stmt->execute([$id]); + return $stmt->fetch(); + } + + public function create($grouping, $parentId = 0) + { + $stmt = $this->db->prepare("INSERT INTO `grouping` (`grouping`, parentId) VALUES (?, ?)"); + $stmt->execute([$grouping, $parentId]); + return $this->db->lastInsertId(); + } + + public function update($id, $grouping) + { + $stmt = $this->db->prepare("UPDATE `grouping` SET `grouping` = ? WHERE id = ?"); + return $stmt->execute([$grouping, $id]); + } + + public function softDelete($id, $userId) + { + $stmt = $this->db->prepare( + "UPDATE `grouping` SET is_deleted = 1, deleted_at = NOW(), deleted_by = ? WHERE id = ?" + ); + return $stmt->execute([$userId, $id]); + } +} diff --git a/biji-php/src/Models/MarkdownFile.php b/biji-php/src/Models/MarkdownFile.php new file mode 100644 index 0000000..3cfcca6 --- /dev/null +++ b/biji-php/src/Models/MarkdownFile.php @@ -0,0 +1,122 @@ +db = Database::getInstance()->getConnection(); + } + + public function findById($id, $includeDeleted = false) + { + $sql = "SELECT * FROM markdown_file WHERE id = ?"; + if (!$includeDeleted) { + $sql .= " AND is_deleted = 0"; + } + $stmt = $this->db->prepare($sql); + $stmt->execute([$id]); + return $stmt->fetch(); + } + + public function getAll() + { + $stmt = $this->db->prepare("SELECT * FROM markdown_file WHERE is_deleted = 0 ORDER BY updated_at DESC"); + $stmt->execute(); + return $stmt->fetchAll(); + } + + public function getByGroupingId($groupingId) + { + $stmt = $this->db->prepare( + "SELECT id, title, file_name, created_at, updated_at, is_private, grouping_id + FROM markdown_file + WHERE grouping_id = ? AND is_deleted = 0 + ORDER BY updated_at DESC" + ); + $stmt->execute([$groupingId]); + return $stmt->fetchAll(); + } + + public function searchByTitle($keyword) + { + $stmt = $this->db->prepare( + "SELECT * FROM markdown_file + WHERE title LIKE ? AND is_deleted = 0 + ORDER BY updated_at DESC" + ); + $stmt->execute(['%' . $keyword . '%']); + return $stmt->fetchAll(); + } + + public function getRecent($limit = 12) + { + $stmt = $this->db->prepare( + "SELECT id, title, file_name, created_at, updated_at, is_private, grouping_id + FROM markdown_file + WHERE is_deleted = 0 + ORDER BY updated_at DESC + LIMIT ?" + ); + $stmt->execute([$limit]); + return $stmt->fetchAll(); + } + + public function update($id, $data) + { + $fields = []; + $values = []; + + if (isset($data['title'])) { + $fields[] = "title = ?"; + $values[] = $data['title']; + } + if (isset($data['content'])) { + $fields[] = "content = ?"; + $values[] = $data['content']; + } + if (isset($data['grouping_id'])) { + $fields[] = "grouping_id = ?"; + $values[] = $data['grouping_id']; + } + if (isset($data['is_private'])) { + $fields[] = "is_private = ?"; + $values[] = $data['is_private']; + } + + $fields[] = "updated_at = NOW()"; + $values[] = $id; + + $sql = "UPDATE markdown_file SET " . implode(", ", $fields) . " WHERE id = ?"; + $stmt = $this->db->prepare($sql); + return $stmt->execute($values); + } + + public function softDelete($id, $userId) + { + $stmt = $this->db->prepare( + "UPDATE markdown_file SET is_deleted = 1, deleted_at = NOW(), deleted_by = ? WHERE id = ?" + ); + return $stmt->execute([$userId, $id]); + } + + public function restore($id) + { + $stmt = $this->db->prepare( + "UPDATE markdown_file SET is_deleted = 0, deleted_at = NULL, deleted_by = NULL WHERE id = ?" + ); + return $stmt->execute([$id]); + } + + public function permanentDelete($id) + { + $stmt = $this->db->prepare("DELETE FROM markdown_file WHERE id = ?"); + return $stmt->execute([$id]); + } +} diff --git a/biji-php/src/Models/RegistrationCode.php b/biji-php/src/Models/RegistrationCode.php new file mode 100644 index 0000000..9b84ee1 --- /dev/null +++ b/biji-php/src/Models/RegistrationCode.php @@ -0,0 +1,38 @@ +db = Database::getInstance()->getConnection(); + } + + public function generateCode($createdBy) + { + $code = bin2hex(random_bytes(16)); + $expiryTime = date('Y-m-d H:i:s', strtotime('+7 days')); + + $stmt = $this->db->prepare( + "INSERT INTO registration_codes (code, expiry_time, created_by, created_at) VALUES (?, ?, ?, NOW())" + ); + $stmt->execute([$code, $expiryTime, $createdBy]); + + return $code; + } + + public function validateCode($code) + { + $stmt = $this->db->prepare( + "SELECT * FROM registration_codes WHERE code = ? AND expiry_time > NOW()" + ); + $stmt->execute([$code]); + return $stmt->fetch() !== false; + } +} diff --git a/biji-php/src/Models/SystemSetting.php b/biji-php/src/Models/SystemSetting.php new file mode 100644 index 0000000..137cd43 --- /dev/null +++ b/biji-php/src/Models/SystemSetting.php @@ -0,0 +1,44 @@ +db = Database::getInstance()->getConnection(); + } + + public function getSetting($key) + { + $stmt = $this->db->prepare("SELECT setting_value FROM system_settings WHERE setting_key = ?"); + $stmt->execute([$key]); + $result = $stmt->fetch(); + return $result ? $result['setting_value'] : null; + } + + public function setSetting($key, $value) + { + $stmt = $this->db->prepare( + "INSERT INTO system_settings (setting_key, setting_value) VALUES (?, ?) + ON DUPLICATE KEY UPDATE setting_value = ?" + ); + return $stmt->execute([$key, $value, $value]); + } + + public function isRegistrationEnabled() + { + $value = $this->getSetting('registration_enabled'); + return $value === '1' || $value === 'true'; + } + + public function setRegistrationEnabled($enabled) + { + return $this->setSetting('registration_enabled', $enabled ? '1' : '0'); + } +} diff --git a/biji-php/src/Models/User.php b/biji-php/src/Models/User.php new file mode 100644 index 0000000..82e9834 --- /dev/null +++ b/biji-php/src/Models/User.php @@ -0,0 +1,64 @@ +db = Database::getInstance()->getConnection(); + } + + public function findByUsername($username) + { + $stmt = $this->db->prepare("SELECT * FROM user WHERE username = ?"); + $stmt->execute([$username]); + return $stmt->fetch(); + } + + public function findById($id) + { + $stmt = $this->db->prepare("SELECT * FROM user WHERE id = ?"); + $stmt->execute([$id]); + return $stmt->fetch(); + } + + public function create($username, $password, $email) + { + $hashedPassword = password_hash($password, PASSWORD_BCRYPT); + $stmt = $this->db->prepare( + "INSERT INTO user (username, password, email, created_at, updated_at) VALUES (?, ?, ?, NOW(), NOW())" + ); + $stmt->execute([$username, $hashedPassword, $email]); + return $this->db->lastInsertId(); + } + + public function updateToken($userId, $token, $expireTime) + { + $stmt = $this->db->prepare("UPDATE user SET token = ?, token_enddata = ? WHERE id = ?"); + return $stmt->execute([$token, $expireTime, $userId]); + } + + public function updatePassword($userId, $newPassword) + { + $hashedPassword = password_hash($newPassword, PASSWORD_BCRYPT); + $stmt = $this->db->prepare("UPDATE user SET password = ?, updated_at = NOW() WHERE id = ?"); + return $stmt->execute([$hashedPassword, $userId]); + } + + public function delete($userId) + { + $stmt = $this->db->prepare("DELETE FROM user WHERE id = ?"); + return $stmt->execute([$userId]); + } + + public function verifyPassword($password, $hashedPassword) + { + return password_verify($password, $hashedPassword); + } +} diff --git a/biji-php/src/Utils/Database.php b/biji-php/src/Utils/Database.php new file mode 100644 index 0000000..1988e4d --- /dev/null +++ b/biji-php/src/Utils/Database.php @@ -0,0 +1,49 @@ +connection = new PDO( + $dsn, + $config['username'], + $config['password'], + $config['options'] + ); + } catch (PDOException $e) { + throw new \Exception("数据库连接失败: " . $e->getMessage()); + } + } + + public static function getInstance() + { + if (self::$instance === null) { + self::$instance = new self(); + } + return self::$instance; + } + + public function getConnection() + { + return $this->connection; + } +} diff --git a/biji-php/src/Utils/JWTUtil.php b/biji-php/src/Utils/JWTUtil.php new file mode 100644 index 0000000..5c5b275 --- /dev/null +++ b/biji-php/src/Utils/JWTUtil.php @@ -0,0 +1,45 @@ + 'biji-php', + 'iat' => time(), + 'exp' => time() + self::$expire, + 'userId' => $userId, + 'username' => $username + ]; + + return JWT::encode($payload, self::$secret, 'HS256'); + } + + public static function decode($token) + { + self::init(); + + try { + $decoded = JWT::decode($token, new Key(self::$secret, 'HS256')); + return (array)$decoded; + } catch (\Exception $e) { + return null; + } + } +} diff --git a/biji-php/src/Utils/Response.php b/biji-php/src/Utils/Response.php new file mode 100644 index 0000000..891ad9d --- /dev/null +++ b/biji-php/src/Utils/Response.php @@ -0,0 +1,41 @@ + 200, + 'message' => $message, + 'data' => $data + ]; + } + + public static function fail($message = '操作失败', $code = 400) + { + return [ + 'code' => $code, + 'message' => $message, + 'data' => null + ]; + } + + public static function error($message = '服务器错误', $code = 500) + { + return [ + 'code' => $code, + 'message' => $message, + 'data' => null + ]; + } + + /** + * 将数组转换为 JSON 字符串(中文不转义) + */ + public static function json($data) + { + return json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); + } +} diff --git a/biji-php/uploads/.gitkeep b/biji-php/uploads/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/sql/mysql/biji_db.sql b/sql/mysql/biji_db.sql new file mode 100644 index 0000000..5f05bd3 --- /dev/null +++ b/sql/mysql/biji_db.sql @@ -0,0 +1,172 @@ +/* + Navicat Premium Dump SQL + + Source Server : 日本中转狐帝云4-4MySQL + Source Server Type : MySQL + Source Server Version : 50744 (5.7.44) + Source Host : panel-jp.998521.xyz:37857 + Source Schema : biji_db + + Target Server Type : MySQL + Target Server Version : 50744 (5.7.44) + File Encoding : 65001 + + Date: 08/01/2026 17:53:56 +*/ + +SET NAMES utf8mb4; +SET FOREIGN_KEY_CHECKS = 0; + +-- ---------------------------- +-- Table structure for grouping +-- ---------------------------- +DROP TABLE IF EXISTS `grouping`; +CREATE TABLE `grouping` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `grouping` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `parentId` bigint(11) NULL DEFAULT NULL, + `is_deleted` tinyint(1) NULL DEFAULT 0, + `deleted_at` datetime NULL DEFAULT NULL, + `deleted_by` bigint(20) NULL DEFAULT NULL, + PRIMARY KEY (`id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 399468715122495489 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of grouping +-- ---------------------------- +INSERT INTO `grouping` VALUES (399187436334354432, '测试', 0, 0, NULL, NULL); +INSERT INTO `grouping` VALUES (399217651215372288, '你好', 0, 1, '2026-01-07 07:09:59', NULL); +INSERT INTO `grouping` VALUES (399217655514533888, '你好', 0, 1, '2026-01-07 07:10:01', NULL); +INSERT INTO `grouping` VALUES (399217657649434624, '你好', 0, 1, '2026-01-07 07:20:11', NULL); +INSERT INTO `grouping` VALUES (399461168948842496, '科技', 0, 0, NULL, NULL); +INSERT INTO `grouping` VALUES (399461225492254720, '魔法', 399461168948842496, 0, NULL, NULL); +INSERT INTO `grouping` VALUES (399462299703840768, 'Linux软件', 0, 0, NULL, NULL); +INSERT INTO `grouping` VALUES (399468715122495488, '各种命令', 0, 0, NULL, NULL); + +-- ---------------------------- +-- Table structure for image +-- ---------------------------- +DROP TABLE IF EXISTS `image`; +CREATE TABLE `image` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `markdown_id` bigint(20) NULL DEFAULT NULL, + `original_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `stored_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `url` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `size` bigint(20) NOT NULL, + `content_type` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `created_at` datetime NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `uk_stored_name`(`stored_name`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 26 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of image +-- ---------------------------- + +-- ---------------------------- +-- Table structure for image_name +-- ---------------------------- +DROP TABLE IF EXISTS `image_name`; +CREATE TABLE `image_name` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `markdown_id` bigint(20) NOT NULL, + `file_name` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + PRIMARY KEY (`id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of image_name +-- ---------------------------- + +-- ---------------------------- +-- Table structure for markdown_file +-- ---------------------------- +DROP TABLE IF EXISTS `markdown_file`; +CREATE TABLE `markdown_file` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `grouping_id` bigint(20) NOT NULL DEFAULT 1, + `title` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `file_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `content` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `created_at` datetime NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` datetime NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `is_deleted` tinyint(1) NULL DEFAULT 0, + `deleted_at` datetime NULL DEFAULT NULL, + `deleted_by` bigint(20) NULL DEFAULT NULL, + `is_private` tinyint(1) NULL DEFAULT 0, + PRIMARY KEY (`id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 399812978381295617 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of markdown_file +-- ---------------------------- +INSERT INTO `markdown_file` VALUES (399461629332426752, 399461225492254720, 'v2ray自己的全部节点连接', 'v2ray自己的全部节点连接.md', 'trojan://ikmkj@1.cloudflare.182682.xyz:443?security=tls&sni=jcmei.311169.xyz&fp=randomized&alpn=h3&insecure=0&allowInsecure=0&type=ws&host=jcmei.311169.xyz&path=%2F%3Fproxyip%3D168.138.165.174#tor%E2%80%94%E2%80%94%E7%BE%8E%E5%9B%BD\nvless://91658e42-f7e2-48e5-8d19-f7cbab273cde@www.visa.com.sg:8880?encryption=none&security=none&type=ws&host=nihao888.lol1980299316.workers.dev&path=%2F%3Fed%3D2560#nihao888.lol1980299316.workers.dev\nvless://91658e42-f7e2-48e5-8d19-f7cbab273cde@www.visa.com.sg:8443?encryption=none&security=tls&sni=mei.311169.xyz&fp=random&insecure=0&allowInsecure=0&type=ws&host=mei.311169.xyz&path=%2F%3Fed%3D2560#mei.311169.xyz\nvless://e613109b-6b91-4703-a11a-1c648f305678@ukv6.311169.xyz:8443?encryption=none&security=tls&fp=chrome&alpn=h3%2Ch2%2Chttp%2F1.1&insecure=0&allowInsecure=0&type=ws&path=%2Fe613109b#v6-uk-vless-c7qtqypo\ntrojan://ikmkj@1.cloudflare.182682.xyz:443?security=tls&sni=jcmei.311169.xyz&fp=randomized&alpn=h3&insecure=1&allowInsecure=1&type=ws&host=jcmei.311169.xyz&path=%2F%3Fproxyip%3D168.138.165.174#tor%E2%80%94%E2%80%94%E7%BE%8E%E5%9B%BD\nvless://12915e0e-e144-4cda-a6e7-9cb281e14358@8.163.48.245:5004?encryption=none&security=none&type=tcp&headerType=none#%E9%98%BF%E9%87%8C%E5%B9%BF%E5%B7%9E-IXA2-%E6%97%A5%E6%9C%AC%E8%90%BD%E5%9C%B0\nvless://12915e0e-e144-4cda-a6e7-9cb281e14358@119.23.79.195:17843?encryption=none&security=none&type=tcp&headerType=none#LXC%E6%B7%B1%E5%9C%B3-%E9%98%BF%E9%87%8C%E5%B9%BF%E5%B7%9E-IXA2-%E6%97%A5%E6%9C%AC%E8%90%BD%E5%9C%B0\nvless://3ac79030-a996-4172-bd6b-e66e44e7d014@151.242.85.19:46134?encryption=none&security=reality&sni=yahoo.com&fp=chrome&pbk=hZ1ZeNMNYN30Go81VZ5EbeBucHjDXmekqxIROYnY3ws&sid=1f08427ed7fd4048&spx=%2F&type=tcp&headerType=none#LXC%E9%A6%99%E6%B8%AF\nss://MjAyMi1ibGFrZTMtYWVzLTI1Ni1nY206Y1IySkJNUGlod0xCV1hwWFRteDVLRXpqeHVyZlllUWduclhHNFYzaTNWRT06U0pHYzJOOVFINFVDekJzcUcxWlBvbFBIS2VMSVVZSGZ0U09vUjUrOTZqbz0@119.23.79.195:15249?#LXC%E6%B7%B1%E5%9C%B3-%E6%B5%81%E9%87%8F%E4%B8%AD%E8%BD%AC%E6%97%A5%E6%9C%AC-LXC%E9%A6%99%E6%B8%AFss\nss://MjAyMi1ibGFrZTMtYWVzLTI1Ni1nY206Y1IySkJNUGlod0xCV1hwWFRteDVLRXpqeHVyZlllUWduclhHNFYzaTNWRT06U0pHYzJOOVFINFVDekJzcUcxWlBvbFBIS2VMSVVZSGZ0U09vUjUrOTZqbz0@panel-jp.998521.xyz:39074?#%E6%B5%81%E9%87%8F%E4%B8%AD%E8%BD%AC-%E6%97%A5%E6%9C%AC-LXC%E9%A6%99%E6%B8%AFss\nvless://783c96b4-43c8-4e33-af6e-5629a7d5c33e@103.189.141.119:15565?encryption=mlkem768x25519plus.native.0rtt.HK3E6afKcTURqiOAWUlL1Uq2_zJ4TS5xgsLdBBrS8F4&security=reality&sni=www.icloud.com&fp=chrome&pbk=mk5o206PNEsv3og1pMY4B5vJOIbqaNqoBMWLcu1-zF0&sid=17a7cf7ac73104&spx=%2F&pqv=9ZKPgxeXWLj8Pcl4nF4PA0wvhQFfpL7TgmOxJ6veD1m4rkQX_TnVYnJb3U-F-My_s18KRUpCHAjC27GZxgSfC0tRuO69np-o2Ah091sRolp4jyel9BA8_eVaA-P-ORCSdJy_lUWzqNc2uWm3KTARWmujkuWeuwzghrQy4PF_HPECcB6YV8f3RnJKnHdOAVfV6scbt55xEJBmff1w5-H7LJrh5qMjTnl8yM7SsqHGrHgaC9IwiAxUP3-8cr3yc9VzvbbEZPJjQMlitQz_LlcXiT_KgBWPQB2Wcy2zDK7mFnkoxnPvizThlm3hi1888-eFgLaEmS8itvc98Z1c5TYwgjoOY6nzL6eXiVMJrwEI7SUY8vQ1gxIxL8WNOy6bDRNQ2tfdsItfxQol_2_UFwy6mr9JeVFSDFXDKMjbE9lfZPPRPxMxR2Y8FVsHg2v4RQaMv0K57aY8a5Zlo2R7kWG41QpavscAoxLmL2vdvINk4FsPVinvhKiQL8snT_zz4DhNhOPYft4b53Farr270uZRBaN6YztyJVF1L9HQyDB7-01CBT0YkSg4bQgx9QaVV5ZnYgnrSDgm8p1_1OX_D4p6spRog-CrcoHGAaoo3BzLuEBJa-NiIVNPBL-Ow0apSWH08XYjwZDEWGPDIUZpqPfapQCBRjs9aHpOUIlvCHl1DrtdJyFZfRWv7dHnCTtiAJ4rJoA_D2ZCuGyVtNSBbqGDugGOJ0kW0uxk0UJAjf-VAJSAdhMCBDY4Xw2733U4r6zzR37cT6jro7dl-glxpb8ro8fdrGNUl8OjhWyh0qcBMMS4XP44sTX6374uFdAjXmhQMPOUnJRIxiffZWtpAV4k-j22SV5coaJ5DikHAe86i0tNfyapj3Q80q1Z_CfTBxtnO_8ORuwqSybYiFZr0DyFq2JUQzU23XI8bxzsNwlA6Se75oNJdjkaSHv2ynW83F-yuy79sUR4f0GUZUM_Cs-Sy-bocH_zPc4aBt2MMrbKS4vpATfl6SGCU1jIofOOAvFtGXR8_jjVDwqNEFLSDbOt-f5t0eiXiOg-yibImd5Eoy5Zw1z0OmZUdL0-lw3r9aMgWiBIbRaadhhFrzcMUZ_4Rmr_NtilrccQK9PKfXo7r18umlrowc_j82lZA9Lo4S1xW3kgzYBKw2n8LUtWpzwlY3KQTERGwJT-ETDZO2YIuww1El3OMM5YCTv2cg9uBEtsez5rC0ApranaIXWNYTvqqQOhkP9Nugf7yCcY_qsB1SOov4pnRfZV0y895TPevb-pTChjuaFAdP2heKveB-Us7J0Jv_nLECWaa8MbIrU4cbwpvf6Tj1txYfZLAlwre8wr0EXqGTrzok-0P61d2sz8evVhszf4YmAI0fQFdPXVBhAhcb8H4c00-BbfpZGoIdSH8tn-tj95SHrUseGOSpOL8mrT94mbYEfRCCbx0-13DgpA9Eu3qmpiXocdM-O9C_SvNWy6d6Yf6Zgk_P5bFwnbSGygtZZj8hBX-_Z45Mw34JzC3qX81t0NdCutJALdH0Q_mZb4FZoRuhU5-NfASfgMgPNo90Hq0eMnVEStRWPq39HqQO4nZSD1MdM5GPkZ8fT_zUAQ5Bsx5o7YubL4pA9MzL0EWNgXQ-7cykrXBmmgZv6zwsQY5-N4keLLR9iTXDV42p3IoW26oFd8kkzaEKfXjYoMW5rXXCOcKyJuGEJ8oRFsy4_zf4qZfm_edsjcJrunEhTFZ0tpb-U5_j-AdZ4eVYm-d20C3nexb1WBwt3s01c837yyCnkcCPCtBJ-G3ONPx7Vtt0txSViACuCmXwFrLlGG_arWj3D2jasY6pe1KjC2LyhVi5Li8YWP4ANFIKZLLegzK1QAXMoYXX4pY2McC6gaaL8wkJuht1CRzR9vm7qsxGFlvNSonpgoPjMLBbyjGNQ7BAxJqsEdfprj8qnbXgTCTSZQShirC73-AhLMjUOvqy_KS_vwVnb1cYJ4E4_R_wes9f1ZcUDWi1gMpQRm2EQ-OvWL3T_V7jBMaQckNbosD9IU4qIU40dwTE6GVNeJPS83Pl596-AhUPWuH1tsgCH6WDI_2drXxhiSNr8IOO8FoAJQC8gqp6qNfR1wEAWd_dQ3HWZ-zIhQyswz5juRujE9MnH8FbzN9WxaDBF4os--WUveUcV5UiwRvLVyqHc0Q5_uZc5DeiMZXTLGm5Iz4fccc4HzpkoNocbOT1taurUo0lc6r9cCMZ9Otn3F6o29usxAdY_zp0NVkcCidNiMX8ufVYf0r1hRd8oig6brUHoIPg9idOPQCuXPGktt1gmXCL-bGLtTC2H1QsQ-RzlsZQutRZkqFUiXtk3tQE85BmW2PQJ4xqmPuG7VsmsTo3TF8NdjLAkaRMTbD4P0B5BM9Llj0xwI2xuFWj1a2DwYBww5-of-Erxmim6m0HXqYy7cDfaulJXcZC40y69HhEUXMJZ8i9vfzQ5xbvO-WS3lmwjSH43IFQZv_NUcE6kQp82-bmjjSqsfMWEfoFMIgWJp8MaGRvKBavyk8ZDCIdiXRuTsPVmy1EDjdMhzsPeCGZUaKqBAvGwyFY_9gIFL3ZwDbGjuN5uua8ht0AyIamiYp1I&type=xhttp&path=%2Fmlkem768&mode=auto#%E6%9E%97%E6%9F%9A%E4%BA%91%E9%A6%99%E6%B8%AF\nvless://783c96b4-43c8-4e33-af6e-5629a7d5c33e@119.23.79.195:39957?encryption=mlkem768x25519plus.native.0rtt.HK3E6afKcTURqiOAWUlL1Uq2_zJ4TS5xgsLdBBrS8F4&security=reality&sni=www.icloud.com&fp=chrome&pbk=mk5o206PNEsv3og1pMY4B5vJOIbqaNqoBMWLcu1-zF0&sid=17a7cf7ac73104&spx=%2F&pqv=9ZKPgxeXWLj8Pcl4nF4PA0wvhQFfpL7TgmOxJ6veD1m4rkQX_TnVYnJb3U-F-My_s18KRUpCHAjC27GZxgSfC0tRuO69np-o2Ah091sRolp4jyel9BA8_eVaA-P-ORCSdJy_lUWzqNc2uWm3KTARWmujkuWeuwzghrQy4PF_HPECcB6YV8f3RnJKnHdOAVfV6scbt55xEJBmff1w5-H7LJrh5qMjTnl8yM7SsqHGrHgaC9IwiAxUP3-8cr3yc9VzvbbEZPJjQMlitQz_LlcXiT_KgBWPQB2Wcy2zDK7mFnkoxnPvizThlm3hi1888-eFgLaEmS8itvc98Z1c5TYwgjoOY6nzL6eXiVMJrwEI7SUY8vQ1gxIxL8WNOy6bDRNQ2tfdsItfxQol_2_UFwy6mr9JeVFSDFXDKMjbE9lfZPPRPxMxR2Y8FVsHg2v4RQaMv0K57aY8a5Zlo2R7kWG41QpavscAoxLmL2vdvINk4FsPVinvhKiQL8snT_zz4DhNhOPYft4b53Farr270uZRBaN6YztyJVF1L9HQyDB7-01CBT0YkSg4bQgx9QaVV5ZnYgnrSDgm8p1_1OX_D4p6spRog-CrcoHGAaoo3BzLuEBJa-NiIVNPBL-Ow0apSWH08XYjwZDEWGPDIUZpqPfapQCBRjs9aHpOUIlvCHl1DrtdJyFZfRWv7dHnCTtiAJ4rJoA_D2ZCuGyVtNSBbqGDugGOJ0kW0uxk0UJAjf-VAJSAdhMCBDY4Xw2733U4r6zzR37cT6jro7dl-glxpb8ro8fdrGNUl8OjhWyh0qcBMMS4XP44sTX6374uFdAjXmhQMPOUnJRIxiffZWtpAV4k-j22SV5coaJ5DikHAe86i0tNfyapj3Q80q1Z_CfTBxtnO_8ORuwqSybYiFZr0DyFq2JUQzU23XI8bxzsNwlA6Se75oNJdjkaSHv2ynW83F-yuy79sUR4f0GUZUM_Cs-Sy-bocH_zPc4aBt2MMrbKS4vpATfl6SGCU1jIofOOAvFtGXR8_jjVDwqNEFLSDbOt-f5t0eiXiOg-yibImd5Eoy5Zw1z0OmZUdL0-lw3r9aMgWiBIbRaadhhFrzcMUZ_4Rmr_NtilrccQK9PKfXo7r18umlrowc_j82lZA9Lo4S1xW3kgzYBKw2n8LUtWpzwlY3KQTERGwJT-ETDZO2YIuww1El3OMM5YCTv2cg9uBEtsez5rC0ApranaIXWNYTvqqQOhkP9Nugf7yCcY_qsB1SOov4pnRfZV0y895TPevb-pTChjuaFAdP2heKveB-Us7J0Jv_nLECWaa8MbIrU4cbwpvf6Tj1txYfZLAlwre8wr0EXqGTrzok-0P61d2sz8evVhszf4YmAI0fQFdPXVBhAhcb8H4c00-BbfpZGoIdSH8tn-tj95SHrUseGOSpOL8mrT94mbYEfRCCbx0-13DgpA9Eu3qmpiXocdM-O9C_SvNWy6d6Yf6Zgk_P5bFwnbSGygtZZj8hBX-_Z45Mw34JzC3qX81t0NdCutJALdH0Q_mZb4FZoRuhU5-NfASfgMgPNo90Hq0eMnVEStRWPq39HqQO4nZSD1MdM5GPkZ8fT_zUAQ5Bsx5o7YubL4pA9MzL0EWNgXQ-7cykrXBmmgZv6zwsQY5-N4keLLR9iTXDV42p3IoW26oFd8kkzaEKfXjYoMW5rXXCOcKyJuGEJ8oRFsy4_zf4qZfm_edsjcJrunEhTFZ0tpb-U5_j-AdZ4eVYm-d20C3nexb1WBwt3s01c837yyCnkcCPCtBJ-G3ONPx7Vtt0txSViACuCmXwFrLlGG_arWj3D2jasY6pe1KjC2LyhVi5Li8YWP4ANFIKZLLegzK1QAXMoYXX4pY2McC6gaaL8wkJuht1CRzR9vm7qsxGFlvNSonpgoPjMLBbyjGNQ7BAxJqsEdfprj8qnbXgTCTSZQShirC73-AhLMjUOvqy_KS_vwVnb1cYJ4E4_R_wes9f1ZcUDWi1gMpQRm2EQ-OvWL3T_V7jBMaQckNbosD9IU4qIU40dwTE6GVNeJPS83Pl596-AhUPWuH1tsgCH6WDI_2drXxhiSNr8IOO8FoAJQC8gqp6qNfR1wEAWd_dQ3HWZ-zIhQyswz5juRujE9MnH8FbzN9WxaDBF4os--WUveUcV5UiwRvLVyqHc0Q5_uZc5DeiMZXTLGm5Iz4fccc4HzpkoNocbOT1taurUo0lc6r9cCMZ9Otn3F6o29usxAdY_zp0NVkcCidNiMX8ufVYf0r1hRd8oig6brUHoIPg9idOPQCuXPGktt1gmXCL-bGLtTC2H1QsQ-RzlsZQutRZkqFUiXtk3tQE85BmW2PQJ4xqmPuG7VsmsTo3TF8NdjLAkaRMTbD4P0B5BM9Llj0xwI2xuFWj1a2DwYBww5-of-Erxmim6m0HXqYy7cDfaulJXcZC40y69HhEUXMJZ8i9vfzQ5xbvO-WS3lmwjSH43IFQZv_NUcE6kQp82-bmjjSqsfMWEfoFMIgWJp8MaGRvKBavyk8ZDCIdiXRuTsPVmy1EDjdMhzsPeCGZUaKqBAvGwyFY_9gIFL3ZwDbGjuN5uua8ht0AyIamiYp1I&type=xhttp&path=%2Fmlkem768&mode=auto#%E6%B7%B1%E5%9C%B3LXC-%E6%9E%97%E6%9F%9A%E4%BA%91%E9%A6%99%E6%B8%AF\nvless://63c3e73a-f54a-4978-b7d9-8579adf34988@panel-us-cn2.998521.xyz:17154?encryption=mlkem768x25519plus.native.0rtt.iagOuteMjLYYhMLJRFWLUrZdKUBl1rNjgNit7EkT2gGgPWROxFpnkcag4IyZb_dKi9Rl9LKYoZAbPbuG8cmjMlA3G7eqW5emzGQgQ8OBq4tWQFWVWadhityWgEuaw0yPPMAGs_FYonS6m3i-KGRQNeNtO2aUM6pNYqO_QbozFdoeZkab0oSiYSUR4WFBpeWBc9lVWosryKFaI-Er7OS2FgYQaYTKqvYqb0bIhOqC3lARVqRse3YclheyzZJLFNWmP9eAmwNf-KQUzik-fqEGV1e20bmb7SEzGQZs6KkbJrxej6Y48fxBuRAS89myDAlw_PVEQRNH-rzOadtnBelf_HGq9fZjzosmhyawgGHE3Vk2q_U-1wIXEeUsvNQFXTaCcAi5SZdgFGtPCFcfoop-tvN6OWRMOXdw_FaEspcVgUo9z5FPM4dmdRZxP_R_DkG0j5ZTJWNNZfjF0ICdKohFPppJ4YCzHCq6YySt4bOHuFEdg4mpylIRX8Us97JrOrw_90tQF4st6jlphiZ5KlgF70BghlkMAZWDCnF3OWTA6JBuwSe1Slsb8tCa3cYovhwEWiMhIktaMkASsIUjVJMxmAp9pxQo7ogcIPq9XBYZ7QCGiSzAQjKzWeU6RMirEsqZurkpbZE9LoiAAInMdvds0fSGVYl3-7NnhDy92pp35lOU4oIXkNckMBZRBVsDkLuB--ADocZiSSRKayShkICICDVGb-CNcxmDcHnBH5zNzgrQlReEqbYy4IGt0mF2x1OB80J5gwvLf4siPugfcmGlSDIlIqu_X8IbAYJ30igzw5aPVUo4kpwzzhQSobSFBnOAaNsa0CVtzHasICltFne2melutzOpqLOomfiGXitCHcGi5jilHnM0t0Mc2lCrkeofvewUZFg_vDGy-QQc2ue57lpVFqRpXxt02Io2zFWRzhgp9pAlJTcUe4Y-5GIX0hm-cDITCZiBdbWdkyiT_Oyrf2l5MYQKGjcF7FQKh1ZF1gSonOklGpu60tU4pOljqYRLoGtz8AR7hcKQgStPptIPXAa0CkAKs7TKemXB9Gdfm2V_AVhWbjcW8uqX-SWLTWKxHStNlLkJ18IrvnMfVEBk9pa3COZCtKuiHOKzwBIo1sx2W9rIU5yl7YrEz3tAh1IRumi2-bBcL-MFkESxkVxCvAaZAWWzbTxQ5Uoywmi1LuQUjBVjHtoud1EhwAwjLfnJi5qrpVQHI4UrBnp8mpwwCkyl-ti-_sRIbwcZH-J2wSpoRHoXLQYrGSg5N7xum7WqT_JQa5Smhwys4jprOAOLP2BbK_dACFnAxqgykIQ28CMX4HjAPXvGtVR-YRQrFgGPu-tzh6A-6ZDIlxpH-JubfdBEwWF0cRyQjEUzKeyxWdxLIxaYTvEsBGugNwZPGtFfw_uEtyOxPJLPRqfFblpmJOe9B2i2jFtbJoO-iDlCAQCVJWg4DUgn91avTOM3zWwHGmW5IflVtxmZpUl5TilMWLCQPJKswMZd4neHVlqzYNVgzWQ_yutOtMOAUzOKePd6fZtssOsOk0KUz26J6EuK3fFgw_8i-TZEB7X65DXGsOg&security=reality&sni=www.oracle.com&fp=chrome&pbk=K2HdlaIKpLeP7bV1BWJktk2OvbGbXpUKEIBLvpKAo10&sid=045a002d21&spx=%2F&pqv=VGFSAZ_u0ensou9dfXRaTUiPugqDXI83xELepQtraXPglG5Vkj3RyqcyaizSvfrsSFUyY8tiqwElk-0XblPPKS44RHt1vgHzeB1Z5hq4MaMC6xjUvvpSi-HwmheQIdj3dd-frvblVYhRzuc-hZtiQZczhkuLlMW1dL-62IjNd30jrbzNxBk4KI0yWwmgpnNCGnXhu7rH6B8ED5zS-pUyGw3LS1Ro1aEdMRosdEqITAoI2M_MK89PcSIKfoHrmLSXy8YO89X7GzHSo4iE7G-iZUvuXCswCmn2l8rz8_kT-7AASbJ50_XZnq83YMVdVinfbcNpnzi59tZ6QE9pYUswppULtDqqYBxxeTyXjkKbRJcoQjuDsFp8SQImZw1On6_11p77LwmTlwUWmtk38uKkpJ2kU6sPzubWEX-I7IWjZd45idoPGPOXhmsWGeOJe4GFUL92ZT9cpW_uCSPmdgiSxiN5RKUXHcw2jq7rRrMsgwwZA6RxkTDArkld6MYoBYfi7Ozc2rX0QHPmHNYZWoDW6UR7XmFFLYj5maGb_AO6P0XUlw54leDHoQfyu9NKKDNBcgsMz8oCgCMgiEdlTFDo38QOloAf1QfBFw-YhQxCsEEZOWPEL-K9fw7Y742vd1RZVG2eIyuNYKvdFh3uI5v_h_LQ0vZpAJLCtd9bPS6L2RSKZ1I8zQFOL4DWrZw7_DZz0H3NVVR52kYYGeV6Dh4Q3yecgslS65WHcxAYgWy1gZD7qQM-7p5D_2Ki70haIrpBihRlG0J4fDAj-FzN-IwilYlFdBk22jT4ZhM1v9-CeUkeVrMzDbqIyeC0T0mQnKpnmnenEDwcPm0coxuTeS6NKRv_XmPKxtkiYisJiO6ESqyylEbn8lXjuLr7ArRrAeY5iZ96z4VZ1ZPh6Cl0-YgfavA9EZqjraDId0sqeqCyCth1kW4adh6kWskGu9kjccqs44y1G099rkt5UaNfHFQM4sk-gdqYRnBt-uc_vTXALLWiFB4hzrc6vLUNxsswmPsHkY8lRLRtBz1S9PmUfuQj2LZZFky4UAjOUbwSeXYPw2_zhUcbcExWs0BEUimfzeq3-KBvJvkLSarr_UEy-Plf7hm5ADObFL4EAfb3UCMg2LA4DX-WAyd_6VYyhF1ATwGzLDg-Qpx5DZBcCtTz-veL0b3RWbHc9el6I4sy_i-tAE7kypViUXvldJGILFIrRR6p22PJgs4ULOrYEV0C7nQ7JjWsMEYK6PJssRkjuToIBnUYSvY48dWleFN1lpSuCCDZTCqlT3NjLHSP1xNcPUkdsOqG43WbUB-b32ofnug56QsLLrYosw3rxCg3APbYrTmOrzX6R4bZwdWiHwWQWtMk-_U81eAYA3eTykKym3stDk4ue3HkRNJ8PvjQQMWImWrYGbI7gmX1B7yZbAkg6kF7RjgSXx47sQqz8dg4RJWrc6vI6_1XCFZ0nU7XxKIbMWoKdktu5FGFJQkIv9bPUReyrvcWevFLIMXevDGXLeNlw48u9QeY619ra_D07Q_BVom07ea1YZ8VFajXyfoQj2jE3Iq3Vsz2ZAmclriNIzh54OR6MoYIBIAr5cA56Af9BIigonpSg4ec12oyNBH9O3WDnpOK2jNuwRC4y2tpX27BnfEaMXCjVswzJbjvIS1QkvUMgWDwY5uuZeAFiPFXuyKJ941rdJ9Zo88GE1_iThmHJPdjwSRKl6JaSzIY80tnUQtCowvh7CQgnKMV61-NbBdnzSBDCmmvdlHvUdW2FSPQbl_jn7BwfDnW58XOppUUESpAEf9NULvdXPrq2rNTmgMY9qYrHNHrE0MjoXRJL_C_rJKRgkU_A8FkvF9PfLWfPHGtVpMHszXpgex8IKwUTE6zGQ7iHimVqLWkP1N7LMgZjavqL8Jn8rvP-RutQURe2iAU9MDxn5bDWg1beZEePEhtzncnEQMqLhT2mQTlCWuPErKbvYLbHKA677GajO2s4Y7ur2VbWXrm2Kjvxr-Jz2RGAtjJXqpW2k_8n7w2xj-jv06eROaakFokg53d23VwY3odTPm1P3-MFc1IvBkVLaXfIry3i2Pd5xJ9ugG9JnaHEDJXtU6WFnSqZuorRK1exMckOlsXevY69hAfV0TS58WF5F4Pp_60G9dQfH-iu98htr1H7sEWsKSNusjUQ4_Qar_JOON1992nCwC1Ui_9cuwgH6FlSGNl03OptCiGrLR9Yd4ih4KSFD4Ye4rnjymgE8symxDDY893WW0ma9A_IorUrsPHK3tv45BkJeNRqyNYUAaRHogxRxkX9gR_V5yYpyFpbv9Y-E3by3DzN4g7rpY7sldAHSaQ_UlnQsNxzFC3-gr1c8C5RXQ0I_cOTv03uiMOtfnsMTQYD66Ml-0BDLLDT7HgASQd2_trlyenAFNepNayPdWtKpFu3D8NaQrqoABN3t8fwHWVkl3-HJF4LetnlAdWmnJVUaMEWWgsMNMxZEVGkl0w_5VzHpDiUAuaHM1SjpsSoRJST-RiTy2bsScRJgcFh2EajWg66qZYmlzZ5sNQY1p6fIw_rmJhLIBgow9t3HG1Gzq3f3O5dS3ZFmsFRAhJhiPDPYjqsGi8FW9stHQ&type=xhttp&path=%2Fmlkem76&mode=auto#%E6%B5%81%E9%87%8F%E9%9D%A2%E6%9D%BFUS-%E7%8B%90%E5%B8%9D%E4%BA%91%E7%BE%8E%E5%9B%BD4-4\nvless://63c3e73a-f54a-4978-b7d9-8579adf34988@38.55.178.226:43640?encryption=mlkem768x25519plus.native.0rtt.iagOuteMjLYYhMLJRFWLUrZdKUBl1rNjgNit7EkT2gGgPWROxFpnkcag4IyZb_dKi9Rl9LKYoZAbPbuG8cmjMlA3G7eqW5emzGQgQ8OBq4tWQFWVWadhityWgEuaw0yPPMAGs_FYonS6m3i-KGRQNeNtO2aUM6pNYqO_QbozFdoeZkab0oSiYSUR4WFBpeWBc9lVWosryKFaI-Er7OS2FgYQaYTKqvYqb0bIhOqC3lARVqRse3YclheyzZJLFNWmP9eAmwNf-KQUzik-fqEGV1e20bmb7SEzGQZs6KkbJrxej6Y48fxBuRAS89myDAlw_PVEQRNH-rzOadtnBelf_HGq9fZjzosmhyawgGHE3Vk2q_U-1wIXEeUsvNQFXTaCcAi5SZdgFGtPCFcfoop-tvN6OWRMOXdw_FaEspcVgUo9z5FPM4dmdRZxP_R_DkG0j5ZTJWNNZfjF0ICdKohFPppJ4YCzHCq6YySt4bOHuFEdg4mpylIRX8Us97JrOrw_90tQF4st6jlphiZ5KlgF70BghlkMAZWDCnF3OWTA6JBuwSe1Slsb8tCa3cYovhwEWiMhIktaMkASsIUjVJMxmAp9pxQo7ogcIPq9XBYZ7QCGiSzAQjKzWeU6RMirEsqZurkpbZE9LoiAAInMdvds0fSGVYl3-7NnhDy92pp35lOU4oIXkNckMBZRBVsDkLuB--ADocZiSSRKayShkICICDVGb-CNcxmDcHnBH5zNzgrQlReEqbYy4IGt0mF2x1OB80J5gwvLf4siPugfcmGlSDIlIqu_X8IbAYJ30igzw5aPVUo4kpwzzhQSobSFBnOAaNsa0CVtzHasICltFne2melutzOpqLOomfiGXitCHcGi5jilHnM0t0Mc2lCrkeofvewUZFg_vDGy-QQc2ue57lpVFqRpXxt02Io2zFWRzhgp9pAlJTcUe4Y-5GIX0hm-cDITCZiBdbWdkyiT_Oyrf2l5MYQKGjcF7FQKh1ZF1gSonOklGpu60tU4pOljqYRLoGtz8AR7hcKQgStPptIPXAa0CkAKs7TKemXB9Gdfm2V_AVhWbjcW8uqX-SWLTWKxHStNlLkJ18IrvnMfVEBk9pa3COZCtKuiHOKzwBIo1sx2W9rIU5yl7YrEz3tAh1IRumi2-bBcL-MFkESxkVxCvAaZAWWzbTxQ5Uoywmi1LuQUjBVjHtoud1EhwAwjLfnJi5qrpVQHI4UrBnp8mpwwCkyl-ti-_sRIbwcZH-J2wSpoRHoXLQYrGSg5N7xum7WqT_JQa5Smhwys4jprOAOLP2BbK_dACFnAxqgykIQ28CMX4HjAPXvGtVR-YRQrFgGPu-tzh6A-6ZDIlxpH-JubfdBEwWF0cRyQjEUzKeyxWdxLIxaYTvEsBGugNwZPGtFfw_uEtyOxPJLPRqfFblpmJOe9B2i2jFtbJoO-iDlCAQCVJWg4DUgn91avTOM3zWwHGmW5IflVtxmZpUl5TilMWLCQPJKswMZd4neHVlqzYNVgzWQ_yutOtMOAUzOKePd6fZtssOsOk0KUz26J6EuK3fFgw_8i-TZEB7X65DXGsOg&security=reality&sni=www.oracle.com&fp=chrome&pbk=K2HdlaIKpLeP7bV1BWJktk2OvbGbXpUKEIBLvpKAo10&sid=045a002d21&spx=%2F&pqv=VGFSAZ_u0ensou9dfXRaTUiPugqDXI83xELepQtraXPglG5Vkj3RyqcyaizSvfrsSFUyY8tiqwElk-0XblPPKS44RHt1vgHzeB1Z5hq4MaMC6xjUvvpSi-HwmheQIdj3dd-frvblVYhRzuc-hZtiQZczhkuLlMW1dL-62IjNd30jrbzNxBk4KI0yWwmgpnNCGnXhu7rH6B8ED5zS-pUyGw3LS1Ro1aEdMRosdEqITAoI2M_MK89PcSIKfoHrmLSXy8YO89X7GzHSo4iE7G-iZUvuXCswCmn2l8rz8_kT-7AASbJ50_XZnq83YMVdVinfbcNpnzi59tZ6QE9pYUswppULtDqqYBxxeTyXjkKbRJcoQjuDsFp8SQImZw1On6_11p77LwmTlwUWmtk38uKkpJ2kU6sPzubWEX-I7IWjZd45idoPGPOXhmsWGeOJe4GFUL92ZT9cpW_uCSPmdgiSxiN5RKUXHcw2jq7rRrMsgwwZA6RxkTDArkld6MYoBYfi7Ozc2rX0QHPmHNYZWoDW6UR7XmFFLYj5maGb_AO6P0XUlw54leDHoQfyu9NKKDNBcgsMz8oCgCMgiEdlTFDo38QOloAf1QfBFw-YhQxCsEEZOWPEL-K9fw7Y742vd1RZVG2eIyuNYKvdFh3uI5v_h_LQ0vZpAJLCtd9bPS6L2RSKZ1I8zQFOL4DWrZw7_DZz0H3NVVR52kYYGeV6Dh4Q3yecgslS65WHcxAYgWy1gZD7qQM-7p5D_2Ki70haIrpBihRlG0J4fDAj-FzN-IwilYlFdBk22jT4ZhM1v9-CeUkeVrMzDbqIyeC0T0mQnKpnmnenEDwcPm0coxuTeS6NKRv_XmPKxtkiYisJiO6ESqyylEbn8lXjuLr7ArRrAeY5iZ96z4VZ1ZPh6Cl0-YgfavA9EZqjraDId0sqeqCyCth1kW4adh6kWskGu9kjccqs44y1G099rkt5UaNfHFQM4sk-gdqYRnBt-uc_vTXALLWiFB4hzrc6vLUNxsswmPsHkY8lRLRtBz1S9PmUfuQj2LZZFky4UAjOUbwSeXYPw2_zhUcbcExWs0BEUimfzeq3-KBvJvkLSarr_UEy-Plf7hm5ADObFL4EAfb3UCMg2LA4DX-WAyd_6VYyhF1ATwGzLDg-Qpx5DZBcCtTz-veL0b3RWbHc9el6I4sy_i-tAE7kypViUXvldJGILFIrRR6p22PJgs4ULOrYEV0C7nQ7JjWsMEYK6PJssRkjuToIBnUYSvY48dWleFN1lpSuCCDZTCqlT3NjLHSP1xNcPUkdsOqG43WbUB-b32ofnug56QsLLrYosw3rxCg3APbYrTmOrzX6R4bZwdWiHwWQWtMk-_U81eAYA3eTykKym3stDk4ue3HkRNJ8PvjQQMWImWrYGbI7gmX1B7yZbAkg6kF7RjgSXx47sQqz8dg4RJWrc6vI6_1XCFZ0nU7XxKIbMWoKdktu5FGFJQkIv9bPUReyrvcWevFLIMXevDGXLeNlw48u9QeY619ra_D07Q_BVom07ea1YZ8VFajXyfoQj2jE3Iq3Vsz2ZAmclriNIzh54OR6MoYIBIAr5cA56Af9BIigonpSg4ec12oyNBH9O3WDnpOK2jNuwRC4y2tpX27BnfEaMXCjVswzJbjvIS1QkvUMgWDwY5uuZeAFiPFXuyKJ941rdJ9Zo88GE1_iThmHJPdjwSRKl6JaSzIY80tnUQtCowvh7CQgnKMV61-NbBdnzSBDCmmvdlHvUdW2FSPQbl_jn7BwfDnW58XOppUUESpAEf9NULvdXPrq2rNTmgMY9qYrHNHrE0MjoXRJL_C_rJKRgkU_A8FkvF9PfLWfPHGtVpMHszXpgex8IKwUTE6zGQ7iHimVqLWkP1N7LMgZjavqL8Jn8rvP-RutQURe2iAU9MDxn5bDWg1beZEePEhtzncnEQMqLhT2mQTlCWuPErKbvYLbHKA677GajO2s4Y7ur2VbWXrm2Kjvxr-Jz2RGAtjJXqpW2k_8n7w2xj-jv06eROaakFokg53d23VwY3odTPm1P3-MFc1IvBkVLaXfIry3i2Pd5xJ9ugG9JnaHEDJXtU6WFnSqZuorRK1exMckOlsXevY69hAfV0TS58WF5F4Pp_60G9dQfH-iu98htr1H7sEWsKSNusjUQ4_Qar_JOON1992nCwC1Ui_9cuwgH6FlSGNl03OptCiGrLR9Yd4ih4KSFD4Ye4rnjymgE8symxDDY893WW0ma9A_IorUrsPHK3tv45BkJeNRqyNYUAaRHogxRxkX9gR_V5yYpyFpbv9Y-E3by3DzN4g7rpY7sldAHSaQ_UlnQsNxzFC3-gr1c8C5RXQ0I_cOTv03uiMOtfnsMTQYD66Ml-0BDLLDT7HgASQd2_trlyenAFNepNayPdWtKpFu3D8NaQrqoABN3t8fwHWVkl3-HJF4LetnlAdWmnJVUaMEWWgsMNMxZEVGkl0w_5VzHpDiUAuaHM1SjpsSoRJST-RiTy2bsScRJgcFh2EajWg66qZYmlzZ5sNQY1p6fIw_rmJhLIBgow9t3HG1Gzq3f3O5dS3ZFmsFRAhJhiPDPYjqsGi8FW9stHQ&type=xhttp&path=%2Fmlkem76&mode=auto#%E7%8B%90%E5%B8%9D%E4%BA%91%E7%BE%8E%E5%9B%BD4-4\nvless://29dfa560-29ee-4280-9ed1-fa8830cf49ff@38.12.22.253:50010?encryption=mlkem768x25519plus.native.0rtt.wIGxOPmJqAPMUzLO27KrFHtvKURNvyrNYYq9UZQd8_UWbOiX_RAUSjBa42gVR_EtGLsLTASdCduQXxPPveife6a7jUddKmh1q-oFGBJ3eUNfKcuW-KnI9zZQWMgzdEG1VYSiOmOunbXKEfpa4wgMk6iIGmfKKIuyGrjBQWQzXoxqa2GVYiMFeQh26MUdZVuvYkkDnTEqn_wr0EGTAzWz0Gp95DpEWhOQW1PBdARFyoKb-FlybUhXjvd96JxDNumAPOpvA9x5qziO9SKWX_BQC2sR_ytVrYKpGTuqn7p6HooSsGJrYIQmm4GhvkchOGIZCUUxCia8cOQh4kdis3jCduSkcYJnsMZHSwoc9hGjN2ERffJhVikseIvHqHVMiMcwyOVc6UEJSum2hGs0UEtR6nxJY1et_QDNZEjAkJFCFJiqFLBumKwjC9YRxqdO84g76PWuEgo81IODUFoxxzOZtOQyreK_cvyatEEPu6eOcByst8kljIgH7JmgvqQfo0SjQMgeOuMsdzp0fsLPC-NcB4U3tlckZ8N7jYV_WCaOCUe1yevPWtZQuqYs9rtEkfk3UeQk2LRTdyFhlFBMX_Z1s6ZsQVZ-QDFe5uufzUK4HppYSdWM_kPCt8qSRQii9QMfIWp_HTcu4_pJQHQz2UKohZMTL8k8i9BswUm0RWkWmYNCmjOJTqt0iOiKaWCeYXhIiWJB3cS5_gZKdRQrXjCY-ldiFAwLKImK7ogsrGQ2SEk6IOkeKXDJrUsmjcw3SAu6GaynowTEl_d_wFzN3bGIOJt791QqPnk4ALRhm6Jz_KhTHJEeGggpkuZQxVgTXnDKxtJFa-me-qk9anio8KR3cAWL2KkbFQVIOVVmpGZZm_mPXjihs0otJlm5lAJyPABxZDMhd6G679ZV_vZdsCUwKmSZYUmAS5SmfGoj-8YoTdViwhJG6ntjO8S2fXox9udwctM6FzUgvLhrquK62GEbuNJNZDwh9jphetxN9Uq9_1sgDDpYVdmqsZha6bgiAfQBxao6kihSmfFMdsUNgyBwaCYgIvQuQYSCspEOg4pTxYs6mtVP0zu9UQNyM-MBzDARYnBZNwqHFyYSabRm5JQGwFZGl2AT3aXL6SS4lxqkT4oRmWyvN5qbZrgxzmOTKVe3KjqB8NBZfnlEAUKz_JlXlAwX0jNLKBBfjCZYKoCB0VRReigLZ_KfTbWi8qQws5YetGoxq8MQ55IDh5PLR4MEPFIzTPWanyaom1ZuuOhVM7dSyuRCa5sblgZwO0xVwByBcmOMZRQa5dSv5eEMeCfNiUZ0vfQ-r8pFEKJh80G6ZSsnwfxlBNipKdOPVrCMrLAEjeomxNNAoZlhhWldf4ZrYLZDrYEEPbByW3BoCKeIbLiJ8AqJ1ac8YTgcj4JbTqnOjFkrUJV1bLILKYhYM1sEFgLLU1kVOXi8c9w6MtlMzWy5feRlRww12cxEqOcGZ2NINweF-8hwaaMPCsLEfleABAiCgbabT5xJxDuZY_Qx3twxR1cn9eK7SKoJ7Ie-JlxPrI1NBTfErarhzzR095j6vaWLMBqd92HxqPXEk3Xvvy0&security=reality&sni=www.sony.com&fp=chrome&pbk=bCZzpVyXlz2OGkubjhmMZKV6U0z3kh9tdPAvyVZgUgA&sid=09fe05253e&spx=%2F&pqv=Z5MTiNHztMhjKRtMP78ArIuTcVo2BeCW2EeGFjA01x-E1Eqq6xMcvvbxToQk6pniWTxfvc5t6SzODM78vkhdo0So6-A5MPdm1Sth6j86Eq49evMTxy28kNdt-I7XUuPxmF6pVAKORGU21VhtdP5cRcGwLM8ED_azM2KXXPPDDQAypykrIaRLvLWLj54WgITy_YP4wPjsg77qJg0Tw6R79HFiFMTYukWW7ANjaQRSL57vk9CKd_VSY64uodoZwlmR7mX7XpDw_5ApLT5elDiY0-e-UaTUvXQn2PRevo1pn7ayzcpc-hgshUZnrJx-MLp_4Kk39B-wQjXlEj0iA4o2VLDCUrWDj9hC-NChA4vR-GlKu0lhm8faoq8ysviyX4BvyKpc9Qk962KFL-dXBxP12wCyP5Lttbw7tJ8cQY3vLM3XG2bjh1EFRpmHa3hLA-31q2LOCM0Br1qc8nRGJxxIjfRaWgz-9AEGc-2geDO8ob09c8v8zCePLhNpCQ3gKv1ki7sl7FdkL3aAToXbs0AtIN9LjEgqbLHLfq3gmWw2HWQID9R2yGr-Gl3DhPVr96rVxYA9MgWaW9a6RkekdoqrJeh0NIMLRFlH_ePPQfUNrBoeEDmzrb-k4B1PWBe-nlrIlC9A2PfX_tjCdsQne9olKrC3XjikuUfK0QLRMarmlS-wDbi_au3aNqx-m-4-P6RcgyBoQU48nsWKyJmM3aI1YUM5iDev8bGoLZWG1H_AgS2NUdfAgCfQZ9oWuNlN1yvVbVRxQfusmuO60tH1q0K6wEglxwXqNIa-dKiPMS8fxVQCU0GmesEgsv_hOgDT0WIIKqkwoJIS0rhGmiNuCNPKL13MlF9zGhjPaiBsWOn3eBI32inbfd6uDQlywj0jxLp0c_JHKfm9FNK3p1TKvYfHH2k173gXWVUT5TAxyotPIJjsCi3Q7LqXsFckPysLwiCdricLsbnjlOtM-76lJGZEuEIrKPRUQK7Md_7FTkjqchG55AiK1XMCRh1vNCTNMvMRmQQYfD1PkHriLXCcc73G96xRZuLJmbQrR3sF7hhtuFW9oN2vHaWI5XIkdTbI6mkjqQ3h2q6CK9ddP9rDr2QBeUReMiqnVEmF8mJaBXS8Aege2xiJJYfF5SWNxtdXi9VccO8bPkqKZ7WUVO2NlwPyl8Wgmqdh318nUlIhPC2xpw8xu4Q2FJqoKHiAvMp2Cnvca4F8CNtiAv66XXAj7AknX3XDLKBH_Rh_XX6Dq1wc09qWYA2pAEqJr7jqXqyHyMZr8kY_-lR8M7Jlh5qx5olbavu4Cm1jtU_DpJpFXTHP3qtT8Zdrt8V34x-64zCwb7_inGsg-gMxiX2TPoF7IA4tWZL2yTZBYikOwZkoZsX_b7xeB6T1TenV7dSBdYkHH5dbFNPNKGXg7mJGHtK3lsn95R8vqnGrCJljIJ6qHNyyAKEuyee8N4i0r31X3fOQoblYAYvGa0IKmMZ3jsDp3GhvINwfZiumlY6Z01DLay9jys3a9S212iIaWdKtJtjUdbbrD_VXm9TsmqZi531DxpkDiD05OmN611LeB4oBhDXUTbrIISMgVEkAmGm85YsNwbwDusPsdaH6ue72u0tyIJVePgfIT4zwN8ePzuNbeAgU-H0rDS1KjU4WCJkqNo86TY7pR1x33fmhEL13PiY5SnYKabd8nD0kI4t-AmdBlnXXLbTirFmSI3uQ6hOOGSBM38Fbs_K5L0HlSMiyG7yRfFQ83__PtTuA4RBstLMx6uocBVwM1FqR0qAYSynEjnMUxwPkUkEeQiTt4UA_DMN_x3dSm5hAkm4D3fkQgliMO3WIsBqcd9oynzxjNiMC9xhxXADCDyEO9CVmojY9rPuqlgOOadCkbRjhDEsPeU-b3ZYOoxkPK3k-pG3RwDmQRax5fwMi3Lgt1Wwb3-VVa7YsBA8YKe3SdPa8c39DeQED6pZE0aLgrtsyPEQdVQD4QOu1VcP5J7ATOe0fsyLaNZCQE0C67ZR0zOTMZYpZusvnPA1uNLsMyepCz6ulXF2k3yyIE66qNE4NBdOWfrLu4b0Dh3r4BmWjA3wiXCJljEZjXdr5eIi10RtEUgNFcDSUROEV1wKiAOohOkdQzlhngWh-sA9R9torMtzzgdaPObTtjeKKu27tgLHMQF5CQ_QyHCh3as3zI3sUCYsGc_CZ9ojZ8dARjAogw9OuOkp0oLhv091ShnL66osc7J8-LW6QMF_y5s0PdmBMjWHHdEtp9eRTJDfHMzL9DBFimbiqBc4juvg6aB2vPA42HtgUt_HuaP5S-4s2S8qRAXZYun62Yb_LF8Zpff3KlYm4pVl2fEP4ekxSfP-mB-eGOrM0yAuS72fh6EAqwwJQ9E_lL56c8fvz0Qfv7a_vwQgyZOBlugwW8VBAH1AiarU9pnyzPBRulDfuYjK9Jtt-XxzvRa68zvX7xlX5KhjpwaTP8386iAl1m-KfLC6zDS5JGrstMUPua84Fc6uW63iWavHTaHfQxY_wHLGRv07vdG3KC51C--EPA-gBhTz-w32XT5XmIIAE6eif6PXhKCrpS42yNB4jRZfNNdR7xR_RGs4ib-YOb7FBLIdCE3A&type=xhttp&path=%2Fmlkem76&mode=auto#%E7%8B%90%E5%B8%9D%E4%BA%91%E7%BE%8E%E5%9B%BD2-4\nvless://29dfa560-29ee-4280-9ed1-fa8830cf49ff@panel-jp.998521.xyz:29073?encryption=mlkem768x25519plus.native.0rtt.wIGxOPmJqAPMUzLO27KrFHtvKURNvyrNYYq9UZQd8_UWbOiX_RAUSjBa42gVR_EtGLsLTASdCduQXxPPveife6a7jUddKmh1q-oFGBJ3eUNfKcuW-KnI9zZQWMgzdEG1VYSiOmOunbXKEfpa4wgMk6iIGmfKKIuyGrjBQWQzXoxqa2GVYiMFeQh26MUdZVuvYkkDnTEqn_wr0EGTAzWz0Gp95DpEWhOQW1PBdARFyoKb-FlybUhXjvd96JxDNumAPOpvA9x5qziO9SKWX_BQC2sR_ytVrYKpGTuqn7p6HooSsGJrYIQmm4GhvkchOGIZCUUxCia8cOQh4kdis3jCduSkcYJnsMZHSwoc9hGjN2ERffJhVikseIvHqHVMiMcwyOVc6UEJSum2hGs0UEtR6nxJY1et_QDNZEjAkJFCFJiqFLBumKwjC9YRxqdO84g76PWuEgo81IODUFoxxzOZtOQyreK_cvyatEEPu6eOcByst8kljIgH7JmgvqQfo0SjQMgeOuMsdzp0fsLPC-NcB4U3tlckZ8N7jYV_WCaOCUe1yevPWtZQuqYs9rtEkfk3UeQk2LRTdyFhlFBMX_Z1s6ZsQVZ-QDFe5uufzUK4HppYSdWM_kPCt8qSRQii9QMfIWp_HTcu4_pJQHQz2UKohZMTL8k8i9BswUm0RWkWmYNCmjOJTqt0iOiKaWCeYXhIiWJB3cS5_gZKdRQrXjCY-ldiFAwLKImK7ogsrGQ2SEk6IOkeKXDJrUsmjcw3SAu6GaynowTEl_d_wFzN3bGIOJt791QqPnk4ALRhm6Jz_KhTHJEeGggpkuZQxVgTXnDKxtJFa-me-qk9anio8KR3cAWL2KkbFQVIOVVmpGZZm_mPXjihs0otJlm5lAJyPABxZDMhd6G679ZV_vZdsCUwKmSZYUmAS5SmfGoj-8YoTdViwhJG6ntjO8S2fXox9udwctM6FzUgvLhrquK62GEbuNJNZDwh9jphetxN9Uq9_1sgDDpYVdmqsZha6bgiAfQBxao6kihSmfFMdsUNgyBwaCYgIvQuQYSCspEOg4pTxYs6mtVP0zu9UQNyM-MBzDARYnBZNwqHFyYSabRm5JQGwFZGl2AT3aXL6SS4lxqkT4oRmWyvN5qbZrgxzmOTKVe3KjqB8NBZfnlEAUKz_JlXlAwX0jNLKBBfjCZYKoCB0VRReigLZ_KfTbWi8qQws5YetGoxq8MQ55IDh5PLR4MEPFIzTPWanyaom1ZuuOhVM7dSyuRCa5sblgZwO0xVwByBcmOMZRQa5dSv5eEMeCfNiUZ0vfQ-r8pFEKJh80G6ZSsnwfxlBNipKdOPVrCMrLAEjeomxNNAoZlhhWldf4ZrYLZDrYEEPbByW3BoCKeIbLiJ8AqJ1ac8YTgcj4JbTqnOjFkrUJV1bLILKYhYM1sEFgLLU1kVOXi8c9w6MtlMzWy5feRlRww12cxEqOcGZ2NINweF-8hwaaMPCsLEfleABAiCgbabT5xJxDuZY_Qx3twxR1cn9eK7SKoJ7Ie-JlxPrI1NBTfErarhzzR095j6vaWLMBqd92HxqPXEk3Xvvy0&security=reality&sni=www.sony.com&fp=chrome&pbk=bCZzpVyXlz2OGkubjhmMZKV6U0z3kh9tdPAvyVZgUgA&sid=09fe05253e&spx=%2F&pqv=Z5MTiNHztMhjKRtMP78ArIuTcVo2BeCW2EeGFjA01x-E1Eqq6xMcvvbxToQk6pniWTxfvc5t6SzODM78vkhdo0So6-A5MPdm1Sth6j86Eq49evMTxy28kNdt-I7XUuPxmF6pVAKORGU21VhtdP5cRcGwLM8ED_azM2KXXPPDDQAypykrIaRLvLWLj54WgITy_YP4wPjsg77qJg0Tw6R79HFiFMTYukWW7ANjaQRSL57vk9CKd_VSY64uodoZwlmR7mX7XpDw_5ApLT5elDiY0-e-UaTUvXQn2PRevo1pn7ayzcpc-hgshUZnrJx-MLp_4Kk39B-wQjXlEj0iA4o2VLDCUrWDj9hC-NChA4vR-GlKu0lhm8faoq8ysviyX4BvyKpc9Qk962KFL-dXBxP12wCyP5Lttbw7tJ8cQY3vLM3XG2bjh1EFRpmHa3hLA-31q2LOCM0Br1qc8nRGJxxIjfRaWgz-9AEGc-2geDO8ob09c8v8zCePLhNpCQ3gKv1ki7sl7FdkL3aAToXbs0AtIN9LjEgqbLHLfq3gmWw2HWQID9R2yGr-Gl3DhPVr96rVxYA9MgWaW9a6RkekdoqrJeh0NIMLRFlH_ePPQfUNrBoeEDmzrb-k4B1PWBe-nlrIlC9A2PfX_tjCdsQne9olKrC3XjikuUfK0QLRMarmlS-wDbi_au3aNqx-m-4-P6RcgyBoQU48nsWKyJmM3aI1YUM5iDev8bGoLZWG1H_AgS2NUdfAgCfQZ9oWuNlN1yvVbVRxQfusmuO60tH1q0K6wEglxwXqNIa-dKiPMS8fxVQCU0GmesEgsv_hOgDT0WIIKqkwoJIS0rhGmiNuCNPKL13MlF9zGhjPaiBsWOn3eBI32inbfd6uDQlywj0jxLp0c_JHKfm9FNK3p1TKvYfHH2k173gXWVUT5TAxyotPIJjsCi3Q7LqXsFckPysLwiCdricLsbnjlOtM-76lJGZEuEIrKPRUQK7Md_7FTkjqchG55AiK1XMCRh1vNCTNMvMRmQQYfD1PkHriLXCcc73G96xRZuLJmbQrR3sF7hhtuFW9oN2vHaWI5XIkdTbI6mkjqQ3h2q6CK9ddP9rDr2QBeUReMiqnVEmF8mJaBXS8Aege2xiJJYfF5SWNxtdXi9VccO8bPkqKZ7WUVO2NlwPyl8Wgmqdh318nUlIhPC2xpw8xu4Q2FJqoKHiAvMp2Cnvca4F8CNtiAv66XXAj7AknX3XDLKBH_Rh_XX6Dq1wc09qWYA2pAEqJr7jqXqyHyMZr8kY_-lR8M7Jlh5qx5olbavu4Cm1jtU_DpJpFXTHP3qtT8Zdrt8V34x-64zCwb7_inGsg-gMxiX2TPoF7IA4tWZL2yTZBYikOwZkoZsX_b7xeB6T1TenV7dSBdYkHH5dbFNPNKGXg7mJGHtK3lsn95R8vqnGrCJljIJ6qHNyyAKEuyee8N4i0r31X3fOQoblYAYvGa0IKmMZ3jsDp3GhvINwfZiumlY6Z01DLay9jys3a9S212iIaWdKtJtjUdbbrD_VXm9TsmqZi531DxpkDiD05OmN611LeB4oBhDXUTbrIISMgVEkAmGm85YsNwbwDusPsdaH6ue72u0tyIJVePgfIT4zwN8ePzuNbeAgU-H0rDS1KjU4WCJkqNo86TY7pR1x33fmhEL13PiY5SnYKabd8nD0kI4t-AmdBlnXXLbTirFmSI3uQ6hOOGSBM38Fbs_K5L0HlSMiyG7yRfFQ83__PtTuA4RBstLMx6uocBVwM1FqR0qAYSynEjnMUxwPkUkEeQiTt4UA_DMN_x3dSm5hAkm4D3fkQgliMO3WIsBqcd9oynzxjNiMC9xhxXADCDyEO9CVmojY9rPuqlgOOadCkbRjhDEsPeU-b3ZYOoxkPK3k-pG3RwDmQRax5fwMi3Lgt1Wwb3-VVa7YsBA8YKe3SdPa8c39DeQED6pZE0aLgrtsyPEQdVQD4QOu1VcP5J7ATOe0fsyLaNZCQE0C67ZR0zOTMZYpZusvnPA1uNLsMyepCz6ulXF2k3yyIE66qNE4NBdOWfrLu4b0Dh3r4BmWjA3wiXCJljEZjXdr5eIi10RtEUgNFcDSUROEV1wKiAOohOkdQzlhngWh-sA9R9torMtzzgdaPObTtjeKKu27tgLHMQF5CQ_QyHCh3as3zI3sUCYsGc_CZ9ojZ8dARjAogw9OuOkp0oLhv091ShnL66osc7J8-LW6QMF_y5s0PdmBMjWHHdEtp9eRTJDfHMzL9DBFimbiqBc4juvg6aB2vPA42HtgUt_HuaP5S-4s2S8qRAXZYun62Yb_LF8Zpff3KlYm4pVl2fEP4ekxSfP-mB-eGOrM0yAuS72fh6EAqwwJQ9E_lL56c8fvz0Qfv7a_vwQgyZOBlugwW8VBAH1AiarU9pnyzPBRulDfuYjK9Jtt-XxzvRa68zvX7xlX5KhjpwaTP8386iAl1m-KfLC6zDS5JGrstMUPua84Fc6uW63iWavHTaHfQxY_wHLGRv07vdG3KC51C--EPA-gBhTz-w32XT5XmIIAE6eif6PXhKCrpS42yNB4jRZfNNdR7xR_RGs4ib-YOb7FBLIdCE3A&type=xhttp&path=%2Fmlkem76&mode=auto#%E6%B5%81%E9%87%8F%E9%9D%A2%E6%9D%BFJP-%E7%8B%90%E5%B8%9D%E4%BA%91%E7%BE%8E%E5%9B%BD2-4\ntuic://7cdf7f94-ea0b-44b1-99a3-51c03a305e10%3A7cdf7f94-ea0b-44b1-99a3-51c03a305e10@47.79.145.248:43464?sni=www.bing.com&alpn=h3&insecure=1&allowInsecure=1&congestion_control=bbr#%E7%88%AA%E4%BA%91%E6%97%A5%E6%9C%AC-https%3A%2F%2Frun.claw.cloud%2F\nvless://7cdf7f94-ea0b-44b1-99a3-51c03a305e10@ffgzhcgzwuzm.ap-northeast-1.clawcloudrun.com:443?encryption=none&security=tls&sni=ffgzhcgzwuzm.ap-northeast-1.clawcloudrun.com&fp=chrome&insecure=1&allowInsecure=1&type=ws&host=ffgzhcgzwuzm.ap-northeast-1.clawcloudrun.com&path=%2F#%E7%88%AA%E4%BA%91%E6%97%A5%E6%9C%AC-https%3A%2F%2Frun.claw.cloud%2F\n', '2026-01-07 07:17:54', '2026-01-07 07:17:57', 0, NULL, NULL, 1); +INSERT INTO `markdown_file` VALUES (399462116077211648, 399461225492254720, '节点订阅链接', '节点订阅链接.md', 'https://jcmei.311169.xyz/ikmkj\n\nhttps://mei.311169.xyz/91658e42-f7e2-48e5-8d19-f7cbab273cde\n\nhttps://huggingface.ikmkj.dpdns.org/sub\n\nhttps://joey-mf.pages.dev/ef1f6415-2664-4460-8688-1d83c3b68548/sub\n', '2026-01-07 07:19:50', '2026-01-07 07:19:50', 0, NULL, NULL, 1); +INSERT INTO `markdown_file` VALUES (399462427638501376, 399462299703840768, 'containerd安装', 'containerd安装.md', '# 下载 nerdctl-full 包(含 containerd、runc、CNI 插件)\n\nwget https://github.com/containerd/nerdctl/releases/download/v1.7.7/nerdctl-full-1.7.7-linux-amd64.tar.gz\n\n# 解压到 /usr/local\n\nsudo tar Cxzvf /usr/local nerdctl-full-1.7.7-linux-amd64.tar.gz\n\n# 启动 containerd(如果尚未启动)\n\nsudo systemctl enable --now containerd\n\n# 验证安装\n\nnerdctl version\n\n**nerdctl 和 docker 的使用方法是一致的。**\n\n因为你Docker 和 containerd(nerdctl)的镜像存储路径是分开的,它们不共享镜像缓存。\nDocker 的镜像默认存储在 /var/lib/docker,而 nerdctl 使用的是 containerd 的镜像存储路径 /var/lib/containerd/io.containerd.content.v1.content。\n\n如何让 nerdctl 使用 Docker 的镜像?\n方法1:导出 Docker 镜像 → 导入 nerdctl\n\n# 1. 导出 Docker 镜像\n\ndocker save myimage:latest -o myimage.tar\n\n# 2. 导入到 nerdctl\n\nnerdctl load -i myimage.tar\n\n# 3. 验证\n\nnerdctl images | grep myimage\n\n方法2:重新拉取镜像到 nerdctl\n\nnerdctl pull myimage:latest\n\n# 基础版的\n\n说明 containerd命令\n查看本地镜像 ctr images ls\n拉取镜像 ctr images pull imagename\n推送镜像 ctr images push imagename\n给镜像打标签 ctr images tag imagename tagname\n导出镜像 ctr images export filename imagename\n导入镜像 ctr image import filename\n运行并创建容器 ctr run [options] imagenamecontainername\n\n进入容器 docker exec [options] names commond ctr task exec [options] names commond\n查看运行的容器 docker ps ctr task list\n删除容器 docker rm [options] names ctr task rm -f names / ctr c rm -f names\n', '2026-01-07 07:21:04', '2026-01-07 07:21:10', 0, NULL, NULL, 0); +INSERT INTO `markdown_file` VALUES (399462537969668096, 399462299703840768, 'sqlit初始化', 'sqlit初始化.md', '# 输入创建数据库命令\n\n~~~sql\n在目录中运行该文件\nsqlite3 mydatabase.db\n~~~\n\n# 输入命令后\n\n~~~sql\nC:\\it\\houtaigunli\\liu>sqlite3 mydatabase.db\n-- 显示下面的\nSQLite version 3.49.2 2025-05-07 10:39:52\nEnter \".help\" for usage hints.\nsqlite>\n~~~\n\n# 后续输入下面命令才显示\n\n~~~sql\nsqlite> .databases\n-- 显示下面的\nmain: C:\\it\\houtaigunli\\liu\\mydatabase.db r/w\nsqlite>\n~~~\n\n# 使用可视化工具连接后创建数据库\n\n~~~sql\n运行sql.sql文件\n~~~\n', '2026-01-07 07:21:31', '2026-01-07 07:21:31', 0, NULL, NULL, 0); +INSERT INTO `markdown_file` VALUES (399462674594926592, 399462299703840768, 'ssh修改端口脚本', 'ssh修改端口脚本.md', '```\n#!/bin/bash\n# 2. 赋予执行权限\n#chmod +x ssh_port_change.sh (或者把-x改成777)\n# SSH安全加固脚本 (仅修改端口)\n# 功能:交互式修改SSH端口,保留root登录和密码认证\n# 兼容: CentOS/RHEL/Rocky, Debian/Ubuntu, Kylin V10\n\n# 检查root权限\nif [ \"$EUID\" -ne 0 ]; then\n echo \"请使用sudo或root用户运行此脚本\"\n exit 1\nfi\n\n# 颜色定义\nRED=\'\\033[0;31m\'\nGREEN=\'\\033[0;32m\'\nYELLOW=\'\\033[1;33m\'\nNC=\'\\033[0m\'\n\n# 获取当前SSH端口\nCURRENT_PORT=$(grep -E \"^#?Port\\s+\" /etc/ssh/sshd_config | awk \'{print $2}\')\nCURRENT_PORT=${CURRENT_PORT:-22}\n\necho -e \"${YELLOW}当前SSH端口: ${CURRENT_PORT}${NC}\"\n\n# 备份原始配置\nBACKUP_FILE=\"/etc/ssh/sshd_config.bak-$(date +%Y%m%d%H%M%S)\"\ncp /etc/ssh/sshd_config \"$BACKUP_FILE\"\necho -e \"${YELLOW}已创建SSH配置备份: $BACKUP_FILE${NC}\"\n\n# 1. 修改SSH端口\nread -p \"是否要修改SSH默认端口? [y/N] \" change_port\nif [[ $change_port =~ ^[Yy]$ ]]; then\n while true; do\n read -p \"请输入新的SSH端口 (1024-49151, 当前: $CURRENT_PORT): \" new_port\n\n # 验证端口范围\n if ! [[ \"$new_port\" =~ ^[0-9]+$ ]] || [ \"$new_port\" -lt 1024 ] || [ \"$new_port\" -gt 49151 ]; then\n echo -e \"${RED}错误: 端口号必须在1024-49151范围内${NC}\"\n continue\n fi\n\n # 检查端口是否已使用\n if ss -tuln | grep -q \":$new_port\\b\"; then\n echo -e \"${RED}错误: 端口 $new_port 已被占用${NC}\"\n else\n break\n fi\n done\n\n # 更新配置\n if grep -qE \"^#?Port\\s+\" /etc/ssh/sshd_config; then\n sed -i \"s/^#*Port\\s\\+.*/Port $new_port/\" /etc/ssh/sshd_config\n else\n echo \"Port $new_port\" >> /etc/ssh/sshd_config\n fi\n\n # 确保保留root登录和密码认证\n sed -i \'s/^#*PermitRootLogin\\s\\+.*/PermitRootLogin yes/\' /etc/ssh/sshd_config\n sed -i \'s/^#*PasswordAuthentication\\s\\+.*/PasswordAuthentication yes/\' /etc/ssh/sshd_config\n\n # 应用配置\n systemctl restart sshd\n\n echo -e \"${GREEN}已设置SSH端口为: $new_port${NC}\"\n echo -e \"${GREEN}保留root登录和密码认证${NC}\"\n echo -e \"${YELLOW}重要: 请在新终端测试SSH连接 (端口 $new_port) 后再关闭当前会话!${NC}\"\n echo -e \"${YELLOW}提示: 使用防火墙管理脚本开放新端口${NC}\"\nelse\n echo -e \"${YELLOW}未修改SSH端口,保持当前设置${NC}\"\nfi\n\n# 显示最终配置\necho -e \"\\n${YELLOW}=== 当前SSH配置 ===\"\ngrep -E \"Port|PermitRootLogin|PasswordAuthentication\" /etc/ssh/sshd_config\necho -e \"==================${NC}\"\n\necho -e \"\\n${GREEN}SSH配置更新完成!${NC}\"\n```\n', '2026-01-07 07:22:03', '2026-01-07 07:22:10', 0, NULL, NULL, 0); +INSERT INTO `markdown_file` VALUES (399462924118265856, 399462299703840768, 'linux初始化脚本', 'linux初始化脚本.md', '~~~bash\n#!/bin/bash\n# ============================================================\n# Linux 初始化脚本(CentOS / RHEL / Rocky / Debian / Ubuntu / KylinV10)\n#https://zhichao.org/posts/0b9dfa 这里面有常用的\n# ============================================================\nset -euo pipefail\n\n# =======================\n# 0. 必须以 root 执行\n# =======================\n[[ $EUID -ne 0 ]] && { echo \"请使用 root 运行此脚本\"; exit 1; }\n\n# =======================\n# 1. 通用变量\n# =======================\nTIMEZONE=\"Asia/Shanghai\"\nREG_MIRROR=\"https://docker.1ms.run\" # 官方中国区镜像,更稳定\nREG_MIRROR1=\"https://docker.ikmkj.dpdns.org\"\nDOCKER_COMPOSE_VERSION=\"v2.27.1\" # 仅当下载二进制时生效,plugin 版本跟随仓库\nSKIP_DOCKER_INSTALL=0 # 默认不跳过\n\n\n# =======================\n# 0.1 交互式确认函数\n# =======================\nask_yes_or_no() {\n while true; do\n read -p \"$1 [y/N] \" answer\n case $answer in\n [Yy]*) return 0 ;;\n [Nn]*|\"\" ) return 1 ;;\n esac\n done\n}\n# 判断包管理器(这里不能注释,用来判断是哪个系统的,只能注释有 =========的)\nif command -v apt-get >/dev/null 2>&1; then\n PM=\"apt\"\nelif command -v yum >/dev/null 2>&1; then\n PM=\"yum\"\nelif command -v dnf >/dev/null 2>&1; then\n PM=\"dnf\"\nelse\n echo \"未知包管理器,退出\"; exit 1\nfi\n\n# 检测 Rocky Linux\nIS_ROCKY=0\nif [[ -f /etc/rocky-release ]] || \n ([[ -f /etc/redhat-release ]] && grep -qi \'rocky\' /etc/redhat-release); then\n IS_ROCKY=1\nfi\n\n# =======================\n# 2. 更新系统 & 缓存\n# =======================\necho \">> 1. 更新系统软件包 ...\"\ncase $PM in\n apt)\n apt-get update\n apt-get -y full-upgrade\n ;;\n yum|dnf)\n $PM -y update\n ;;\nesac\n\n# =======================\n# 3. 安装常用工具\n# =======================\necho \">> 2. 安装常用工具 ...\"\ncase $PM in\n apt)\n apt-get -y install curl wget vim git htop tree lrzsz unzip net-tools lsof tar\n ;;\n yum|dnf)\n $PM -y install epel-release\n $PM -y install curl wget vim git htop tree lrzsz unzip net-tools lsof tar\n ;;\nesac\n\n# =======================\n# 4. 设置时区 & 时间同步\n# =======================\necho \">> 3. 设置时区 Asia/Shanghai ...\"\n# ---------- 1. 确保时区文件存在 ----------\nzone_file=\"/usr/share/zoneinfo/${TIMEZONE}\"\nif [[ ! -e \"$zone_file\" ]]; then\n echo \"⚠️ 时区文件缺失,尝试安装 tzdata ...\"\n case \"$PM\" in\n apt) apt-get -y install tzdata ;;\n yum|dnf) $PM -y install tzdata ;;\n esac\nfi\ntimedatectl set-timezone \"$TIMEZONE\"\n\n# ---------- 2. 时间同步 ----------\necho \">> 配置时间同步 ...\"\nif command -v chronyd >/dev/null 2>&1; then\n systemctl enable --now chronyd\nelif systemctl list-unit-files | grep -q systemd-timesyncd; then\n sed -i \"s/^#NTP=.*/NTP=ntp.aliyun.com cn.pool.ntp.org/\" /etc/systemd/timesyncd.conf\n systemctl enable --now systemd-timesyncd\nelse\n # 最简方案:ntpdate 定时\n case \"$PM\" in\n apt) apt-get -y install ntpdate ;;\n yum|dnf) $PM -y install ntpdate ;;\n esac\n ntpdate ntp.aliyun.com\n echo \"0 */6 * * * root ntpdate ntp.aliyun.com >/dev/null 2>&1\" >> /etc/crontab\nfi\n\n# =======================\n# 5. 彻底关闭 SELinux & Firewalld\n# =======================\necho \">> 4. 关闭 SELinux、防火墙 ...\"\n\nif ask_yes_or_no \"警告:是否要彻底关闭防火墙 (firewalld, ufw) 和 SELinux?这是一个高风险操作,仅建议在安全的内网环境中使用。\"; then\n # 1. 关闭并禁用防火墙服务\n if systemctl list-unit-files | grep -q firewalld; then\n systemctl stop firewalld\n systemctl disable firewalld\n echo \"已关闭并禁用 firewalld\"\n fi\n if command -v ufw >/dev/null 2>&1; then\n ufw --force disable\n ufw --force reset\n echo \"已关闭并重置 ufw\"\n fi\n\n # 2. 清空所有防火墙规则\n for iptables_cmd in iptables ip6tables; do\n if command -v $iptables_cmd >/dev/null 2>&1; then\n $iptables_cmd -F; $iptables_cmd -X; $iptables_cmd -t nat -F; $iptables_cmd -t nat -X; $iptables_cmd -t mangle -F; $iptables_cmd -t mangle -X;\n $iptables_cmd -P INPUT ACCEPT; $iptables_cmd -P FORWARD ACCEPT; $iptables_cmd -P OUTPUT ACCEPT;\n echo \"已清空 $iptables_cmd 规则\"\n fi\n done\n\n # 3. 永久禁用 SELinux\n if [[ -f /etc/selinux/config ]]; then\n [ ! -f /etc/selinux/config.bak ] && cp /etc/selinux/config /etc/selinux/config.bak\n sed -i \'s/^SELINUX=.*/SELINUX=disabled/\' /etc/selinux/config\n setenforce 0 >/dev/null 2>&1 || true\n fi\n\n # 3.3 修改 GRUB 内核参数\n if [[ -f /etc/default/grub ]]; then\n [ ! -f /etc/default/grub.bak ] && cp /etc/default/grub /etc/default/grub.bak\n # 确保 GRUB_CMDLINE_LINUX 存在\n if ! grep -q \'^GRUB_CMDLINE_LINUX=\' /etc/default/grub; then\n echo \'GRUB_CMDLINE_LINUX=\"\"\' >> /etc/default/grub\n fi\n \n # 添加 selinux=0 参数(如果不存在)\n if ! grep -q \'selinux=0\' /etc/default/grub; then\n sed -i \'/^GRUB_CMDLINE_LINUX=/s/\"$/ selinux=0\"/\' /etc/default/grub\n fi\n \n # 更新 GRUB 配置\n if command -v grub2-mkconfig >/dev/null 2>&1; then\n if [[ -d /sys/firmware/efi ]]; then\n # UEFI 系统\n [[ -d /boot/efi/EFI/centos ]] && grub2-mkconfig -o /boot/efi/EFI/centos/grub.cfg\n [[ -d /boot/efi/EFI/redhat ]] && grub2-mkconfig -o /boot/efi/EFI/redhat/grub.cfg\n [[ -d /boot/efi/EFI/rocky ]] && grub2-mkconfig -o /boot/efi/EFI/rocky/grub.cfg\n [[ -d /boot/efi/EFI/ubuntu ]] && grub2-mkconfig -o /boot/efi/EFI/ubuntu/grub.cfg\n [[ -d /boot/efi/EFI/debian ]] && grub2-mkconfig -o /boot/efi/EFI/debian/grub.cfg\n else\n # Legacy BIOS 系统\n [[ -f /boot/grub2/grub.cfg ]] && grub2-mkconfig -o /boot/grub2/grub.cfg\n [[ -f /boot/grub/grub.cfg ]] && grub2-mkconfig -o /boot/grub/grub.cfg\n fi\n elif command -v update-grub >/dev/null 2>&1; then\n update-grub\n else\n echo \"⚠️ 无法自动更新 GRUB 配置,请手动执行相应命令!\"\n fi\n else\n echo \"⚠️ 未找到 /etc/default/grub 文件,跳过 GRUB 配置更新\"\n fi\n\n # 4. 对于 Kylin V10 等特殊系统,额外处理\n if [[ -f /etc/kylin-release ]]; then\n systemctl stop secpaver 2>/dev/null || true\n systemctl disable secpaver 2>/dev/null || true\n fi\n echo \">> SELinux 和防火墙已按您的选择进行处理 <<\"\nelse\n echo \">> 已跳过关闭防火墙和 SELinux 的操作 <<\"\nfi\n\n\n# =======================\n# 6. 内核参数优化\n# =======================\necho \">> 5. 优化内核参数 ...\"\ncat >/etc/sysctl.d/99-init.conf <<\'EOF\'\n# 网络\nnet.core.somaxconn = 32768\nnet.ipv4.tcp_max_syn_backlog = 16384\nnet.ipv4.ip_forward = 1\nnet.ipv4.tcp_timestamps = 0\nnet.ipv4.tcp_tw_reuse = 1\nnet.ipv4.tcp_fin_timeout = 10\nfs.file-max = 6553560\n# 内存\nvm.swappiness = 10\nEOF\nsysctl -p /etc/sysctl.d/99-init.conf\n\n# =======================\n# 7. SSH 优化\n# =======================\necho \">> 6. 优化 SSH ...\"\n[ ! -f /etc/ssh/sshd_config.bak ] && cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak\nsed -i -e \'s/^#UseDNS yes/UseDNS no/\' \\\n -e \'s/^GSSAPIAuthentication yes/GSSAPIAuthentication no/\' \\\n -e \'s/^#PermitRootLogin .*/#PermitRootLogin prohibit-password/\' \\\n -e \'s/^#ClientAliveInterval.*/ClientAliveInterval 300/\' \\\n -e \'s/^#ClientAliveCountMax.*/ClientAliveCountMax 2/\' \\\n /etc/ssh/sshd_config\nsystemctl restart sshd\n\n# =======================\n# 8. 字符集中文\n# =======================\necho \">> 7. 设置系统字符集为 zh_CN.UTF-8 ...\"\ncase $PM in\n apt)\n apt-get -y install locales\n sed -i \'/zh_CN.UTF-8/s/^# //g\' /etc/locale.gen\n locale-gen zh_CN.UTF-8\n update-locale LANG=zh_CN.UTF-8\n ;;\n yum|dnf)\n $PM -y install glibc-langpack-zh\n ;;\nesac\nlocalectl set-locale LANG=zh_CN.UTF-8\n\n# =======================\n# 9. 安装 Docker(官方脚本,最新稳定版)[^1^]\n# =======================\n\n\n# 下载 nerdctl-full 包(含 containerd、runc、CNI 插件)\nwget https://github.com/containerd/nerdctl/releases/download/v1.7.7/nerdctl-full-1.7.7-linux-amd64.tar.gz\n\n# 解压到 /usr/local\nsudo tar Cxzvf /usr/local nerdctl-full-1.7.7-linux-amd64.tar.gz\n\n# 启动 containerd(如果尚未启动)\nsudo systemctl enable --now containerd\n\n# 验证安装\nnerdctl version\n\n# 确保配置目录存在\nmkdir -p /etc/containerd/certs.d/docker.io\n\ncat < /etc/containerd/config.toml\n[plugins.\"io.containerd.grpc.v1.cri\".registry]\n config_path = \"/etc/containerd/certs.d\"\nEOF\n\n# 写入 hosts.toml 配置\ncat < /etc/containerd/certs.d/docker.io/hosts.toml\nserver = \"https://registry-1.docker.io\"\n\n[host.\"$REG_MIRROR1\"]\n capabilities = [\"pull\", \"resolve\"]\nEOF\n\n# 重启 containerd 服务\nsystemctl restart containerd\n\necho \"Containerd 镜像加速器配置完成,加速器地址为: $REG_MIRROR1\"\n\n#\n## CentOS 7 特殊处理\n#if [[ -f /etc/centos-release ]] && grep -q \'CentOS Linux release 7\' /etc/centos-release; then\n# echo \">> CentOS 7 补丁:改用官方脚本安装 Docker ...\"\n# yum -y remove docker* containerd.io\n# curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun\n# systemctl enable --now docker\n# SKIP_DOCKER_INSTALL=1\n#fi\n#\n## 其他系统安装\n#if [[ $SKIP_DOCKER_INSTALL -eq 0 ]]; then\n# echo \">> 安装最新 Docker ...\"\n# case $PM in\n# yum|dnf)\n# echo \">> 移除可能冲突的旧版 Docker 及相关软件包...\"\n# # 增加移除 Podman 及其相关组件\n# $PM -y remove docker docker-client docker-client-latest docker-common \\\n# docker-latest docker-latest-logrotate docker-logrotate \\\n# docker-engine podman buildah cri-o runc containerd \\\n# >/dev/null 2>&1 || true\n# $PM -y install dnf-plugins-core yum-utils\n# # 处理 Rocky 标识\n# if [[ $IS_ROCKY -eq 1 ]]; then\n# echo \">> 为 Rocky Linux 启用 Docker CE 仓库...\"\n# $PM config-manager --add-repo=https://download.docker.com/linux/centos/docker-ce.repo\n# # 添加 SELinux 策略包\n# $PM -y install container-selinux\n# fi\n#\n# # 安装前加载内核模块\n# modprobe overlay || true\n# modprobe br_netfilter || true\n# $PM -y install docker-ce docker-ce-cli containerd.io docker-compose-plugin\n# ;;\n# apt)\n# curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun\n# ;;\n# esac\n#fi\n#\n## Docker 通用配置\n#echo \">> 配置 Docker 镜像加速器及 cgroup 驱动...\"\n#mkdir -p /etc/docker\n#cat >/etc/docker/daemon.json < /etc/modules-load.d/docker.conf < /etc/sysctl.d/docker.conf </dev/null 2>&1\n#\n#systemctl daemon-reload\n#systemctl enable --now docker\n#systemctl restart docker\n\n# =======================\n# 10. 安装 docker-compose v2(plugin 优先,否则二进制)\n# =======================\necho \">> 9. 安装 docker-compose ...\"\nif docker compose version >/dev/null 2>&1; then\n echo \"docker compose plugin 已存在\"\nelse\n # 设置版本号(你可以改成你需要的版本)\n DOCKER_COMPOSE_VERSION=\"${DOCKER_COMPOSE_VERSION:-v2.24.5}\"\n\n # 正确的 URL 拼接\n curl -L \"https://github.com/docker/compose/releases/download/${DOCKER_COMPOSE_VERSION}/docker-compose-$(uname -s)-$(uname -m)\" \\\n -o /usr/local/bin/docker-compose\n chmod +x /usr/local/bin/docker-compose\n # 软链接,兼容老命令\n ln -sf /usr/local/bin/docker-compose /usr/bin/docker-compose || true\nfi\n\n# =======================\n# 11. 安装 CrowdSec\n# =======================\necho \">> 安装并配置 CrowdSec ...\"\ncase $PM in\n apt)\n curl -s https://packagecloud.io/install/repositories/crowdsec/crowdsec/script.deb.sh | sudo bash\n apt-get update\n apt-get -y install crowdsec\n ;;\n yum|dnf)\n rpm --import https://packagecloud.io/crowdsec/crowdsec/gpgkey\n cat > /etc/yum.repos.d/crowdsec.repo <<\'EOF\'\n[crowdsec]\nname=crowdsec\nbaseurl=https://packagecloud.io/crowdsec/crowdsec/el/$releasever/$basearch\ngpgcheck=1\nenabled=1\nEOF\n $PM -y install crowdsec\n ;;\nesac\n\n# 启动并启用 CrowdSec 服务\nsystemctl enable --now crowdsec\nsystemctl enable --now crowdsec-firewall-bouncer-iptables\n\n# ---------- CrowdSec 配置结束 ----------\n# =======================\n# 13. 清理无用内核 / 缓存包\n# =======================\necho \">> 11. 清理旧内核及缓存 ...\"\ncase $PM in\n apt)\n apt-get -y autoremove --purge\n apt-get clean\n update-grub\n ;;\n yum)\n yum -y install yum-utils\n yum -y autoremove --oldkernels --count=1 || true\n yum clean all\n ;;\n dnf)\n dnf -y autoremove\n dnf clean all\n ;;\nesac\n\n# =======================\n# 14. 设置默认编辑器\n# =======================\necho \">> 12. 设置默认编辑器为 vim ...\"\nVIM_PATH=$(command -v vim 2>/dev/null || command -v vi 2>/dev/null)\nif [[ -n \"$VIM_PATH\" ]]; then\n # 兼容 Debian/Ubuntu\n if command -v update-alternatives &>/dev/null; then\n update-alternatives --set editor \"$VIM_PATH\" >/dev/null 2>&1 || true\n fi\n echo \"export EDITOR=$VIM_PATH\" >>/etc/profile\nelse\n echo \"⚠️ 未找到 vim 或 vi 编辑器\"\nfi\n\n\necho \"==== 初始化完成,请手动执行 reboot 重启系统 ====\"\n~~~\n', '2026-01-07 07:23:03', '2026-01-07 07:23:03', 0, NULL, NULL, 0); +INSERT INTO `markdown_file` VALUES (399463926930214912, 399462299703840800, 'Caddy', 'Caddy.md', '## 创建所需要的目录文件\n\n~~~bash\nmkdir -p /docker/caddy/{config/caddy.d,config/l4-enabled,html,certs,caddy_data,caddy_config}\n~~~\n\n### Caddyfile(主配置)\n\n```\n~~~ Caddyfile\n{\n # 全局选项:可关掉自动 HTTPS(测试环境)\n # auto_https off\n\n # tcp/udp流量的\n layer4 {\n # 引入所有 TCP/UDP 子配置\n import /etc/caddy/l4-enabled/*.caddy\n }\n\n}\n\n# 自动引入子配置\nimport /etc/caddy/sites-enabled/*.caddy\n```\n\n### 反向代理一个已经创建好的网站\n\n```\n~~~ Caddyfile\n# 将 your-proxy-domain.com 替换为代理服务器的域名(或IP)\nyour-proxy-domain.com {\n # 反向代理到目标服务器https://biji2.311169.xyz\n reverse_proxy https://biji2.311169.xyz {\n # 使用 {upstream_hostport} 自动获取目标网站的主机名\n header_up Host {upstream_hostport}\n \n # 其他推荐头部设置\n header_up X-Forwarded-For {remote_host}\n header_up X-Real-IP {remote}\n header_up X-Forwarded-Proto {scheme}\n }\n}\n~~~\n\n### 反向代理,需要跨域的\n~~~ Caddyfile\n# 在代理层处理CORS的配置\napp.example.com {\n # 处理预检请求 (OPTIONS)\n @options {\n method OPTIONS\n }\n respond @options 204 {\n header Access-Control-Allow-Origin \"*\"\n header Access-Control-Allow-Methods \"GET, POST, PUT, DELETE\"\n header Access-Control-Allow-Headers \"Content-Type, Authorization\"\n }\n\n # 代理到后端\n reverse_proxy https://api.example.com {\n header_up Host {upstream_hostport}\n \n # 添加CORS响应头\n header_down Access-Control-Allow-Origin \"*\"\n header_down Access-Control-Allow-Credentials \"true\"\n header_down Access-Control-Expose-Headers \"X-Custom-Header\"\n header_down Access-Control-Allow-Headers \"Content-Type, Authorization\"\n header_down Access-Control-Max-Age \"3600\"\n }\n}\n~~~\n\n\n\n### 创建站点\n~~~ Caddyfile\nbiji2.311169.xyz { # 自己的域名\n # 开启 Gzip / zstd 压缩\n encode zstd gzip\n\n # 1. 配置API反向代理\n # 匹配 /api/ 开头的请求,并保留完整路径进行转发\n\n handle_path /api/* {\n reverse_proxy http://huanan.311169.xyz:8084\n }\n\n # 2. 配置前端静态文件服务\n root * /usr/share/caddy\n file_server browse\n}\n~~~\n```\n\n### 反向代理+负载均衡\n\n~~~yaml\naaa.com {\n encode gzip zstd\n\n reverse_proxy {\n # 后端列表,权重 5:3:2\n to A1.com:443{weight=5}\n to B1.com:443{weight=3}\n to C1.com:443{weight=2}\n\n lb_policy weighted_round_robin\n\n # 健康检查(任选一台机吐的静态路径)\n health {\n uri /healthz\n interval 15s\n timeout 3s\n passes 2\n fails 2\n }\n\n fail_duration 30s\n max_fails 3\n\n # 关键:向后端握手时使用它自己的域名\n transport http {\n tls\n tls_server_name {upstream_hostport} # 自动取 A1.com / B1.com / C1.com\n keepalive 30s\n }\n }\n\n # 日志,可以不用\n log {\n output file /data/logs/aaa.com_access.log\n format json\n }\n}\n~~~\n\n### 反代tcp/udp流量\n\n~~~bash\n# 把 3307 端口收到的纯 TCP 流量转到后端 3306\n0.0.0.0:3307 {\n route {\n proxy {\n upstream 10.0.0.55:3306\n }\n }\n}\n~~~\n\n## docker-compose\n\n~~~yaml\nservices:\n caddy:\n image: local/caddy-l4:2.7 #能TCP反代\n container_name: caddy-rp\n restart: unless-stopped\n ports:\n - \"80:80\"\n - \"443:443\"\n environment:\n TZ: Asia/Shanghai\n CLOUDFLARE_API_TOKEN: m-4Ph7InLvQ_xSo-v2y72Cmp6ZgazBz3bH14dKO3\n networks: [caddy-net]\n volumes:\n - /docker/caddy/config/Caddyfile:/etc/caddy/Caddyfile:ro #主配置文件\n - /docker/caddy/config/caddy.d:/etc/caddy/sites-enabled:ro # http/https子配置文件\n - /docker/caddy/config/l4-enabled:/etc/caddy/l4-enabled:ro # tcp/udp子配置文件\n - /docker/caddy/html:/usr/share/caddy:ro # 站点静态文件\n - /docker/caddy/certs:/certs:ro # ← 手动证书\n - caddy_data:/data #申请的证书\n - caddy_config:/config # caddy的内置文件\n\nvolumes:\n caddy_data:\n driver: local\n driver_opts:\n type: none\n device: /docker/caddy/caddy_data\n o: bind\n caddy_config:\n driver: local\n driver_opts:\n type: none\n device: /docker/caddy/caddy_config\n o: bind\n\nnetworks:\n caddy-net:\n driver: bridge\n~~~\n', '2026-01-07 07:27:02', '2026-01-07 08:27:43', 0, NULL, NULL, 0); +INSERT INTO `markdown_file` VALUES (399468868004876288, 399468715122495488, '各种打包命令', '各种打包命令.md', '```\n\n# 后端打包\n\n## 需要push到远程仓库\n`mvn clean package docker:build docker:push`\n\n## 忽略测试用例\n`mvn clean package -D maven.test.skip=true -am docker:build`\n## 忽略测试用例,指定环境\n`mvn clean package -P prod -D maven.test.skip=true -am docker:build`\n\n# 在dockerfile中已经设置好了启动哪个配置文件\nENV SPRING_PROFILES_ACTIVE=test \\\nJAVA_OPTS=\"-Xmx512m -XX:MaxRAMPercentage=75\"\n\n# 前端\n\n## 直接在根目录运行\n- 开发环境:进入 liu-vue 目录运行 `npm run dev` → 使用开发 API \n cd liu-vue\n npm run dev # 使用开发API\n\n- 生产环境:在根目录运行 `npm run build:frontend` → 使用生产 API\n- 主要是看package.json在哪里,在该文件夹中(运行、构建等地方)添加 `build:frontend\": \"vite build`\n npm run build:frontend # 使用生产API\n\n# lunix命令\n\n## 查看内存\nfree -h\ntotal表示内存总量,used表示已使用的内存量,free表示空闲的内存量\n\n## 查看cpu\n查看使用\ntop\n\n查看型号\ncat /proc/cpuinfo | grep \"model name\" | uniq -c\n\n\n# windows将本地镜像导入服务器中\n## windows上\n查看当前镜像\ndocker images\n\n 导出镜像为tar文件\ndocker save biji-houdaun:0.0.1-SNAPSHOT -o biji-houdaun.tar\n## 在Linux服务器上导入镜像\ndocker load -i biji-houdaun.tar\n\n验证镜像是否导入成功\ndocker images\n\n\n# MySQL数据库创建用户单独使用一个数据库\n-- 创建用户\nCREATE USER \'用户名称\'@\'localhost\'(本地)/\'%\'(所有主机) IDENTIFIED BY \'密码\';\n\n-- 授予对biji_db数据库的权限\nGRANT ALL PRIVILEGES ON biji_db.* TO \'用户名称\'@\'%\';\n\n-- 刷新权限\nFLUSH PRIVILEGES;\n\n\n\n# docker启动命令\n\n## 运行命令\n\n--name 名称,-p 机器端口:容器端口,--memory 内存限制,--cpus CPU限制,--restart 重启策略,\n-v 本地路径:容器路径,-d 运行后台\n--log-opt max-size=10m 日志大小限制,--log-opt max-file=3 日志文件数量限制,liu:latest 镜像名称\n~~~bash\ndocker run -d \\\n --name myapp-container \\\n -p 80:8082 \\\n -v /root/docker/liu/data:/data \\\n --memory 600m \\\n --cpus 1.0 \\\n --restart unless-stopped \\\n --log-opt max-size=5m \\\n --log-opt max-file=3 \\\n liu:latest\n ~~~\n\n## 查看启动日志\nmyapp-container 容器名称\n`docker logs -f myapp-container`\n\n## 强制删除启动中的容器\n`docker rm -f myapp-container`\n\n## 停止并删除容器\n`docker stop myapp-container`\n\n## 删除镜像\n`docker rmi liu:latest`\n\n\n\n# docker compose启动命令\n\n## 启动docker-compose.yml中的所有容器\n`docker-compose up -d`\n\n## 启动某一个容器\n\ndocker-compose up -d [服务名称]\n\n`docker-compose up -d myapp-container`\n\n~~~yaml\nversion: \'3.8\'\nmyapp-container:\n myapp:\n image: liu:latest\n~~~\n```\n', '2026-01-07 07:46:40', '2026-01-07 07:46:44', 0, NULL, NULL, 0); +INSERT INTO `markdown_file` VALUES (399479402179727360, 399462299703840768, 'caddy能反代tcp/udp编译', 'caddy能反代tcp/udp编译.md', '~~~dockerfile\n# ① 用 2.10 分支的 builder(已带 Go 1.22)\nFROM caddy:2.10-builder-alpine AS builder\nRUN xcaddy build --with github.com/mholt/caddy-l4\n\nFROM caddy:2.10-alpine\nCOPY --from=builder /usr/bin/caddy /usr/bin/caddy\nENTRYPOINT [\"caddy\"]\nCMD [\"run\", \"--config\", \"/etc/caddy/Caddyfile\", \"--adapter\", \"caddyfile\"]\n~~~\n', '2026-01-07 08:28:31', '2026-01-07 08:28:35', 0, NULL, NULL, 0); +INSERT INTO `markdown_file` VALUES (399812465187229696, 399462299703840768, 'docker部署mysql,redis', 'docker部署mysql,redis.md', '## docker-compose.yml\n\n```yaml\nversion: \"3.9\"\n\nservices:\n mysql:\n image: mysql:8.0-oraclelinux8 # 官方镜像,体积稍小\n container_name: mysql\n restart: always\n ports:\n - \"3306:3306\"\n environment:\n MYSQL_ROOT_PASSWORD: 123456 #root密码\n MYSQL_DATABASE: usera #创建的数据库\n MYSQL_USER: usera #创建数据库的用户\n MYSQL_PASSWORD: 123456 #创建数据库的密码\n volumes:\n - /docker/mysql/conf:/etc/mysql/conf.d:ro #配置文件\n - /docker/mysql/data:/var/lib/mysql #数据库文件\n command:\n - \"--defaults-file=/etc/mysql/conf.d/my.cnf\"\n deploy:\n resources:\n limits:\n memory: 1.5G # 硬上限 1.5 GB\n reservations:\n memory: 1G\n\n redis:\n image: redis:7-alpine\n container_name: redis\n restart: always\n ports:\n - \"6379:6379\"\n command: redis-server /usr/local/etc/redis/redis.conf\n volumes:\n - /docker/redis/config/redis.conf:/usr/local/etc/redis/redis.conf:ro\n - /docker/redis/data:/data\n deploy:\n resources:\n limits:\n memory: 1.5G # 硬上限 600 MB\n reservations:\n memory: 1G\n\n```\n\n### mysql.cnf\n\n```yaml\n[mysqld]\n# 基础\nuser=mysql\ndatadir=/var/lib/mysql\nsocket=/var/run/mysqld/mysqld.sock\n\n# 字符集\ncharacter-set-server=utf8mb4\ncollation-server=utf8mb4_unicode_ci\n\n# 认证插件(兼容老客户端)\ndefault-authentication-plugin=mysql_native_password\n\n# 内存池(4G 机器给 1G 足够)\ninnodb_buffer_pool_size = 1G\ninnodb_log_buffer_size = 16M\ninnodb_log_file_size = 256M\nkey_buffer_size = 32M\ntmp_table_size = 32M\nmax_heap_table_size = 32M\n\n# 连接数\nmax_connections = 100\n\n# 其他\nexplicit_defaults_for_timestamp = 1\n\n\n```\n\n### redis.conf\n\n```yaml\n# 网络\nbind 0.0.0.0\nport 6379\nprotected-mode yes\n# 密码\nrequirepass 123456\n\n# 内存上限 & 淘汰策略\nmaxmemory 1G\nmaxmemory-policy allkeys-lru\n\n# 持久化(混合 RDB + AOF,量小机子可关闭 AOF 省 IO)\nsave 900 1\nsave 300 10\nsave 60 10000\nappendonly yes\nappendfsync everysec \n # 如果磁盘 IO 吃紧可改成 no\n\n# 日志\nloglevel notice\nlogfile \"\"\n\n# 工作目录\ndir /data\n```\n', '2026-01-08 06:32:00', '2026-01-08 06:32:12', 0, NULL, NULL, 0); +INSERT INTO `markdown_file` VALUES (399812718460276736, 399462299703840768, 'docker镜像', 'docker镜像.md', '~~~bash\nsudo mkdir -p /etc/docker\nsudo tee /etc/docker/daemon.json <<\'EOF\'\n{\n \"registry-mirrors\": [\n \"https://registry.aliyuncs.com\",\n \"https://docker.mirrors.ustc.edu.cn\",\n \"https://docker.1ms.run\",\n \"https://docker.xuanyuan.me\",\n \"https://0dj0t5fb.mirror.aliyuncs.com\",\n \"https://docker.mirrors.ustc.edu.cn\",\n \"https://registry.docker-cn.com\"\n ]\n}\nEOF\n~~~\n', '2026-01-08 06:33:00', '2026-01-08 06:33:03', 0, NULL, NULL, 0); +INSERT INTO `markdown_file` VALUES (399812978381295616, 399462299703840768, 'docker部署nginx+域名ssl', 'docker部署nginx+域名ssl.md', '## docker-compose.yml\n\n```yaml\n\nversion: \"3.8\"\nservices:\n nginx:\n image: nginx:alpine\n container_name: nginx\n ports:\n - \"80:80\"\n - \"443:443\"\n volumes:\n - /docker/nginx/nginx.conf:/etc/nginx/nginx.conf:ro #主配置文件\n - /docker/nginx/conf.d:/etc/nginx/conf.d:ro #子配置文件(tcp/udp)\n # - /docker/nginx/conf.d/sites:/etc/nginx/sites:ro #站点配置文件\n - /docker/nginx/ssl:/etc/nginx/ssl:ro # Let\'s Encrypt 落盘\n - /docker/nginx/html:/etc/nginx/html:ro # 前端静态文件\n - /docker/nginx/certbot/www:/var/www/certbot:ro # 验证文件\n depends_on:\n - mysql\n - redis\n - certbot\n restart: unless-stopped\n\n certbot:\n image: certbot/certbot:latest\n container_name: certbot\n volumes:\n - ./docker/nginx/ssl:/etc/letsencrypt\n - /docker/nginx/certbot/www:/var/www/certbot\n entrypoint: \"/bin/sh -c \'trap exit TERM; while :; do certbot renew; sleep 12h & wait $${!}; done;\'\"\n\nvolumes:\n redis_data:\n\n```\n\n## 首先创建一个基础的配置文件\n\n```dsconfig\nserver {\n listen 80;\n server_name hdyg-mysql.311169.xyz hdyg-redis.311169.xyz; 要申请的域名\n \n location ^~ /.well-known/acme-challenge/ {\n root /var/www/certbot; #重要,必须和容器内的路径一样\n try_files $uri =404;\n }\n\n root /etc/nginx/html;\n index index.html;\n\n location / {\n try_files $uri $uri/ =404;\n }\n}\n\n```\n\n## 申请 Let\'s Encrypt 证书(替换域名 & 邮箱)。\n\n```bash\ndocker compose exec certbot certbot certonly \\\n --webroot -w /var/www/certbot \\\n -d hdyg-mysql.311169.xyz -d hdyg-redis.311169.xyz \\\n --email 1@qq.com --agree-tos --no-eff-email\n```\n', '2026-01-08 06:34:02', '2026-01-08 06:34:02', 0, NULL, NULL, 0); + +-- ---------------------------- +-- Table structure for registration_codes +-- ---------------------------- +DROP TABLE IF EXISTS `registration_codes`; +CREATE TABLE `registration_codes` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `code` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `expiry_time` datetime NOT NULL, + `created_by` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, + `created_at` datetime NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `uk_code`(`code`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of registration_codes +-- ---------------------------- + +-- ---------------------------- +-- Table structure for system_settings +-- ---------------------------- +DROP TABLE IF EXISTS `system_settings`; +CREATE TABLE `system_settings` ( + `setting_key` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `setting_value` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL, + `description` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL, + PRIMARY KEY (`setting_key`) USING BTREE +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of system_settings +-- ---------------------------- + +-- ---------------------------- +-- Table structure for user +-- ---------------------------- +DROP TABLE IF EXISTS `user`; +CREATE TABLE `user` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `username` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, + `email` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, + `created_at` datetime NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` datetime NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `token` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, + `token_enddata` datetime NULL DEFAULT NULL, + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `uk_username`(`username`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 6 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Records of user +-- ---------------------------- +INSERT INTO `user` VALUES (4, 'admin', '$2a$10$apE9cmOHi4022KK0hfJnbOS6dGZEibYVhItK7UYCHGyl.JYl8bIse', NULL, '2025-07-31 02:56:59', '2025-07-31 02:56:59', NULL, NULL); + +SET FOREIGN_KEY_CHECKS = 1; diff --git a/uploads/1fc870d6-d162-4ac3-812a-d862b930576c.png b/uploads/1fc870d6-d162-4ac3-812a-d862b930576c.png new file mode 100644 index 0000000000000000000000000000000000000000..6a0da7cdf7485dc91b74e5e3deb412a819ccd4c7 GIT binary patch literal 9733 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