diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..c3f0d2b85 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,14 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true + +[*.md] +trim_trailing_whitespace = false + +[*.{php,js,css,html,json,xml,yml,yaml,md,txt}] +indent_style = space +indent_size = 4 diff --git a/.env.example b/.env.example index 2a609e1f7..ac3dbf5ca 100644 --- a/.env.example +++ b/.env.example @@ -14,10 +14,3 @@ CACHE_REDIS_HOST=127.0.0.1 CACHE_REDIS_PORT=6379 CACHE_REDIS_SELECT= CACHE_REDIS_PASSWORD= - -# 会话配置 -SESSION_TYPE=file -SESSION_NAME=ssid -SESSION_STORE= -SESSION_EXPIRE=7200 -SESSION_PREFIX= \ No newline at end of file diff --git a/.github/clear-github-releases.py b/.github/clear-github-releases.py new file mode 100644 index 000000000..aed5047c7 --- /dev/null +++ b/.github/clear-github-releases.py @@ -0,0 +1,21 @@ +import os +from github import Github + +def delete_all_releases(repo): + for release in repo.get_releases(): + try: + release.delete_release() + print(f"Deleted release: {release.title}") + except Exception as e: + print(f"Failed to delete release {release.title}: {e}") + +def main(): + token = os.getenv("GITHUB_TOKEN") + repo_name = os.getenv("GITHUB_REPOSITORY").split('/')[-1] # 获取仓库名 + owner = os.getenv("GITHUB_REPOSITORY").split('/')[0] # 获取仓库所有者 + g = Github(token) + repo = g.get_repo(f"{owner}/{repo_name}") + delete_all_releases(repo) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/.github/clear-github-tags.cmd b/.github/clear-github-tags.cmd new file mode 100644 index 000000000..1c12279fb --- /dev/null +++ b/.github/clear-github-tags.cmd @@ -0,0 +1,13 @@ +@echo off +setlocal + +git pull + +echo Deleting remote tags... +for /f "delims=" %%x in ('git tag') do git push --delete origin %%x + +echo Deleting local tags... +for /f "delims=" %%i in ('git tag') do git tag -d %%i + +echo All tags deleted. +pause \ No newline at end of file diff --git a/.github/release.sh b/.github/release.sh new file mode 100644 index 000000000..e07d7c104 --- /dev/null +++ b/.github/release.sh @@ -0,0 +1,47 @@ +#!/usr/bin/env bash +set -e +if (( "$#" == 0 )) +then + echo "Tag has to be provided" + exit 1 +fi + +NOW=$(date +%s) +CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD) +VERSION=$1 +BASEPATH=$(cd `dirname $0`; cd ../plugin/; pwd) + +if [ -z $2 ] ; then + repos=$(ls $BASEPATH) +else + repos=${@:2} +fi + +for REMOTE in $repos +do + echo "" + echo "" + echo "Cloning $REMOTE"; + TMP_DIR="/tmp/ThinkAdminSplit" + REMOTE_URL="git@github.com:zoujingli/$REMOTE.git" + + rm -rf $TMP_DIR; + mkdir $TMP_DIR; + + ( + cd $TMP_DIR; + + git clone $REMOTE_URL . + git checkout "$CURRENT_BRANCH"; + + if [[ $(git log --pretty="%d" -n 1 | grep tag --count) -eq 0 ]]; then + echo "Releasing $REMOTE" + git tag $VERSION + git push origin --tags + fi + ) +done + +TIME=$(echo "$(date +%s) - $NOW" | bc) + +printf "Execution time: %f seconds" $TIME \ No newline at end of file diff --git a/.github/split-linux.sh b/.github/split-linux.sh new file mode 100755 index 000000000..fa41a66de --- /dev/null +++ b/.github/split-linux.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash +set -e +set -x +CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD) +BASEPATH=$(cd `dirname $0`; cd ../plugin/; pwd) +REPOS=$@ +function split() +{ + SHA1=`.github/splitsh-lite-linux --prefix=$1` + git push $2 "$SHA1:refs/heads/$CURRENT_BRANCH" -f +} + +function remote() +{ + git remote add $1 $2 || true +} + +git pull origin $CURRENT_BRANCH + +if [[ $# -eq 0 ]]; then + REPOS=$(ls $BASEPATH) +fi + +for REPO in $REPOS ; do + remote $REPO git@github.com:zoujingli/$REPO.git + split "plugin/$REPO" $REPO +done \ No newline at end of file diff --git a/.github/splitsh-lite-linux b/.github/splitsh-lite-linux new file mode 100755 index 000000000..ddefe95ad Binary files /dev/null and b/.github/splitsh-lite-linux differ diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 000000000..aead818e9 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,33 @@ +name: CI + +on: + push: + branches: + - '**' + tags: + - 'v*' + pull_request: + +jobs: + verify: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '8.1' + extensions: bcmath, curl, gd, mbstring, openssl + coverage: none + + - name: Install dependencies + run: composer install --no-interaction --prefer-dist --no-progress + + - name: Run tests + run: composer test + + - name: Run static analysis + run: composer analyse diff --git a/.github/workflows/clear.yml b/.github/workflows/clear.yml new file mode 100644 index 000000000..ca5bf2c02 --- /dev/null +++ b/.github/workflows/clear.yml @@ -0,0 +1,26 @@ +name: Clear Releases + +on: + workflow_dispatch: + +jobs: + delete: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.11' + + - name: Install dependencies and run script + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + python -m pip install --upgrade pip + pip install PyGithub + python .github/clear-github-releases.py \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 20e0bad38..ef21c7c6a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,14 +1,51 @@ +####### 可解析的提交前缀 ######## +# ci: 持续集成 +# fix: 修改 +# feat: 新增 +# refactor: 重构 +# docs: 文档 +# style: 样式 +# chore: 其他 +# build: 构建 +# pref: 优化 +# test: 测试 +############################### + on: push: - # Sequence of patterns matched against refs/tags tags: - - 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10 + - 'v*' # 仅匹配 v* 版本标签,如 v1.0、v20.15.10 name: Create Release -permissions: write-all +permissions: + contents: write jobs: + verify: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '8.1' + extensions: bcmath, curl, gd, mbstring, openssl + coverage: none + + - name: Install dependencies + run: composer install --no-interaction --prefer-dist --no-progress + + - name: Run tests + run: composer test + + - name: Run static analysis + run: composer analyse + release: + needs: + - verify runs-on: ubuntu-latest steps: - name: Checkout code @@ -16,50 +53,39 @@ jobs: with: fetch-depth: 0 - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: 18 - - - name: Install dependencies - run: npm install -g gen-git-log - - - name: Find Last Tag - id: last_tag + - name: Resolve Tags + id: tags run: | - - # 获取所有标签,按版本排序(降序) - Tags=$(git tag --list --sort=-version:refname) - - # 获取最新的标签(即列表中的第一个) - LATEST_TAG=$(echo "$Tags" | awk 'NR==1 {print $1; exit}') - - # 获取倒数第二个标签(如果存在) - if [[ -n "$Tags" ]]; then - # 使用 tail 获取除了最后一个标签之外的所有标签,然后用 head 获取第一个 - SECOND_LATEST_TAG=$(echo "$Tags" | tail -n +2 | head -n 1) - else - SECOND_LATEST_TAG="" - fi - - # 设置输出变量 - echo "::set-output name=tag_last::${LATEST_TAG:-v1.0.0}" - echo "::set-output name=tag_second::${SECOND_LATEST_TAG:-v1.0.0}" + CURRENT_TAG="${GITHUB_REF_NAME}" + PREVIOUS_TAG=$(git tag --list 'v*' --sort=-version:refname | grep -Fxv "$CURRENT_TAG" | head -n 1 || true) + + echo "CURRENT_TAG=$CURRENT_TAG" >> "$GITHUB_ENV" + echo "PREVIOUS_TAG=$PREVIOUS_TAG" >> "$GITHUB_ENV" - name: Generate Release Notes run: | rm -rf log - newTag=${{ steps.last_tag.outputs.tag_last }} - git-log -m tag -f -S ${{ steps.last_tag.outputs.tag_second }} -v ${newTag#v} + mkdir -p log + { + echo "## Release ${CURRENT_TAG}" + echo + if [ -n "${PREVIOUS_TAG}" ]; then + echo "Compare: ${PREVIOUS_TAG}..${CURRENT_TAG}" + echo + git log --pretty=format:'- %s (%h)' "${PREVIOUS_TAG}..${CURRENT_TAG}" + else + echo "Initial tagged release." + echo + git log --pretty=format:'- %s (%h)' "${CURRENT_TAG}" + fi + echo + } > "log/${CURRENT_TAG}.md" - name: Create Release - id: create_release - uses: actions/create-release@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + uses: softprops/action-gh-release@v2 with: - tag_name: ${{ steps.last_tag.outputs.tag_last }} - release_name: Release ${{ steps.last_tag.outputs.tag_last }} - body_path: log/${{steps.last_tag.outputs.tag_last}}.md + tag_name: ${{ env.CURRENT_TAG }} + name: Release ${{ env.CURRENT_TAG }} + body_path: log/${{ env.CURRENT_TAG }}.md draft: false - prerelease: false \ No newline at end of file + prerelease: false diff --git a/.github/workflows/split.yml b/.github/workflows/split.yml new file mode 100644 index 000000000..1454c0289 --- /dev/null +++ b/.github/workflows/split.yml @@ -0,0 +1,30 @@ +name: Split Repositorys + +on: [ workflow_dispatch ] + +jobs: + split: + if: github.repository == 'zoujingli/ThinkAdminDeveloper' + runs-on: ubuntu-latest + env: + SSH_PRIVATE_KEY: ${{ secrets.SPLIT_PRIVATE_KEY }} + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup Private Key + run: | + mkdir -p ~/.ssh + echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_rsa + chmod 600 ~/.ssh/id_rsa + ssh-keyscan github.com >> ~/.ssh/known_hosts + echo "StrictHostKeyChecking no" >> ~/.ssh/config + + - name: Split And Push + run: | + git config pull.rebase true + git config --global user.name "Anyon" + git config --global user.email "zoujingli@qq.com" + ./.github/split-linux.sh diff --git a/.gitignore b/.gitignore index 42f4ba84d..e900ffeb5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,34 +1,59 @@ -.env -.git -.svn -.idea -.fleet -.vscode -.DS_Store - -/vendor -/runtime -/safefile -/nbproject -/composer.lock -!composer.json - -/404.html -/.user.ini -/index.html -/public/upload -/public/404.html -/public/.user.ini -/public/index.html -/public/favicon.ico -/database/sqlite.db - -/public/static/theme/css/_*.css* -/public/static/theme/css/node_modules -/public/static/theme/css/package-lock.json - -### 屏蔽数据库脚本 -/database/migrations/*_install_*.php -/database/migrations/*_upgrade_*.php -!database/migrations/*_install*_table.php -!database/migrations/*_install_package.php +.env +.git +.svn +.idea +.fleet +.vscode +.DS_Store +.phpunit.result.cache +.phpunit.cache/ + +/log +/vendor +/runtime +/safefile +/build +/admin.phar +/nbproject +/composer.lock +!composer.json + +### 屏蔽环境文件 +/404.html +/.user.ini +/index.html +/public/upload +/public/404.html +/public/.htaccess +/public/.user.ini +/public/index.html +/public/favicon.ico +/database/*.db +/database/*.sqlite +/database/*.sqlite3 +/database/*-journal +/database/*-shm +/database/*-wal + +### 屏蔽插件文件 +/app/admin +/app/wechat + +### 屏蔽配置文件 +/config/app.php +/config/cookie.php +/config/lang.php +/config/log.php +/config/route.php +/config/session.php +/config/view.php + +/public/static/plugs +/public/static/theme +/public/static/login.js +/public/static/system.js + +### 屏蔽数据库发布产物 +/database/migrations/* +!/database/migrations/.gitkeep +!/database/migrations/.published.json diff --git a/.php-cs-fixer.php b/.php-cs-fixer.php new file mode 100644 index 000000000..b58d9a420 --- /dev/null +++ b/.php-cs-fixer.php @@ -0,0 +1,120 @@ +setRiskyAllowed(true)->setParallelConfig(new ParallelConfig(8, 24)); +$finder = Finder::create()->in(__DIR__)->exclude(['vendor', 'public', 'runtime']); +return $config->setFinder($finder)->setUsingCache(false)->setRules([ + '@PSR2' => true, + '@Symfony' => true, + '@DoctrineAnnotation' => true, + '@PhpCsFixer' => true, + 'header_comment' => [ + 'comment_type' => 'PHPDoc', + 'header' => $header, + 'separate' => 'none', + 'location' => 'after_declare_strict', + ], + 'array_syntax' => [ + 'syntax' => 'short', + ], + 'list_syntax' => [ + 'syntax' => 'short', + ], + 'blank_line_before_statement' => [ + 'statements' => [ + 'declare', + ], + ], + 'general_phpdoc_annotation_remove' => [ + 'annotations' => [ + 'author', + ], + ], + 'ordered_imports' => [ + 'imports_order' => [ + 'class', 'function', 'const', + ], + 'sort_algorithm' => 'alpha', + ], + 'single_line_comment_style' => [ + 'comment_types' => [ + ], + ], + 'yoda_style' => [ + 'always_move_variable' => false, + 'equal' => false, + 'identical' => false, + ], + 'phpdoc_align' => [ + 'align' => 'left', + ], + 'multiline_whitespace_before_semicolons' => [ + 'strategy' => 'no_multi_line', + ], + 'constant_case' => [ + 'case' => 'lower', + ], + 'encoding' => true, // PHP代码必须只使用没有BOM的UTF-8 + 'line_ending' => true, // 所有的PHP文件编码必须一致 + 'single_quote' => true, // 简单字符串应该使用单引号代替双引号 + 'no_empty_statement' => true, // 不应该存在空的结构体 + 'standardize_not_equals' => true, // 使用 <> 代替 != + 'blank_line_after_namespace' => true, // 命名空间之后空一行 + 'no_empty_phpdoc' => true, // 不应该存在空的 phpdoc + 'no_empty_comment' => true, // 不应该存在空注释 + 'no_singleline_whitespace_before_semicolons' => true, // 禁止在关闭分号前使用单行空格 + 'concat_space' => ['spacing' => 'one'], // 连接字符是否需要空格,可选配置项 none:不需要 one:一个空格 + 'no_leading_import_slash' => true, // use 语句中取消前置斜杠 + 'cast_spaces' => ['space' => 'none'], + 'class_attributes_separation' => true, + 'combine_consecutive_unsets' => true, + 'declare_strict_types' => true, + 'lowercase_static_reference' => true, + 'linebreak_after_opening_tag' => true, + 'multiline_comment_opening_closing' => true, + 'no_useless_else' => true, + 'no_unused_imports' => true, + 'not_operator_with_successor_space' => false, + 'not_operator_with_space' => false, + 'ordered_class_elements' => true, + 'php_unit_strict' => false, + 'phpdoc_separation' => false, +]); diff --git a/app/common.php b/app/common.php new file mode 100644 index 000000000..d1dcb6d6c --- /dev/null +++ b/app/common.php @@ -0,0 +1,18 @@ +redirect(sysuri('admin/login/index')); + $this->redirect(sysuri('system/login/index')); } } diff --git a/composer.json b/composer.json index 331030ef6..0cd7055d6 100644 --- a/composer.json +++ b/composer.json @@ -6,7 +6,8 @@ "description": "Application Development Framework", "keywords": [ "ThinkAdmin", - "ThinkLibrary" + "ThinkLibrary", + "WeChatDeveloper" ], "authors": [ { @@ -14,26 +15,122 @@ "email": "zoujingli@qq.com" } ], - "require": { - "php": ">=7.1", - "ext-json": "*", - "topthink/think-orm": "^2.0|^3.0", - "zoujingli/think-library": "^6.1", - "zoujingli/think-plugs-admin": "^1.0", - "zoujingli/think-plugs-wechat": "^1.0" - }, "autoload": { - "psr-0": { - "": "extend" - }, "psr-4": { - "app\\": "app" + "app\\": "app/" } }, - "config": { - "sort-packages": true, - "allow-plugins": { - "zoujingli/think-install": true + "require": { + "php": "^8.1", + "ext-gd": "*", + "ext-json": "*", + "ext-openssl": "*", + "topthink/framework": "^8.1", + "topthink/think-orm": "^4.0", + "workerman/workerman": "^5.1.9", + "zoujingli/think-plugs-account": "^8.0", + "zoujingli/think-plugs-builder": "^8.0", + "zoujingli/think-plugs-install": "^8.0", + "zoujingli/think-plugs-payment": "^8.0", + "zoujingli/think-plugs-system": "^8.0", + "zoujingli/think-plugs-wechat-client": "^8.0", + "zoujingli/think-plugs-wechat-service": "^8.0", + "zoujingli/think-plugs-wemall": "^8.0", + "zoujingli/think-plugs-worker": "^8.0", + "zoujingli/think-plugs-wuma": "^8.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.0", + "phpunit/phpunit": "^9.5|^10.0", + "phpstan/phpstan": "^2.0" + }, + "scripts": { + "sync": [ + "php-cs-fixer fix", + "@composer analyse" + ], + "rewrite-model": [ + "@php think xadmin:helper:model --reset", + "php-cs-fixer fix" + ], + "database:publish": [ + "@php think xadmin:publish" + ], + "database:migrate": [ + "@php think xadmin:publish --migrate" + ], + "build:phar": [ + "@database:publish", + "@php -d phar.readonly=0 think xadmin:builder --name=admin.phar" + ], + "test:smoke": [ + "php tests/smoke/plugin_refactor_smoke.php" + ], + "test:unit": [ + "vendor/bin/phpunit -c phpunit.xml.dist" + ], + "test": [ + "@test:smoke", + "@test:unit" + ], + "analyse": "phpstan analyse --memory-limit 2G -c phpstan.neon ./app ./config ./plugin" + }, + "repositories": [ + { + "type": "path", + "url": "plugin/think-plugs-wuma" + }, + { + "type": "path", + "url": "plugin/think-plugs-install" + }, + { + "type": "path", + "url": "plugin/think-plugs-builder" + }, + { + "type": "path", + "url": "plugin/think-plugs-worker" + }, + { + "type": "path", + "url": "plugin/think-plugs-wechat-client" + }, + { + "type": "path", + "url": "plugin/think-plugs-wechat-service" + }, + { + "type": "path", + "url": "plugin/think-plugs-system" + }, + { + "type": "path", + "url": "plugin/think-plugs-account" + }, + { + "type": "path", + "url": "plugin/think-plugs-payment" + }, + { + "type": "path", + "url": "plugin/think-plugs-wemall" + }, + { + "type": "path", + "url": "plugin/think-plugs-static" + }, + { + "type": "path", + "url": "plugin/think-library" } + ], + "minimum-stability": "dev", + "prefer-stable": true, + "config": { + "allow-plugins": { + "zoujingli/think-plugs-install": true + }, + "sort-packages": true } } diff --git a/config/app.php b/config/app.php index 275eb3b59..a6d006e96 100644 --- a/config/app.php +++ b/config/app.php @@ -1,55 +1,94 @@ '', - // 应用快速访问 - 'app_express' => true, + 'app_namespace' => '', + // 默认本地应用兼容回退(建议优先使用 route.default_app) + 'single_app' => 'index', + // 插件机制配置 + 'plugin' => [ + // 插件编码 => 前缀 或 前缀数组,配置后覆盖插件默认前缀 + 'bindings' => [], + // 插件 API 统一入口前缀,例如 /api/{plugin}/... + 'api_prefix' => 'api', + // 动态插件切换默认关闭,仅在显式开启时作为调试或兼容入口 + 'switch' => [ + 'enabled' => false, + 'query' => '_plugin', + 'header' => 'X-Plugin-App', + ], + ], // 是否启用路由 - 'with_route' => true, + 'with_route' => true, // 超级用户账号 - 'super_user' => 'admin', + 'super_user' => 'admin', // 默认时区 - 'default_timezone' => 'Asia/Shanghai', - // 应用映射(多应用模式有效) - 'app_map' => [], - // 域名绑定(多应用模式有效) - 'domain_bind' => [], - // 禁止访问(多应用模式有效) - 'deny_app_list' => [], + 'default_timezone' => 'Asia/Shanghai', + // 表现层模式:view 仅渲染页面,api 仅返回 JSON,mixed 根据控制器命名空间、Token 请求头与 Accept 自动切换 + 'presentation' => [ + 'mode' => 'mixed', + 'api_header' => 'Authorization', + ], + // 后台 JWT 有效期(秒,0 表示不过期) + 'system_token_expire' => 604800, + // 后台 JWT 认证 Cookie 名称(Authorization 优先,其次读取此 Cookie) + 'system_token_cookie' => 'system_access_token', + // 上传令牌有效期(秒) + 'system_upload_token_expire' => 1800, + // Token 会话默认有效期(秒,0 表示不过期) + 'token_session_expire' => 7200, + // Token 会话读取时是否自动续期 + 'token_session_touch' => true, + // Token 会话惰性清理间隔(秒) + 'token_session_gc_interval' => 300, + // Token 会话指定缓存仓库,留空使用默认仓库 + 'token_session_store' => '', + // 认证 Cookie 中的 Token 是否加密存储(Header 中仍使用原始 Bearer Token) + 'token_cookie_encrypt' => true, + // 认证 Cookie Token 加密密钥,留空默认复用 jwtkey + 'token_cookie_secret' => '', + // 终端账号 JWT 认证 Cookie 名称(Authorization 优先,其次读取此 Cookie) + 'account_token_cookie' => 'account_access_token', + // 存储管理器实现类(由 System 插件提供默认实现) + 'storage_manager_class' => \plugin\system\storage\StorageManager::class, // CORS 启用状态(默认开启跨域) - 'cors_on' => true, + 'cors_on' => true, // CORS 配置跨域域名(仅需填域名,留空则自动域名) - 'cors_host' => [], + 'cors_host' => [], // CORS 授权请求方法 - 'cors_methods' => 'GET,PUT,POST,PATCH,DELETE', + 'cors_methods' => 'GET,PUT,POST,PATCH,DELETE', + // CORS 是否允许携带 Cookie 等凭证 + 'cors_credentials' => false, // CORS 跨域头部字段 - 'cors_headers' => 'Api-Type,Api-Name,Api-Uuid,Jwt-Token,Api-Token,User-Form-Token,User-Token,Token', + 'cors_headers' => 'X-Device-Code,X-Device-Type', // X-Frame-Options 配置 - 'cors_frame' => 'sameorigin', + 'cors_frame' => 'sameorigin', // RBAC 登录页面(填写登录地址) - 'rbac_login' => '', + 'rbac_login' => '', // RBAC 忽略应用(填写应用名称) - 'rbac_ignore' => ['index'], + 'rbac_ignore' => ['index'], // 显示错误消息内容,仅生产模式有效 - 'error_message' => '页面错误!请稍后再试~', + 'error_message' => '页面错误!请稍后再试~', // 异常状态模板配置,仅生产模式有效 'http_exception_template' => [ 404 => syspath('public/static/theme/err/404.html'), 500 => syspath('public/static/theme/err/500.html'), ], -]; \ No newline at end of file +]; diff --git a/config/cache.php b/config/cache.php index dc78ad360..352eb9bd0 100644 --- a/config/cache.php +++ b/config/cache.php @@ -1,59 +1,62 @@ env('CACHE_TYPE', 'file'), // 缓存连接配置 - 'stores' => [ - 'file' => [ + 'stores' => [ + 'file' => [ // 驱动方式 - 'type' => 'File', + 'type' => 'File', // 缓存保存目录 - 'path' => '', + 'path' => '', // 缓存名称前缀 - 'prefix' => '', + 'prefix' => '', // 缓存有效期 0 表示永久缓存 - 'expire' => 0, + 'expire' => 0, // 缓存标签前缀 'tag_prefix' => 'tag:', // 序列化机制 - 'serialize' => [], + 'serialize' => [], ], - 'safe' => [ + 'safe' => [ // 驱动方式 - 'type' => 'File', + 'type' => 'File', // 缓存保存目录 - 'path' => syspath('safefile/cache/'), + 'path' => runpath('safefile/cache/'), // 缓存名称前缀 - 'prefix' => '', + 'prefix' => '', // 缓存有效期 0 表示永久缓存 - 'expire' => 0, + 'expire' => 0, // 缓存标签前缀 'tag_prefix' => 'tag:', // 序列化机制 - 'serialize' => [], + 'serialize' => [], ], 'redis' => [ // 驱动方式 - 'type' => 'redis', - 'host' => env('CACHE_REDIS_HOST', '127.0.0.1'), - 'port' => env('CACHE_REDIS_PORT', 6379), - 'select' => env('CACHE_REDIS_SELECT', 0), + 'type' => 'redis', + 'host' => env('CACHE_REDIS_HOST', '127.0.0.1'), + 'port' => env('CACHE_REDIS_PORT', 6379), + 'select' => env('CACHE_REDIS_SELECT', 0), 'password' => env('CACHE_REDIS_PASSWORD', ''), - ] + ], ], -]; \ No newline at end of file +]; diff --git a/config/cookie.php b/config/cookie.php index de985c001..139513dd9 100644 --- a/config/cookie.php +++ b/config/cookie.php @@ -1,32 +1,35 @@ 0, + 'expire' => 0, // cookie 保存路径 - 'path' => '/', + 'path' => '/', // cookie 有效域名 - 'domain' => '', + 'domain' => '', // httponly 访问设置 - 'httponly' => true, - // 是否使用 setcookie - 'setcookie' => true, + 'httponly' => true, + // 是否允许服务端写回 Cookie(默认关闭,开启后可回写认证与语言 Cookie) + 'setcookie' => false, // cookie 安全传输,只支持 https 协议 - 'secure' => request()->isSsl(), + 'secure' => request()->isSsl(), // samesite 安全设置,支持 'strict' 'lax' 'none' - 'samesite' => request()->isSsl() ? 'none' : 'lax', + 'samesite' => request()->isSsl() ? 'none' : 'lax', ]; diff --git a/config/database.php b/config/database.php index ea58c067d..29934877e 100644 --- a/config/database.php +++ b/config/database.php @@ -1,83 +1,86 @@ env('DB_TYPE', 'sqlite'), + 'default' => env('DB_TYPE', 'sqlite'), // 自定义时间查询规则 'time_query_rule' => [], // 自动写入时间戳字段 - 'auto_timestamp' => true, + 'auto_timestamp' => true, // 时间字段取出后的默认时间格式 'datetime_format' => 'Y-m-d H:i:s', // 数据库连接配置信息 - 'connections' => [ - 'mysql' => [ + 'connections' => [ + 'mysql' => [ // 数据库类型 - 'type' => 'mysql', + 'type' => 'mysql', // 服务器地址 - 'hostname' => env('DB_MYSQL_HOST', '127.0.0.1'), + 'hostname' => env('DB_MYSQL_HOST', '127.0.0.1'), // 服务器端口 - 'hostport' => env('DB_MYSQL_PORT', '3306'), + 'hostport' => env('DB_MYSQL_PORT', '3306'), // 数据库名 - 'database' => env('DB_MYSQL_DATABASE', 'thinkadmin'), + 'database' => env('DB_MYSQL_DATABASE', 'thinkadmin'), // 用户名 - 'username' => env('DB_MYSQL_USERNAME', 'root'), + 'username' => env('DB_MYSQL_USERNAME', 'root'), // 密码 - 'password' => env('DB_MYSQL_PASSWORD', ''), + 'password' => env('DB_MYSQL_PASSWORD', ''), // 数据库连接参数 - 'params' => [], + 'params' => [], // 数据库表前缀 - 'prefix' => env('DB_MYSQL_PREFIX', ''), + 'prefix' => env('DB_MYSQL_PREFIX', ''), // 数据库编码默认采用 utf8mb4 - 'charset' => env('DB_MYSQL_CHARSET', 'utf8mb4'), + 'charset' => env('DB_MYSQL_CHARSET', 'utf8mb4'), // 数据库部署方式:0 集中式(单一服务器),1 分布式(主从服务器) - 'deploy' => 0, + 'deploy' => 0, // 数据库读写是否分离 主从式有效 - 'rw_separate' => false, + 'rw_separate' => false, // 读写分离后 主服务器数量 - 'master_num' => 1, + 'master_num' => 1, // 指定从服务器序号 - 'slave_no' => '', + 'slave_no' => '', // 检查字段是否存在 - 'fields_strict' => true, + 'fields_strict' => true, // 是否需要断线重连 'break_reconnect' => false, // 监听SQL执行日志 - 'trigger_sql' => true, + 'trigger_sql' => true, // 开启字段类型缓存 - 'fields_cache' => isOnline(), + 'fields_cache' => isOnline(), ], 'sqlite' => [ // 数据库类型 - 'type' => 'sqlite', + 'type' => 'sqlite', // 数据库文件 - 'database' => syspath('database/sqlite.db'), + 'database' => runpath('database/sqlite.db'), // 数据库编码默认采用 utf8 - 'charset' => 'utf8', + 'charset' => 'utf8', // 监听执行日志 'trigger_sql' => true, // 其他参数字段 - 'deploy' => 0, - 'suffix' => '', - 'prefix' => '', - 'hostname' => '', - 'hostport' => '', - 'username' => '', - 'password' => '', + 'deploy' => 0, + 'suffix' => '', + 'prefix' => '', + 'hostname' => '', + 'hostport' => '', + 'username' => '', + 'password' => '', ], ], ]; diff --git a/config/lang.php b/config/lang.php index fedfb2df2..d4f633239 100644 --- a/config/lang.php +++ b/config/lang.php @@ -1,39 +1,42 @@ 'zh-cn', + 'default_lang' => 'zh-cn', // 允许的语言列表 'allow_lang_list' => ['zh-cn'], // 转义为对应语言包名称 'accept_language' => [ - 'en' => 'en-us', + 'en' => 'en-us', 'zh-hans-cn' => 'zh-cn', ], // 多语言自动侦测变量名 - 'detect_var' => 'lang', - // 多语言 Cookie 变量 - 'cookie_var' => 'lang', + 'detect_var' => 'lang', + // 多语言 Cookie 变量(禁用 Cookie 持久化) + 'cookie_var' => '__lang_disabled__', // 多语言 Header 变量 - 'header_var' => 'lang', + 'header_var' => 'lang', // 使用 Cookie 记录 - 'use_cookie' => true, + 'use_cookie' => false, // 是否支持语言分组 - 'allow_group' => false, + 'allow_group' => false, // 扩展语言包 - 'extend_list' => [], -]; \ No newline at end of file + 'extend_list' => [], +]; diff --git a/config/log.php b/config/log.php index 179bfa297..1b58bf4e0 100644 --- a/config/log.php +++ b/config/log.php @@ -1,57 +1,60 @@ 'file', + 'default' => 'file', // 日志记录级别 - 'level' => [], + 'level' => [], // 日志类型记录的通道 'type_channel' => [], // 关闭全局日志写入 - 'close' => false, + 'close' => false, // 全局日志处理 支持闭包 - 'processor' => null, + 'processor' => null, // 日志通道列表 - 'channels' => [ + 'channels' => [ 'file' => [ // 日志记录方式 - 'type' => 'File', + 'type' => 'File', // 日志保存目录 - 'path' => '', + 'path' => '', // 单文件日志写入 - 'single' => true, + 'single' => true, // 独立日志级别 - 'apart_level' => true, + 'apart_level' => true, // 每个文件大小 ( 10兆 ) - 'file_size' => 10485760, + 'file_size' => 10485760, // 日志日期格式 - 'time_format' => 'Y-m-d H:i:s', + 'time_format' => 'Y-m-d H:i:s', // 最大日志文件数量 - 'max_files' => 100, + 'max_files' => 100, // 使用JSON格式记录 - 'json' => false, + 'json' => false, // 日志处理 - 'processor' => null, + 'processor' => null, // 关闭通道日志写入 - 'close' => false, + 'close' => false, // 日志输出格式化 - 'format' => '[%s][%s] %s', + 'format' => '[%s][%s] %s', // 是否实时写入 'realtime_write' => false, ], ], -]; \ No newline at end of file +]; diff --git a/config/phinx.php b/config/phinx.php index a9ee86682..18168f8d9 100644 --- a/config/phinx.php +++ b/config/phinx.php @@ -1,19 +1,22 @@ [], @@ -21,4 +24,4 @@ return [ 'tables' => [], // 备份数据表,填写表名 'backup' => [], -]; \ No newline at end of file +]; diff --git a/config/route.php b/config/route.php index 86727191d..670845e63 100644 --- a/config/route.php +++ b/config/route.php @@ -1,58 +1,61 @@ '/', + 'pathinfo_depr' => '/', // URL伪静态后缀 - 'url_html_suffix' => 'html', + 'url_html_suffix' => 'html', // URL普通方式参数 用于自动生成 - 'url_common_param' => true, + 'url_common_param' => true, // 是否开启路由延迟解析 - 'url_lazy_route' => false, + 'url_lazy_route' => false, // 是否强制使用路由 - 'url_route_must' => false, + 'url_route_must' => false, // 合并路由规则 - 'route_rule_merge' => true, + 'route_rule_merge' => true, // 路由是否完全匹配 - 'route_complete_match' => true, + 'route_complete_match' => true, // 访问控制器层名称 - 'controller_layer' => 'controller', + 'controller_layer' => 'controller', // 空控制器名 - 'empty_controller' => 'Error', + 'empty_controller' => 'Error', // 是否使用控制器后缀 - 'controller_suffix' => false, + 'controller_suffix' => false, // 默认的路由变量规则 'default_route_pattern' => '[\w\.]+', // 是否开启请求缓存 true 自动缓存 支持设置请求缓存规则 - 'request_cache' => false, + 'request_cache' => false, // 请求缓存有效期 - 'request_cache_expire' => null, + 'request_cache_expire' => null, // 全局请求缓存排除规则 - 'request_cache_except' => [], - // 默认应用 - 'default_app' => 'index', + 'request_cache_except' => [], + // 默认本地应用 + 'default_app' => 'index', // 默认控制器名 - 'default_controller' => 'Index', + 'default_controller' => 'Index', // 默认操作名 - 'default_action' => 'index', + 'default_action' => 'index', // 操作方法后缀 - 'action_suffix' => '', + 'action_suffix' => '', // 默认JSONP格式返回的处理方法 'default_jsonp_handler' => 'jsonpReturn', // 默认JSONP处理方法 - 'var_jsonp_handler' => 'callback', -]; \ No newline at end of file + 'var_jsonp_handler' => 'callback', +]; diff --git a/config/session.php b/config/session.php deleted file mode 100644 index 5025ac6b7..000000000 --- a/config/session.php +++ /dev/null @@ -1,28 +0,0 @@ - env('SESSION_NAME', 'ssid'), - // 驱动方式 - 'type' => env('SESSION_TYPE', 'file'), - // 存储连接 - 'store' => env('SESSION_STORE', ''), - // 过期时间 - 'expire' => env('SESSION_EXPIRE', 7200), - // 文件前缀 - 'prefix' => env('SESSION_PREFIX', ''), -]; \ No newline at end of file diff --git a/config/view.php b/config/view.php index 1de5e3eb1..78c9ea7eb 100644 --- a/config/view.php +++ b/config/view.php @@ -1,42 +1,45 @@ 'Think', + 'type' => 'Think', // 默认模板渲染规则 1.解析为小写+下划线 2.全部转换小写 3.保持操作方法 - 'auto_rule' => 1, + 'auto_rule' => 1, // 模板目录名 - 'view_dir_name' => 'view', + 'view_dir_name' => 'view', // 模板文件后缀 - 'view_suffix' => 'html', + 'view_suffix' => 'html', // 模板文件名分隔符 - 'view_depr' => DIRECTORY_SEPARATOR, + 'view_depr' => DIRECTORY_SEPARATOR, // 模板缓存配置 - 'tpl_cache' => isOnline(), + 'tpl_cache' => isOnline(), // 模板引擎标签开始标记 - 'tpl_begin' => '{', + 'tpl_begin' => '{', // 模板引擎标签结束标记 - 'tpl_end' => '}', + 'tpl_end' => '}', // 标签库标签开始标记 - 'taglib_begin' => '{', + 'taglib_begin' => '{', // 标签库标签结束标记 - 'taglib_end' => '}', + 'taglib_end' => '}', // 去除HTML空格换行 - 'strip_space' => true, + 'strip_space' => true, // 标签默认过滤输出方法 'default_filter' => 'htmlentities=###,ENT_QUOTES', -]; \ No newline at end of file +]; diff --git a/config/worker.php b/config/worker.php new file mode 100644 index 000000000..32a7764ce --- /dev/null +++ b/config/worker.php @@ -0,0 +1,71 @@ + [ + 'runtime' => [ + // 'stdout_file' => syspath('safefile/worker/shared.stdout.log'), + // 'log_max_size' => 10 * 1024 * 1024, + // 'stop_timeout' => 2, + // 'event_loop' => \Workerman\Events\Event::class, + ], + 'monitor' => [ + 'files' => [ + 'enabled' => true, + 'interval' => 3, + 'paths' => ['app', 'config', 'route', 'plugin'], + 'extensions' => ['php', 'env', 'ini', 'yaml', 'yml'], + ], + 'memory' => [ + 'enabled' => true, + 'interval' => 60, + 'limit' => '1G', + ], + ], + ], + 'services' => [ + 'http' => [ + 'enabled' => true, + 'label' => 'ThinkAdmin HTTP', + 'driver' => 'http', + 'server' => [ + 'host' => '127.0.0.1', + 'port' => 2346, + 'context' => [], + ], + 'process' => [ + 'name' => 'ThinkAdminHttp', + 'count' => 4, + ], + ], + 'queue' => [ + 'enabled' => true, + 'label' => 'ThinkAdmin Queue', + 'driver' => 'queue', + 'process' => [ + 'name' => 'ThinkAdminQueue', + 'count' => 2, + ], + 'queue' => [ + 'scan_interval' => 1, + 'batch_limit' => 20, + ], + ], + ], +]; diff --git a/license b/license deleted file mode 100644 index 9d38efe8e..000000000 --- a/license +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2014~2025 Anyon - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/public/index.php b/public/index.php index 8e17dcd85..5ad1fb198 100644 --- a/public/index.php +++ b/public/index.php @@ -3,7 +3,7 @@ declare(strict_types=1); /** * // +---------------------------------------------------------------------- - * // | Payment Plugin for ThinkAdmin + * // | ThinkAdmin Web Entry * // +---------------------------------------------------------------------- * // | 版权所有 2014~2026 ThinkAdmin [ thinkadmin.top ] * // +---------------------------------------------------------------------- @@ -11,7 +11,7 @@ declare(strict_types=1); * // +---------------------------------------------------------------------- * // | 开源协议 ( https://mit-license.org ) * // | 免责声明 ( https://thinkadmin.top/disclaimer ) - * // | 会员免费 ( https://thinkadmin.top/vip-introduce ) + * // | 会员特权 ( https://thinkadmin.top/vip-introduce ) * // +---------------------------------------------------------------------- * // | gitee 代码仓库:https://gitee.com/zoujingli/ThinkAdmin * // | github 代码仓库:https://github.com/zoujingli/ThinkAdmin diff --git a/public/robots.txt b/public/robots.txt index 6c4bd3c47..d4ce9faab 100644 --- a/public/robots.txt +++ b/public/robots.txt @@ -1,4 +1,4 @@ User-agent: * Disallow: /data -Disallow: /admin +Disallow: /system Disallow: /wechat diff --git a/public/router.php b/public/router.php index bdf0b0f29..62617b17e 100644 --- a/public/router.php +++ b/public/router.php @@ -1,22 +1,24 @@