From b8fcd0dc331ce73994f85ce078c03509bb8cc5ea Mon Sep 17 00:00:00 2001 From: ThatOneCalculator Date: Sat, 15 Jul 2023 14:13:21 -0700 Subject: [PATCH 01/14] fix: :lock: prevent potential SSRF through media proxy --- packages/backend/src/misc/download-url.ts | 3 +- .../backend/src/server/proxy/proxy-media.ts | 36 +++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/packages/backend/src/misc/download-url.ts b/packages/backend/src/misc/download-url.ts index 7fafb635b..b96871e72 100644 --- a/packages/backend/src/misc/download-url.ts +++ b/packages/backend/src/misc/download-url.ts @@ -21,9 +21,10 @@ export async function downloadUrl(url: string, path: string): Promise { const maxSize = config.maxFileSize || 262144000; const req = got - .stream(url, { + .stream(url, { headers: { "User-Agent": config.userAgent, + "Host": new URL(url).hostname, }, timeout: { lookup: timeout, diff --git a/packages/backend/src/server/proxy/proxy-media.ts b/packages/backend/src/server/proxy/proxy-media.ts index a9c257bfe..b3bb03124 100644 --- a/packages/backend/src/server/proxy/proxy-media.ts +++ b/packages/backend/src/server/proxy/proxy-media.ts @@ -1,4 +1,6 @@ import * as fs from "node:fs"; +import net from "node:net"; +import { promises } from "node:dns"; import type Koa from "koa"; import sharp from "sharp"; import type { IImage } from "@/services/drive/image-processor.js"; @@ -19,6 +21,40 @@ export async function proxyMedia(ctx: Koa.Context) { return; } + const { hostname } = new URL(url); + let resolvedIps; + try { + resolvedIps = await promises.resolve(hostname); + } catch (error) { + ctx.status = 400; + ctx.body = { message: "Invalid URL" }; + return; + } + + const isSSRF = resolvedIps.some((ip) => { + if (net.isIPv4(ip)) { + const parts = ip.split(".").map(Number); + return ( + parts[0] === 10 || + (parts[0] === 172 && parts[1] >= 16 && parts[1] < 32) || + (parts[0] === 192 && parts[1] === 168) || + parts[0] === 127 || + parts[0] === 0 + ); + } else if (net.isIPv6(ip)) { + return ( + ip.startsWith("::") || ip.startsWith("fc00:") || ip.startsWith("fe80:") + ); + } + return false; + }); + + if (isSSRF) { + ctx.status = 400; + ctx.body = { message: "Access to this URL is not allowed" }; + return; + } + // Create temp file const [path, cleanup] = await createTemp(); From 9fbcdb529ed01a3391f4fbf56630fa4c79116c9f Mon Sep 17 00:00:00 2001 From: Mizunashi Mana Date: Sun, 16 Jul 2023 18:57:38 +0900 Subject: [PATCH 02/14] feat: Move json5 to prod dependencies --- packages/backend/package.json | 2 +- pnpm-lock.yaml | 94 ++++++----------------------------- 2 files changed, 17 insertions(+), 79 deletions(-) diff --git a/packages/backend/package.json b/packages/backend/package.json index fe8c078a0..8564ca832 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -71,6 +71,7 @@ "is-svg": "4.3.2", "js-yaml": "4.1.0", "jsdom": "20.0.3", + "json5": "2.2.3", "jsonld": "8.2.0", "jsrsasign": "10.8.6", "koa": "2.14.2", @@ -185,7 +186,6 @@ "cross-env": "7.0.3", "eslint": "^8.44.0", "execa": "6.1.0", - "json5": "2.2.3", "json5-loader": "4.0.1", "mocha": "10.2.0", "pug": "3.0.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index adead9c6f..540d94109 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -219,6 +219,9 @@ importers: jsdom: specifier: 20.0.3 version: 20.0.3 + json5: + specifier: 2.2.3 + version: 2.2.3 jsonld: specifier: 8.2.0 version: 8.2.0 @@ -563,9 +566,6 @@ importers: execa: specifier: 6.1.0 version: 6.1.0 - json5: - specifier: 2.2.3 - version: 2.2.3 json5-loader: specifier: 4.0.1 version: 4.0.1(webpack@5.88.1) @@ -595,7 +595,7 @@ importers: version: 5.1.6 webpack: specifier: ^5.88.1 - version: 5.88.1(@swc/core@1.3.68) + version: 5.88.1(@swc/core@1.3.68)(webpack-cli@5.1.3) ws: specifier: 8.13.0 version: 8.13.0 @@ -780,7 +780,7 @@ importers: version: 2.30.0 emojilib: specifier: github:thatonecalculator/emojilib - version: github.com/thatonecalculator/emojilib/1d6adc1af8105b4937d2f0e7479acf5bd565bd9b + version: github.com/thatonecalculator/emojilib/d3c8c6a77d4362b3b3180099f1d2eac344ce245c escape-regexp: specifier: 0.0.1 version: 0.0.1 @@ -5138,7 +5138,7 @@ packages: /axios@0.24.0: resolution: {integrity: sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA==} dependencies: - follow-redirects: 1.15.2 + follow-redirects: 1.15.2(debug@4.3.4) transitivePeerDependencies: - debug dev: false @@ -5164,7 +5164,7 @@ packages: /axios@1.4.0: resolution: {integrity: sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==} dependencies: - follow-redirects: 1.15.2 + follow-redirects: 1.15.2(debug@4.3.4) form-data: 4.0.0 proxy-from-env: 1.1.0 transitivePeerDependencies: @@ -6843,17 +6843,6 @@ packages: dependencies: ms: 2.0.0 - /debug@3.2.7: - resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - dependencies: - ms: 2.1.3 - dev: false - /debug@3.2.7(supports-color@8.1.1): resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} peerDependencies: @@ -6864,7 +6853,6 @@ packages: dependencies: ms: 2.1.3 supports-color: 8.1.1 - dev: true /debug@4.3.3: resolution: {integrity: sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==} @@ -8377,16 +8365,6 @@ packages: tabbable: 6.2.0 dev: true - /follow-redirects@1.15.2: - resolution: {integrity: sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==} - engines: {node: '>=4.0'} - peerDependencies: - debug: '*' - peerDependenciesMeta: - debug: - optional: true - dev: false - /follow-redirects@1.15.2(debug@4.3.4): resolution: {integrity: sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==} engines: {node: '>=4.0'} @@ -9355,7 +9333,7 @@ packages: engines: {node: '>= 4.5.0'} dependencies: agent-base: 4.3.0 - debug: 3.2.7 + debug: 3.2.7(supports-color@8.1.1) transitivePeerDependencies: - supports-color dev: false @@ -11280,7 +11258,7 @@ packages: json5: 2.2.3 loader-utils: 2.0.4 schema-utils: 3.3.0 - webpack: 5.88.1(@swc/core@1.3.68) + webpack: 5.88.1(@swc/core@1.3.68)(webpack-cli@5.1.3) dev: true /json5@1.0.2: @@ -11535,7 +11513,7 @@ packages: resolution: {integrity: sha512-UqyYyH5YEXaJrf9S8E23GoJFQZXkBVJ9zYYMPGz919MSX1KuvAcycIuS0ci150HCoPf4XQVhQ84Qf8xRPWxFaQ==} engines: {node: '>= 7.6.0'} dependencies: - debug: 3.2.7 + debug: 3.2.7(supports-color@8.1.1) koa-send: 5.0.1 transitivePeerDependencies: - supports-color @@ -12579,7 +12557,7 @@ packages: engines: {node: '>= 4.4.x'} hasBin: true dependencies: - debug: 3.2.7 + debug: 3.2.7(supports-color@8.1.1) iconv-lite: 0.4.24 sax: 1.2.4 transitivePeerDependencies: @@ -15558,7 +15536,7 @@ packages: webpack: '>=2' dependencies: '@swc/core': 1.3.68 - webpack: 5.88.1(@swc/core@1.3.68) + webpack: 5.88.1(@swc/core@1.3.68)(webpack-cli@5.1.3) dev: true /swiper@10.0.4: @@ -15677,7 +15655,7 @@ packages: schema-utils: 3.3.0 serialize-javascript: 6.0.1 terser: 5.19.0 - webpack: 5.88.1(@swc/core@1.3.68) + webpack: 5.88.1(@swc/core@1.3.68)(webpack-cli@5.1.3) dev: true /terser@5.19.0: @@ -16016,7 +15994,7 @@ packages: micromatch: 4.0.5 semver: 7.5.4 typescript: 5.1.6 - webpack: 5.88.1(@swc/core@1.3.68) + webpack: 5.88.1(@swc/core@1.3.68)(webpack-cli@5.1.3) dev: true /ts-node@10.4.0(@swc/core@1.3.68)(@types/node@20.3.1)(typescript@5.1.3): @@ -16933,46 +16911,6 @@ packages: engines: {node: '>=10.13.0'} dev: true - /webpack@5.88.1(@swc/core@1.3.68): - resolution: {integrity: sha512-FROX3TxQnC/ox4N+3xQoWZzvGXSuscxR32rbzjpXgEzWudJFEJBpdlkkob2ylrv5yzzufD1zph1OoFsLtm6stQ==} - engines: {node: '>=10.13.0'} - hasBin: true - peerDependencies: - webpack-cli: '*' - peerDependenciesMeta: - webpack-cli: - optional: true - dependencies: - '@types/eslint-scope': 3.7.4 - '@types/estree': 1.0.1 - '@webassemblyjs/ast': 1.11.6 - '@webassemblyjs/wasm-edit': 1.11.6 - '@webassemblyjs/wasm-parser': 1.11.6 - acorn: 8.10.0 - acorn-import-assertions: 1.9.0(acorn@8.10.0) - browserslist: 4.21.9 - chrome-trace-event: 1.0.3 - enhanced-resolve: 5.15.0 - es-module-lexer: 1.3.0 - eslint-scope: 5.1.1 - events: 3.3.0 - glob-to-regexp: 0.4.1 - graceful-fs: 4.2.11 - json-parse-even-better-errors: 2.3.1 - loader-runner: 4.3.0 - mime-types: 2.1.35 - neo-async: 2.6.2 - schema-utils: 3.3.0 - tapable: 2.2.1 - terser-webpack-plugin: 5.3.9(@swc/core@1.3.68)(webpack@5.88.1) - watchpack: 2.4.0 - webpack-sources: 3.2.3 - transitivePeerDependencies: - - '@swc/core' - - esbuild - - uglify-js - dev: true - /webpack@5.88.1(@swc/core@1.3.68)(webpack-cli@5.1.3): resolution: {integrity: sha512-FROX3TxQnC/ox4N+3xQoWZzvGXSuscxR32rbzjpXgEzWudJFEJBpdlkkob2ylrv5yzzufD1zph1OoFsLtm6stQ==} engines: {node: '>=10.13.0'} @@ -17486,8 +17424,8 @@ packages: url-polyfill: 1.1.12 dev: true - github.com/thatonecalculator/emojilib/1d6adc1af8105b4937d2f0e7479acf5bd565bd9b: - resolution: {tarball: https://codeload.github.com/thatonecalculator/emojilib/tar.gz/1d6adc1af8105b4937d2f0e7479acf5bd565bd9b} + github.com/thatonecalculator/emojilib/d3c8c6a77d4362b3b3180099f1d2eac344ce245c: + resolution: {tarball: https://codeload.github.com/thatonecalculator/emojilib/tar.gz/d3c8c6a77d4362b3b3180099f1d2eac344ce245c} name: emojilib version: 3.0.10 dev: true From 2ac7a750ec44c5a42583c21892b04a3e816dfbd0 Mon Sep 17 00:00:00 2001 From: ThatOneCalculator Date: Sun, 16 Jul 2023 13:57:38 -0700 Subject: [PATCH 03/14] dev79 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a30d85b3d..818b50ab3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "calckey", - "version": "14.0.0-dev78", + "version": "14.0.0-dev79", "codename": "aqua", "repository": { "type": "git", From d425c29347b63440ee554c4d9d2490fc53729bb6 Mon Sep 17 00:00:00 2001 From: ThatOneCalculator Date: Sun, 16 Jul 2023 15:32:32 -0700 Subject: [PATCH 04/14] refactor: :rotating_light: linting fix --- package.json | 2 +- .../1678426061773-tweak-varchar-length.js | 18 +- packages/backend/native-utils/package.json | 1 + .../src/model/repository/antenna.rs | 2 +- .../native-utils/src/model/schema/antenna.rs | 2 +- .../native-utils/src/model/schema/app.rs | 2 +- packages/backend/native-utils/tests/common.rs | 4 +- .../tests/model/repository/antenna.rs | 10 +- packages/backend/src/const.ts | 5 +- packages/backend/src/misc/download-url.ts | 4 +- packages/client/.eslintrc.json | 7 + packages/client/package.json | 10 +- .../client/src/components/MkAbuseReport.vue | 4 +- .../src/components/MkAbuseReportWindow.vue | 2 +- .../client/src/components/MkAnalogClock.vue | 40 +- .../client/src/components/MkAutocomplete.vue | 18 +- packages/client/src/components/MkButton.vue | 4 +- packages/client/src/components/MkCaptcha.vue | 8 +- .../client/src/components/MkChannelList.vue | 3 +- packages/client/src/components/MkChart.vue | 59 +- .../client/src/components/MkChatPreview.vue | 2 +- .../client/src/components/MkContextMenu.vue | 12 +- .../client/src/components/MkCropperDialog.vue | 10 +- packages/client/src/components/MkCwButton.vue | 2 +- .../src/components/MkDateSeparatedList.vue | 5 +- packages/client/src/components/MkDialog.vue | 16 +- packages/client/src/components/MkDonation.vue | 6 +- .../src/components/MkEmojiPickerDialog.vue | 1 - packages/client/src/components/MkMedia.vue | 4 +- .../client/src/components/MkNoteDetailed.vue | 6 +- packages/client/src/components/MkTab.vue | 2 +- packages/client/src/components/form/input.vue | 6 +- packages/client/src/components/form/radio.vue | 4 +- packages/client/src/components/form/range.vue | 6 +- .../client/src/components/form/select.vue | 12 +- .../client/src/components/form/suspense.vue | 3 +- .../client/src/components/form/switch.vue | 4 +- .../client/src/components/form/textarea.vue | 6 +- .../client/src/components/global/MkAcct.vue | 2 +- .../client/src/components/global/MkAd.vue | 2 +- .../client/src/components/global/MkAvatar.vue | 2 +- .../client/src/components/global/MkEmoji.vue | 2 +- .../global/MkMisskeyFlavoredMarkdown.vue | 4 +- .../src/components/global/MkPageHeader.vue | 20 +- .../client/src/components/global/MkSpacer.vue | 8 +- .../components/global/MkStickyContainer.vue | 9 +- .../client/src/components/global/MkTime.vue | 6 +- .../src/components/global/MkUserName.vue | 2 +- .../src/components/global/RouterView.vue | 14 +- packages/client/src/ui/deck.vue | 10 +- .../client/src/ui/deck/antenna-column.vue | 5 +- .../client/src/ui/deck/channel-column.vue | 5 +- packages/client/src/ui/deck/column-core.vue | 2 +- packages/client/src/ui/deck/column.vue | 26 +- packages/client/src/ui/deck/deck-store.ts | 4 +- packages/client/src/ui/deck/direct-column.vue | 2 +- packages/client/src/ui/deck/list-column.vue | 5 +- packages/client/src/ui/deck/main-column.vue | 10 +- .../client/src/ui/deck/mentions-column.vue | 2 +- .../src/ui/deck/notifications-column.vue | 2 +- packages/client/src/ui/deck/tl-column.vue | 9 +- .../client/src/ui/deck/widgets-column.vue | 2 +- packages/client/src/ui/universal.vue | 9 +- packages/client/src/ui/universal.widgets.vue | 4 +- packages/client/src/ui/visitor.vue | 2 +- packages/client/src/ui/visitor/a.vue | 2 +- packages/client/src/ui/visitor/b.vue | 13 +- packages/client/src/ui/visitor/kanban.vue | 2 +- packages/client/src/ui/zen.vue | 5 +- .../client/src/widgets/activity.chart.vue | 20 +- packages/client/src/widgets/activity.vue | 11 +- packages/client/src/widgets/aiscript.vue | 11 +- packages/client/src/widgets/button.vue | 11 +- packages/client/src/widgets/calendar.vue | 13 +- packages/client/src/widgets/clock.vue | 11 +- packages/client/src/widgets/digital-clock.vue | 11 +- packages/client/src/widgets/federation.vue | 11 +- .../client/src/widgets/instance-cloud.vue | 8 +- packages/client/src/widgets/job-queue.vue | 11 +- packages/client/src/widgets/memo.vue | 11 +- packages/client/src/widgets/notifications.vue | 11 +- packages/client/src/widgets/online-users.vue | 11 +- packages/client/src/widgets/photos.vue | 11 +- packages/client/src/widgets/post-form.vue | 11 +- packages/client/src/widgets/rss-ticker.vue | 11 +- packages/client/src/widgets/rss.vue | 11 +- packages/client/src/widgets/server-info.vue | 7 +- .../src/widgets/server-metric/cpu-mem.vue | 32 +- .../client/src/widgets/server-metric/cpu.vue | 2 +- .../src/widgets/server-metric/index.vue | 11 +- .../src/widgets/server-metric/meilisearch.vue | 12 +- .../client/src/widgets/server-metric/mem.vue | 10 +- .../client/src/widgets/server-metric/net.vue | 32 +- packages/client/src/widgets/slideshow.vue | 11 +- packages/client/src/widgets/timeline.vue | 11 +- packages/client/src/widgets/trends.vue | 11 +- packages/client/src/widgets/unix-clock.vue | 11 +- packages/client/src/widgets/user-list.vue | 15 +- packages/megalodon/package.json | 2 +- packages/megalodon/src/axios.d.ts | 2 +- packages/megalodon/src/cancel.ts | 16 +- packages/megalodon/src/converter.ts | 2 +- packages/megalodon/src/default.ts | 6 +- packages/megalodon/src/entity.ts | 2 +- packages/megalodon/src/filter_context.ts | 14 +- packages/megalodon/src/index.ts | 54 +- packages/megalodon/src/megalodon.ts | 2753 ++++---- packages/megalodon/src/misskey.ts | 5701 +++++++++-------- packages/megalodon/src/notification.ts | 20 +- packages/megalodon/src/oauth.ts | 204 +- packages/megalodon/src/parser.ts | 142 +- packages/megalodon/src/proxy_config.ts | 165 +- packages/megalodon/src/response.ts | 12 +- packages/sw/package.json | 2 +- pnpm-lock.yaml | 1269 +++- rome.json | 4 + 116 files changed, 6537 insertions(+), 4697 deletions(-) create mode 100644 packages/client/.eslintrc.json diff --git a/package.json b/package.json index 818b50ab3..d4460c878 100644 --- a/package.json +++ b/package.json @@ -57,7 +57,7 @@ "gulp-replace": "1.1.4", "gulp-terser": "2.1.0", "install-peers": "^1.0.4", - "rome": "^12.1.3", + "rome": "^v12.1.3-nightly.f65b0d9", "start-server-and-test": "1.15.2", "typescript": "5.1.6" } diff --git a/packages/backend/migration/1678426061773-tweak-varchar-length.js b/packages/backend/migration/1678426061773-tweak-varchar-length.js index 883374599..00ddcaebe 100644 --- a/packages/backend/migration/1678426061773-tweak-varchar-length.js +++ b/packages/backend/migration/1678426061773-tweak-varchar-length.js @@ -1,10 +1,16 @@ export class tweakVarcharLength1678426061773 { - name = 'tweakVarcharLength1678426061773' + name = "tweakVarcharLength1678426061773"; - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "meta" ALTER COLUMN "smtpUser" TYPE character varying(1024)`, undefined); - await queryRunner.query(`ALTER TABLE "meta" ALTER COLUMN "smtpPass" TYPE character varying(1024)`, undefined); - } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "meta" ALTER COLUMN "smtpUser" TYPE character varying(1024)`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "meta" ALTER COLUMN "smtpPass" TYPE character varying(1024)`, + undefined, + ); + } - async down(queryRunner) {} + async down(queryRunner) {} } diff --git a/packages/backend/native-utils/package.json b/packages/backend/native-utils/package.json index 385330d77..962b4bc4c 100644 --- a/packages/backend/native-utils/package.json +++ b/packages/backend/native-utils/package.json @@ -43,6 +43,7 @@ "universal": "napi universal", "version": "napi version", "format": "cargo fmt --all", + "lint": "cargo clippy --fix", "cargo:test": "pnpm run cargo:unit && pnpm run cargo:integration", "cargo:unit": "cargo test unit_test && cargo test -F napi unit_test", "cargo:integration": "cargo test -F noarray int_test -- --test-threads=1" diff --git a/packages/backend/native-utils/src/model/repository/antenna.rs b/packages/backend/native-utils/src/model/repository/antenna.rs index 7c614b954..2b761173e 100644 --- a/packages/backend/native-utils/src/model/repository/antenna.rs +++ b/packages/backend/native-utils/src/model/repository/antenna.rs @@ -46,7 +46,7 @@ impl Repository for antenna::Model { src: self.src.try_into()?, user_list_id: self.user_list_id, user_group_id, - users: self.users.into(), + users: self.users, instances: self.instances.into(), case_sensitive: self.case_sensitive, notify: self.notify, diff --git a/packages/backend/native-utils/src/model/schema/antenna.rs b/packages/backend/native-utils/src/model/schema/antenna.rs index 4ec1e0794..da2c3061b 100644 --- a/packages/backend/native-utils/src/model/schema/antenna.rs +++ b/packages/backend/native-utils/src/model/schema/antenna.rs @@ -58,7 +58,7 @@ impl TryFrom for super::AntennaSrc { // ---- TODO: could be macro impl Schema for super::Antenna {} -pub static VALIDATOR: Lazy = Lazy::new(|| super::Antenna::validator()); +pub static VALIDATOR: Lazy = Lazy::new(super::Antenna::validator); // ---- cfg_if! { diff --git a/packages/backend/native-utils/src/model/schema/app.rs b/packages/backend/native-utils/src/model/schema/app.rs index 682b82ec0..9b5691154 100644 --- a/packages/backend/native-utils/src/model/schema/app.rs +++ b/packages/backend/native-utils/src/model/schema/app.rs @@ -91,7 +91,7 @@ pub enum AppPermission { impl Schema for App {} -pub static VALIDATOR: Lazy = Lazy::new(|| App::validator()); +pub static VALIDATOR: Lazy = Lazy::new(App::validator); #[cfg(test)] mod unit_test { diff --git a/packages/backend/native-utils/tests/common.rs b/packages/backend/native-utils/tests/common.rs index 186e862bd..b134319ca 100644 --- a/packages/backend/native-utils/tests/common.rs +++ b/packages/backend/native-utils/tests/common.rs @@ -148,8 +148,8 @@ async fn setup_model(db: &DbConn) { let user_model = entity::user::Model { id: user_id.to_owned(), created_at: Utc::now().into(), - username: name.to_lowercase().to_string(), - username_lower: name.to_lowercase().to_string(), + username: name.to_lowercase(), + username_lower: name.to_lowercase(), name: Some(name.to_string()), token: Some(gen_string(16)), is_admin: true, diff --git a/packages/backend/native-utils/tests/model/repository/antenna.rs b/packages/backend/native-utils/tests/model/repository/antenna.rs index 3bda2ca18..80eea6771 100644 --- a/packages/backend/native-utils/tests/model/repository/antenna.rs +++ b/packages/backend/native-utils/tests/model/repository/antenna.rs @@ -43,18 +43,16 @@ mod int_test { keywords: vec![ vec!["foo".to_string(), "bar".to_string()], vec!["foobar".to_string()], - ] - .into(), + ], exclude_keywords: vec![ vec!["abc".to_string()], vec!["def".to_string(), "ghi".to_string()], - ] - .into(), + ], src: schema::AntennaSrc::All, user_list_id: None, user_group_id: None, - users: vec![].into(), - instances: vec![].into(), + users: vec![], + instances: vec![], case_sensitive: true, notify: true, with_replies: false, diff --git a/packages/backend/src/const.ts b/packages/backend/src/const.ts index 49f012c5e..2a955ee52 100644 --- a/packages/backend/src/const.ts +++ b/packages/backend/src/const.ts @@ -1,5 +1,8 @@ import config from "@/config/index.js"; -import { DB_MAX_NOTE_TEXT_LENGTH, DB_MAX_IMAGE_COMMENT_LENGTH } from "@/misc/hard-limits.js"; +import { + DB_MAX_NOTE_TEXT_LENGTH, + DB_MAX_IMAGE_COMMENT_LENGTH, +} from "@/misc/hard-limits.js"; export const MAX_NOTE_TEXT_LENGTH = Math.min( config.maxNoteLength ?? 3000, diff --git a/packages/backend/src/misc/download-url.ts b/packages/backend/src/misc/download-url.ts index b96871e72..e9975f348 100644 --- a/packages/backend/src/misc/download-url.ts +++ b/packages/backend/src/misc/download-url.ts @@ -21,10 +21,10 @@ export async function downloadUrl(url: string, path: string): Promise { const maxSize = config.maxFileSize || 262144000; const req = got - .stream(url, { + .stream(url, { headers: { "User-Agent": config.userAgent, - "Host": new URL(url).hostname, + Host: new URL(url).hostname, }, timeout: { lookup: timeout, diff --git a/packages/client/.eslintrc.json b/packages/client/.eslintrc.json new file mode 100644 index 000000000..fd4718003 --- /dev/null +++ b/packages/client/.eslintrc.json @@ -0,0 +1,7 @@ +{ + "extends": ["@eslint-sets/vue3", "@eslint-sets/vue3-ts"], + "plugins": ["file-progress", "prettier"], + "rules": { + "file-progress/activate": 1 + } +} diff --git a/packages/client/package.json b/packages/client/package.json index 95fb9f9b2..3fe101b81 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -4,11 +4,14 @@ "scripts": { "watch": "pnpm vite build --watch --mode development", "build": "pnpm vite build", - "lint": "pnpm rome check \"src/**/*.{ts,vue}\"", - "format": "pnpm rome format * --write && pnpm prettier --write '**/*.{scss,vue}'" + "lint": "pnpm rome check **/*.ts --apply && pnpm run lint:vue", + "lint:vue": "pnpm paralint --ext .vue --fix '**/*.vue' --cache", + "format": "pnpm rome format * --write && pnpm prettier --write '**/*.{scss,vue}' --cache --cache-strategy metadata" }, "devDependencies": { "@discordapp/twemoji": "14.1.2", + "@eslint-sets/eslint-config-vue3": "^5.6.1", + "@eslint-sets/eslint-config-vue3-ts": "^3.3.0", "@phosphor-icons/web": "^2.0.3", "@rollup/plugin-alias": "3.1.9", "@rollup/plugin-json": "4.1.0", @@ -46,6 +49,8 @@ "date-fns": "2.30.0", "emojilib": "github:thatonecalculator/emojilib", "escape-regexp": "0.0.1", + "eslint-config-prettier": "^8.6.0", + "eslint-plugin-file-progress": "^1.3.0", "eventemitter3": "5.0.1", "fast-blurhash": "^1.1.2", "focus-trap": "^7.5.2", @@ -57,6 +62,7 @@ "katex": "0.16.8", "matter-js": "0.18.0", "mfm-js": "0.23.3", + "paralint": "^1.2.1", "photoswipe": "5.3.8", "prettier": "3.0.0", "prettier-plugin-vue": "1.1.6", diff --git a/packages/client/src/components/MkAbuseReport.vue b/packages/client/src/components/MkAbuseReport.vue index ccb85d722..f92d10ba7 100644 --- a/packages/client/src/components/MkAbuseReport.vue +++ b/packages/client/src/components/MkAbuseReport.vue @@ -80,11 +80,11 @@ const emit = defineEmits<{ (ev: "resolved", reportId: string): void; }>(); -let forward = $ref(props.report.forwarded); +const forward = $ref(props.report.forwarded); function resolve() { os.apiWithDialog("admin/resolve-abuse-user-report", { - forward: forward, + forward, reportId: props.report.id, }).then(() => { emit("resolved", props.report.id); diff --git a/packages/client/src/components/MkAbuseReportWindow.vue b/packages/client/src/components/MkAbuseReportWindow.vue index 6fdf3b9e0..fc80cd66f 100644 --- a/packages/client/src/components/MkAbuseReportWindow.vue +++ b/packages/client/src/components/MkAbuseReportWindow.vue @@ -41,7 +41,7 @@ diff --git a/packages/client/src/components/MkContextMenu.vue b/packages/client/src/components/MkContextMenu.vue index a21547780..33506b96f 100644 --- a/packages/client/src/components/MkContextMenu.vue +++ b/packages/client/src/components/MkContextMenu.vue @@ -57,15 +57,11 @@ onMounted(() => { rootEl.style.top = `${top}px`; rootEl.style.left = `${left}px`; - for (const el of Array.from(document.querySelectorAll("body *"))) { - el.addEventListener("mousedown", onMousedown); - } + document.body.addEventListener("mousedown", onMousedown); }); onBeforeUnmount(() => { - for (const el of Array.from(document.querySelectorAll("body *"))) { - el.removeEventListener("mousedown", onMousedown); - } + document.body.removeEventListener("mousedown", onMousedown); }); function onMousedown(evt: Event) { From d39b1d830fe367a7d1ef11b8373973cfb7a53f26 Mon Sep 17 00:00:00 2001 From: Kainoa Kanter Date: Mon, 17 Jul 2023 05:31:34 +0000 Subject: [PATCH 09/14] =?UTF-8?q?feat:=20=E2=9C=A8=20verify=20links=20with?= =?UTF-8?q?=20rel=3Dme=20(#10506)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds Mastodon-style `rel=me` link verification, and creates a background job to verify said links Closes #9341 ![image](/attachments/861e01eb-660f-4c62-8d83-d824cb79da48) Co-authored-by: ThatOneCalculator Co-authored-by: Namekuji Reviewed-on: https://codeberg.org/calckey/calckey/pulls/10506 --- .gitignore | 3 +- CALCKEY.md | 2 +- locales/ar-SA.yml | 1 - locales/bn-BD.yml | 2 +- locales/ca-ES.yml | 4 +- locales/de-DE.yml | 2 +- locales/en-US.yml | 7 ++- locales/es-ES.yml | 2 +- locales/fr-FR.yml | 2 +- locales/id-ID.yml | 2 +- locales/it-IT.yml | 2 +- locales/ja-JP.yml | 2 +- locales/ko-KR.yml | 2 +- locales/pl-PL.yml | 2 +- locales/ru-RU.yml | 2 +- locales/sk-SK.yml | 2 +- locales/tr-TR.yml | 2 +- locales/uk-UA.yml | 2 +- locales/vi-VN.yml | 2 +- locales/zh-CN.yml | 2 +- locales/zh-TW.yml | 2 +- .../src/models/entities/user-profile.ts | 1 + packages/backend/src/queue/index.ts | 10 +++++ .../src/queue/processors/system/index.ts | 2 + .../queue/processors/system/verify-links.ts | 44 +++++++++++++++++++ .../src/server/api/endpoints/i/update.ts | 43 ++++++++++++++---- packages/backend/src/services/fetch-rel-me.ts | 33 ++++++++++++++ packages/calckey-js/src/entities.ts | 6 ++- .../client/src/pages/settings/profile.vue | 7 ++- packages/client/src/pages/user/home.vue | 13 ++++++ .../megalodon/src/misskey/entities/field.ts | 1 + 31 files changed, 176 insertions(+), 33 deletions(-) create mode 100644 packages/backend/src/queue/processors/system/verify-links.ts create mode 100644 packages/backend/src/services/fetch-rel-me.ts diff --git a/.gitignore b/.gitignore index 6bf2ea1b1..ce9e4f8a1 100644 --- a/.gitignore +++ b/.gitignore @@ -27,7 +27,7 @@ coverage !/.config/helm_values_example.yml !/.config/LICENSE -#docker dev config +# docker dev config /dev/docker-compose.yml # misskey @@ -46,6 +46,7 @@ files ormconfig.json packages/backend/assets/instance.css packages/backend/assets/sounds/None.mp3 +packages/backend/assets/LICENSE !packages/backend/src/db diff --git a/CALCKEY.md b/CALCKEY.md index bb28e5bbf..e561c3a5a 100644 --- a/CALCKEY.md +++ b/CALCKEY.md @@ -16,7 +16,6 @@ ## Work in progress -- Link verification - Better Messaging UI - Better API Documentation - Remote follow button @@ -118,6 +117,7 @@ - Non-mangled unicode emojis - Skin tone selection support - [DragonflyDB](https://dragonflydb.io/) support as a Redis alternative +- Link verification ## Implemented (remote) diff --git a/locales/ar-SA.yml b/locales/ar-SA.yml index 7e97a99eb..59dda2bcf 100644 --- a/locales/ar-SA.yml +++ b/locales/ar-SA.yml @@ -1179,7 +1179,6 @@ _profile: youCanIncludeHashtags: "يمكنك أيضًا إضافة وسوم إلى سيرتك التعريفية." metadata: "معلومات إضافية" metadataEdit: "عدّل المعلومات الإضافية" - metadataDescription: "يُمكنك عرض 4 حقول معلومات في ملفك الشخصي" metadataLabel: "التسمية" metadataContent: "المحتوى" changeAvatar: "غيّر الصورة الرمزية" diff --git a/locales/bn-BD.yml b/locales/bn-BD.yml index e3fbf8cb9..2f37a02a2 100644 --- a/locales/bn-BD.yml +++ b/locales/bn-BD.yml @@ -1268,7 +1268,7 @@ _profile: youCanIncludeHashtags: "হ্যাশট্যাগ অন্তর্ভুক্ত করা যেতে পারে।" metadata: "অতিরিক্ত তথ্য" metadataEdit: "অতিরিক্ত তথ্য সম্পাদনা করুন" - metadataDescription: "আপনি আপনার প্রোফাইলে একটি টেবিল হিসাবে চারটি অতিরিক্ত তথ্য দেখাতে পারেন।" + metadataDescription: "আপনি আপনার প্রোফাইলে একটি টেবিল হিসাবে চারটি অতিরিক্ত তথ্য দেখাতে পারেন।. আপনি আপনার প্রোফাইলে লিঙ্কটি যাচাই করতে {rel} এর সাথে একটি {a} ট্যাগ বা {l} ট্যাগ যোগ করতে পারেন!" metadataLabel: "লেবেল" metadataContent: "বিষয়বস্তু" changeAvatar: "অ্যাভাটার পরিবর্তন করুন" diff --git a/locales/ca-ES.yml b/locales/ca-ES.yml index 8fb57e879..b4cf42852 100644 --- a/locales/ca-ES.yml +++ b/locales/ca-ES.yml @@ -409,8 +409,8 @@ _profile: locationDescription: Si primer introduïu la vostra ciutat, es mostrarà l'hora local a altres usuaris. name: Nom - metadataDescription: Fent servir això, podràs mostrar camps d'informació addicionals - al vostre perfil. + metadataDescription: "Fent servir això, podràs mostrar camps d'informació addicionals + al vostre perfil. Podeu afegir una etiqueta {a} o una etiqueta {l} amb {rel} per verificar l'enllaç al vostre perfil." _exportOrImport: followingList: "Usuaris que segueixes" muteList: "Silencia" diff --git a/locales/de-DE.yml b/locales/de-DE.yml index 9e3a07654..c2c89f15c 100644 --- a/locales/de-DE.yml +++ b/locales/de-DE.yml @@ -1551,7 +1551,7 @@ _profile: metadata: "Zusätzliche Informationen" metadataEdit: "Zusätzliche Informationen bearbeiten" metadataDescription: "Hierdurch kannst du auf deinem Profil zusätzliche Informationsblöcke - anzeigen lassen." + anzeigen lassen. Sie können ein {a}-Tag oder ein {l}-Tag mit {rel} hinzufügen, um den Link in Ihrem Profil zu überprüfen!" metadataLabel: "Beschriftung" metadataContent: "Inhalt" changeAvatar: "Profilbild ändern" diff --git a/locales/en-US.yml b/locales/en-US.yml index 76bc5c24a..eeafa6ffd 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -1124,6 +1124,7 @@ remindMeLater: "Maybe later" removeQuote: "Remove quote" removeRecipient: "Remove recipient" removeMember: "Remove member" +verifiedLink: "Verified link" _sensitiveMediaDetection: description: "Reduces the effort of server moderation through automatically recognizing @@ -1676,8 +1677,10 @@ _profile: youCanIncludeHashtags: "You can also include hashtags in your bio." metadata: "Additional Information" metadataEdit: "Edit additional Information" - metadataDescription: "Using these, you can display additional information fields - in your profile." + metadataDescription: + "Using these, you can display additional information fields + in your profile. You can add an {a} tag or {l} tag with {rel} + to verify the link on your profile!" metadataLabel: "Label" metadataContent: "Content" changeAvatar: "Change avatar" diff --git a/locales/es-ES.yml b/locales/es-ES.yml index faf9ba282..f5c002505 100644 --- a/locales/es-ES.yml +++ b/locales/es-ES.yml @@ -1475,7 +1475,7 @@ _profile: youCanIncludeHashtags: "Puedes añadir hashtags" metadata: "información adicional" metadataEdit: "Editar información adicional" - metadataDescription: "Muestra la información adicional en el perfil" + metadataDescription: "Muestra la información adicional en el perfil. ¡Puede agregar una etiqueta {a} o una etiqueta {l} con {rel} para verificar el enlace en su perfil!" metadataLabel: "Etiqueta" metadataContent: "Contenido" changeAvatar: "Cambiar avatar" diff --git a/locales/fr-FR.yml b/locales/fr-FR.yml index f81ef4520..660be0347 100644 --- a/locales/fr-FR.yml +++ b/locales/fr-FR.yml @@ -1413,7 +1413,7 @@ _profile: metadata: "Informations supplémentaires" metadataEdit: "Éditer les informations supplémentaires" metadataDescription: "Vous pouvez afficher jusqu'à quatre informations supplémentaires - dans votre profil." + dans votre profil. Vous pouvez ajouter une balise {a} ou une balise {l} avec {rel} pour vérifier le lien sur votre profil!" metadataLabel: "Étiquette" metadataContent: "Contenu" changeAvatar: "Changer l'image de profil" diff --git a/locales/id-ID.yml b/locales/id-ID.yml index 17bebe99c..6aaa726db 100644 --- a/locales/id-ID.yml +++ b/locales/id-ID.yml @@ -1399,7 +1399,7 @@ _profile: metadata: "Informasi tambahan" metadataEdit: "Sunting informasi tambahan" metadataDescription: "Kamu dapat menampilkan hingga 4 bagian informasi tambahan\ - \ ke dalam profilmu." + \ ke dalam profilmu. Anda dapat menambahkan tag {a} atau tag {l} dengan {rel} untuk memverifikasi tautan di profil Anda!" metadataLabel: "Label" metadataContent: "Isi" changeAvatar: "Ubah avatar" diff --git a/locales/it-IT.yml b/locales/it-IT.yml index bdf7cab54..93dd5fcf3 100644 --- a/locales/it-IT.yml +++ b/locales/it-IT.yml @@ -1266,7 +1266,7 @@ _profile: metadata: "Informazioni aggiuntive" metadataEdit: "Modifica informazioni aggiuntive" metadataDescription: "Puoi pubblicare fino a quattro informazioni aggiuntive sul - profilo." + profilo. Puoi aggiungere un tag {a} o {l} con {rel} per verificare il link sul tuo profilo!" metadataLabel: "Etichetta" metadataContent: "Contenuto" changeAvatar: "Modifica immagine profilo" diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 1043e3524..755afa8d3 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -1491,7 +1491,7 @@ _profile: youCanIncludeHashtags: "ハッシュタグを含められます。" metadata: "追加情報" metadataEdit: "追加情報を編集" - metadataDescription: "プロフィールに表として追加情報を表示できます。" + metadataDescription: "プロフィールに表として追加情報を表示できます。{a}タグまたは{l}タグを{rel}とともに追加すると、プロフィールのリンクを確認できます。" metadataLabel: "ラベル" metadataContent: "内容" changeAvatar: "アバター画像を変更" diff --git a/locales/ko-KR.yml b/locales/ko-KR.yml index 2c8e548bd..459274166 100644 --- a/locales/ko-KR.yml +++ b/locales/ko-KR.yml @@ -1319,7 +1319,7 @@ _profile: youCanIncludeHashtags: "해시 태그를 포함할 수 있습니다." metadata: "추가 정보" metadataEdit: "추가 정보 편집" - metadataDescription: "프로필에 추가 정보를 표시할 수 있어요" + metadataDescription: "프로필에 추가 정보를 표시할 수 있어요. {rel}과 함께 {a} 태그 또는 {l} 태그를 추가하여 프로필의 링크를 확인할 수 있습니다!" metadataLabel: "라벨" metadataContent: "내용" changeAvatar: "아바타 이미지 변경" diff --git a/locales/pl-PL.yml b/locales/pl-PL.yml index 571f6af95..58624303c 100644 --- a/locales/pl-PL.yml +++ b/locales/pl-PL.yml @@ -1404,7 +1404,7 @@ _profile: metadata: "Dodatkowe informacje" metadataEdit: "Edytuj dodatkowe informacje" metadataDescription: "Możesz wyświetlać do czterech sekcji dodatkowych informacji - na swoim profilu." + na swoim profilu. Możesz dodać tag {a} lub tag {l} z {rel}, aby zweryfikować link w swoim profilu!" metadataLabel: "Etykieta" metadataContent: "Treść" changeAvatar: "Zmień awatar" diff --git a/locales/ru-RU.yml b/locales/ru-RU.yml index bfef68d10..643a84eef 100644 --- a/locales/ru-RU.yml +++ b/locales/ru-RU.yml @@ -1398,7 +1398,7 @@ _profile: youCanIncludeHashtags: "Можете использовать здесь хэштеги." metadata: "Дополнительные сведения" metadataEdit: "Редактировать дополнительные сведения" - metadataDescription: "Можно добавить до четырёх дополнительных граф в профиль." + metadataDescription: "Можно добавить до четырёх дополнительных граф в профиль. Вы можете добавить тег {a} или тег {l} с {rel}, чтобы подтвердить ссылку в своем профиле!" metadataLabel: "Метка" metadataContent: "Содержимое" changeAvatar: "Поменять аватар" diff --git a/locales/sk-SK.yml b/locales/sk-SK.yml index dce23d755..046e67aec 100644 --- a/locales/sk-SK.yml +++ b/locales/sk-SK.yml @@ -1337,7 +1337,7 @@ _profile: youCanIncludeHashtags: "Vo svojom bio môžete mať aj hashtagy." metadata: "Dodatočné informácie" metadataEdit: "Upraviť dodatočné informácie" - metadataDescription: "Vo svojom profile môžete uviesť až štyri dodatočné informačné polia." + metadataDescription: "Vo svojom profile môžete uviesť až štyri dodatočné informačné polia. Dodate lahko oznako {a} ali oznako {l} z {rel}, da preverite povezavo v svojem profile!" metadataLabel: "Popisok" metadataContent: "Obsah" changeAvatar: "Zmeniť avatara" diff --git a/locales/tr-TR.yml b/locales/tr-TR.yml index a3021221d..f73d693a8 100644 --- a/locales/tr-TR.yml +++ b/locales/tr-TR.yml @@ -182,7 +182,7 @@ _profile: gösterecektir. youCanIncludeHashtags: Hakkımdan'da etiket kullanabilirsin. description: Hakkımda - metadataDescription: Bunları kullanarak profilinizde ek bilgi alanları görüntüleyebilirsiniz. + metadataDescription: 'Bunları kullanarak profilinizde ek bilgi alanları görüntüleyebilirsiniz. Profilinizdeki bağlantıyı doğrulamak için {rel} ile bir {a} etiketi veya {l} etiketi ekleyebilirsiniz!' metadata: Ek Bilgi metadataContent: İçerik metadataLabel: Etiket diff --git a/locales/uk-UA.yml b/locales/uk-UA.yml index 78aaf2bbd..b182bf62d 100644 --- a/locales/uk-UA.yml +++ b/locales/uk-UA.yml @@ -1277,7 +1277,7 @@ _profile: metadata: "Додаткова інформація" metadataEdit: "Редагувати додаткову інформацію" metadataDescription: "Ви можете вказати до чотирьох пунктів додаткової інформації - у своєму профілі." + у своєму профілі. Ви можете додати тег {a} або {l} за допомогою {rel}, щоб підтвердити посилання у своєму профілі!" metadataLabel: "Назва" metadataContent: "Вміст" changeAvatar: "Змінити аватар" diff --git a/locales/vi-VN.yml b/locales/vi-VN.yml index ddd79084f..40b3909ce 100644 --- a/locales/vi-VN.yml +++ b/locales/vi-VN.yml @@ -1342,7 +1342,7 @@ _profile: youCanIncludeHashtags: "Bạn có thể dùng hashtag trong tiểu sử." metadata: "Thông tin bổ sung" metadataEdit: "Sửa thông tin bổ sung" - metadataDescription: "Sử dụng phần này, bạn có thể hiển thị các mục thông tin bổ sung trong hồ sơ của mình." + metadataDescription: "Sử dụng phần này, bạn có thể hiển thị các mục thông tin bổ sung trong hồ sơ của mình. Bạn có thể thêm thẻ {a} hoặc thẻ {l} với {rel} để xác minh liên kết trên tiểu sử của mình!" metadataLabel: "Nhãn" metadataContent: "Nội dung" changeAvatar: "Đổi ảnh đại diện" diff --git a/locales/zh-CN.yml b/locales/zh-CN.yml index 36453551d..a0353e0ec 100644 --- a/locales/zh-CN.yml +++ b/locales/zh-CN.yml @@ -1402,7 +1402,7 @@ _profile: youCanIncludeHashtags: "您可以包含一个话题标签。" metadata: "附加信息" metadataEdit: "附加信息编辑" - metadataDescription: "使用这些,您可以在您的个人资料中显示其它信息字段。" + metadataDescription: "使用这些,您可以在您的个人资料中显示其它信息字段。您可以添加带有 {rel} 的 {a} 标签或 {l} 标签来验证您个人资料上的链接!" metadataLabel: "标签" metadataContent: "内容" changeAvatar: "修改头像" diff --git a/locales/zh-TW.yml b/locales/zh-TW.yml index d1bad4ad1..2ea2c4a43 100644 --- a/locales/zh-TW.yml +++ b/locales/zh-TW.yml @@ -1361,7 +1361,7 @@ _profile: youCanIncludeHashtags: "你也可以在「關於我」中加上 #tag。" metadata: "進階資訊" metadataEdit: "編輯進階資訊" - metadataDescription: "可以在個人資料中以表格形式顯示其他資訊。" + metadataDescription: "可以在個人資料中以表格形式顯示其他資訊。您可以添加帶有 {rel} 的 {a} 標籤或 {l} 標籤來驗證您個人資料上的鏈接!" metadataLabel: "標籤" metadataContent: "内容" changeAvatar: "更換大頭貼" diff --git a/packages/backend/src/models/entities/user-profile.ts b/packages/backend/src/models/entities/user-profile.ts index 119eecdc7..002247d3a 100644 --- a/packages/backend/src/models/entities/user-profile.ts +++ b/packages/backend/src/models/entities/user-profile.ts @@ -51,6 +51,7 @@ export class UserProfile { public fields: { name: string; value: string; + verified?: boolean; }[]; @Column("varchar", { diff --git a/packages/backend/src/queue/index.ts b/packages/backend/src/queue/index.ts index d7580a4f6..93aed7cb8 100644 --- a/packages/backend/src/queue/index.ts +++ b/packages/backend/src/queue/index.ts @@ -576,6 +576,16 @@ export default function () { { removeOnComplete: true, removeOnFail: true }, ); + systemQueue.add( + "verifyLinks", + {}, + { + repeat: { cron: "0 0 * * 0" }, + removeOnComplete: true, + removeOnFail: true, + }, + ); + processSystemQueue(systemQueue); } diff --git a/packages/backend/src/queue/processors/system/index.ts b/packages/backend/src/queue/processors/system/index.ts index 53321de5f..697d24d06 100644 --- a/packages/backend/src/queue/processors/system/index.ts +++ b/packages/backend/src/queue/processors/system/index.ts @@ -5,6 +5,7 @@ import { cleanCharts } from "./clean-charts.js"; import { checkExpiredMutings } from "./check-expired-mutings.js"; import { clean } from "./clean.js"; import { setLocalEmojiSizes } from "./local-emoji-size.js"; +import { verifyLinks } from "./verify-links.js"; const jobs = { tickCharts, @@ -13,6 +14,7 @@ const jobs = { checkExpiredMutings, clean, setLocalEmojiSizes, + verifyLinks, } as Record< string, | Bull.ProcessCallbackFunction> diff --git a/packages/backend/src/queue/processors/system/verify-links.ts b/packages/backend/src/queue/processors/system/verify-links.ts new file mode 100644 index 000000000..3ddda9baf --- /dev/null +++ b/packages/backend/src/queue/processors/system/verify-links.ts @@ -0,0 +1,44 @@ +import type Bull from "bull"; + +import { UserProfiles } from "@/models/index.js"; +import { Not } from "typeorm"; +import { queueLogger } from "../../logger.js"; +import { verifyLink } from "@/services/fetch-rel-me.js"; +import config from "@/config/index.js"; + +const logger = queueLogger.createSubLogger("verify-links"); + +export async function verifyLinks( + job: Bull.Job>, + done: any, +): Promise { + logger.info("Verifying links..."); + + const usersToVerify = await UserProfiles.findBy({ + fields: Not(null), + userHost: "", + }); + for (const user of usersToVerify) { + for (const field of user.fields) { + if (!field || field.name === "" || field.value === "") { + continue; + } + if (field.value.startsWith("http") && user.user?.username) { + field.verified = await verifyLink(field.value, user.user.username); + } + } + if (user.fields.length > 0) { + try { + await UserProfiles.update(user.userId, { + fields: user.fields, + }); + } catch (e) { + logger.error(`Failed to update user ${user.userId} ${e}`); + done(e); + } + } + } + + logger.succ("All links successfully verified."); + done(); +} diff --git a/packages/backend/src/server/api/endpoints/i/update.ts b/packages/backend/src/server/api/endpoints/i/update.ts index 0637251a6..6d3bde2b8 100644 --- a/packages/backend/src/server/api/endpoints/i/update.ts +++ b/packages/backend/src/server/api/endpoints/i/update.ts @@ -12,7 +12,9 @@ import type { UserProfile } from "@/models/entities/user-profile.js"; import { notificationTypes } from "@/types.js"; import { normalizeForSearch } from "@/misc/normalize-for-search.js"; import { langmap } from "@/misc/langmap.js"; +import { verifyLink } from "@/services/fetch-rel-me.js"; import { ApiError } from "../../error.js"; +import config from "@/config/index.js"; import define from "../../define.js"; export const meta = { @@ -58,6 +60,18 @@ export const meta = { code: "INVALID_REGEXP", id: "0d786918-10df-41cd-8f33-8dec7d9a89a5", }, + + invalidFieldName: { + message: "Invalid field name.", + code: "INVALID_FIELD_NAME", + id: "8f81972e-8b53-4d30-b0d2-efb026dda673", + }, + + invalidFieldValue: { + message: "Invalid field value.", + code: "INVALID_FIELD_VALUE", + id: "aede7444-244b-11ee-be56-0242ac120002", + }, }, res: { @@ -234,16 +248,29 @@ export default define(meta, paramDef, async (ps, _user, token) => { } if (ps.fields) { + for (const field of ps.fields) { + if (!field || field.name === "" || field.value === "") { + continue; + } + if (typeof field.name !== "string" || field.name === "") { + throw new ApiError(meta.errors.invalidFieldName); + } + if (typeof field.value !== "string" || field.value === "") { + throw new ApiError(meta.errors.invalidFieldValue); + } + if (field.value.startsWith("http")) { + field.verified = await verifyLink(field.value, user.username); + } + } + profileUpdates.fields = ps.fields - .filter( - (x) => - typeof x.name === "string" && - x.name !== "" && - typeof x.value === "string" && - x.value !== "", - ) + .filter((x) => Object.keys(x).length !== 0) .map((x) => { - return { name: x.name, value: x.value }; + return { + name: x.name, + value: x.value, + verified: x.verified, + }; }); } diff --git a/packages/backend/src/services/fetch-rel-me.ts b/packages/backend/src/services/fetch-rel-me.ts new file mode 100644 index 000000000..7b450c229 --- /dev/null +++ b/packages/backend/src/services/fetch-rel-me.ts @@ -0,0 +1,33 @@ +import { getHtml } from "@/misc/fetch.js"; +import { JSDOM } from "jsdom"; +import config from "@/config/index.js"; + +async function getRelMeLinks(url: string): Promise { + try { + const html = await getHtml(url); + const dom = new JSDOM(html); + const relMeLinks = [ + ...dom.window.document.querySelectorAll("a[rel='me']"), + ...dom.window.document.querySelectorAll("link[rel='me']"), + ].map((a) => (a as HTMLAnchorElement | HTMLLinkElement).href); + return relMeLinks; + } catch { + return []; + } +} + +export async function verifyLink(link: string, username: string): Promise { + let verified = false; + if (link.startsWith("http")) { + const relMeLinks = await getRelMeLinks(link); + verified = relMeLinks.some((href) => + new RegExp( + `^https?:\/\/${config.host.replace( + /[.*+\-?^${}()|[\]\\]/g, + "\\$&", + )}\/@${username.replace(/[.*+\-?^${}()|[\]\\]/g, "\\$&")}$`, + ).test(href), + ); + } + return verified; +} diff --git a/packages/calckey-js/src/entities.ts b/packages/calckey-js/src/entities.ts index 5a581a54c..346830e0f 100644 --- a/packages/calckey-js/src/entities.ts +++ b/packages/calckey-js/src/entities.ts @@ -38,7 +38,11 @@ export type UserDetailed = UserLite & { createdAt: DateString; description: string | null; ffVisibility: "public" | "followers" | "private"; - fields: { name: string; value: string }[]; + fields: { + name: string; + value: string; + verified?: boolean; + }[]; followersCount: number; followingCount: number; hasPendingFollowRequestFromYou: boolean; diff --git a/packages/client/src/pages/settings/profile.vue b/packages/client/src/pages/settings/profile.vue index 9b5a079f9..04051d8a1 100644 --- a/packages/client/src/pages/settings/profile.vue +++ b/packages/client/src/pages/settings/profile.vue @@ -126,7 +126,11 @@ @@ -173,6 +177,7 @@ import { i18n } from "@/i18n"; import { $i } from "@/account"; import { langmap } from "@/scripts/langmap"; import { definePageMetadata } from "@/scripts/page-metadata"; +import { host } from "@/config"; const profile = reactive({ name: $i?.name, diff --git a/packages/client/src/pages/user/home.vue b/packages/client/src/pages/user/home.vue index 9d643c0b6..9192d1691 100644 --- a/packages/client/src/pages/user/home.vue +++ b/packages/client/src/pages/user/home.vue @@ -288,10 +288,17 @@
+ { margin-bottom: 8px; } + &.verified { + background-color: var(--hover); + border-radius: 10px; + color: var(--badge) !important; + } + > .name { width: 30%; overflow: hidden; diff --git a/packages/megalodon/src/misskey/entities/field.ts b/packages/megalodon/src/misskey/entities/field.ts index f56d21b63..57a2eb43d 100644 --- a/packages/megalodon/src/misskey/entities/field.ts +++ b/packages/megalodon/src/misskey/entities/field.ts @@ -2,5 +2,6 @@ namespace MisskeyEntity { export type Field = { name: string value: string + verified?: string } } From 0c212059a62e04ee7f9927d63cb318b0581519c0 Mon Sep 17 00:00:00 2001 From: Xalis Ratt Date: Sun, 16 Jul 2023 09:53:16 +0000 Subject: [PATCH 10/14] chore: Translated using Weblate (Ukrainian) Currently translated at 100.0% (1831 of 1831 strings) Translation: Calckey/locales Translate-URL: https://hosted.weblate.org/projects/calckey/locales/uk/ --- locales/uk-UA.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locales/uk-UA.yml b/locales/uk-UA.yml index 78aaf2bbd..ee195ed1c 100644 --- a/locales/uk-UA.yml +++ b/locales/uk-UA.yml @@ -1250,7 +1250,7 @@ _poll: _visibility: public: "Публічний" publicDescription: "Ваш запис буде видно в усіх публічних стрічках" - home: "Скритий" + home: "Домашній" homeDescription: "Лише на домашній стрічці" followers: "Підписники" followersDescription: "Зробити видимим тільки для ваших підписників і згаданих користувачів" From b5d0a757b989df21e5542c13eed5e803bc7ccd51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=AA=A0=E8=AA=A0-ChengCheng?= Date: Sun, 16 Jul 2023 11:02:41 +0000 Subject: [PATCH 11/14] chore: Translated using Weblate (Chinese (Traditional)) Currently translated at 95.5% (1750 of 1831 strings) Translation: Calckey/locales Translate-URL: https://hosted.weblate.org/projects/calckey/locales/zh_Hant/ --- locales/zh-TW.yml | 44 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 38 insertions(+), 6 deletions(-) diff --git a/locales/zh-TW.yml b/locales/zh-TW.yml index d1bad4ad1..cbc0a8202 100644 --- a/locales/zh-TW.yml +++ b/locales/zh-TW.yml @@ -984,6 +984,12 @@ _aboutMisskey: donate: "贊助Calckey" morePatrons: "還有許許多多幫助我們的其他人,非常感謝你們。 🥰" patrons: "贊助者" + patronsList: 按時間順序列出,而不是按贊助規模列出。使用上面的連結贊助,在這裡獲得顯示您名字的機會! + sponsors: Calckey 贊助者們 + donateTitle: 覺得 Calckey 棒嗎? + pleaseDonateToCalckey: 請考慮向 Calckey 贊助以支持其發展。 + pleaseDonateToHost: 還請考慮捐贈給您在使用的伺服器 {host},以支援龐大的運營成本。 + donateHost: 贊助給 {host} _nsfw: respect: "隱藏敏感內容" ignore: "不隱藏敏感內容" @@ -1060,6 +1066,8 @@ _mfm: position: 位置 alwaysPlay: 自動播放所有MFM動畫 positionDescription: 按指定數量移動內容。 + advancedDescription: 如果禁用,則僅允許基本標記,除非正在播放 MFM 動畫 + advanced: 高級MFM _instanceTicker: none: "隱藏" remote: "向遠端使用者顯示" @@ -1202,14 +1210,14 @@ _tutorial: step1_1: "歡迎!" step1_2: "讓我們把你安排好。你很快就會啟動並運行!" step2_1: "首先,請完成你的個人資料。" - step2_2: "通過提供一些關於你自己的資料,其他人會更容易了解他們是否想看到你的帖子或關注你。" + step2_2: "通過提供一些關於你自己的資料,其他人會更容易了解他們是否想看到你的貼文或關注你。" step3_1: "現在是時候追隨一些人了!" step3_2: "你的主頁和社交時間線是基於你所追蹤的人,所以試著先追蹤幾個帳戶。\n點擊個人資料右上角的加號圈就可以關注它。" step4_1: "讓我們出去找你。" step4_2: "對於他們的第一條信息,有些人喜歡做 {introduction} 或一個簡單的 \"hello world!\"" step5_1: "時間線,到處都是時間線!" step5_2: "您的伺服器已啟用了{timelines}個時間線。" - step5_3: "首頁 {icon} 時間線是顯示你追蹤的帳號的帖子。" + step5_3: "首頁 {icon} 時間線是顯示你追蹤的帳號的貼文。" step5_4: "本地 {icon} 時間線是你可以看到伺服器中所有其他用戶的貼文的時間線。" step5_5: "社交 {icon} 時間線是你的 首頁時間線 和 本地時間線 的結合體。" step5_6: "推薦 {icon} 時間線是顯示你的伺服器管理員推薦的貼文。" @@ -1820,12 +1828,12 @@ _experiments: title: 試驗功能 findOtherInstance: 找找另一個伺服器 noGraze: 瀏覽器擴展 "Graze for Mastodon" 會與Calckey發生衝突,請停用該擴展。 -userSaysSomethingReasonRenote: '{name} 轉傳了包含 {reason} 的帖子' +userSaysSomethingReasonRenote: '{name} 轉傳了包含 {reason} 的貼文' pushNotificationNotSupported: 你的瀏覽器或伺服器不支援推送通知 accessibility: 輔助功能 -userSaysSomethingReasonReply: '{name} 回復了包含 {reason} 的帖子' +userSaysSomethingReasonReply: '{name} 回覆了包含 {reason} 的貼文' hiddenTags: 隱藏主題標籤 -indexPosts: 索引帖子 +indexPosts: 索引貼文 indexNotice: 現在開始索引。 這可能需要一段時間,請不要在一個小時內重啟你的伺服器。 deleted: 已刪除 editNote: 編輯筆記 @@ -1861,9 +1869,33 @@ audio: 音訊 sendPushNotificationReadMessageCaption: 包含文本 “{emptyPushNotificationMessage}” 的通知將顯示一小段時間。 這可能會增加您設備的電池使用量(如果適用)。 channelFederationWarn: 頻道功能尚未與聯邦宇宙連動 -swipeOnMobile: 允許在頁面之間滑動 +swipeOnMobile: 允許以滑動在頁面之間切換 sendPushNotificationReadMessage: 閱讀相關通知或消息後刪除推送通知 image: 圖片 seperateRenoteQuote: 分別獨立的轉傳及引用按鈕 clipsDesc: 摘錄就像一個可以分享的書籤。 你可以從每個貼文的菜單創建新摘錄或將貼文加入已有的摘錄。 noteId: 貼文 ID +sendModMail: 發送審核通知 +enableIdenticonGeneration: 啟用碎片生成 +enableServerMachineStats: 啟用伺服器硬體統計資訊 +reactionPickerSkinTone: 首選表情符號膚色 +indexFromDescription: 留空以索引每個貼文 +preventAiLearning: 防止 AI 機器人抓取 +preventAiLearningDescription: 請求第三方 AI 語言模型不要研究您上傳的內容,例如貼文和圖像。 +indexFrom: 從貼文 ID 開始的索引 +isLocked: 該帳戶已獲得以下批准 +isModerator: 板主 +isAdmin: 管理員 +isPatron: Calckey 項目贊助者 +silencedWarning: 顯示此頁面是因為這些使用者來自您伺服器管理員已靜音的伺服器,因此他們可能是垃圾訊息。 +signupsDisabled: 該伺服器上的註冊當前已被禁用,但您隨時可以在另一台伺服器上註冊!或是您有該伺服器的邀請碼,請在下面輸入。 +showPopup: 通過彈出式視窗通知用戶 +showWithSparkles: 閃閃發光的顯示 +youHaveUnreadAnnouncements: 您有未讀的公告 +donationLink: 連結到贊助頁面 +neverShow: 不再顯示 +remindMeLater: 可能之後 +removeQuote: 删除引用 +removeRecipient: 刪除收件者 +removeMember: 刪除成員 +isBot: 此帳戶是機器人 From dbf5594237f6cd9992681c8181d0097b660d9edd Mon Sep 17 00:00:00 2001 From: Syuilo Date: Sun, 16 Jul 2023 22:51:50 -0700 Subject: [PATCH 12/14] =?UTF-8?q?fix:=20=F0=9F=90=9B=20Scrolling=20Issue?= =?UTF-8?q?=20in=20Safari=20for=20Top=20and=20Bottom=20Bars?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/client/src/ui/deck.vue | 21 +++++++++++++++++++++ packages/client/src/ui/universal.vue | 21 +++++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/packages/client/src/ui/deck.vue b/packages/client/src/ui/deck.vue index 6fdcfb28d..9037ad156 100644 --- a/packages/client/src/ui/deck.vue +++ b/packages/client/src/ui/deck.vue @@ -384,6 +384,27 @@ async function deleteProfile() { } + + +