diff --git a/.circleci/conditional_config.yml b/.circleci/conditional_config.yml index 58ffe0783..181601d57 100644 --- a/.circleci/conditional_config.yml +++ b/.circleci/conditional_config.yml @@ -10,7 +10,7 @@ executors: - image: cimg/base:stable node: docker: - - image: 'cimg/node:20.12.1' + - image: "cimg/node:20.12.1" go: docker: - image: cimg/go:1.22.1 @@ -29,11 +29,11 @@ jobs: executor: go working_directory: ~/go/src/github.com/ArtalkJS/Artalk environment: - GO111MODULE: 'on' + GO111MODULE: "on" steps: - checkout - run: - name: 'Print the Go version' + name: "Print the Go version" command: > go version - restore_cache: @@ -45,7 +45,7 @@ jobs: - save_cache: key: go-mod-1.22.1-{{ checksum "go.sum" }} paths: - - '~/go/pkg/mod' + - "~/go/pkg/mod" - run: name: Run tests command: | diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 0cfca7b7b..2d853bdcd 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -40,9 +40,7 @@ module.exports = { settings: { 'import/resolver': { typescript: { - project: ['ui/artalk/tsconfig.json'].map((p) => - path.resolve(__dirname, p), - ), + project: ['ui/artalk/tsconfig.json'].map((p) => path.resolve(__dirname, p)), }, }, polyfills: ['AbortController'], diff --git a/.github/actions/comment/action.yml b/.github/actions/comment/action.yml index d5931d065..a8e27150a 100644 --- a/.github/actions/comment/action.yml +++ b/.github/actions/comment/action.yml @@ -10,7 +10,7 @@ inputs: description: The content of comment runs: - using: 'composite' + using: "composite" steps: - uses: actions/github-script@v7 env: diff --git a/.github/chglog/config.yml b/.github/chglog/config.yml index 770e730e7..1eb399858 100755 --- a/.github/chglog/config.yml +++ b/.github/chglog/config.yml @@ -4,7 +4,7 @@ info: title: CHANGELOG repository_url: https://github.com/ArtalkJS/Artalk options: - tag_filter_pattern: '^v' + tag_filter_pattern: "^v" sort: date commits: filters: diff --git a/.github/dependabot.yml b/.github/dependabot.yml index f39704ea0..4411a2deb 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -10,11 +10,11 @@ updates: schedule: interval: monthly commit-message: - prefix: 'chore(deps/go): ' + prefix: "chore(deps/go): " groups: deps: patterns: - - '*' + - "*" open-pull-requests-limit: 15 - package-ecosystem: npm @@ -22,7 +22,7 @@ updates: schedule: interval: monthly commit-message: - prefix: 'chore(deps/ui): ' + prefix: "chore(deps/ui): " groups: prod-deps: dependency-type: production @@ -35,11 +35,11 @@ updates: schedule: interval: monthly commit-message: - prefix: 'chore(deps/docs): ' + prefix: "chore(deps/docs): " groups: deps: patterns: - - '*' + - "*" open-pull-requests-limit: 15 - package-ecosystem: docker @@ -47,11 +47,11 @@ updates: schedule: interval: monthly commit-message: - prefix: 'chore(deps/docker): ' + prefix: "chore(deps/docker): " groups: deps: patterns: - - '*' + - "*" open-pull-requests-limit: 15 - package-ecosystem: github-actions @@ -59,9 +59,9 @@ updates: schedule: interval: monthly commit-message: - prefix: 'chore(deps/ci): ' + prefix: "chore(deps/ci): " groups: deps: patterns: - - '*' + - "*" open-pull-requests-limit: 15 diff --git a/.github/workflows/build-nightly.yml b/.github/workflows/build-nightly.yml index 58831743e..70fc31756 100644 --- a/.github/workflows/build-nightly.yml +++ b/.github/workflows/build-nightly.yml @@ -3,11 +3,11 @@ run-name: Build Nightly on: schedule: - - cron: '0 10 * * *' + - cron: "0 10 * * *" workflow_dispatch: inputs: dry_run: - description: 'Dry run' + description: "Dry run" type: boolean default: true @@ -59,7 +59,7 @@ jobs: with: node-version: 20.x registry-url: https://registry.npmjs.org/ - cache: 'pnpm' + cache: "pnpm" - name: Get pnpm store directory shell: bash diff --git a/.github/workflows/build-tagging.yml b/.github/workflows/build-tagging.yml index a6a6204fa..d0791a7a1 100644 --- a/.github/workflows/build-tagging.yml +++ b/.github/workflows/build-tagging.yml @@ -11,7 +11,7 @@ on: type: number outputs: version: - description: 'Version' + description: "Version" value: ${{ jobs.tagging.outputs.version }} jobs: diff --git a/.github/workflows/build-ui.yml b/.github/workflows/build-ui.yml index a2fca10ed..b54b59b95 100644 --- a/.github/workflows/build-ui.yml +++ b/.github/workflows/build-ui.yml @@ -30,7 +30,7 @@ jobs: with: node-version: 20.x registry-url: https://registry.npmjs.org/ - cache: 'pnpm' + cache: "pnpm" - name: Install dependencies run: pnpm install --frozen-lockfile diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4fc9e7a1f..3a366e6d0 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -9,13 +9,13 @@ on: workflow_dispatch: inputs: dry_run: - description: 'Dry run' + description: "Dry run" type: boolean default: true build_items: - description: 'Build items' + description: "Build items" type: string - default: 'ui,app,docker' + default: "ui,app,docker" jobs: # Tag the release version code before building diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 077a9a8dc..7d07c4e54 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -20,7 +20,7 @@ on: branches: - master schedule: - - cron: '40 17 * * 4' + - cron: "40 17 * * 4" jobs: analyze: @@ -74,4 +74,4 @@ jobs: - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v3 with: - category: '/language:${{matrix.language}}' + category: "/language:${{matrix.language}}" diff --git a/.github/workflows/docs-cn.yml b/.github/workflows/docs-cn.yml index add9cbde1..44705073d 100644 --- a/.github/workflows/docs-cn.yml +++ b/.github/workflows/docs-cn.yml @@ -2,8 +2,8 @@ name: Docs CN Mirror Deploy on: push: - branches: ['master'] - paths: ['docs/**'] + branches: ["master"] + paths: ["docs/**"] jobs: deploy: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 807f41f76..159198424 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -13,7 +13,7 @@ on: - major required: true dry_run: - description: 'Dry run?' + description: "Dry run?" type: boolean default: false @@ -72,9 +72,9 @@ jobs: uses: peter-evans/create-pull-request@v6 if: ${{ !inputs.dry_run }} with: - branch: 'release/${{ env.VERSION }}' - commit-message: 'chore: release ${{ env.VERSION }}' - title: 'chore: release ${{ env.VERSION }}' + branch: "release/${{ env.VERSION }}" + commit-message: "chore: release ${{ env.VERSION }}" + title: "chore: release ${{ env.VERSION }}" labels: release body: | ## Release ${{ env.VERSION }} 📦🚀 diff --git a/.github/workflows/repo-dispatch.yml b/.github/workflows/repo-dispatch.yml index 472df0b21..d8f775f05 100644 --- a/.github/workflows/repo-dispatch.yml +++ b/.github/workflows/repo-dispatch.yml @@ -8,7 +8,7 @@ on: type: string description: Event Type required: true - default: '' + default: "" jobs: release: diff --git a/.github/workflows/test-docs.yml b/.github/workflows/test-docs.yml index 592374d64..8ec7670c0 100644 --- a/.github/workflows/test-docs.yml +++ b/.github/workflows/test-docs.yml @@ -2,12 +2,12 @@ name: Test Docs on: push: - branches: ['*', '!release/*', '!nightly'] - paths: ['docs/**'] - tags-ignore: ['v*'] + branches: ["*", "!release/*", "!nightly"] + paths: ["docs/**"] + tags-ignore: ["v*"] pull_request: - paths: ['docs/**'] - branches: ['*', '!release/*', '!nightly'] + paths: ["docs/**"] + branches: ["*", "!release/*", "!nightly"] jobs: test_docs: diff --git a/.github/workflows/test-frontend.yml b/.github/workflows/test-frontend.yml index f8a89144a..4613af325 100644 --- a/.github/workflows/test-frontend.yml +++ b/.github/workflows/test-frontend.yml @@ -2,12 +2,12 @@ name: Test Frontend on: push: - branches: ['*', '!release/*', '!nightly'] - paths: ['ui/**'] - tags-ignore: ['v*'] + branches: ["*", "!release/*", "!nightly"] + paths: ["ui/**"] + tags-ignore: ["v*"] pull_request: - paths: ['ui/**'] - branches: ['*', '!release/*', '!nightly'] + paths: ["ui/**"] + branches: ["*", "!release/*", "!nightly"] jobs: test_ui: @@ -42,5 +42,5 @@ jobs: run: pnpm test # https://github.com/arethetypeswrong/arethetypeswrong.github.io - - name: 'arethetypeswrong test' + - name: "arethetypeswrong test" run: pnpm -F artalk attw || true diff --git a/.goreleaser.yml b/.goreleaser.yml index b512606a8..f11688c9b 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -33,7 +33,7 @@ builds: env: - CC=x86_64-linux-gnu-gcc - CXX=x86_64-linux-gnu-g++ - binary: '{{.ProjectName}}' + binary: "{{.ProjectName}}" main: ./main.go ldflags: &common_ldflags | -X github.com/ArtalkJS/Artalk/internal/config.Version={{.Version}} @@ -49,7 +49,7 @@ builds: env: - CC=aarch64-linux-gnu-gcc - CXX=aarch64-linux-gnu-g++ - binary: '{{.ProjectName}}' + binary: "{{.ProjectName}}" main: ./main.go ldflags: *common_ldflags @@ -60,11 +60,11 @@ builds: goarch: - arm goarm: - - '7' + - "7" env: - CC=arm-linux-gnueabihf-gcc - CXX=arm-linux-gnueabihf-g++ - binary: '{{.ProjectName}}' + binary: "{{.ProjectName}}" main: ./main.go ldflags: *common_ldflags @@ -81,7 +81,7 @@ builds: env: - CC=o64-clang - CXX=o64-clang++ - binary: '{{.ProjectName}}' + binary: "{{.ProjectName}}" main: ./main.go ldflags: *common_ldflags @@ -94,7 +94,7 @@ builds: env: - CC=oa64-clang - CXX=oa64-clang++ - binary: '{{.ProjectName}}' + binary: "{{.ProjectName}}" main: ./main.go ldflags: *common_ldflags @@ -111,7 +111,7 @@ builds: env: - CC=x86_64-w64-mingw32-gcc - CXX=x86_64-w64-mingw32-g++ - binary: '{{.ProjectName}}' + binary: "{{.ProjectName}}" main: ./main.go ldflags: *common_ldflags flags: @@ -129,7 +129,7 @@ builds: env: - CC=/llvm-mingw/bin/aarch64-w64-mingw32-gcc - CXX=/llvm-mingw/bin/aarch64-w64-mingw32-g++ - binary: '{{.ProjectName}}' + binary: "{{.ProjectName}}" main: ./main.go ldflags: *common_ldflags flags: *win_common_flags @@ -144,7 +144,7 @@ archives: - linux-arm7 - linux-arm64 - darwin-arm64 - name_template: '{{.ProjectName}}_v{{.Version}}_{{.Os}}_{{.Arch}}{{.Arm}}' + name_template: "{{.ProjectName}}_v{{.Version}}_{{.Os}}_{{.Arch}}{{.Arm}}" format: tar.gz format_overrides: - goos: windows @@ -156,10 +156,10 @@ archives: - artalk.yml checksum: - name_template: 'checksums.txt' + name_template: "checksums.txt" snapshot: - name_template: '{{.Version}}-SNAPSHOT-{{.ShortCommit}}' + name_template: "{{.Version}}-SNAPSHOT-{{.ShortCommit}}" # changelog: # sort: asc diff --git a/.prettierignore b/.prettierignore index be2ce0c2a..2dd50a18a 100644 --- a/.prettierignore +++ b/.prettierignore @@ -4,6 +4,14 @@ CDN.html internal/template/notify_tpl/default.html # generated files +# @link https://github.com/prettier/prettier/issues/3634 +pnpm-lock.yaml +**/auto-imports.d.ts +**/components.d.ts +**/typed-router.d.ts +ui/artalk/src/api/v2.ts docs/swagger/swagger.json docs/swagger/swagger.yaml -pnpm-lock.yaml \ No newline at end of file +docs/** +ui/artalk-sidebar/src/lib/md5.js +ui/artalk/src/lib/detect.ts diff --git a/.prettierrc b/.prettierrc index c2c65a1da..7e674532e 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,6 +1,26 @@ { - "semi": false, "arrowParens": "always", + "bracketSpacing": true, + "htmlWhitespaceSensitivity": "css", + "insertPragma": false, + "bracketSameLine": false, + "jsxSingleQuote": false, + "printWidth": 100, + "proseWrap": "preserve", + "quoteProps": "as-needed", + "requirePragma": false, + "semi": false, "singleQuote": true, - "htmlWhitespaceSensitivity": "ignore" + "tabWidth": 2, + "trailingComma": "all", + "useTabs": false, + "vueIndentScriptAndStyle": false, + "overrides": [ + { + "files": ["*.yaml", "*.yml"], + "options": { + "singleQuote": false + } + } + ] } diff --git a/conf/artalk.example.simple.yml b/conf/artalk.example.simple.yml index 759a9b1a3..47e546a9d 100644 --- a/conf/artalk.example.simple.yml +++ b/conf/artalk.example.simple.yml @@ -1,6 +1,6 @@ -host: '0.0.0.0' +host: "0.0.0.0" port: 23366 -app_key: '' +app_key: "" debug: false locale: en timezone: Asia/Shanghai @@ -9,12 +9,12 @@ login_timeout: 259200 db: type: sqlite file: ./data/artalk.db - table_prefix: '' + table_prefix: "" name: artalk host: localhost port: 3306 user: root - password: '' + password: "" charset: utf8mb4 ssl: false prepare_stmt: true @@ -26,30 +26,30 @@ cache: type: builtin expires: 30 warm_up: false - server: '' + server: "" redis: network: tcp - username: '' - password: '' + username: "" + password: "" db: 0 trusted_domains: [] ssl: enabled: false - cert_path: '' - key_path: '' + cert_path: "" + key_path: "" moderator: pending_default: false api_fail_block: false - akismet_key: '' + akismet_key: "" tencent: enabled: false - secret_id: '' - secret_key: '' + secret_id: "" + secret_key: "" region: ap-guangzhou aliyun: enabled: false - access_key_id: '' - access_key_secret: '' + access_key_id: "" + access_key_secret: "" region: cn-shanghai keywords: enabled: false @@ -65,17 +65,17 @@ captcha: action_limit: 3 action_reset: 60 turnstile: - site_key: '' - secret_key: '' + site_key: "" + secret_key: "" recaptcha: - site_key: '' - secret_key: '' + site_key: "" + secret_key: "" hcaptcha: - site_key: '' - secret_key: '' + site_key: "" + secret_key: "" geetest: - captcha_id: '' - captcha_key: '' + captcha_id: "" + captcha_key: "" img_upload: enabled: true path: ./data/artalk-img/ @@ -88,18 +88,18 @@ img_upload: email: enabled: false send_type: smtp - send_name: '{{reply_nick}}' + send_name: "{{reply_nick}}" send_addr: noreply@example.com - mail_subject: '[{{site_name}}] You got a reply from @{{reply_nick}}' + mail_subject: "[{{site_name}}] You got a reply from @{{reply_nick}}" mail_tpl: default smtp: host: smtp.qq.com port: 587 username: example@qq.com - password: '' + password: "" ali_dm: - access_key_id: '' - access_key_secret: '' + access_key_id: "" + access_key_secret: "" account_name: noreply@example.com admin_notify: notify_tpl: default @@ -108,10 +108,10 @@ admin_notify: email: enabled: true mail_subject: '[{{site_name}}] Post "{{page_title}}" has new a comment' - mail_tpl: '' + mail_tpl: "" telegram: enabled: false - api_token: '' + api_token: "" receivers: - 7777777 bark: @@ -119,38 +119,38 @@ admin_notify: server: http://day.app/xxxxxxx/ lark: enabled: false - webhook_url: '' + webhook_url: "" webhook: enabled: false - url: '' + url: "" ding_talk: enabled: false - token: '' - secret: '' + token: "" + secret: "" slack: enabled: false - oauth_token: '' + oauth_token: "" receivers: - - 'CHANNEL_ID' + - "CHANNEL_ID" line: enabled: false - channel_secret: '' - channel_access_token: '' + channel_secret: "" + channel_access_token: "" receivers: - USER_ID_1 - GROUP_ID_1 frontend: - placeholder: '' - noComment: '' - sendBtn: '' + placeholder: "" + noComment: "" + sendBtn: "" editorTravel: true emoticons: https://cdn.jsdelivr.net/gh/ArtalkJS/Emoticons/grps/default.json vote: true voteDown: false uaBadge: false listSort: true - pvEl: '#ArtalkPV' - countEl: '#ArtalkCount' + pvEl: "#ArtalkPV" + countEl: "#ArtalkCount" preview: true flatMode: auto nestMax: 2 diff --git a/conf/artalk.example.yml b/conf/artalk.example.yml index 910256d1a..7c1989e71 100644 --- a/conf/artalk.example.yml +++ b/conf/artalk.example.yml @@ -1,11 +1,11 @@ # Listen host -host: '0.0.0.0' +host: "0.0.0.0" # Listen port port: 23366 # App Key (for generation of JWT) -app_key: '' +app_key: "" # Debug mode debug: false @@ -31,7 +31,7 @@ db: # Database file (only for SQLite) file: ./data/artalk.db # Table prefix (e.g. "atk_") - table_prefix: '' + table_prefix: "" # -- The following is not necessary for SQLite -- # Database name name: artalk @@ -42,7 +42,7 @@ db: # Database user user: root # Database password - password: '' + password: "" # Database charset charset: utf8mb4 # Enable SSL mode @@ -69,15 +69,15 @@ cache: warm_up: false # -- The following is not necessary for `builtin` cache -- # Cache server address (e.g. "localhost:6379") - server: '' + server: "" # Redis config redis: # Connection type ["tcp", "unix"] network: tcp # Redis username - username: '' + username: "" # Redis password - password: '' + password: "" # Redis database number (e.g. 0) db: 0 @@ -91,10 +91,10 @@ ssl: enabled: false # Certificate file path # (e.g. "/etc/letsencrypt/live/example.com/fullchain.pem") - cert_path: '' + cert_path: "" # Key file path # (e.g. "/etc/letsencrypt/live/example.com/privkey.pem") - key_path: '' + key_path: "" # Moderator # -- Comment examination before being public -- @@ -105,22 +105,22 @@ moderator: api_fail_block: false # Akismet Key # (Akismet anti-spam service, https://akismet.com) - akismet_key: '' + akismet_key: "" # Tencent Cloud Content Security # (Auto review comments with Tencent Cloud Content Security) # -- see https://cloud.tencent.com/document/product/1124/64508 -- tencent: enabled: false - secret_id: '' - secret_key: '' + secret_id: "" + secret_key: "" region: ap-guangzhou # Aliyun Content Security # (Auto review comments with Aliyun Content Security) # -- see https://help.aliyun.com/document_detail/28417.html -- aliyun: enabled: false - access_key_id: '' - access_key_secret: '' + access_key_id: "" + access_key_secret: "" region: cn-shanghai # Keyword filter (local offline dictionary) keywords: @@ -150,21 +150,21 @@ captcha: # Turnstile # (https://www.cloudflare.com/products/turnstile/) turnstile: - site_key: '' - secret_key: '' + site_key: "" + secret_key: "" # reCaptcha # (https://www.google.com/recaptcha/about/) recaptcha: - site_key: '' - secret_key: '' + site_key: "" + secret_key: "" # hCaptcha (https://www.hcaptcha.com/) hcaptcha: - site_key: '' - secret_key: '' + site_key: "" + secret_key: "" # Geetest (https://www.geetest.com) geetest: - captcha_id: '' - captcha_key: '' + captcha_id: "" + captcha_key: "" # Upload img_upload: @@ -192,11 +192,11 @@ email: # Send method ["smtp", "ali_dm", "sendmail"] send_type: smtp # Nick name of sender - send_name: '{{reply_nick}}' + send_name: "{{reply_nick}}" # Email address of sender send_addr: noreply@example.com # Email subject - mail_subject: '[{{site_name}}] You got a reply from @{{reply_nick}}' + mail_subject: "[{{site_name}}] You got a reply from @{{reply_nick}}" # Email template file (set to file path to use custom template) mail_tpl: default # SMTP send (set send method to "smtp" to enable) @@ -208,12 +208,12 @@ email: # Email address of sender username: example@qq.com # Password - password: '' + password: "" # Aliyun mail push # (set send method to "ali_dm" to enable; see: https://help.aliyun.com/document_detail/29444.html) ali_dm: - access_key_id: '' - access_key_secret: '' + access_key_id: "" + access_key_secret: "" account_name: noreply@example.com # Multi-Push @@ -234,11 +234,11 @@ admin_notify: # Email subject (email subject sent to admin) mail_subject: '[{{site_name}}] Post "{{page_title}}" has new a comment' # Admin email template file (set to file path to use custom template) - mail_tpl: '' + mail_tpl: "" # Telegram telegram: enabled: false - api_token: '' + api_token: "" receivers: - 7777777 # Bark @@ -247,26 +247,26 @@ admin_notify: server: http://day.app/xxxxxxx/ lark: enabled: false - webhook_url: '' + webhook_url: "" # WebHook webhook: enabled: false - url: '' + url: "" ding_talk: enabled: false - token: '' - secret: '' + token: "" + secret: "" # Slack slack: enabled: false - oauth_token: '' + oauth_token: "" receivers: - - 'CHANNEL_ID' + - "CHANNEL_ID" # LINE line: enabled: false - channel_secret: '' - channel_access_token: '' + channel_secret: "" + channel_access_token: "" receivers: - USER_ID_1 - GROUP_ID_1 @@ -274,11 +274,11 @@ admin_notify: # UI Settings frontend: # Comment box placeholder - placeholder: '' + placeholder: "" # Text to display when there is - noComment: '' + noComment: "" # Text of the send button - sendBtn: '' + sendBtn: "" # Movable comment box editorTravel: true # Emoticons @@ -292,9 +292,9 @@ frontend: # Comment sorting listSort: true # Page PV binding element - pvEl: '#ArtalkPV' + pvEl: "#ArtalkPV" # Comment count binding element - countEl: '#ArtalkCount' + countEl: "#ArtalkCount" # Editor real-time preview preview: true # Flatten mode ["auto", true, false] diff --git a/conf/artalk.example.zh-CN.yml b/conf/artalk.example.zh-CN.yml index 77a416f8a..4e19b02f2 100644 --- a/conf/artalk.example.zh-CN.yml +++ b/conf/artalk.example.zh-CN.yml @@ -1,11 +1,11 @@ # 服务器地址 -host: '0.0.0.0' +host: "0.0.0.0" # 服务器端口 port: 23366 # 加密密钥 -app_key: '' +app_key: "" # 调试模式 debug: false @@ -37,11 +37,11 @@ db: # 数据库账户 user: root # 数据库密码 - password: '' + password: "" # 编码格式 charset: utf8mb4 # 表前缀 (例如:"atk_") - table_prefix: '' + table_prefix: "" # 启用 SSL ssl: false # 预编译语句 @@ -65,15 +65,15 @@ cache: # 缓存启动预热 (程序启动时预热缓存) warm_up: false # 缓存服务器地址 (例如:"localhost:6379") - server: '' + server: "" # Redis 配置 redis: # 连接方式 ["tcp", "unix"] network: tcp # 用户名 - username: '' + username: "" # 密码 - password: '' + password: "" # 数据库编号 (例如使用零号数据库填写 0) db: 0 @@ -85,9 +85,9 @@ ssl: # 启用 SSL enabled: false # 证书文件路径 - cert_path: '' + cert_path: "" # 密钥文件路径 - key_path: '' + key_path: "" # 管理员账户 admin_users: @@ -105,20 +105,20 @@ moderator: api_fail_block: false # Akismet Key # (Akismet 反垃圾服务,https://akismet.com) - akismet_key: '' + akismet_key: "" # 腾讯云文本内容安全 # (https://cloud.tencent.com/document/product/1124/64508) tencent: enabled: false - secret_id: '' - secret_key: '' + secret_id: "" + secret_key: "" region: ap-guangzhou # 阿里云内容安全 # (https://help.aliyun.com/document_detail/28417.html) aliyun: enabled: false - access_key_id: '' - access_key_secret: '' + access_key_id: "" + access_key_secret: "" region: cn-shanghai # 关键词过滤 (本地离线词库) keywords: @@ -148,21 +148,21 @@ captcha: # Turnstile # (https://www.cloudflare.com/products/turnstile/) turnstile: - site_key: '' - secret_key: '' + site_key: "" + secret_key: "" # reCaptcha # (https://www.google.com/recaptcha/about/) recaptcha: - site_key: '' - secret_key: '' + site_key: "" + secret_key: "" # hCaptcha (https://www.hcaptcha.com/) hcaptcha: - site_key: '' - secret_key: '' + site_key: "" + secret_key: "" # Geetest 极验 (https://www.geetest.com) geetest: - captcha_id: '' - captcha_key: '' + captcha_id: "" + captcha_key: "" # IP 属地 ip_region: @@ -200,11 +200,11 @@ email: # 发送方式 ["smtp", "ali_dm", "sendmail"] send_type: smtp # 发信人昵称 - send_name: '{{reply_nick}}' + send_name: "{{reply_nick}}" # 发信人地址 send_addr: noreply@example.com # 邮件标题 - mail_subject: '[{{site_name}}] 您收到了来自 @{{reply_nick}} 的回复' + mail_subject: "[{{site_name}}] 您收到了来自 @{{reply_nick}} 的回复" # 邮件模板文件 (填入文件路径使用自定义模板) mail_tpl: default # SMTP 发送 (启用请将发送方式设为 "smtp") @@ -216,12 +216,12 @@ email: # 用户名 username: example@qq.com # 密码 - password: '' + password: "" # 阿里云邮件推送 # (启用请将发送方式设为 "ali_dm";参考:https://help.aliyun.com/document_detail/29444.html) ali_dm: - access_key_id: '' - access_key_secret: '' + access_key_id: "" + access_key_secret: "" account_name: noreply@example.com # 多元推送 @@ -237,13 +237,13 @@ admin_notify: # 开启 (当使用其他推送方式时,可以关闭管理员邮件通知) enabled: true # 邮件标题 (发送给管理员的邮件标题) - mail_subject: '[{{site_name}}] 您的文章「{{page_title}}」有新回复' + mail_subject: "[{{site_name}}] 您的文章「{{page_title}}」有新回复" # 管理员邮件模板文件 (填入文件路径使用自定义模板) - mail_tpl: '' + mail_tpl: "" # Telegram telegram: enabled: false - api_token: '' + api_token: "" receivers: - 7777777 # Bark @@ -253,27 +253,27 @@ admin_notify: # 飞书 lark: enabled: false - webhook_url: '' + webhook_url: "" # WebHook webhook: enabled: false - url: '' + url: "" # 钉钉 ding_talk: enabled: false - token: '' - secret: '' + token: "" + secret: "" # Slack slack: enabled: false - oauth_token: '' + oauth_token: "" receivers: - CHANNEL_ID # LINE line: enabled: false - channel_secret: '' - channel_access_token: '' + channel_secret: "" + channel_access_token: "" receivers: - USER_ID_1 - GROUP_ID_1 @@ -281,11 +281,11 @@ admin_notify: # 界面配置 frontend: # 评论框占位文字 - placeholder: '' + placeholder: "" # 无评论显示文字 - noComment: '' + noComment: "" # 发送按钮文字 - sendBtn: '' + sendBtn: "" # 评论框穿梭 editorTravel: true # 表情包 @@ -299,9 +299,9 @@ frontend: # 评论排序功能 listSort: true # 页面 PV 绑定元素 - pvEl: '#ArtalkPV' + pvEl: "#ArtalkPV" # 评论数绑定元素 - countEl: '#ArtalkCount' + countEl: "#ArtalkCount" # 编辑器实时预览功能 preview: true # 平铺模式 ["auto", true, false] diff --git a/docker-compose.yml b/docker-compose.yml index 44b137591..cdad2d3a2 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,4 +1,4 @@ -version: '3.5' +version: "3.5" services: artalk: container_name: artalk diff --git a/i18n/en.yml b/i18n/en.yml index 251659136..1c296db16 100644 --- a/i18n/en.yml +++ b/i18n/en.yml @@ -1,76 +1,76 @@ -'Access denied': -'Account': -'Admin access required': -'Admin': -'Cannot reply to this comment': -'Captcha required': -'Checking for updates': -'Comment count': -'Comment failed': -'Comment': -'Config file read failed': -'Confirm to continue?': -'Contains invalid URL': -'Create admin account': -'Current version is the latest': -'Downloading': -'Email': -'Enter {{name}}': -'Export complete': -'Export error': -'File': -'First comment': -'Image exceeds {{file_size}} limit': -'Image upload forbidden': -'Import complete': -'Invalid request': -'Invalid request. Please check your `trusted_domains` config.': -'Invalid {{name}}': -'Link': -'Login failed': -'Name': -'New version available': -'Nickname': -'No comment': -'Notify': -'Page fetch failed': -'Page': -'Parameter': -'Parent comment': -'Password update failed': -'Password updated': -'Password': -'Pending': -'Please review': -'Retype {{name}}': -'Save failed': -'Saving': -'Services restart complete': -'Site `{{name}}` not found. Please create it in control center.': -'Site': -'Sub-comment': -'Target Site': -'Task executing in background, please wait...': -'Task in progress, please wait a moment': -'Type': -'URL Resolver': -'Unable to get `{{name}}`': -'Unspecified': -'Unsupported formats': -'Update complete': -'Update failed': -'Upload image via {{method}} failed': -'User': -'Username': -'Verification failed': -'Working directory retrieval failed': -'Wrong captcha': -'{{count}} items imported': -'{{done}} of {{total}} done': -'{{name}} already exists': -'{{name}} cannot be empty': -'{{name}} creation failed': -'{{name}} deletion failed': -'{{name}} is required': -'{{name}} not found': -'{{name}} save failed': +"Access denied": +"Account": +"Admin access required": +"Admin": +"Cannot reply to this comment": +"Captcha required": +"Checking for updates": +"Comment count": +"Comment failed": +"Comment": +"Config file read failed": +"Confirm to continue?": +"Contains invalid URL": +"Create admin account": +"Current version is the latest": +"Downloading": +"Email": +"Enter {{name}}": +"Export complete": +"Export error": +"File": +"First comment": +"Image exceeds {{file_size}} limit": +"Image upload forbidden": +"Import complete": +"Invalid request": +"Invalid request. Please check your `trusted_domains` config.": +"Invalid {{name}}": +"Link": +"Login failed": +"Name": +"New version available": +"Nickname": +"No comment": +"Notify": +"Page fetch failed": +"Page": +"Parameter": +"Parent comment": +"Password update failed": +"Password updated": +"Password": +"Pending": +"Please review": +"Retype {{name}}": +"Save failed": +"Saving": +"Services restart complete": +"Site `{{name}}` not found. Please create it in control center.": +"Site": +"Sub-comment": +"Target Site": +"Task executing in background, please wait...": +"Task in progress, please wait a moment": +"Type": +"URL Resolver": +"Unable to get `{{name}}`": +"Unspecified": +"Unsupported formats": +"Update complete": +"Update failed": +"Upload image via {{method}} failed": +"User": +"Username": +"Verification failed": +"Working directory retrieval failed": +"Wrong captcha": +"{{count}} items imported": +"{{done}} of {{total}} done": +"{{name}} already exists": +"{{name}} cannot be empty": +"{{name}} creation failed": +"{{name}} deletion failed": +"{{name}} is required": +"{{name}} not found": +"{{name}} save failed": diff --git a/i18n/fr.yml b/i18n/fr.yml index b837186c5..78f6d1197 100644 --- a/i18n/fr.yml +++ b/i18n/fr.yml @@ -1,76 +1,76 @@ -'Access denied': Accès refusé -'Account': Compte -'Admin access required': Accès administrateur requis -'Admin': Administrateur -'Cannot reply to this comment': Impossible de répondre à ce commentaire -'Captcha required': Captcha requis -'Checking for updates': Vérification des mises à jour -'Comment count': Nombre de commentaires -'Comment failed': Le commentaire a écho -'Comment': Commentaire -'Config file read failed': Échec de la lecture du fichier de configuration -'Confirm to continue?': Confirmez pour continuer? -'Contains invalid URL': Contient une URL invalide -'Create admin account': Créer un compte administrateur -'Current version is the latest': La version actuelle est la plus récente -'Downloading': Téléchargement -'Email': Email -'Enter {{name}}': Entrez {{name}} -'Export complete': Exportation terminée -'Export error': Erreur d'exportation -'File': Fichier -'First comment': Premier commentaire -'Image exceeds {{file_size}} limit': L'image dépasse la limite de {{file_size}} -'Image upload forbidden': Téléchargement d'images interdit -'Import complete': Importation complète -'Invalid request': Requête invalide -'Invalid request. Please check your `trusted_domains` config.': Requête invalide. Veuillez vérifier votre configuration `trusted_domains`. -'Invalid {{name}}': '{{name}} invalide' -'Link': Lien -'Login failed': La connexion a échoué -'Name': Nom -'New version available': Nouvelle version disponible -'Nickname': Surnom -'No comment': Pas de commentaire -'Notify': Notifier -'Page fetch failed': Échec de la récupération de la page -'Page': Page -'Parameter': Paramètre -'Parent comment': Commentaire parent -'Password update failed': La mise à jour du mot de passe a échoué -'Password updated': Mot de passe mis à jour -'Password': Mot de passe -'Pending': En attente -'Please review': Veuillez réviser -'Retype {{name}}': 'Saisir à nouveau {{name}}' -'Save failed': L'enregistrement a échoué -'Saving': Sauver -'Services restart complete': Redémarrage des services terminé -'Site `{{name}}` not found. Please create it in control center.': Le site `{{name}}` n'a pas été trouvé. Veuillez le créer dans le centre de contrôle. -'Site': Site -'Sub-comment': Sous-commentaire -'Target Site': Site cible -'Task executing in background, please wait...': Tâche exécutée en arrière-plan, veuillez patienter... -'Task in progress, please wait a moment': Tâche en cours, veuillez patienter un instant -'Type': Type -'URL Resolver': Résolveur d'URL -'Unable to get `{{name}}`': "Impossible d'obtenir `{{name}}`" -'Unspecified': Non spécifié -'Unsupported formats': Formats non supporté -'Update complete': Mise à jour terminée -'Update failed': Échec de la mise à jour -'Upload image via {{method}} failed': "Échec de l'envoie d'une image via {{méthod}}" -'User': Utilisateur -'Username': Nom d'utilisateur -'Verification failed': Échec de la vérification -'Working directory retrieval failed': Échec de la récupération du répertoire de travail -'Wrong captcha': Mauvais captcha -'{{count}} items imported': '{{count}} articles importés' -'{{done}} of {{total}} done': '{{done}} de {{total}} fait' -'{{name}} already exists': '{{name}} existe déjà' -'{{name}} cannot be empty': '{{name}} ne peut pas être vide' -'{{name}} creation failed': 'La création de {{name}} a échoué' -'{{name}} deletion failed': 'Échec de la suppression de {{name}}' -'{{name}} is required': '{{name}} est obligatoire' -'{{name}} not found': '{{name}} introuvable' -'{{name}} save failed': 'Échec de la sauvegarde de {{name}}' +"Access denied": Accès refusé +"Account": Compte +"Admin access required": Accès administrateur requis +"Admin": Administrateur +"Cannot reply to this comment": Impossible de répondre à ce commentaire +"Captcha required": Captcha requis +"Checking for updates": Vérification des mises à jour +"Comment count": Nombre de commentaires +"Comment failed": Le commentaire a écho +"Comment": Commentaire +"Config file read failed": Échec de la lecture du fichier de configuration +"Confirm to continue?": Confirmez pour continuer? +"Contains invalid URL": Contient une URL invalide +"Create admin account": Créer un compte administrateur +"Current version is the latest": La version actuelle est la plus récente +"Downloading": Téléchargement +"Email": Email +"Enter {{name}}": Entrez {{name}} +"Export complete": Exportation terminée +"Export error": Erreur d'exportation +"File": Fichier +"First comment": Premier commentaire +"Image exceeds {{file_size}} limit": L'image dépasse la limite de {{file_size}} +"Image upload forbidden": Téléchargement d'images interdit +"Import complete": Importation complète +"Invalid request": Requête invalide +"Invalid request. Please check your `trusted_domains` config.": Requête invalide. Veuillez vérifier votre configuration `trusted_domains`. +"Invalid {{name}}": "{{name}} invalide" +"Link": Lien +"Login failed": La connexion a échoué +"Name": Nom +"New version available": Nouvelle version disponible +"Nickname": Surnom +"No comment": Pas de commentaire +"Notify": Notifier +"Page fetch failed": Échec de la récupération de la page +"Page": Page +"Parameter": Paramètre +"Parent comment": Commentaire parent +"Password update failed": La mise à jour du mot de passe a échoué +"Password updated": Mot de passe mis à jour +"Password": Mot de passe +"Pending": En attente +"Please review": Veuillez réviser +"Retype {{name}}": "Saisir à nouveau {{name}}" +"Save failed": L'enregistrement a échoué +"Saving": Sauver +"Services restart complete": Redémarrage des services terminé +"Site `{{name}}` not found. Please create it in control center.": Le site `{{name}}` n'a pas été trouvé. Veuillez le créer dans le centre de contrôle. +"Site": Site +"Sub-comment": Sous-commentaire +"Target Site": Site cible +"Task executing in background, please wait...": Tâche exécutée en arrière-plan, veuillez patienter... +"Task in progress, please wait a moment": Tâche en cours, veuillez patienter un instant +"Type": Type +"URL Resolver": Résolveur d'URL +"Unable to get `{{name}}`": "Impossible d'obtenir `{{name}}`" +"Unspecified": Non spécifié +"Unsupported formats": Formats non supporté +"Update complete": Mise à jour terminée +"Update failed": Échec de la mise à jour +"Upload image via {{method}} failed": "Échec de l'envoie d'une image via {{méthod}}" +"User": Utilisateur +"Username": Nom d'utilisateur +"Verification failed": Échec de la vérification +"Working directory retrieval failed": Échec de la récupération du répertoire de travail +"Wrong captcha": Mauvais captcha +"{{count}} items imported": "{{count}} articles importés" +"{{done}} of {{total}} done": "{{done}} de {{total}} fait" +"{{name}} already exists": "{{name}} existe déjà" +"{{name}} cannot be empty": "{{name}} ne peut pas être vide" +"{{name}} creation failed": "La création de {{name}} a échoué" +"{{name}} deletion failed": "Échec de la suppression de {{name}}" +"{{name}} is required": "{{name}} est obligatoire" +"{{name}} not found": "{{name}} introuvable" +"{{name}} save failed": "Échec de la sauvegarde de {{name}}" diff --git a/i18n/zh-CN.yml b/i18n/zh-CN.yml index 50fa556d6..704c7f156 100644 --- a/i18n/zh-CN.yml +++ b/i18n/zh-CN.yml @@ -1,76 +1,76 @@ -'Access denied': 无权限 -'Account': 账户 -'Admin access required': 需要管理员权限 -'Admin': 管理员 -'Cannot reply to this comment': 无法回复此评论 -'Captcha required': 需要验证码 -'Checking for updates': 正在检查更新 -'Comment count': 评论数 -'Comment failed': 评论失败 -'Comment': 评论 -'Config file read failed': 配置文件读取失败 -'Confirm to continue?': 确认继续? -'Contains invalid URL': 包含无效的 URL -'Create admin account': 创建管理员账户 -'Current version is the latest': 当前版本已是最新的 -'Downloading': 下载中 -'Email': 邮箱 -'Enter {{name}}': 输入{{name}} -'Export complete': 导出完毕 -'Export error': 导出失败 -'File': 文件 -'First comment': 第一条评论 -'Image exceeds {{file_size}} limit': 图片超过大小限制 {{file_size}} -'Image upload forbidden': 禁止上传图片 -'Import complete': 导入完毕 -'Invalid request': 无效的请求 -'Invalid request. Please check your `trusted_domains` config.': 请求无效, 请检查 `trusted_domains` 配置项 -'Invalid {{name}}': 无效的{{name}} -'Link': 链接 -'Login failed': 登录失败 -'Name': 名称 -'New version available': 有更新可用 -'Nickname': 昵称 -'No comment': 无评论 -'Notify': 通知 -'Page fetch failed': 页面获取失败 -'Page': 页面 -'Parameter': 参数 -'Parent comment': 父评论 -'Password update failed': 密码修改失败 -'Password updated': 密码已修改 -'Password': 密码 -'Pending': 待审核 -'Please review': 请过目 -'Retype {{name}}': 重新输入{{name}} -'Save failed': 保存失败 -'Saving': 保存中 -'Services restart complete': 服务重启完毕 -'Site `{{name}}` not found. Please create it in control center.': 未找到站点:`{{name}}`,请在控制台创建站点 -'Site': 站点 -'Sub-comment': 子评论 -'Target Site': 目标站点 -'Task executing in background, please wait...': 任务已开始在后台执行,请稍后... -'Task in progress, please wait a moment': 任务执行中,请稍后 -'Type': 类型 -'URL Resolver': URL 解析器 -'Unable to get `{{name}}`': 无法获取 `{{name}}` -'Unspecified': 未指定 -'Unsupported formats': 不支持的格式 -'Update complete': 更新完毕 -'Update failed': 更新失败 -'Upload image via {{method}} failed': 通过 {{method}} 上传图片失败 -'User': 用户 -'Username': 用户名 -'Verification failed': 验证失败 -'Working directory retrieval failed': 工作目录获取失败 -'Wrong captcha': 验证码错误 -'{{count}} items imported': 已导入 {{count}} 个项目 -'{{done}} of {{total}} done': 已完成 {{done}} 共 {{total}} 个 -'{{name}} already exists': '{{name}}已存在' -'{{name}} cannot be empty': '{{name}}不能为空' -'{{name}} creation failed': '{{name}}创建失败' -'{{name}} deletion failed': '{{name}}删除失败' -'{{name}} is required': '{{name}}必须填写' -'{{name}} not found': '{{name}}未找到' -'{{name}} save failed': '{{name}}保存失败' +"Access denied": 无权限 +"Account": 账户 +"Admin access required": 需要管理员权限 +"Admin": 管理员 +"Cannot reply to this comment": 无法回复此评论 +"Captcha required": 需要验证码 +"Checking for updates": 正在检查更新 +"Comment count": 评论数 +"Comment failed": 评论失败 +"Comment": 评论 +"Config file read failed": 配置文件读取失败 +"Confirm to continue?": 确认继续? +"Contains invalid URL": 包含无效的 URL +"Create admin account": 创建管理员账户 +"Current version is the latest": 当前版本已是最新的 +"Downloading": 下载中 +"Email": 邮箱 +"Enter {{name}}": 输入{{name}} +"Export complete": 导出完毕 +"Export error": 导出失败 +"File": 文件 +"First comment": 第一条评论 +"Image exceeds {{file_size}} limit": 图片超过大小限制 {{file_size}} +"Image upload forbidden": 禁止上传图片 +"Import complete": 导入完毕 +"Invalid request": 无效的请求 +"Invalid request. Please check your `trusted_domains` config.": 请求无效, 请检查 `trusted_domains` 配置项 +"Invalid {{name}}": 无效的{{name}} +"Link": 链接 +"Login failed": 登录失败 +"Name": 名称 +"New version available": 有更新可用 +"Nickname": 昵称 +"No comment": 无评论 +"Notify": 通知 +"Page fetch failed": 页面获取失败 +"Page": 页面 +"Parameter": 参数 +"Parent comment": 父评论 +"Password update failed": 密码修改失败 +"Password updated": 密码已修改 +"Password": 密码 +"Pending": 待审核 +"Please review": 请过目 +"Retype {{name}}": 重新输入{{name}} +"Save failed": 保存失败 +"Saving": 保存中 +"Services restart complete": 服务重启完毕 +"Site `{{name}}` not found. Please create it in control center.": 未找到站点:`{{name}}`,请在控制台创建站点 +"Site": 站点 +"Sub-comment": 子评论 +"Target Site": 目标站点 +"Task executing in background, please wait...": 任务已开始在后台执行,请稍后... +"Task in progress, please wait a moment": 任务执行中,请稍后 +"Type": 类型 +"URL Resolver": URL 解析器 +"Unable to get `{{name}}`": 无法获取 `{{name}}` +"Unspecified": 未指定 +"Unsupported formats": 不支持的格式 +"Update complete": 更新完毕 +"Update failed": 更新失败 +"Upload image via {{method}} failed": 通过 {{method}} 上传图片失败 +"User": 用户 +"Username": 用户名 +"Verification failed": 验证失败 +"Working directory retrieval failed": 工作目录获取失败 +"Wrong captcha": 验证码错误 +"{{count}} items imported": 已导入 {{count}} 个项目 +"{{done}} of {{total}} done": 已完成 {{done}} 共 {{total}} 个 +"{{name}} already exists": "{{name}}已存在" +"{{name}} cannot be empty": "{{name}}不能为空" +"{{name}} creation failed": "{{name}}创建失败" +"{{name}} deletion failed": "{{name}}删除失败" +"{{name}} is required": "{{name}}必须填写" +"{{name}} not found": "{{name}}未找到" +"{{name}} save failed": "{{name}}保存失败" diff --git a/i18n/zh-TW.yml b/i18n/zh-TW.yml index 268fbceaf..98335f4ea 100644 --- a/i18n/zh-TW.yml +++ b/i18n/zh-TW.yml @@ -1,76 +1,76 @@ -'Access denied': 無權限 -'Account': 賬戶 -'Admin access required': 需要管理員權限 -'Admin': 管理員 -'Cannot reply to this comment': 無法回复此評論 -'Captcha required': 需要驗證碼 -'Checking for updates': 正在檢查更新 -'Comment count': 評論數 -'Comment failed': 評論失敗 -'Comment': 評論 -'Config file read failed': 配置文件讀取失敗 -'Confirm to continue?': 確認繼續? -'Contains invalid URL': 包含無效的 URL -'Create admin account': 創建管理員賬戶 -'Current version is the latest': 當前版本已是最新的 -'Downloading': 下載中 -'Email': 郵箱 -'Enter {{name}}': 輸入{{name}} -'Export complete': 導出完畢 -'Export error': 導出失敗 -'File': 文件 -'First comment': 第一條評論 -'Image exceeds {{file_size}} limit': 圖片超過大小限制 {{file_size}} -'Image upload forbidden': 禁止上傳圖片 -'Import complete': 導入完畢 -'Invalid request': 無效的請求 -'Invalid request. Please check your `trusted_domains` config.': 請求無效, 請檢查 `trusted_domains` 配置項 -'Invalid {{name}}': 無效的{{name}} -'Link': 鏈接 -'Login failed': 登錄失敗 -'Name': 名稱 -'New version available': 有更新可用 -'Nickname': 暱稱 -'No comment': 無評論 -'Notify': 通知 -'Page fetch failed': 頁面獲取失敗 -'Page': 頁面 -'Parameter': 參數 -'Parent comment': 父評論 -'Password update failed': 密碼修改失敗 -'Password updated': 密碼已修改 -'Password': 密碼 -'Pending': 待審核 -'Please review': 請過目 -'Retype {{name}}': 重新輸入{{name}} -'Save failed': 保存失敗 -'Saving': 保存中 -'Services restart complete': 服務重啟完畢 -'Site `{{name}}` not found. Please create it in control center.': 未找到站點:`{{name}}`,請在控制台創建站點 -'Site': 站點 -'Sub-comment': 子評論 -'Target Site': 目標站點 -'Task executing in background, please wait...': 任務已開始在後台執行,請稍後... -'Task in progress, please wait a moment': 任務執行中,請稍後 -'Type': 類型 -'URL Resolver': URL 解析器 -'Unable to get `{{name}}`': 無法獲取 `{{name}}` -'Unspecified': 未指定 -'Unsupported formats': 不支持的格式 -'Update complete': 更新完畢 -'Update failed': 更新失敗 -'Upload image via {{method}} failed': 通過 {{method}} 上傳圖片失敗 -'User': 用戶 -'Username': 用戶名 -'Verification failed': 驗證失敗 -'Working directory retrieval failed': 工作目錄獲取失敗 -'Wrong captcha': 驗證碼錯誤 -'{{count}} items imported': 已導入 {{count}} 個項目 -'{{done}} of {{total}} done': 已完成 {{done}} 共 {{total}} 個 -'{{name}} already exists': '{{name}}已存在' -'{{name}} cannot be empty': '{{name}}不能為空' -'{{name}} creation failed': '{{name}}創建失敗' -'{{name}} deletion failed': '{{name}}刪除失敗' -'{{name}} is required': '{{name}}必須填寫' -'{{name}} not found': '{{name}}未找到' -'{{name}} save failed': '{{name}}保存失敗' +"Access denied": 無權限 +"Account": 賬戶 +"Admin access required": 需要管理員權限 +"Admin": 管理員 +"Cannot reply to this comment": 無法回复此評論 +"Captcha required": 需要驗證碼 +"Checking for updates": 正在檢查更新 +"Comment count": 評論數 +"Comment failed": 評論失敗 +"Comment": 評論 +"Config file read failed": 配置文件讀取失敗 +"Confirm to continue?": 確認繼續? +"Contains invalid URL": 包含無效的 URL +"Create admin account": 創建管理員賬戶 +"Current version is the latest": 當前版本已是最新的 +"Downloading": 下載中 +"Email": 郵箱 +"Enter {{name}}": 輸入{{name}} +"Export complete": 導出完畢 +"Export error": 導出失敗 +"File": 文件 +"First comment": 第一條評論 +"Image exceeds {{file_size}} limit": 圖片超過大小限制 {{file_size}} +"Image upload forbidden": 禁止上傳圖片 +"Import complete": 導入完畢 +"Invalid request": 無效的請求 +"Invalid request. Please check your `trusted_domains` config.": 請求無效, 請檢查 `trusted_domains` 配置項 +"Invalid {{name}}": 無效的{{name}} +"Link": 鏈接 +"Login failed": 登錄失敗 +"Name": 名稱 +"New version available": 有更新可用 +"Nickname": 暱稱 +"No comment": 無評論 +"Notify": 通知 +"Page fetch failed": 頁面獲取失敗 +"Page": 頁面 +"Parameter": 參數 +"Parent comment": 父評論 +"Password update failed": 密碼修改失敗 +"Password updated": 密碼已修改 +"Password": 密碼 +"Pending": 待審核 +"Please review": 請過目 +"Retype {{name}}": 重新輸入{{name}} +"Save failed": 保存失敗 +"Saving": 保存中 +"Services restart complete": 服務重啟完畢 +"Site `{{name}}` not found. Please create it in control center.": 未找到站點:`{{name}}`,請在控制台創建站點 +"Site": 站點 +"Sub-comment": 子評論 +"Target Site": 目標站點 +"Task executing in background, please wait...": 任務已開始在後台執行,請稍後... +"Task in progress, please wait a moment": 任務執行中,請稍後 +"Type": 類型 +"URL Resolver": URL 解析器 +"Unable to get `{{name}}`": 無法獲取 `{{name}}` +"Unspecified": 未指定 +"Unsupported formats": 不支持的格式 +"Update complete": 更新完畢 +"Update failed": 更新失敗 +"Upload image via {{method}} failed": 通過 {{method}} 上傳圖片失敗 +"User": 用戶 +"Username": 用戶名 +"Verification failed": 驗證失敗 +"Working directory retrieval failed": 工作目錄獲取失敗 +"Wrong captcha": 驗證碼錯誤 +"{{count}} items imported": 已導入 {{count}} 個項目 +"{{done}} of {{total}} done": 已完成 {{done}} 共 {{total}} 個 +"{{name}} already exists": "{{name}}已存在" +"{{name}} cannot be empty": "{{name}}不能為空" +"{{name}} creation failed": "{{name}}創建失敗" +"{{name}} deletion failed": "{{name}}刪除失敗" +"{{name}} is required": "{{name}}必須填寫" +"{{name}} not found": "{{name}}未找到" +"{{name}} save failed": "{{name}}保存失敗" diff --git a/internal/captcha/pages/hcaptcha.html b/internal/captcha/pages/hcaptcha.html index b600d9ad3..a714da99a 100644 --- a/internal/captcha/pages/hcaptcha.html +++ b/internal/captcha/pages/hcaptcha.html @@ -21,11 +21,7 @@
- + diff --git a/ui/artalk-sidebar/src/components/LogTerminal.vue b/ui/artalk-sidebar/src/components/LogTerminal.vue index 6a56a719d..84e428fe6 100644 --- a/ui/artalk-sidebar/src/components/LogTerminal.vue +++ b/ui/artalk-sidebar/src/components/LogTerminal.vue @@ -10,7 +10,7 @@ const emit = defineEmits<{ (evt: 'back'): void }>() -const logWrapEl = ref(null) +const logWrapEl = ref(null) onMounted(() => { // 创建 iframe @@ -71,7 +71,8 @@ function back() { } } - .atk-log {} + .atk-log { + } .atk-iframe { width: 100%; diff --git a/ui/artalk-sidebar/src/components/PageEditor.vue b/ui/artalk-sidebar/src/components/PageEditor.vue index fc5d16246..4bbea4cc4 100644 --- a/ui/artalk-sidebar/src/components/PageEditor.vue +++ b/ui/artalk-sidebar/src/components/PageEditor.vue @@ -13,8 +13,10 @@ const emit = defineEmits<{ }>() const { page } = toRefs(props) -const editFieldKey = ref(null) -const editFieldVal = computed(() => String(editFieldKey ? page.value[editFieldKey.value!] || '' : '')) +const editFieldKey = ref(null) +const editFieldVal = computed(() => + String(editFieldKey ? page.value[editFieldKey.value!] || '' : ''), +) const isLoading = ref(false) const { t } = useI18n() @@ -30,12 +32,19 @@ async function editAdminOnly() { isLoading.value = true let p: ArtalkType.PageData try { - p = (await artalk!.ctx.getApi().pages.updatePage(page.value.id, { ...page.value, admin_only: !page.value.admin_only })).data + p = ( + await artalk!.ctx.getApi().pages.updatePage(page.value.id, { + ...page.value, + admin_only: !page.value.admin_only, + }) + ).data } catch (err: any) { alert(`修改失败:${err.message || '未知错误'}`) console.log(err) return - } finally { isLoading.value = false } + } finally { + isLoading.value = false + } emit('update', p) } @@ -48,7 +57,9 @@ async function sync() { alert(`同步失败:${err.message || '未知错误'}`) console.log(err) return - } finally { isLoading.value = false } + } finally { + isLoading.value = false + } emit('update', p) } @@ -61,12 +72,13 @@ function del() { console.log(err) alert(`删除失败 ${String(err)}`) return - } finally { isLoading.value = false } + } finally { + isLoading.value = false + } emit('remove', page.value.id) } - if (window.confirm( - `确认删除页面 "${page.value.title || page.value.key}"?将会删除所有相关数据` - )) del() + if (window.confirm(`确认删除页面 "${page.value.title || page.value.key}"?将会删除所有相关数据`)) + del() } function close() { @@ -78,12 +90,19 @@ async function onFieldEditorYes(val: string) { isLoading.value = true let p: ArtalkType.PageData try { - p = (await artalk!.ctx.getApi().pages.updatePage(page.value.id, { ...page.value, [editFieldKey.value as any]: val })).data + p = ( + await artalk!.ctx.getApi().pages.updatePage(page.value.id, { + ...page.value, + [editFieldKey.value as any]: val, + }) + ).data } catch (err: any) { alert(`修改失败:${err.message || '未知错误'}`) console.error(err) return false - } finally { isLoading.value = false } + } finally { + isLoading.value = false + } emit('update', p) } @@ -100,13 +119,19 @@ function onFiledEditorNo() { - + diff --git a/ui/artalk-sidebar/src/components/SiteEditor.vue b/ui/artalk-sidebar/src/components/SiteEditor.vue index 5d7bfde99..f488e1dad 100644 --- a/ui/artalk-sidebar/src/components/SiteEditor.vue +++ b/ui/artalk-sidebar/src/components/SiteEditor.vue @@ -14,7 +14,7 @@ const emit = defineEmits<{ const { site } = toRefs(props) const isLoading = ref(false) -const editFieldKey = ref(null) +const editFieldKey = ref(null) const editFieldVal = computed(() => { if (editFieldKey.value === 'urls') return site.value.urls_raw || '' return String(editFieldKey ? site.value[editFieldKey.value!] || '' : '') @@ -47,12 +47,12 @@ function del() { console.log(err) alert(`删除失败 ${String(err)}`) return - } finally { isLoading.value = false } + } finally { + isLoading.value = false + } emit('remove', site.value.id) } - if (window.confirm( - `确认删除站点 "${site.value.name}"?将会删除所有相关数据` - )) del() + if (window.confirm(`确认删除站点 "${site.value.name}"?将会删除所有相关数据`)) del() } async function onFieldEditorYes(val: string) { @@ -62,14 +62,25 @@ async function onFieldEditorYes(val: string) { isLoading.value = true let s: ArtalkType.SiteData try { - let finalVal: string|string[] = val - if (Array.isArray(site.value[editFieldKey.value])) finalVal = val.split(',').map((v) => v.trim()).filter((v) => !!v) - s = (await artalk!.ctx.getApi().sites.updateSite(site.value.id, { ...site.value, [editFieldKey.value]: finalVal })).data + let finalVal: string | string[] = val + if (Array.isArray(site.value[editFieldKey.value])) + finalVal = val + .split(',') + .map((v) => v.trim()) + .filter((v) => !!v) + s = ( + await artalk!.ctx.getApi().sites.updateSite(site.value.id, { + ...site.value, + [editFieldKey.value]: finalVal, + }) + ).data } catch (err: any) { alert(`修改失败:${err.message || '未知错误'}`) console.error(err) return false - } finally { isLoading.value = false } + } finally { + isLoading.value = false + } emit('update', s) } @@ -86,17 +97,13 @@ function onFiledEditorNo() {
- {{ site.name }} + + {{ site.name }} + -
{{ url }}
+
+ {{ url }} +
@@ -105,7 +112,9 @@ function onFiledEditorNo() {
-
{{ t('rename') }}
+
+ {{ t('rename') }} +
{{ t('edit') }} URL
@@ -126,6 +135,4 @@ function onFiledEditorNo() {
- + diff --git a/ui/artalk-sidebar/src/components/SiteSwitcher.vue b/ui/artalk-sidebar/src/components/SiteSwitcher.vue index 004bd7d7f..941ba2c92 100644 --- a/ui/artalk-sidebar/src/components/SiteSwitcher.vue +++ b/ui/artalk-sidebar/src/components/SiteSwitcher.vue @@ -1,10 +1,10 @@ diff --git a/ui/artalk-sidebar/src/pages/index.vue b/ui/artalk-sidebar/src/pages/index.vue index 272956847..d771a72c5 100644 --- a/ui/artalk-sidebar/src/pages/index.vue +++ b/ui/artalk-sidebar/src/pages/index.vue @@ -18,11 +18,11 @@ onMounted(() => { if (splitted[1]) bootParams.viewParams = JSON.parse(splitted[1]) } - const LinkMap: {[key:string]:string} = { + const LinkMap: { [key: string]: string } = { comments: '/comments', pages: '/pages', sites: '/sites', - settings: '/settings' + settings: '/settings', } router.replace(LinkMap[bootParams.view] || '/comments') }) diff --git a/ui/artalk-sidebar/src/pages/login.vue b/ui/artalk-sidebar/src/pages/login.vue index ae0fd2412..c1752ebfc 100644 --- a/ui/artalk-sidebar/src/pages/login.vue +++ b/ui/artalk-sidebar/src/pages/login.vue @@ -15,19 +15,22 @@ const router = useRouter() const { t } = useI18n() let userForm = ref({ - email: '', - password: '' + email: '', + password: '', }) let version = ref('') let buildHash = ref('') let loginErr = ref('') -let userSelector = ref(null) +let userSelector = ref(null) onMounted(() => { - getArtalk()!.ctx.getApi().version.getVersion().then((res) => { - version.value = res.data.version - buildHash.value = res.data.commit_hash - }) + getArtalk()! + .ctx.getApi() + .version.getVersion() + .then((res) => { + version.value = res.data.version + buildHash.value = res.data.commit_hash + }) }) function onFocus() { @@ -40,28 +43,37 @@ function login(username?: string) { const artalk = getArtalk() if (!artalk) throw new Error('Artalk instance not initialized') - artalk.ctx.getApi().user.login({ - name: username || '', - email: userForm.value.email, - password: userForm.value.password - }).then((res) => { - const user = res.data.user - artalk.ctx.get('user').update({ - nick: user.name, - email: user.email, - link: user.link, - isAdmin: user.is_admin, - token: res.data.token, + artalk.ctx + .getApi() + .user.login({ + name: username || '', + email: userForm.value.email, + password: userForm.value.password, }) - useUserStore().sync() - router.replace('/') - }).catch((e: ArtalkType.FetchError) => { - if (e.data?.need_name_select) { - userSelector.value = e.data?.need_name_select - } else { - loginErr.value = e.message || t('loginFailure') - } - }) + .then((res) => { + const user = res.data.user + artalk.ctx.get('user').update({ + nick: user.name, + email: user.email, + link: user.link, + isAdmin: user.is_admin, + token: res.data.token, + }) + useUserStore().sync() + router.replace('/') + }) + .catch((e: ArtalkType.FetchError) => { + if (e.data?.need_name_select) { + userSelector.value = e.data?.need_name_select + } else { + loginErr.value = e.message || t('loginFailure') + } + }) +} + +function selectUser(username: string) { + userSelector.value = null + login(username) } @@ -71,20 +83,29 @@ function login(username?: string) {
{{ t('loginSelectHint') }}
-
{{u}}
+
+ {{ u }} +
@@ -156,7 +177,7 @@ function login(username?: string) { display: block; &:hover { - opacity: .95; + opacity: 0.95; } } @@ -211,7 +232,7 @@ function login(username?: string) { & > .item { cursor: pointer; padding: 10px 20px; - transition: border .3s ease; + transition: border 0.3s ease; border-left: 2px solid transparent; &:hover { diff --git a/ui/artalk-sidebar/src/pages/pages.vue b/ui/artalk-sidebar/src/pages/pages.vue index 3b792695b..61a4cf593 100644 --- a/ui/artalk-sidebar/src/pages/pages.vue +++ b/ui/artalk-sidebar/src/pages/pages.vue @@ -9,7 +9,7 @@ import Pagination from '../components/Pagination.vue' const nav = useNavStore() const { site: curtSite } = storeToRefs(useUserStore()) const pages = ref([]) -const curtEditPageID = ref(null) +const curtEditPageID = ref(null) const { t } = useI18n() const pageSize = ref(20) @@ -22,9 +22,7 @@ const refreshBtn = ref({ }) onMounted(() => { - nav.updateTabs({ - - }, '') + nav.updateTabs({}, '') reqPages(0) @@ -34,7 +32,7 @@ onMounted(() => { }) // Refresh task status recovery - getRefreshTaskStatus().then(d => { + getRefreshTaskStatus().then((d) => { if (d.is_progress === true) { refreshBtn.value.isRun = true refreshBtn.value.statusText = d.msg @@ -50,7 +48,7 @@ onUnmounted(() => { }) function scrollHandler() { - showActBarBorder.value = (nav.scrollableArea!.scrollTop > 10) + showActBarBorder.value = nav.scrollableArea!.scrollTop > 10 } function editPage(page: ArtalkType.PageData) { @@ -59,16 +57,19 @@ function editPage(page: ArtalkType.PageData) { function reqPages(offset: number) { nav.setPageLoading(true) - artalk?.ctx.getApi().pages.getPages({ - site_name: curtSite.value, - offset: offset, - limit: pageSize.value, - }) - .then(res => { + artalk?.ctx + .getApi() + .pages.getPages({ + site_name: curtSite.value, + offset: offset, + limit: pageSize.value, + }) + .then((res) => { pageTotal.value = res.data.count pages.value = res.data.pages nav.scrollPageToTop() - }).finally(() => { + }) + .finally(() => { nav.setPageLoading(false) }) } @@ -78,17 +79,17 @@ function onChangePage(offset: number) { } function onPageItemUpdate(page: ArtalkType.PageData) { - const index = pages.value.findIndex(p => p.id === page.id) + const index = pages.value.findIndex((p) => p.id === page.id) if (index != -1) { const orgPage = pages.value[index] - Object.keys(page).forEach(key => { + Object.keys(page).forEach((key) => { ;(orgPage as any)[key] = (page as any)[key] }) } } function onPageItemRemove(id: number) { - const index = pages.value.findIndex(p => p.id === id) + const index = pages.value.findIndex((p) => p.id === id) pages.value.splice(index, 1) } @@ -138,11 +139,19 @@ async function refreshAllPages() { } function cacheFlush() { - artalk!.ctx.getApi().cache.flushCache().then((res) => alert(res.data.msg)).catch(() => alert(t('opFailed'))) + artalk!.ctx + .getApi() + .cache.flushCache() + .then((res) => alert(res.data.msg)) + .catch(() => alert(t('opFailed'))) } function cacheWarm() { - artalk!.ctx.getApi().cache.warmUpCache().then((res) => alert(res.data.msg)).catch(() => alert(t('opFailed'))) + artalk!.ctx + .getApi() + .cache.warmUpCache() + .then((res) => alert(res.data.msg)) + .catch(() => alert(t('opFailed'))) } function openPage(url: string) { @@ -152,18 +161,26 @@ function openPage(url: string) {