diff --git a/.config/ci.yml b/.config/ci.yml deleted file mode 100644 index 5534e265..00000000 --- a/.config/ci.yml +++ /dev/null @@ -1,195 +0,0 @@ -#━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -# Firefish configuration -#━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - -# ┌─────┐ -#───┘ URL └───────────────────────────────────────────────────── - -# Final accessible URL seen by a user. -url: https://example.tld/ - -# ONCE YOU HAVE STARTED THE INSTANCE, DO NOT CHANGE THE -# URL SETTINGS AFTER THAT! - -# ┌───────────────────────┐ -#───┘ Port and TLS settings └─────────────────────────────────── - -# -# Misskey requires a reverse proxy to support HTTPS connections. -# -# +----- https://example.tld/ ------------+ -# +------+ |+-------------+ +----------------+| -# | User | ---> || Proxy (443) | ---> | Misskey (3000) || -# +------+ |+-------------+ +----------------+| -# +---------------------------------------+ -# -# You need to set up a reverse proxy. (e.g. nginx) -# An encrypted connection with HTTPS is highly recommended -# because tokens may be transferred in GET requests. - -# The port that your Misskey server should listen on. -port: 3000 - -# ┌──────────────────────────┐ -#───┘ PostgreSQL configuration └──────────────────────────────── - -db: - host: postgres - port: 5432 - - # Database name - db: postgres - - # Auth - user: postgres - pass: test - - # Whether disable Caching queries - #disableCache: true - - # Extra Connection options - #extra: - # ssl: true - -# ┌─────────────────────┐ -#───┘ Redis configuration └───────────────────────────────────── - -redis: - host: redis - port: 6379 - #family: 0 # 0=Both, 4=IPv4, 6=IPv6 - #pass: example-pass - #prefix: example-prefix - #db: 1 - -# ┌─────────────────────────────┐ -#───┘ Elasticsearch configuration └───────────────────────────── - -#elasticsearch: -# host: localhost -# port: 9200 -# ssl: false -# user: -# pass: - -# ┌───────────────┐ -#───┘ ID generation └─────────────────────────────────────────── - -# You can select the ID generation method. -# You don't usually need to change this setting, but you can -# change it according to your preferences. - -# Available methods: -# aid ... Short, Millisecond accuracy -# meid ... Similar to ObjectID, Millisecond accuracy -# ulid ... Millisecond accuracy -# objectid ... This is left for backward compatibility - -# ONCE YOU HAVE STARTED THE INSTANCE, DO NOT CHANGE THE -# ID SETTINGS AFTER THAT! - -id: 'aid' - -# ┌─────────────────────┐ -#───┘ Other configuration └───────────────────────────────────── - -# Max note length, should be < 8000. -#maxNoteLength: 3000 - -# Whether disable HSTS -#disableHsts: true - -# Number of worker processes -#clusterLimit: 1 - -# Job concurrency per worker -# deliverJobConcurrency: 128 -# inboxJobConcurrency: 16 - -# Job rate limiter -# deliverJobPerSec: 128 -# inboxJobPerSec: 16 - -# Job attempts -# deliverJobMaxAttempts: 12 -# inboxJobMaxAttempts: 8 - -# IP address family used for outgoing request (ipv4, ipv6 or dual) -#outgoingAddressFamily: ipv4 - -# Syslog option -#syslog: -# host: localhost -# port: 514 - -# Proxy for HTTP/HTTPS -#proxy: http://127.0.0.1:3128 - -#proxyBypassHosts: [ -# 'example.com', -# '192.0.2.8' -#] - -# Proxy for SMTP/SMTPS -#proxySmtp: http://127.0.0.1:3128 # use HTTP/1.1 CONNECT -#proxySmtp: socks4://127.0.0.1:1080 # use SOCKS4 -#proxySmtp: socks5://127.0.0.1:1080 # use SOCKS5 - -# Media Proxy -#mediaProxy: https://example.com/proxy - -# Proxy remote files (default: false) -#proxyRemoteFiles: true - -#allowedPrivateNetworks: [ -# '127.0.0.1/32' -#] - -# Upload or download file size limits (bytes) -#maxFileSize: 262144000 - -# Managed hosting settings -# !!!!!!!!!! -# >>>>>> NORMAL SELF-HOSTERS, STAY AWAY! <<<<<< -# >>>>>> YOU DON'T NEED THIS! <<<<<< -# !!!!!!!!!! -# Each category is optional, but if each item in each category is mandatory! -# If you mess this up, that's on you, you've been warned... - -#maxUserSignups: 100 -#isManagedHosting: true -#deepl: -# managed: true -# authKey: '' -# isPro: false -# -#email: -# managed: true -# address: 'example@email.com' -# host: 'email.com' -# port: 587 -# user: 'example@email.com' -# pass: '' -# useImplicitSslTls: false -# -#objectStorage: -# managed: true -# baseUrl: '' -# bucket: '' -# prefix: '' -# endpoint: '' -# region: '' -# accessKey: '' -# secretKey: '' -# useSsl: true -# connnectOverProxy: false -# setPublicReadOnUpload: true -# s3ForcePathStyle: true - -# !!!!!!!!!! -# >>>>>> AGAIN, NORMAL SELF-HOSTERS, STAY AWAY! <<<<<< -# >>>>>> YOU DON'T NEED THIS, ABOVE SETTINGS ARE FOR MANAGED HOSTING ONLY! <<<<<< -# !!!!!!!!!! - -# Seriously. Do NOT fill out the above settings if you're self-hosting. -# They're much better off being set from the control panel. diff --git a/.config/devenv.yml b/.config/devenv.yml deleted file mode 100644 index 65b819b5..00000000 --- a/.config/devenv.yml +++ /dev/null @@ -1,38 +0,0 @@ -url: http://localhost:3000 -port: 3000 - -db: - host: 127.0.0.1 - port: 5432 - - db: firefish - - user: firefish - pass: firefish - -redis: - host: localhost - port: 6379 - family: 4 -#sonic: -# host: localhost -# port: 1491 -# auth: SecretPassword -# collection: notes -# bucket: default - -#elasticsearch: -# host: localhost -# port: 9200 -# ssl: false -# user: -# pass: - -id: 'aid' - -reservedUsernames: - - root - - admin - - administrator - - me - - system diff --git a/.config/docker_ci.env b/.config/docker_ci.env deleted file mode 100644 index 437b8eb5..00000000 --- a/.config/docker_ci.env +++ /dev/null @@ -1,4 +0,0 @@ -# db settings -POSTGRES_PASSWORD=test -POSTGRES_USER=postgres -POSTGRES_DB=postgres diff --git a/.config/helm_values_example.yml b/.config/helm_values_example.yml deleted file mode 100644 index 5c86acca..00000000 --- a/.config/helm_values_example.yml +++ /dev/null @@ -1,82 +0,0 @@ -replicaCount: 1 - -resources: - requests: - cpu: 0.5 - memory: 512Mi - limits: - cpu: 1 - memory: 1Gi - -firefish: - domain: example.tld - smtp: - from_address: noreply@example.tld - port: 587 - server: smtp.gmail.com - useImplicitSslTls: false - login: me@example.tld - password: CHANGEME - objectStorage: - baseUrl: https://example-bucket.nyc3.cdn.digitaloceanspaces.com - access_key: CHANGEME - access_secret: CHANGEME - bucket: example-bucket - endpoint: nyc3.digitaloceanspaces.com:443 - region: nyc3 - allowedPrivateNetworks: [] - -ingress: - enabled: true - annotations: - cert-manager.io/cluster-issuer: letsencrypt - hosts: - - host: example.tld - paths: - - path: / - pathType: ImplementationSpecific - tls: - - secretName: example-tld-certificate - hosts: - - example.tld - -elasticsearch: - enabled: false - -postgresql: - auth: - password: CHANGEME - postgresPassword: CHANGEME - primary: - persistence: - enabled: true - storageClass: vultr-block-storage - size: 25Gi - resources: - requests: - cpu: 0.25 - memory: 256Mi - limits: - cpu: 0.5 - memory: 512Mi - metrics: - enabled: true - -redis: - auth: - password: CHANGEME - master: - resources: - requests: - cpu: 0.25 - memory: 256Mi - limits: - cpu: 0.5 - memory: 256Mi - persistence: - storageclass: vultr-block-storage - size: 10Gi - replica: - replicaCount: 0 - metrics: - enabled: true diff --git a/gulpfile.js b/gulpfile.js index 7220d842..37a8e75b 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -52,7 +52,7 @@ gulp.task("build:backend:script", () => { "./packages/backend/src/server/web/bios.js", "./packages/backend/src/server/web/cli.js", ]) - .pipe(replace("LANGS", JSON.stringify(Object.keys(locales)))) + .pipe(replace("SUPPORTED_LANGS", JSON.stringify(Object.keys(locales)))) .pipe( terser({ toplevel: true, diff --git a/locales/ca-ES.yml b/locales/ca-ES.yml index ce6a7019..80b3e016 100644 --- a/locales/ca-ES.yml +++ b/locales/ca-ES.yml @@ -112,7 +112,7 @@ you: "Tu" clickToShow: "Fes clic per a mostrar" sensitive: "NSFW" add: "Afegeix" -reaction: "Reaccions" +reaction: "Reacció" reactionSetting: "Reaccions a mostrar al selector de reaccions" reactionSettingDescription2: "Arrossega per reordenar, fes clic per suprimir, prem \"+\" per afegir." @@ -792,11 +792,11 @@ customEmojis: Emojis personalitzats cacheRemoteFilesDescription: Quan aquesta opció està desactivada, els fitxers remots es carreguen directament del servidor remot. Desactivar-la farà que baixi l'ús d'emmagatzematge, però incrementa el tràfic, perquè les miniatures no es generaran. -flagAsBot: Marca aquest compte com a bot +flagAsBot: Marca aquest compte com automatitzat flagAsBotDescription: Activa aquesta opció si aquest compte és controlat per un programa. Si s'activa, això actuarà com una bandera per a altres desenvolupadors i ajuda a - prevenir cadenes de interaccions infinites amb altres bots a més d'ajustar els sistemes - interns de Firefish per tractar aquest compte com un bot. + prevenir cadenes de interaccions infinites amb altres comptes automatitzats a més + d'ajustar els sistemes interns de Firefish per tractar aquest compte com automatitzat. flagAsCat: Ets un gat? 🐱 flagShowTimelineReplies: Mostra respostes a la línia de temps flagAsCatDescription: Guanyaràs unes orelles de gat i parlares com un gat! @@ -2127,7 +2127,7 @@ clipsDesc: Els clips són com marcadors categoritzats que es poden compartir. Po selectChannel: Selecciona un canal isLocked: Aquest compte té les següents aprovacions isPatron: Mecenes de Firefish -isBot: Aquest compte és un bot +isBot: Aquest es un compte automatitzat isModerator: Moderador isAdmin: Administrador _filters: @@ -2199,3 +2199,16 @@ openServerInfo: Mostra la informació del servidor fent clic al símbol del serv en un missatge vibrate: Activar vibracions clickToShowPatterns: Fes clic per veure patrons de mòduls +iconSet: Conjunt d'Icones +_iconSets: + fill: Omplerts + regular: Normals + bold: Negreta + duotone: Bitó + light: Prims +showAttachedNotes: Mostrar publicacions que contenen aquest fitxer +reactions: Reaccions +attachedToNotes: Publicacions que contenen aquest fitxer +replies: Respostes +quotes: Cites +renotes: Impulsos diff --git a/locales/de-DE.yml b/locales/de-DE.yml index 693b521c..fa934b00 100644 --- a/locales/de-DE.yml +++ b/locales/de-DE.yml @@ -2218,3 +2218,4 @@ openServerInfo: Anzeigen von Serverinformationen durch Anklicken des Server-Tick in einem Beitrag vibrate: Vibrationen abspielen clickToShowPatterns: Klicken um Modul-Muster anzuzeigen +replies: Antworten diff --git a/locales/en-US.yml b/locales/en-US.yml index ee8c924c..efb021ca 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -312,7 +312,7 @@ agreeTo: "I agree to {0}" tos: "Terms of Service" start: "Begin" home: "Home" -remoteUserCaution: "Information from remote users may be incomplete." +remoteUserCaution: "Information from remote users are incomplete." activity: "Activity" images: "Images" birthday: "Birthday" @@ -2188,5 +2188,5 @@ _iconSets: regular: "Regular" fill: "Filled" duotone: "Duotone" -moreUrls: "pinned pages" +moreUrls: "Pinned pages" moreUrlsDescription: "Enter the pages you want to pin to the help menu in the lower left corner using this notation:\n\"Display name\": https://example.com/" diff --git a/locales/id-ID.yml b/locales/id-ID.yml index 22e0040c..71cfdf72 100644 --- a/locales/id-ID.yml +++ b/locales/id-ID.yml @@ -278,8 +278,7 @@ agreeTo: "Saya setuju kepada {0}" tos: "Syarat dan ketentuan" start: "Mulai" home: "Beranda" -remoteUserCaution: "Informasi ini mungkin tidak mutakhir, karena pengguna ini berasal - dari instansi luar." +remoteUserCaution: "Informasi dari pengguna luar tidak lengkap." activity: "Aktivitas" images: "Gambar" birthday: "Tanggal lahir" @@ -2192,3 +2191,5 @@ reactions: Reaksi replies: Balasan quotes: Kutipan renotes: Postingan ulang +showAttachedNotes: Tampilkan postingan dengan berkas ini +attachedToNotes: Posting dengan berkas ini diff --git a/locales/it-IT.yml b/locales/it-IT.yml index 01e52967..e6e2234c 100644 --- a/locales/it-IT.yml +++ b/locales/it-IT.yml @@ -268,7 +268,7 @@ agreeTo: "Sono d'accordo con {0}" tos: "Termini d'uso" start: "Inizia" home: "Home" -remoteUserCaution: "Le informazioni degli utenti remoti possono essere incomplete." +remoteUserCaution: "Le informazioni degli utenti remoti sono incomplete." activity: "Attività" images: "Immagini" birthday: "Compleanno" @@ -2180,3 +2180,5 @@ reactions: Reazioni replies: Risposte quotes: Citazioni renotes: Boost +showAttachedNotes: Mostra i post con questo allegato +attachedToNotes: Post con questo allegato diff --git a/neko/UPSTREAM_COMMIT_ID b/neko/UPSTREAM_COMMIT_ID index 4824f55f..db355f1d 100644 --- a/neko/UPSTREAM_COMMIT_ID +++ b/neko/UPSTREAM_COMMIT_ID @@ -1 +1 @@ -465a585f47037ce26834587429f0bd341e35f76f +a6aa7d2b74020dfc9303995918e63727fb3cb2c0 diff --git a/neko/pnpm-lock.yaml b/neko/pnpm-lock.yaml index 680c2857..55069f61 100644 --- a/neko/pnpm-lock.yaml +++ b/neko/pnpm-lock.yaml @@ -139,7 +139,7 @@ importers: specifier: 6.0.1 version: 6.0.1 argon2: - specifier: 0.31.2 + specifier: ^0.31.2 version: 0.31.2 aws-sdk: specifier: 2.1498.0 @@ -624,7 +624,7 @@ importers: specifier: 0.16.0 version: 0.16.0 '@types/autosize': - specifier: 4.0.3 + specifier: ^4.0.3 version: 4.0.3 '@types/glob': specifier: 8.1.0 @@ -717,7 +717,7 @@ importers: specifier: 2.30.0 version: 2.30.0 emojilib: - specifier: 3.0.11 + specifier: ^3.0.11 version: 3.0.11 eslint-config-prettier: specifier: 9.0.0 @@ -810,7 +810,7 @@ importers: specifier: 1.6.0 version: 1.6.0 tinyld: - specifier: 1.3.4 + specifier: ^1.3.4 version: 1.3.4 typescript: specifier: 5.2.2 @@ -831,10 +831,10 @@ importers: specifier: 3.3.8 version: 3.3.8(typescript@5.2.2) vue-draggable-plus: - specifier: 0.2.7 + specifier: ^0.2.7 version: 0.2.7(@types/sortablejs@1.15.5) vue-plyr: - specifier: 7.0.0 + specifier: ^7.0.0 version: 7.0.0 vue-prism-editor: specifier: 2.0.0-alpha.2 diff --git a/package.json b/package.json index 4acb6bbf..1fd8ab49 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "firefish", - "version": "1.0.5-dev21", + "version": "1.0.5-dev22", "codename": "aqua", "repository": { "type": "git", diff --git a/packages/backend/package.json b/packages/backend/package.json index db12ef49..db184a03 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -40,7 +40,7 @@ "adm-zip": "0.5.10", "ajv": "8.12.0", "archiver": "6.0.1", - "argon2": "0.31.2", + "argon2": "^0.31.2", "aws-sdk": "2.1498.0", "axios": "1.6.2", "bcryptjs": "2.4.3", diff --git a/packages/backend/src/misc/check-hit-antenna.ts b/packages/backend/src/misc/check-hit-antenna.ts index 0d0aa7fc..ea295d1a 100644 --- a/packages/backend/src/misc/check-hit-antenna.ts +++ b/packages/backend/src/misc/check-hit-antenna.ts @@ -1,12 +1,12 @@ import * as Acct from "@/misc/acct.js"; import { Cache } from "@/misc/cache.js"; import { getWordHardMute } from "@/misc/check-word-mute.js"; +import { getFullApAccount } from "@/misc/convert-host.js"; import type { Packed } from "@/misc/schema.js"; import type { Antenna } from "@/models/entities/antenna.js"; import type { Note } from "@/models/entities/note.js"; import type { User } from "@/models/entities/user.js"; import { Blockings, Followings, UserProfiles } from "@/models/index.js"; -import { getFullApAccount } from "./convert-host.js"; const blockingCache = new Cache("blocking", 60 * 5); const mutedWordsCache = new Cache("mutedWords", 60 * 5); diff --git a/packages/backend/src/misc/id/aid.ts b/packages/backend/src/misc/id/aid.ts deleted file mode 100644 index 05576d4d..00000000 --- a/packages/backend/src/misc/id/aid.ts +++ /dev/null @@ -1,25 +0,0 @@ -// AID -// 長さ8の[2000年1月1日からの経過ミリ秒をbase36でエンコードしたもの] + 長さ2の[ノイズ文字列] - -import * as crypto from "node:crypto"; - -const TIME2000 = 946684800000; -let counter = crypto.randomBytes(2).readUInt16LE(0); - -function getTime(time: number) { - time = time - TIME2000; - if (time < 0) time = 0; - - return time.toString(36).padStart(8, "0"); -} - -function getNoise() { - return counter.toString(36).padStart(2, "0").slice(-2); -} - -export function genAid(date: Date): string { - const t = date.getTime(); - if (Number.isNaN(t)) throw "Failed to create AID: Invalid Date"; - counter++; - return getTime(t) + getNoise(); -} diff --git a/packages/backend/src/misc/id/meid.ts b/packages/backend/src/misc/id/meid.ts deleted file mode 100644 index ee78eb8d..00000000 --- a/packages/backend/src/misc/id/meid.ts +++ /dev/null @@ -1,26 +0,0 @@ -const CHARS = "0123456789abcdef"; - -function getTime(time: number) { - if (time < 0) time = 0; - if (time === 0) { - return CHARS[0]; - } - - time += 0x800000000000; - - return time.toString(16).padStart(12, CHARS[0]); -} - -function getRandom() { - let str = ""; - - for (let i = 0; i < 12; i++) { - str += CHARS[Math.floor(Math.random() * CHARS.length)]; - } - - return str; -} - -export function genMeid(date: Date): string { - return getTime(date.getTime()) + getRandom(); -} diff --git a/packages/backend/src/misc/id/meidg.ts b/packages/backend/src/misc/id/meidg.ts deleted file mode 100644 index 4fd39a8b..00000000 --- a/packages/backend/src/misc/id/meidg.ts +++ /dev/null @@ -1,28 +0,0 @@ -const CHARS = "0123456789abcdef"; - -// 4bit Fixed hex value 'g' -// 44bit UNIX Time ms in Hex -// 48bit Random value in Hex - -function getTime(time: number) { - if (time < 0) time = 0; - if (time === 0) { - return CHARS[0]; - } - - return time.toString(16).padStart(11, CHARS[0]); -} - -function getRandom() { - let str = ""; - - for (let i = 0; i < 12; i++) { - str += CHARS[Math.floor(Math.random() * CHARS.length)]; - } - - return str; -} - -export function genMeidg(date: Date): string { - return `g${getTime(date.getTime())}${getRandom()}`; -} diff --git a/packages/backend/src/misc/id/object-id.ts b/packages/backend/src/misc/id/object-id.ts deleted file mode 100644 index 45822f0a..00000000 --- a/packages/backend/src/misc/id/object-id.ts +++ /dev/null @@ -1,26 +0,0 @@ -const CHARS = "0123456789abcdef"; - -function getTime(time: number) { - if (time < 0) time = 0; - if (time === 0) { - return CHARS[0]; - } - - time = Math.floor(time / 1000); - - return time.toString(16).padStart(8, CHARS[0]); -} - -function getRandom() { - let str = ""; - - for (let i = 0; i < 16; i++) { - str += CHARS[Math.floor(Math.random() * CHARS.length)]; - } - - return str; -} - -export function genObjectId(date: Date): string { - return getTime(date.getTime()) + getRandom(); -} diff --git a/packages/backend/src/queue/processors/inbox.ts b/packages/backend/src/queue/processors/inbox.ts index 74540eb5..e4551f31 100644 --- a/packages/backend/src/queue/processors/inbox.ts +++ b/packages/backend/src/queue/processors/inbox.ts @@ -6,6 +6,7 @@ import { shouldBlockInstance } from "@/misc/should-block-instance.js"; import type { UserPublickey } from "@/models/entities/user-publickey.js"; import type { CacheableRemoteUser } from "@/models/entities/user.js"; import { Instances } from "@/models/index.js"; +import { verifySignature } from "@/remote/activitypub/check-fetch.js"; import DbResolver from "@/remote/activitypub/db-resolver.js"; import { LdSignature } from "@/remote/activitypub/misc/ld-signature.js"; import { resolvePerson } from "@/remote/activitypub/models/person.js"; @@ -109,6 +110,12 @@ export default async (job: Bull.Job): Promise => { ); } + if (httpSignatureValidated) { + if (!verifySignature(signature, authUser.key)) { + return "skip: Invalid HTTP signature"; + } + } + // また、signatureのsignerは、activity.actorと一致する必要がある if (!httpSignatureValidated || authUser.user.uri !== activity.actor) { // 一致しなくても、でもLD-Signatureがありそうならそっちも見る diff --git a/packages/backend/src/queue/processors/system/clean-charts.ts b/packages/backend/src/queue/processors/system/clean-charts.ts index 2bd10be2..d5d84b59 100644 --- a/packages/backend/src/queue/processors/system/clean-charts.ts +++ b/packages/backend/src/queue/processors/system/clean-charts.ts @@ -1,7 +1,7 @@ import type Bull from "bull"; import { activeUsersChart } from "@/services/chart/index.js"; -import { queueLogger } from "../../logger.js"; +import { queueLogger } from "@/queue/logger.js"; const logger = queueLogger.createSubLogger("clean-charts"); diff --git a/packages/backend/src/queue/processors/system/index.ts b/packages/backend/src/queue/processors/system/index.ts index 8ff43838..74541cc3 100644 --- a/packages/backend/src/queue/processors/system/index.ts +++ b/packages/backend/src/queue/processors/system/index.ts @@ -3,6 +3,8 @@ import { checkExpiredMutings } from "./check-expired-mutings.js"; import { cleanCharts } from "./clean-charts.js"; import { clean } from "./clean.js"; import { setLocalEmojiSizes } from "./local-emoji-size.js"; +import { resyncCharts } from "./resync-charts.js"; +import { tickCharts } from "./tick-charts.js"; import { verifyLinks } from "./verify-links.js"; const jobs = { diff --git a/packages/backend/src/remote/activitypub/check-fetch.ts b/packages/backend/src/remote/activitypub/check-fetch.ts index 4da50789..77ba7952 100644 --- a/packages/backend/src/remote/activitypub/check-fetch.ts +++ b/packages/backend/src/remote/activitypub/check-fetch.ts @@ -1,4 +1,6 @@ import type { IncomingMessage } from "http"; +import { verify } from "node:crypto"; +import { createHash } from "node:crypto"; import { URL } from "url"; import config from "@/config/index.js"; import { toPuny } from "@/misc/convert-host.js"; @@ -6,9 +8,10 @@ import { fetchMeta } from "@/misc/fetch-meta.js"; import { shouldBlockInstance } from "@/misc/should-block-instance.js"; import type { UserPublickey } from "@/models/entities/user-publickey.js"; import type { CacheableRemoteUser } from "@/models/entities/user.js"; +import { toSingle } from "@/prelude/array.js"; import DbResolver from "@/remote/activitypub/db-resolver.js"; import { getApId } from "@/remote/activitypub/type.js"; -import httpSignature from "@peertube/http-signature"; +import httpSignature, { IParsedSignature } from "@peertube/http-signature"; export async function hasSignature(req: IncomingMessage): Promise { const meta = await fetchMeta(); @@ -28,10 +31,14 @@ export async function hasSignature(req: IncomingMessage): Promise { export async function checkFetch(req: IncomingMessage): Promise { const meta = await fetchMeta(); if (meta.secureMode || meta.privateMode) { + if (req.headers.host !== config.host) return 400; + let signature; try { - signature = httpSignature.parseRequest(req, { headers: [] }); + signature = httpSignature.parseRequest(req, { + headers: ["(request-target)", "host", "date"], + }); } catch (e) { return 401; } @@ -108,6 +115,8 @@ export async function checkFetch(req: IncomingMessage): Promise { if (!httpSignatureValidated) { return 403; } + + return verifySignature(signature, authUser.key) ? 200 : 401; } return 200; } @@ -130,3 +139,39 @@ export async function getSignatureUser(req: IncomingMessage): Promise<{ keyId.hash = ""; return await dbResolver.getAuthUserFromApId(getApId(keyId.toString())); } + +export function verifySignature( + sig: IParsedSignature, + key: UserPublickey, +): boolean { + if (!["hs2019", "rsa-sha256"].includes(sig.algorithm.toLowerCase())) + return false; + try { + return verify( + "rsa-sha256", + Buffer.from(sig.signingString, "utf8"), + key.keyPem, + Buffer.from(sig.params.signature, "base64"), + ); + } catch { + // Algo not supported + return false; + } +} + +export function verifyDigest( + body: string, + digest: string | string[] | undefined, +): boolean { + digest = toSingle(digest); + if ( + body == null || + digest == null || + !digest.toLowerCase().startsWith("sha-256=") + ) + return false; + + return ( + createHash("sha256").update(body).digest("base64") === digest.substring(8) + ); +} diff --git a/packages/backend/src/remote/activitypub/renderer/like.ts b/packages/backend/src/remote/activitypub/renderer/like.ts index 7b3acd4d..8fcdbae8 100644 --- a/packages/backend/src/remote/activitypub/renderer/like.ts +++ b/packages/backend/src/remote/activitypub/renderer/like.ts @@ -1,4 +1,5 @@ import config from "@/config/index.js"; +import { fetchMeta } from "@/misc/fetch-meta.js"; import type { NoteReaction } from "@/models/entities/note-reaction.js"; import type { Note } from "@/models/entities/note.js"; import { Emojis } from "@/models/index.js"; diff --git a/packages/backend/src/server/activitypub.ts b/packages/backend/src/server/activitypub.ts index a0b05d02..e8f32c77 100644 --- a/packages/backend/src/server/activitypub.ts +++ b/packages/backend/src/server/activitypub.ts @@ -1,7 +1,8 @@ import Router from "@koa/router"; import httpSignature from "@peertube/http-signature"; -import json from "koa-json-body"; +import bodyParser from "koa-bodyparser"; +import config from "@/config/index.js"; import { isSelfHost } from "@/misc/convert-host.js"; import { fetchMeta } from "@/misc/fetch-meta.js"; import { getUserKeypair } from "@/misc/keypair-store.js"; @@ -17,6 +18,7 @@ import { inbox as processInbox } from "@/queue/index.js"; import { checkFetch, getSignatureUser, + verifyDigest, } from "@/remote/activitypub/check-fetch.js"; import renderEmoji from "@/remote/activitypub/renderer/emoji.js"; import renderFollow from "@/remote/activitypub/renderer/follow.js"; @@ -26,6 +28,7 @@ import { renderLike } from "@/remote/activitypub/renderer/like.js"; import renderNote from "@/remote/activitypub/renderer/note.js"; import { renderPerson } from "@/remote/activitypub/renderer/person.js"; import { getInstanceActor } from "@/services/instance-actor.js"; +import Koa from "koa"; import { In, IsNull, Not } from "typeorm"; import Featured from "./activitypub/featured.js"; import Followers from "./activitypub/followers.js"; @@ -39,15 +42,27 @@ const router = new Router(); //#region Routing function inbox(ctx: Router.RouterContext) { + if (ctx.req.headers.host !== config.host) { + ctx.status = 400; + return; + } + let signature; try { - signature = httpSignature.parseRequest(ctx.req, { headers: [] }); + signature = httpSignature.parseRequest(ctx.req, { + headers: ["(request-target)", "digest", "host", "date"], + }); } catch (e) { ctx.status = 401; return; } + if (!verifyDigest(ctx.request.rawBody, ctx.headers.digest)) { + ctx.status = 401; + return; + } + processInbox(ctx.request.body, signature); ctx.status = 202; @@ -72,9 +87,23 @@ export function setResponseType(ctx: Router.RouterContext) { } } +async function parseJsonBodyOrFail(ctx: Router.RouterContext, next: Koa.Next) { + const koaBodyParser = bodyParser({ + enableTypes: ["json"], + detectJSON: () => true, + }); + + try { + await koaBodyParser(ctx, next); + } catch { + ctx.status = 400; + return; + } +} + // inbox -router.post("/inbox", json(), inbox); -router.post("/users/:user/inbox", json(), inbox); +router.post("/inbox", parseJsonBodyOrFail, inbox); +router.post("/users/:user/inbox", parseJsonBodyOrFail, inbox); // note router.get("/notes/:note", async (ctx, next) => { diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/import-zip.ts b/packages/backend/src/server/api/endpoints/admin/emoji/import-zip.ts index 0bd97190..2e708f0e 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/import-zip.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/import-zip.ts @@ -1,6 +1,6 @@ import { createImportCustomEmojisJob } from "@/queue/index.js"; import define from "@/server/api/define.js"; -import { ApiError } from "../../../error.js"; +import { ApiError } from "@/server/api/error.js"; export const meta = { tags: ["admin", "emoji"], diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/list-remote.ts b/packages/backend/src/server/api/endpoints/admin/emoji/list-remote.ts index 07760e9e..4e1a85d0 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/list-remote.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/list-remote.ts @@ -3,7 +3,7 @@ import { sqlLikeEscape } from "@/misc/sql-like-escape.js"; import { Emojis } from "@/models/index.js"; import { makePaginationQuery } from "@/server/api/common/make-pagination-query.js"; import define from "@/server/api/define.js"; -import { ApiError } from "../../../error.js"; +import { ApiError } from "@/server/api/error.js"; export const meta = { tags: ["admin", "emoji"], diff --git a/packages/backend/src/server/api/endpoints/charts/ap-request.ts b/packages/backend/src/server/api/endpoints/charts/ap-request.ts index c37e789a..7e02d371 100644 --- a/packages/backend/src/server/api/endpoints/charts/ap-request.ts +++ b/packages/backend/src/server/api/endpoints/charts/ap-request.ts @@ -1,4 +1,4 @@ -import define from "../../define.js"; +import define from "@/server/api/define.js"; export const meta = { tags: ["charts"], diff --git a/packages/backend/src/server/api/endpoints/charts/drive.ts b/packages/backend/src/server/api/endpoints/charts/drive.ts index 979ba2e4..25d66fef 100644 --- a/packages/backend/src/server/api/endpoints/charts/drive.ts +++ b/packages/backend/src/server/api/endpoints/charts/drive.ts @@ -1,4 +1,4 @@ -import define from "../../define.js"; +import define from "@/server/api/define.js"; export const meta = { tags: ["charts", "drive"], diff --git a/packages/backend/src/server/api/endpoints/charts/federation.ts b/packages/backend/src/server/api/endpoints/charts/federation.ts index 96593b7b..29b027e4 100644 --- a/packages/backend/src/server/api/endpoints/charts/federation.ts +++ b/packages/backend/src/server/api/endpoints/charts/federation.ts @@ -1,4 +1,4 @@ -import define from "../../define.js"; +import define from "@/server/api/define.js"; export const meta = { tags: ["charts"], diff --git a/packages/backend/src/server/api/endpoints/charts/hashtag.ts b/packages/backend/src/server/api/endpoints/charts/hashtag.ts index f033bc49..d2d66e72 100644 --- a/packages/backend/src/server/api/endpoints/charts/hashtag.ts +++ b/packages/backend/src/server/api/endpoints/charts/hashtag.ts @@ -1,4 +1,4 @@ -import define from "../../define.js"; +import define from "@/server/api/define.js"; export const meta = { tags: ["charts", "hashtags"], diff --git a/packages/backend/src/server/api/endpoints/charts/instance.ts b/packages/backend/src/server/api/endpoints/charts/instance.ts index 149bf34c..3c302a3d 100644 --- a/packages/backend/src/server/api/endpoints/charts/instance.ts +++ b/packages/backend/src/server/api/endpoints/charts/instance.ts @@ -1,4 +1,4 @@ -import define from "../../define.js"; +import define from "@/server/api/define.js"; export const meta = { tags: ["charts"], diff --git a/packages/backend/src/server/api/endpoints/charts/notes.ts b/packages/backend/src/server/api/endpoints/charts/notes.ts index 9aa89873..e63181c0 100644 --- a/packages/backend/src/server/api/endpoints/charts/notes.ts +++ b/packages/backend/src/server/api/endpoints/charts/notes.ts @@ -1,4 +1,4 @@ -import define from "../../define.js"; +import define from "@/server/api/define.js"; export const meta = { tags: ["charts", "notes"], diff --git a/packages/backend/src/server/api/endpoints/charts/user/drive.ts b/packages/backend/src/server/api/endpoints/charts/user/drive.ts index d5fd2ea6..a8237697 100644 --- a/packages/backend/src/server/api/endpoints/charts/user/drive.ts +++ b/packages/backend/src/server/api/endpoints/charts/user/drive.ts @@ -1,4 +1,4 @@ -import define from "../../../define.js"; +import define from "@/server/api/define.js"; export const meta = { tags: ["charts", "drive", "users"], diff --git a/packages/backend/src/server/api/endpoints/charts/user/notes.ts b/packages/backend/src/server/api/endpoints/charts/user/notes.ts index 38b0745a..0a26c635 100644 --- a/packages/backend/src/server/api/endpoints/charts/user/notes.ts +++ b/packages/backend/src/server/api/endpoints/charts/user/notes.ts @@ -1,4 +1,4 @@ -import define from "../../../define.js"; +import define from "@/server/api/define.js"; export const meta = { tags: ["charts", "users", "notes"], diff --git a/packages/backend/src/server/api/endpoints/charts/user/reactions.ts b/packages/backend/src/server/api/endpoints/charts/user/reactions.ts index 45249157..0b716737 100644 --- a/packages/backend/src/server/api/endpoints/charts/user/reactions.ts +++ b/packages/backend/src/server/api/endpoints/charts/user/reactions.ts @@ -1,4 +1,4 @@ -import define from "../../../define.js"; +import define from "@/server/api/define.js"; export const meta = { tags: ["charts", "users", "reactions"], diff --git a/packages/backend/src/server/api/endpoints/charts/users.ts b/packages/backend/src/server/api/endpoints/charts/users.ts index 16b4ca49..5e4a0a74 100644 --- a/packages/backend/src/server/api/endpoints/charts/users.ts +++ b/packages/backend/src/server/api/endpoints/charts/users.ts @@ -1,4 +1,4 @@ -import define from "../../define.js"; +import define from "@/server/api/define.js"; export const meta = { tags: ["charts", "users"], diff --git a/packages/backend/src/server/api/endpoints/notes/search.ts b/packages/backend/src/server/api/endpoints/notes/search.ts index c4dc2067..2e499416 100644 --- a/packages/backend/src/server/api/endpoints/notes/search.ts +++ b/packages/backend/src/server/api/endpoints/notes/search.ts @@ -1,15 +1,10 @@ import { sqlLikeEscape } from "@/misc/sql-like-escape.js"; import { Note } from "@/models/entities/note.js"; -// import { FindManyOptions, In } from "typeorm"; import { Notes } from "@/models/index.js"; import { generateBlockedUserQuery } from "@/server/api/common/generate-block-query.js"; import { generateMutedUserQuery } from "@/server/api/common/generate-muted-user-query.js"; import { generateVisibilityQuery } from "@/server/api/common/generate-visibility-query.js"; import { makePaginationQuery } from "@/server/api/common/make-pagination-query.js"; -// import config from "@/config/index.js"; -// import es from "@/db/elasticsearch.js"; -// import sonic from "@/db/sonic.js"; -// import meilisearch, { MeilisearchNote } from "@/db/meilisearch.js"; import define from "@/server/api/define.js"; export const meta = { @@ -255,7 +250,7 @@ export default define(meta, paramDef, async (ps, me) => { while (found.length < ps.limit && start < noteIDs.length) { const chunk = noteIDs.slice(start, start + chunkSize); - let query: FindManyOptions = { + const query: FindManyOptions = { where: { id: In(chunk), }, diff --git a/packages/backend/src/server/file/send-drive-file.ts b/packages/backend/src/server/file/send-drive-file.ts index 435f159c..7cd2a4eb 100644 --- a/packages/backend/src/server/file/send-drive-file.ts +++ b/packages/backend/src/server/file/send-drive-file.ts @@ -25,8 +25,7 @@ const assets = `${_dirname}/../../server/file/assets/`; const MAX_BYTE_RANGES = 10; const commonReadableHandlerGenerator = - (ctx: Koa.Context) => - (e: Error): void => { + (ctx: Koa.Context) => (e: Error): void => { serverLogger.error(e); ctx.status = 500; ctx.set("Cache-Control", "max-age=300"); diff --git a/packages/backend/src/server/web/boot.js b/packages/backend/src/server/web/boot.js index 4bd9326a..04538332 100644 --- a/packages/backend/src/server/web/boot.js +++ b/packages/backend/src/server/web/boot.js @@ -25,7 +25,12 @@ //#region Detect language & fetch translations const v = localStorage.getItem("v") || VERSION; - const supportedLangs = LANGS; + let supportedLangs = []; + try { + supportedLangs = SUPPORTED_LANGS; + } catch { + console.warn("LANGS not found"); + } let lang = localStorage.getItem("lang"); if (lang == null || !supportedLangs.includes(lang)) { if (supportedLangs.includes(navigator.language)) { diff --git a/packages/backend/src/services/note/create.ts b/packages/backend/src/services/note/create.ts index 84e4c14e..0fa5a2da 100644 --- a/packages/backend/src/services/note/create.ts +++ b/packages/backend/src/services/note/create.ts @@ -31,7 +31,6 @@ import type { ILocalUser, IRemoteUser, User } from "@/models/entities/user.js"; import { ChannelFollowings, Channels, - Followings, Instances, MutedNotes, Mutings, diff --git a/packages/client/package.json b/packages/client/package.json index 3e90036f..befa7836 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -18,7 +18,7 @@ "@rollup/plugin-json": "6.0.1", "@rollup/pluginutils": "5.0.5", "@syuilo/aiscript": "0.16.0", - "@types/autosize": "4.0.3", + "@types/autosize": "^4.0.3", "@types/glob": "8.1.0", "@types/gulp": "4.0.17", "@types/gulp-rename": "2.0.5", @@ -49,7 +49,7 @@ "compare-versions": "6.1.0", "cropperjs": "2.0.0-beta.4", "date-fns": "2.30.0", - "emojilib": "3.0.11", + "emojilib": "^3.0.11", "eslint-config-prettier": "9.0.0", "eslint-plugin-file-progress": "1.3.0", "eventemitter3": "5.0.1", @@ -80,15 +80,15 @@ "three": "0.158.0", "throttle-debounce": "5.0.0", "tinycolor2": "1.6.0", - "tinyld": "1.3.4", + "tinyld": "^1.3.4", "typescript": "5.2.2", "unicode-emoji-json": "0.4.0", "uuid": "9.0.1", "vite": "5.0.0", "vite-plugin-compression": "0.5.1", "vue": "3.3.8", - "vue-draggable-plus": "0.2.7", - "vue-plyr": "7.0.0", + "vue-draggable-plus": "^0.2.7", + "vue-plyr": "^7.0.0", "vue-prism-editor": "2.0.0-alpha.2" } } diff --git a/packages/client/src/components/MkChatPreview.vue b/packages/client/src/components/MkChatPreview.vue index b309c8b0..27db671b 100644 --- a/packages/client/src/components/MkChatPreview.vue +++ b/packages/client/src/components/MkChatPreview.vue @@ -22,8 +22,8 @@ message.groupId ? message.user : isMe(message) - ? message.recipient - : message.user + ? message.recipient + : message.user " :show-indicator="true" disable-link diff --git a/packages/client/src/components/MkDialog.vue b/packages/client/src/components/MkDialog.vue index 2659f139..a940f7d0 100644 --- a/packages/client/src/components/MkDialog.vue +++ b/packages/client/src/components/MkDialog.vue @@ -327,8 +327,8 @@ async function ok() { const result = props.input ? inputValue.value : props.select - ? selectedValue.value - : true; + ? selectedValue.value + : true; done(false, result); } diff --git a/packages/client/src/components/MkDriveSelectDialog.vue b/packages/client/src/components/MkDriveSelectDialog.vue index 1047acce..af40dc84 100644 --- a/packages/client/src/components/MkDriveSelectDialog.vue +++ b/packages/client/src/components/MkDriveSelectDialog.vue @@ -17,8 +17,8 @@ ? i18n.ts.selectFiles : i18n.ts.selectFolders : type === "file" - ? i18n.ts.selectFile - : i18n.ts.selectFolder + ? i18n.ts.selectFile + : i18n.ts.selectFolder }} >(); diff --git a/packages/client/src/components/MkMedia.vue b/packages/client/src/components/MkMedia.vue index dad2f9e7..24db8d4e 100644 --- a/packages/client/src/components/MkMedia.vue +++ b/packages/client/src/components/MkMedia.vue @@ -113,9 +113,9 @@ const url = props.raw || defaultStore.state.loadRawImages ? props.media.url : defaultStore.state.disableShowingAnimatedImages && - props.media.type.startsWith("image") - ? getStaticImageUrl(props.media.thumbnailUrl) - : props.media.thumbnailUrl; + props.media.type.startsWith("image") + ? getStaticImageUrl(props.media.thumbnailUrl) + : props.media.thumbnailUrl; const mediaType = computed(() => { return props.media.type === "video/quicktime" diff --git a/packages/client/src/components/MkModal.vue b/packages/client/src/components/MkModal.vue index f6b52a62..893cced0 100644 --- a/packages/client/src/components/MkModal.vue +++ b/packages/client/src/components/MkModal.vue @@ -169,22 +169,22 @@ const transitionName = computed(() => ? useSendAnime.value ? "send" : type.value === "drawer" - ? "modal-drawer" - : type.value === "popup" - ? "modal-popup" - : "modal" + ? "modal-drawer" + : type.value === "popup" + ? "modal-popup" + : "modal" : "", ); const transitionDuration = computed(() => transitionName.value === "send" ? 400 : transitionName.value === "modal-popup" - ? 100 - : transitionName.value === "modal" - ? 200 - : transitionName.value === "modal-drawer" - ? 200 - : 0, + ? 100 + : transitionName.value === "modal" + ? 200 + : transitionName.value === "modal-drawer" + ? 200 + : 0, ); let contentClicking = false; diff --git a/packages/client/src/components/MkModalWindow.vue b/packages/client/src/components/MkModalWindow.vue index cab0f07a..1a2b3db8 100644 --- a/packages/client/src/components/MkModalWindow.vue +++ b/packages/client/src/components/MkModalWindow.vue @@ -17,8 +17,8 @@ ? `${props.height}px` : null : height - ? `min(${props.height}px, 100%)` - : '100%', + ? `min(${props.height}px, 100%)` + : '100%', }" tabindex="-1" > diff --git a/packages/client/src/components/MkPagination.vue b/packages/client/src/components/MkPagination.vue index 17d2ef3e..f06e8a11 100644 --- a/packages/client/src/components/MkPagination.vue +++ b/packages/client/src/components/MkPagination.vue @@ -259,12 +259,12 @@ const fetchMore = async (): Promise => { offset: offset.value, } : props.pagination.reversed - ? { - sinceId: items.value[0].id, - } - : { - untilId: items.value[items.value.length - 1].id, - }), + ? { + sinceId: items.value[0].id, + } + : { + untilId: items.value[items.value.length - 1].id, + }), }) .then( (res) => { @@ -320,12 +320,12 @@ const fetchMoreAhead = async (): Promise => { offset: offset.value, } : props.pagination.reversed - ? { - untilId: items.value[0].id, - } - : { - sinceId: items.value[items.value.length - 1].id, - }), + ? { + untilId: items.value[0].id, + } + : { + sinceId: items.value[items.value.length - 1].id, + }), }) .then( (res) => { diff --git a/packages/client/src/components/MkPoll.vue b/packages/client/src/components/MkPoll.vue index 48211f17..21540ba4 100644 --- a/packages/client/src/components/MkPoll.vue +++ b/packages/client/src/components/MkPoll.vue @@ -81,10 +81,10 @@ const timer = computed(() => remaining.value >= 86400 ? "_poll.remainingDays" : remaining.value >= 3600 - ? "_poll.remainingHours" - : remaining.value >= 60 - ? "_poll.remainingMinutes" - : "_poll.remainingSeconds", + ? "_poll.remainingHours" + : remaining.value >= 60 + ? "_poll.remainingMinutes" + : "_poll.remainingSeconds", { s: Math.floor(remaining.value % 60), m: Math.floor(remaining.value / 60) % 60, diff --git a/packages/client/src/components/MkPollEditor.vue b/packages/client/src/components/MkPollEditor.vue index 622ac7fd..d6dcd75b 100644 --- a/packages/client/src/components/MkPollEditor.vue +++ b/packages/client/src/components/MkPollEditor.vue @@ -171,8 +171,8 @@ function get() { ...(expiration.value === "at" ? { expiresAt: calcAt() } : expiration.value === "after" - ? { expiredAfter: calcAfter() } - : {}), + ? { expiredAfter: calcAfter() } + : {}), }; } diff --git a/packages/client/src/components/MkPostForm.vue b/packages/client/src/components/MkPostForm.vue index d5ec6a1c..9d953390 100644 --- a/packages/client/src/components/MkPostForm.vue +++ b/packages/client/src/components/MkPostForm.vue @@ -76,8 +76,8 @@ reply ? 'ph-arrow-u-up-left' : renote - ? 'ph-quotes' - : 'ph-paper-plane-tilt', + ? 'ph-quotes' + : 'ph-paper-plane-tilt', ) " > @@ -360,7 +360,6 @@ const visibleUsers = ref([]); if (props.initialVisibleUsers) { props.initialVisibleUsers.forEach(pushVisibleUser); } -const autocomplete = ref(null); const draghover = ref(false); const quoteId = ref(null); const hasNotSpecifiedMentions = ref(false); @@ -417,10 +416,10 @@ const submitText = computed((): string => { return props.editId ? i18n.ts.edit : props.renote - ? i18n.ts.quote - : props.reply - ? i18n.ts.reply - : i18n.ts.note; + ? i18n.ts.quote + : props.reply + ? i18n.ts.reply + : i18n.ts.note; }); const textLength = computed((): number => { @@ -489,14 +488,14 @@ if (props.reply && props.reply.text != null) { const mention = x.host ? `@${x.username}@${toASCII(x.host)}` : otherHost == null || otherHost === host - ? `@${x.username}` - : `@${x.username}@${toASCII(otherHost)}`; + ? `@${x.username}` + : `@${x.username}@${toASCII(otherHost)}`; - // 自分は除外 + // exclude me if ($i.username === x.username && (x.host == null || x.host === host)) continue; - // 重複は除外 + // remove duplicates if (text.value.includes(`${mention} `)) continue; text.value += `${mention} `; @@ -505,10 +504,10 @@ if (props.reply && props.reply.text != null) { if (props.channel) { visibility.value = "public"; - localOnly.value = true; // TODO: チャンネルが連合するようになった折には消す + localOnly.value = true; // TODO: Delete this once channels get federated } -// 公開以外へのリプライ時は元の公開範囲を引き継ぐ +// Inherit the original visibility if ( props.reply && ["home", "followers", "specified"].includes(props.reply.visibility) @@ -629,10 +628,6 @@ function togglePoll() { } } -function addTag(tag: string) { - insertTextAtCursor(textareaEl.value, ` #${tag} `); -} - function focus() { if (textareaEl.value) { textareaEl.value.focus(); @@ -736,6 +731,8 @@ function clear() { quoteId.value = null; } +// FIXME: ev.which is deprecated +// https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/which function onKeydown(ev: KeyboardEvent) { if ( (ev.which === 10 || ev.which === 13) && @@ -772,7 +769,7 @@ async function onPaste(ev: ClipboardEvent) { } } - const paste = ev.clipboardData.getData("text"); + const paste = ev.clipboardData?.getData("text") ?? ""; if (!props.renote && !quoteId.value && paste.startsWith(url + "/notes/")) { ev.preventDefault(); @@ -787,7 +784,7 @@ async function onPaste(ev: ClipboardEvent) { } quoteId.value = paste - .substr(url.length) + .substring(url.length) .match(/^\/notes\/(.+?)\/?$/)[1]; }); } @@ -886,8 +883,8 @@ async function post() { renoteId: props.renote ? props.renote.id : quoteId.value - ? quoteId.value - : undefined, + ? quoteId.value + : undefined, channelId: props.channel ? props.channel.id : undefined, poll: poll.value, cw: useCw.value ? cw.value || "" : undefined, diff --git a/packages/client/src/components/MkSignup.vue b/packages/client/src/components/MkSignup.vue index 9340d9af..ff41c5a5 100644 --- a/packages/client/src/components/MkSignup.vue +++ b/packages/client/src/components/MkSignup.vue @@ -371,10 +371,10 @@ function onChangeUsername(): void { const err = !username.value.match(/^[a-zA-Z0-9_]+$/) ? "invalid-format" : username.value.length < 1 - ? "min-range" - : username.value.length > 20 - ? "max-range" - : null; + ? "min-range" + : username.value.length > 20 + ? "max-range" + : null; if (err) { usernameState.value = err; @@ -410,16 +410,16 @@ function onChangeEmail(): void { emailState.value = result.available ? "ok" : result.reason === "used" - ? "unavailable:used" - : result.reason === "format" - ? "unavailable:format" - : result.reason === "disposable" - ? "unavailable:disposable" - : result.reason === "mx" - ? "unavailable:mx" - : result.reason === "smtp" - ? "unavailable:smtp" - : "unavailable"; + ? "unavailable:used" + : result.reason === "format" + ? "unavailable:format" + : result.reason === "disposable" + ? "unavailable:disposable" + : result.reason === "mx" + ? "unavailable:mx" + : result.reason === "smtp" + ? "unavailable:smtp" + : "unavailable"; }) .catch(() => { emailState.value = "error"; diff --git a/packages/client/src/components/global/MkTime.vue b/packages/client/src/components/global/MkTime.vue index e2356845..cc7b2d46 100644 --- a/packages/client/src/components/global/MkTime.vue +++ b/packages/client/src/components/global/MkTime.vue @@ -29,11 +29,11 @@ const _time = props.time == null ? NaN : typeof props.time === "number" - ? props.time - : (props.time instanceof Date - ? props.time - : new Date(props.time) - ).getTime(); + ? props.time + : (props.time instanceof Date + ? props.time + : new Date(props.time) + ).getTime(); const invalid = Number.isNaN(_time); const absolute = !invalid ? dateTimeFormat.format(_time) : i18n.ts._ago.invalid; @@ -46,30 +46,30 @@ const relative = computed(() => { return ago >= 31536000 ? i18n.t("_ago.yearsAgo", { n: Math.floor(ago / 31536000).toString() }) : ago >= 2592000 - ? i18n.t("_ago.monthsAgo", { - n: Math.floor(ago / 2592000).toString(), - }) - : ago >= 604800 - ? i18n.t("_ago.weeksAgo", { - n: Math.floor(ago / 604800).toString(), - }) - : ago >= 86400 - ? i18n.t("_ago.daysAgo", { - n: Math.floor(ago / 86400).toString(), - }) - : ago >= 3600 - ? i18n.t("_ago.hoursAgo", { - n: Math.floor(ago / 3600).toString(), - }) - : ago >= 60 - ? i18n.t("_ago.minutesAgo", { n: (~~(ago / 60)).toString() }) - : ago >= 10 - ? i18n.t("_ago.secondsAgo", { - n: (~~(ago % 60)).toString(), - }) - : ago >= -1 - ? i18n.ts._ago.justNow - : i18n.ts._ago.future; + ? i18n.t("_ago.monthsAgo", { + n: Math.floor(ago / 2592000).toString(), + }) + : ago >= 604800 + ? i18n.t("_ago.weeksAgo", { + n: Math.floor(ago / 604800).toString(), + }) + : ago >= 86400 + ? i18n.t("_ago.daysAgo", { + n: Math.floor(ago / 86400).toString(), + }) + : ago >= 3600 + ? i18n.t("_ago.hoursAgo", { + n: Math.floor(ago / 3600).toString(), + }) + : ago >= 60 + ? i18n.t("_ago.minutesAgo", { n: (~~(ago / 60)).toString() }) + : ago >= 10 + ? i18n.t("_ago.secondsAgo", { + n: (~~(ago % 60)).toString(), + }) + : ago >= -1 + ? i18n.ts._ago.justNow + : i18n.ts._ago.future; }); let tickId: number; diff --git a/packages/client/src/pages/about.federation.vue b/packages/client/src/pages/about.federation.vue index d2d00dce..163166a3 100644 --- a/packages/client/src/pages/about.federation.vue +++ b/packages/client/src/pages/about.federation.vue @@ -127,18 +127,18 @@ const pagination = { ...(state.value === "federating" ? { federating: true } : state.value === "subscribing" - ? { subscribing: true } - : state.value === "publishing" - ? { publishing: true } - : state.value === "suspended" - ? { suspended: true } - : state.value === "blocked" - ? { blocked: true } - : state.value === "silenced" - ? { silenced: true } - : state.value === "notResponding" - ? { notResponding: true } - : {}), + ? { subscribing: true } + : state.value === "publishing" + ? { publishing: true } + : state.value === "suspended" + ? { suspended: true } + : state.value === "blocked" + ? { blocked: true } + : state.value === "silenced" + ? { silenced: true } + : state.value === "notResponding" + ? { notResponding: true } + : {}), })), }; diff --git a/packages/client/src/pages/admin/bot-protection.vue b/packages/client/src/pages/admin/bot-protection.vue index 0114aa39..54d1b2ae 100644 --- a/packages/client/src/pages/admin/bot-protection.vue +++ b/packages/client/src/pages/admin/bot-protection.vue @@ -105,8 +105,8 @@ async function init() { provider.value = meta.enableHcaptcha ? "hcaptcha" : meta.enableRecaptcha - ? "recaptcha" - : null; + ? "recaptcha" + : null; } function save() { diff --git a/packages/client/src/pages/admin/overview.queue.chart.vue b/packages/client/src/pages/admin/overview.queue.chart.vue index 6482d99d..2e9195e2 100644 --- a/packages/client/src/pages/admin/overview.queue.chart.vue +++ b/packages/client/src/pages/admin/overview.queue.chart.vue @@ -51,23 +51,23 @@ const label = props.type === "process" ? "Process" : props.type === "active" - ? "Active" - : props.type === "delayed" - ? "Delayed" - : props.type === "waiting" - ? "Waiting" - : ("?" as never); + ? "Active" + : props.type === "delayed" + ? "Delayed" + : props.type === "waiting" + ? "Waiting" + : ("?" as never); const color = props.type === "process" ? "#c4a7e7" : props.type === "active" - ? "#31748f" - : props.type === "delayed" - ? "#eb6f92" - : props.type === "waiting" - ? "#f6c177" - : ("?" as never); + ? "#31748f" + : props.type === "delayed" + ? "#eb6f92" + : props.type === "waiting" + ? "#f6c177" + : ("?" as never); onMounted(() => { const vLineColor = defaultStore.state.darkMode diff --git a/packages/client/src/pages/admin/queue.chart.chart.vue b/packages/client/src/pages/admin/queue.chart.chart.vue index fd3edea9..bd7b781c 100644 --- a/packages/client/src/pages/admin/queue.chart.chart.vue +++ b/packages/client/src/pages/admin/queue.chart.chart.vue @@ -96,23 +96,23 @@ const label = props.type === "process" ? "Process" : props.type === "active" - ? "Active" - : props.type === "delayed" - ? "Delayed" - : props.type === "waiting" - ? "Waiting" - : ("?" as never); + ? "Active" + : props.type === "delayed" + ? "Delayed" + : props.type === "waiting" + ? "Waiting" + : ("?" as never); const color = props.type === "process" ? "#9ccfd8" : props.type === "active" - ? "#31748f" - : props.type === "delayed" - ? "#eb6f92" - : props.type === "waiting" - ? "#f6c177" - : ("?" as never); + ? "#31748f" + : props.type === "delayed" + ? "#eb6f92" + : props.type === "waiting" + ? "#f6c177" + : ("?" as never); onMounted(() => { chartInstance = new Chart(chartEl.value, { diff --git a/packages/client/src/pages/admin/queue.chart.vue b/packages/client/src/pages/admin/queue.chart.vue index cb10e27e..fad0deb6 100644 --- a/packages/client/src/pages/admin/queue.chart.vue +++ b/packages/client/src/pages/admin/queue.chart.vue @@ -117,8 +117,8 @@ onMounted(() => { props.domain === "inbox" ? "admin/queue/inbox-delayed" : props.domain === "deliver" - ? "admin/queue/deliver-delayed" - : null, + ? "admin/queue/deliver-delayed" + : null, {}, ).then((result) => { jobs.value = result; diff --git a/packages/client/src/pages/admin/security.vue b/packages/client/src/pages/admin/security.vue index 7522b53d..dd8eff4f 100644 --- a/packages/client/src/pages/admin/security.vue +++ b/packages/client/src/pages/admin/security.vue @@ -290,14 +290,14 @@ async function init() { meta.sensitiveMediaDetectionSensitivity === "veryLow" ? 0 : meta.sensitiveMediaDetectionSensitivity === "low" - ? 1 - : meta.sensitiveMediaDetectionSensitivity === "medium" - ? 2 - : meta.sensitiveMediaDetectionSensitivity === "high" - ? 3 - : meta.sensitiveMediaDetectionSensitivity === "veryHigh" - ? 4 - : 0; + ? 1 + : meta.sensitiveMediaDetectionSensitivity === "medium" + ? 2 + : meta.sensitiveMediaDetectionSensitivity === "high" + ? 3 + : meta.sensitiveMediaDetectionSensitivity === "veryHigh" + ? 4 + : 0; setSensitiveFlagAutomatically.value = meta.setSensitiveFlagAutomatically; enableSensitiveMediaDetectionForVideos.value = meta.enableSensitiveMediaDetectionForVideos; @@ -317,14 +317,14 @@ function save() { sensitiveMediaDetectionSensitivity.value === 0 ? "veryLow" : sensitiveMediaDetectionSensitivity.value === 1 - ? "low" - : sensitiveMediaDetectionSensitivity.value === 2 - ? "medium" - : sensitiveMediaDetectionSensitivity.value === 3 - ? "high" - : sensitiveMediaDetectionSensitivity.value === 4 - ? "veryHigh" - : 0, + ? "low" + : sensitiveMediaDetectionSensitivity.value === 2 + ? "medium" + : sensitiveMediaDetectionSensitivity.value === 3 + ? "high" + : sensitiveMediaDetectionSensitivity.value === 4 + ? "veryHigh" + : 0, setSensitiveFlagAutomatically: setSensitiveFlagAutomatically.value, enableSensitiveMediaDetectionForVideos: enableSensitiveMediaDetectionForVideos.value, diff --git a/packages/client/src/pages/api-console.vue b/packages/client/src/pages/api-console.vue index 083db86c..63957c11 100644 --- a/packages/client/src/pages/api-console.vue +++ b/packages/client/src/pages/api-console.vue @@ -98,14 +98,14 @@ function onEndpointChange() { p.type === "String" ? "" : p.type === "Number" - ? 0 - : p.type === "Boolean" - ? false - : p.type === "Array" - ? [] - : p.type === "Object" - ? {} - : null; + ? 0 + : p.type === "Boolean" + ? false + : p.type === "Array" + ? [] + : p.type === "Object" + ? {} + : null; } body.value = JSON5.stringify(endpointBody, null, 2); }); diff --git a/packages/client/src/pages/gallery/post.vue b/packages/client/src/pages/gallery/post.vue index 78f497a8..f34fc505 100644 --- a/packages/client/src/pages/gallery/post.vue +++ b/packages/client/src/pages/gallery/post.vue @@ -229,14 +229,7 @@ function edit() { watch(() => props.postId, fetchPost, { immediate: true }); -const headerActions = computed(() => [ - { - icon: `${icon("ph-pencil")}`, - text: i18n.ts.edit, - handler: edit, - }, -]); - +const headerActions = computed(() => []); const headerTabs = computed(() => []); definePageMetadata( diff --git a/packages/client/src/pages/settings/theme.vue b/packages/client/src/pages/settings/theme.vue index 32a84fe5..c4c34d7c 100644 --- a/packages/client/src/pages/settings/theme.vue +++ b/packages/client/src/pages/settings/theme.vue @@ -108,11 +108,11 @@ >{{ i18n.ts._theme.manage }} - {{ i18n.ts._theme.explore }} + > --> A diff --git a/packages/client/src/pages/timeline.vue b/packages/client/src/pages/timeline.vue index da74c803..95c04849 100644 --- a/packages/client/src/pages/timeline.vue +++ b/packages/client/src/pages/timeline.vue @@ -270,12 +270,12 @@ definePageMetadata( src.value === "local" ? "ph-users ph-lg" : src.value === "social" - ? "ph-handshake ph-lg" - : src.value === "recommended" - ? "ph-thumbs-up ph-lg" - : src.value === "global" - ? "ph-planet ph-lg" - : "ph-house ph-lg", + ? "ph-handshake ph-lg" + : src.value === "recommended" + ? "ph-thumbs-up ph-lg" + : src.value === "global" + ? "ph-planet ph-lg" + : "ph-house ph-lg", })), ); diff --git a/packages/client/src/scripts/helpMenu.ts b/packages/client/src/scripts/helpMenu.ts index 7333e23c..b1be25da 100644 --- a/packages/client/src/scripts/helpMenu.ts +++ b/packages/client/src/scripts/helpMenu.ts @@ -1,11 +1,11 @@ import XCheatSheet from "@/components/MkCheatSheetDialog.vue"; import XTutorial from "@/components/MkTutorialDialog.vue"; +import { defaultStore } from "@/store"; import { host } from "@/config"; import { i18n } from "@/i18n"; import { instance } from "@/instance"; import * as os from "@/os"; import icon from "@/scripts/icon"; -import { defaultStore } from "@/store"; import type { MenuItem } from "@/types/menu"; const instanceSpecificItems: MenuItem[] = []; diff --git a/packages/client/src/widgets/timeline.vue b/packages/client/src/widgets/timeline.vue index a51ff32c..d3ab198b 100644 --- a/packages/client/src/widgets/timeline.vue +++ b/packages/client/src/widgets/timeline.vue @@ -35,8 +35,8 @@ widgetProps.src === "list" ? widgetProps.list.name : widgetProps.src === "antenna" - ? widgetProps.antenna.name - : i18n.t("_timelines." + widgetProps.src) + ? widgetProps.antenna.name + : i18n.t("_timelines." + widgetProps.src) }}