diff --git a/.config/LICENSE b/.config/LICENSE new file mode 100644 index 0000000000..cb57aef954 --- /dev/null +++ b/.config/LICENSE @@ -0,0 +1,13 @@ +Copyright 2023 Firefish + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/.config/ci.yml b/.config/ci.yml index 0f5be623bc..fd35a79941 100644 --- a/.config/ci.yml +++ b/.config/ci.yml @@ -1,5 +1,5 @@ #━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -# Calckey configuration +# Firefish configuration #━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ # ┌─────┐ diff --git a/.config/devenv.yml b/.config/devenv.yml new file mode 100644 index 0000000000..65b819b569 --- /dev/null +++ b/.config/devenv.yml @@ -0,0 +1,38 @@ +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_example.env b/.config/docker_example.env index fdd7e31088..25314d7edc 100644 --- a/.config/docker_example.env +++ b/.config/docker_example.env @@ -1,4 +1,4 @@ # db settings -POSTGRES_PASSWORD=example-calckey-pass -POSTGRES_USER=example-calckey-user -POSTGRES_DB=calckey +POSTGRES_PASSWORD=example-firefish-pass +POSTGRES_USER=example-firefish-user +POSTGRES_DB=firefish diff --git a/.config/example.yml b/.config/example.yml index 900ac09051..ffc8a1d346 100644 --- a/.config/example.yml +++ b/.config/example.yml @@ -1,33 +1,32 @@ #━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -# Calckey configuration +# Firefish configuration #━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +# After starting your server, please don't change the URL! Doing so will break federation. + # ┌─────┐ #───┘ 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! +url: https://example.com/ # ┌───────────────────────┐ #───┘ Port and TLS settings └─────────────────────────────────── # -# Misskey requires a reverse proxy to support HTTPS connections. +# Firefish requires a reverse proxy to support HTTPS connections. # -# +----- https://example.tld/ ------------+ +# +----- https://example.com/ ------------+ # +------+ |+-------------+ +----------------+| -# | User | ---> || Proxy (443) | ---> | Misskey (3000) || +# | User | ---> || Proxy (443) | ---> | Firefish (3000) || # +------+ |+-------------+ +----------------+| # +---------------------------------------+ # -# You need to set up a reverse proxy. (e.g. nginx) +# You need to set up a reverse proxy. (e.g. nginx, caddy) # 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. +# The port that your Firefish server should listen on. port: 3000 # ┌──────────────────────────┐ @@ -36,20 +35,22 @@ port: 3000 db: host: localhost port: 5432 - + #ssl: false # Database name - db: calckey + db: firefish # Auth - user: example-calckey-user - pass: example-calckey-pass + user: example-firefish-user + pass: example-firefish-pass # Whether disable Caching queries #disableCache: true # Extra Connection options #extra: - # ssl: true + # ssl: + # host: localhost + # rejectUnauthorized: false # ┌─────────────────────┐ #───┘ Redis configuration └───────────────────────────────────── @@ -57,51 +58,93 @@ db: redis: host: localhost port: 6379 + #tls: + # host: localhost + # rejectUnauthorized: false + #family: 0 # 0=Both, 4=IPv4, 6=IPv6 + #pass: example-pass + #prefix: example-prefix + #db: 1 + #user: default + +# ┌─────────────────────────────┐ +#───┘ Cache server configuration └───────────────────────────────────── + +# A Redis-compatible server (DragonflyDB, Keydb, Redis) for caching +# If left blank, it will use the Redis server from above + +#cacheServer: + #host: localhost + #port: 6379 #family: 0 # 0=Both, 4=IPv4, 6=IPv6 #pass: example-pass #prefix: example-prefix #db: 1 -# ┌─────────────────────────────┐ -#───┘ Elasticsearch configuration └───────────────────────────── +# Please configure either MeiliSearch *or* Sonic. +# If both MeiliSearch and Sonic configurations are present, MeiliSearch will take precedence. -#elasticsearch: -# host: localhost -# port: 9200 +# ┌───────────────────────────┐ +#───┘ MeiliSearch configuration └───────────────────────────────────── +#meilisearch: +# host: meilisearch +# port: 7700 # ssl: false -# user: -# pass: +# apiKey: + +# ┌─────────────────────┐ +#───┘ Sonic configuration └───────────────────────────────────── + +#sonic: +# host: localhost +# port: 1491 +# auth: SecretPassword +# collection: notes +# bucket: default + # ┌───────────────┐ #───┘ 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. +# No need to uncomment in most cases, but you may want to change +# these settings if you plan to run a large and/or distributed server. -# Available methods: -# aid ... Short, Millisecond accuracy -# meid ... Similar to ObjectID, Millisecond accuracy -# ulid ... Millisecond accuracy -# objectid ... This is left for backward compatibility +# cuid: +# # Min 16, Max 24 +# length: 16 +# +# # Set this to a unique string across workers (e.g., machine's hostname) +# # if your workers are running in multiple hosts. +# fingerprint: my-fingerprint -# ONCE YOU HAVE STARTED THE INSTANCE, DO NOT CHANGE THE -# ID SETTINGS AFTER THAT! - -id: 'aid' # ┌─────────────────────┐ #───┘ Other configuration └───────────────────────────────────── -# Max note length, should be < 8000. +# Maximum length of a post (default 3000, max 100000) #maxNoteLength: 3000 +# Maximum length of an image caption (default 1500, max 8192) +#maxCaptionLength: 1500 + +# Reserved usernames that only the administrator can register with +reservedUsernames: [ + 'root', + 'admin', + 'administrator', + 'me', + 'system' +] + # Whether disable HSTS #disableHsts: true # Number of worker processes #clusterLimit: 1 +# Worker only mode +#onlyQueueProcessor: 1 + # Job concurrency per worker # deliverJobConcurrency: 128 # inboxJobConcurrency: 16 @@ -126,6 +169,7 @@ id: 'aid' #proxy: http://127.0.0.1:3128 #proxyBypassHosts: [ +# 'web.kaiteki.app', # 'example.com', # '192.0.2.8' #] @@ -154,13 +198,21 @@ id: 'aid' # Upload or download file size limits (bytes) #maxFileSize: 262144000 +#━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +# Congrats, you've reached the end of the config file needed for most deployments! +# Enjoy your Firefish server! +#━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + + + +#━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ # Managed hosting settings -# !!!!!!!!!! -# >>>>>> NORMAL SELF-HOSTERS, STAY AWAY! <<<<<< -# >>>>>> YOU DON'T NEED THIS! <<<<<< -# !!!!!!!!!! +# >>> 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 @@ -198,4 +250,4 @@ id: 'aid' # !!!!!!!!!! # Seriously. Do NOT fill out the above settings if you're self-hosting. -# They're much better off being set from the control panel. +# They're much better off being set from the control panel. diff --git a/.config/helm_values_example.yml b/.config/helm_values_example.yml index b600eb8aa9..5c86acca3e 100644 --- a/.config/helm_values_example.yml +++ b/.config/helm_values_example.yml @@ -8,7 +8,7 @@ resources: cpu: 1 memory: 1Gi -calckey: +firefish: domain: example.tld smtp: from_address: noreply@example.tld diff --git a/.dockerignore b/.dockerignore index 34bbaac39b..ad6ad169ae 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,17 +1,17 @@ # Visual Studio Code -/.vscode -!/.vscode/extensions.json +.vscode # Intelij-IDEA -/.idea -packages/backend/.idea/backend.iml -packages/backend/.idea/modules.xml -packages/backend/.idea/vcs.xml +.idea # Node.js node_modules +**/node_modules report.*.json +# Rust +packages/backend/native-utils/target + # Cypress cypress/screenshots cypress/videos @@ -20,12 +20,7 @@ cypress/videos coverage # config -/.config/* -!/.config/example.yml -!/.config/docker_example.env - -#docker dev config -/dev/docker-compose.yml +/.config # misskey built @@ -46,3 +41,4 @@ packages/backend/assets/instance.css # dockerignore custom .git Dockerfile +docker-compose.yml diff --git a/.envrc b/.envrc new file mode 100644 index 0000000000..3ce7171a3c --- /dev/null +++ b/.envrc @@ -0,0 +1,4 @@ +if ! has nix_direnv_version || ! nix_direnv_version 2.3.0; then + source_url "https://raw.githubusercontent.com/nix-community/nix-direnv/2.3.0/direnvrc" "sha256-Dmd+j63L84wuzgyjITIfSxSD57Tx7v51DMxVZOsiUD8=" +fi +use flake . --impure diff --git a/.gitignore b/.gitignore index 2613bba00a..ad887b7e2a 100644 --- a/.gitignore +++ b/.gitignore @@ -22,10 +22,12 @@ coverage # config /.config/* !/.config/example.yml +!/.config/devenv.yml !/.config/docker_example.env !/.config/helm_values_example.yml +!/.config/LICENSE -#docker dev config +# docker dev config /dev/docker-compose.yml # misskey @@ -43,6 +45,14 @@ api-docs.json files ormconfig.json packages/backend/assets/instance.css +packages/backend/assets/sounds/None.mp3 +packages/backend/assets/LICENSE + +!/packages/backend/queue/processors/db +!packages/backend/src/db + +packages/megalodon/lib +packages/megalodon/.idea # blender backups *.blend1 @@ -54,3 +64,11 @@ packages/backend/assets/instance.css # old yarn .yarn yarn* + +# Nix Development shell items +.devenv +.direnv + +# Cargo cache for Docker +/.cargo-cache +/.cargo-target diff --git a/.node-version b/.node-version index 7fd023741b..8ddbc0c64a 100644 --- a/.node-version +++ b/.node-version @@ -1 +1 @@ -v16.15.0 +v18.16.0 diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000000..abb787e4c2 --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +use-lockfile-v6=true diff --git a/.okteto/okteto-pipeline.yml b/.okteto/okteto-pipeline.yml deleted file mode 100644 index e2996fbbc9..0000000000 --- a/.okteto/okteto-pipeline.yml +++ /dev/null @@ -1,6 +0,0 @@ -build: - misskey: - args: - - NODE_ENV=development -deploy: - - helm upgrade --install misskey chart --set image=${OKTETO_BUILD_MISSKEY_IMAGE} --set url="https://misskey-$(kubectl config view --minify -o jsonpath='{..namespace}').cloud.okteto.net" --set environment=development diff --git a/.vim/coc-settings.json b/.vim/coc-settings.json deleted file mode 100644 index 62b7b934b2..0000000000 --- a/.vim/coc-settings.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "eslint.packageManager": "pnpm", - "workspace.workspaceFolderCheckCwd": false -} diff --git a/.weblate b/.weblate new file mode 100644 index 0000000000..56430a1bb0 --- /dev/null +++ b/.weblate @@ -0,0 +1,3 @@ +[weblate] +url = https://hosted.weblate.org/api/ +translation = firefish/locales diff --git a/.woodpecker/commit.yml b/.woodpecker/commit.yml index 386484ce22..f57fd9d1e6 100644 --- a/.woodpecker/commit.yml +++ b/.woodpecker/commit.yml @@ -1,7 +1,8 @@ pipeline: testCommit: - image: node:latest + image: node:alpine commands: + - apk add --no-cache cargo python3 make g++ - cp .config/ci.yml .config/default.yml - corepack enable - corepack prepare pnpm@latest --activate @@ -18,4 +19,4 @@ services: image: redis branches: - include: [ main, develop, feature/* ] + include: [ main, beta, develop, feature/* ] diff --git a/.woodpecker/dockerHubRelease.yml b/.woodpecker/dockerHubRelease.yml index bcb6df4904..77b1197dc0 100644 --- a/.woodpecker/dockerHubRelease.yml +++ b/.woodpecker/dockerHubRelease.yml @@ -2,7 +2,7 @@ pipeline: publish-docker-latest: image: plugins/kaniko settings: - repo: thatonecalculator/calckey + repo: thatonecalculator/firefish tags: latest dockerfile: Dockerfile username: diff --git a/.woodpecker/dockerHubReleaseCandidate.yml b/.woodpecker/dockerHubReleaseCandidate.yml index 48bd39525f..dd0d9354d8 100644 --- a/.woodpecker/dockerHubReleaseCandidate.yml +++ b/.woodpecker/dockerHubReleaseCandidate.yml @@ -2,7 +2,7 @@ pipeline: publish-docker-latest: image: plugins/kaniko settings: - repo: thatonecalculator/calckey + repo: thatonecalculator/firefish tags: rc dockerfile: Dockerfile username: diff --git a/.woodpecker/dockerHubTag.yml b/.woodpecker/dockerHubTag.yml index 5543ae2340..4ff3f05022 100644 --- a/.woodpecker/dockerHubTag.yml +++ b/.woodpecker/dockerHubTag.yml @@ -2,7 +2,7 @@ pipeline: publish-docker-tag: image: plugins/kaniko settings: - repo: thatonecalculator/calckey + repo: thatonecalculator/firefish # Uses the tag from git for the container tag tags: ${CI_COMMIT_TAG} dockerfile: Dockerfile diff --git a/.woodpecker/testDocker.yml b/.woodpecker/testDocker.yml index e3fdf0eb93..8c5e2cf9df 100644 --- a/.woodpecker/testDocker.yml +++ b/.woodpecker/testDocker.yml @@ -2,7 +2,7 @@ pipeline: docker-build: image: plugins/kaniko settings: - repo: thatonecalculator/calckey + repo: thatonecalculator/firefish tags: test dockerfile: Dockerfile no_push: true diff --git a/CHANGELOG.md b/CHANGELOG.md index 5377955d6a..c2a379d42d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,20 +1,4348 @@ # Changelog -All changes from v13.0.0 onwards, for a full list of differences read CALCKEY.md +All changes from v13.0.0 onwards, for a list of differences read FIREFISH.md -## [13.1.3] - 2023-02-09 +## [1.0.0] - 2023-07-19 ### Bug Fixes +- Fix: :pencil2: deduplicate class + +- Fix: :adhesive_bandage: ask for reload upon changing skin tone + +- Fix: :bug: properly apply unicode for skin tone selector + +- Fix: 2fa registration code + +- Fix: new posts button mobile position + webkit-mask + +- Fix: ad widget size + +- Fix: prevent jump when new posts button appears + +- Fix: back from the future + +- Fix: new posts button z-index + use darkened accent + +- Fix: :bug: apply skin tone to default reactions, aria labels for tone picker + +- Fix: 🐛 support for SSL data when loading redis and postgres configs + +Closes #10366 + +- Fix: :adhesive_bandage: use redis user for bull and postgres cache interface + +Follow up #10366, d96877033be9790d11b0212ab8825f93202d22a2 + +- Fix: only show option based on device + add option to backups + +- Fix: :wheelchair: don't animate indicator dot if animation is off + +Closes #10372 + +- Fix: :bug: security key display name + +Closes #10313 + +- Fix: disable redis tls if undefined in config + +- Fix: :bug: don't convert time since epoch for ratelimit + +https://calckey.social/notes/9gkasnzglmi07rpa + +- Fix: :bug: fix tapping parent on mobile causing side effects + +- Fix: :bug: standard input for 2FA token + +Closes #10361, removes client dependency + +- Fix: :bug: addSkinTone strip logic + +- Fix: soft mutes not being applied to boosts + +- Fix: misaligned new post button + +I don't understand why margin-top caused that ?? + +- Fix: multiple boost publication by relay + +- Fix: ignore if post boosted by relay is local + +- Fix: await release + +- Fix: webkit blur effects + +- Fix: :loud_sound: log emoji picker errors + +- Fix/refactor: move new posts button to MkTimeline + +- Fix: new posts click + +- Fix: use redis-semaphore for global mutex and memory leak prevention + +- Fix: :bug: patron timeout + +- Fix: temp fix for null metadata + +- Fix: :bug: display emoji stat properly + +- Fix: :lipstick: form switch padding + +- Fix: :zap: immediately fail jobs with invalid signature + +- Fix: videos not appearing when don't play animated images is one + +- Fix: 🐛query search endpoint for local userselect, disregard host + +- Fix: do not deliver poll reulst if local-only + +- Fix: do not deliver poll result if local-only + +- Fix: add back channel display in timeline + +- Fix: :bug: fallback locales for skin tone labels + +Closes #10406 + + +- Fix: 🐛 fix quotes with CW-only quotes + +- Fix: use host as prefix of cacheServer if undefined + +- Fix: :children_crossing: switch account when adding existing account + +- Fix: add megalodon to docker image + +- Fix: copy megalodon before node_modules, fix #10424 + +- Fix: banner blur + +- Fix: :bug: prevent creation of empty antennas + +- Fix: :bug: server stats setting, meta fetching + +- Fix: :bug: server stats setting, meta fetching + +- Fix: :lipstick: url preview title size + +- Fix: MkMedia #10429 ? + styling + +- Fix: nav post button gradient transition ([#10401](https://github.com/orhun/git-cliff/issues/10401)) + half refactor ? + +- Fix: error if no banner + +- Fix reactions_not_public condition + +- Fix: renew entity models + +- Fix config parser + +- Fix: add db to redis uri + +- Fix: :ambulance: correct import for swiper 10 + +- Fix: :page_facing_up: don't gitignore .config license + +Follow-up #10470 + +- Fix: :bug: fix draggable + +Use vue-draggable-plus instead of vuedraggable: https://github.com/SortableJS/vue.draggable.next/issues/216 + +Closes #10467 + +- Fix popup menu + +- Fix: :bug: plus button in reaction settings + +- Fix: :bug: save reactions if added + +- Fix: :lipstick: locales, transition for MkDonation + +- Fix: :lipstick: scope transition style + +- Fix: :bug: properly show instance's link + +- Fix: :bug: give donation link in non-admin meta + +- Fix: :lipstick: properly slide in + +- Fix: :lipstick: ease-out transitions + +- Fix: match custom emoji size to Misskey's + +resolves #10438 + +- Fix: :lipstick: scoped style fix + +- Fix #10483 + +- Fix: collapsing white space + +- Fix: :lipstick: different default font if cjk + +- Fix: :lipstick: mkdonation bg + +- Fix: Follow Request labeling + +resolves #10368 + +- Fix: error in user card if no user desc + +- Fix: 🐛 allow up to 1024 chars for SMTP login + +Closes #10472 + +- Fix: note detailed tabs using wrong styling + +- Fix: move nowrap to proper place + +- Fix: :lock: prevent potential SSRF through media proxy + +- Fix: :wrench: max post length 100000 + +Pleroma doesn't accept >100K (ref: https://blob.cat/objects/82f33d96-534f-45ee-902a-f77c2723db8b) + +- Fix format +- Fix: more reliable not closing emoji picker on shift key + +- Fix: unable to scroll through reactions on mobile + +- Fix lock +- Fix: use hostname as prefix instead of host + +- Fix: 🐛 Scrolling Issue in Safari for Top and Bottom Bars + +- Fix: :globe_with_meridians: missing locale + +- Fix: add back follows you tag + +- Fix: import + +- Fix: skip attachment import if undefined + + +### Documentation + +- Docs: :memo: dragonfly flag + +- Docs: :pencil2: dragonflydb typo + +- Docs: 📝 KeyDB minimum version + +- Docs: 📝 KeyDB, megalodon + +- Docs: 📝 simplify dependencies + +- Docs: fix typo + +- Docs: :memo: join server + +- Docs: :memo: FoundKey migration docs + +Co-authored-by: Jeder +Superscedes #10471 + +- Docs: :memo: FoundKey EOL disclaimer + +- Docs: :memo: descriptions for notes/children+conversation endpoints + +- Docs: :bulb: going past DB_MAX_NOTE_TEXT_LENGTH + +- Docs: :memo: changelog + +- Docs: :memo: changelog + +- Docs: :memo: foundkey patch (thanks volpeon!) + +ref: https://is-a.wyvern.rip/notes/9hac2nd98s + +- Docs: :memo: migration note + +- Docs: 📝 foundkey migration apply patch correctly + + +### Features + +- Feat: Make scrollable widgets flexible + +- Feat: make RSS feed scrollable ([#10108](https://github.com/orhun/git-cliff/issues/10108)) + +- Feat: ALT button ([#9265](https://github.com/orhun/git-cliff/issues/9265)) + +- Feat: toggle-able swiping + +- Feat: horizontally scrolling reactions + +- Feat: :sparkles: introduce local user select dialog + +Used for selecting group members and proxy account. + +Closes #7987, API change: users/search-by-username-and-host now takes `maxDaysSinceLastActive` parameter and doesn't have any active threshold anymore by default. + +- Feat: reveal muted word on press & hold + +Refs: #10363 + +- Feat: link previews redesign + +- Feat: :sparkles: celebrate on calendar when birthday + +- Feat: :sparkles: add emoji count to admin panel + +- Add cache prefix + +- Feat: show alt button even when content hidden + +- Feat: :zap: cache server + +- Feat: :sparkles: Timestamps on announcements + +Closes #10453 + +- Add environment variable to skip copying antenna + +- Add environment variable of read size + +- Feat: :sparkles: don't close emoji picker if shift is held down, like Discord + +https://snug.moe/notes/9h1p04dqytz2qfsz + +- Feat: :sparkles: Donation pop-up with optional admin link + +Co-authored-by: Syuilo + +- Feat: :lipstick: fly in as well + +- Feat: :sparkles: seperate sponsors and patrons + +- Feat: ✨ Add media to Mastodon and Calckey post imports ([#10496](https://github.com/orhun/git-cliff/issues/10496)) + +### What does this PR do? + +Adding files fields in the export notes option, and corresponding import notes + +Current the mastodon import does not import any attachments, this pr will use the "upload from url" feature to include medias if its a valid URL. + +There are many way to convert the outbox.json file, can be simple as upload media_attachments to any web hosting and do string replace on the json file. + +I also create a tool that upload the tar.gz file with auto convert and host the media as simplify the process at https://tempfile.moegirl.live + +Detail example can be found at https://fedi.moegirl.live/notes/9h76gtqnp2gwl5dz + +https://r2temp.moegirl.live/2023/7/15/15356683-050f-423a-b331-c9a05561f52a/shana-settings-_-meng-zhai-le-yuan-xyou-yu-ou-xiang-de-luo-ke-ke-wu-yan-moe-otaku-elysian-x-gloomily-idol-s-rococo-luncheon----mozilla-firefox-private-browsing-2023-07-15-18-36-37.mp4 + +Co-authored-by: CGsama +Reviewed-on: https://codeberg.org/calckey/calckey/pulls/10496 +Co-authored-by: コルセット姫@がんばらない +Co-committed-by: コルセット姫@がんばらない + +- Feat: Move json5 to prod dependencies + +- Feat: add view on remote server to user profile dropdowns + +- Feat: ✨ verify links with rel=me ([#10506](https://github.com/orhun/git-cliff/issues/10506)) + +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 + +- Feat: show snippet of alt text when hovering alt button + +- Feat: :sparkles: copy feeds from user menu + + +### Miscellaneous Tasks + +- Chore: Translated using Weblate (German) + +Currently translated at 99.5% (1796 of 1805 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/de/ + +- Chore: Merge branch 'origin/develop' into Weblate. + +- Chore: Translated using Weblate (Catalan) + +Currently translated at 100.0% (1806 of 1806 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/ca/ + +- Chore: Merge branch 'origin/develop' into Weblate. + +- Chore: Translated using Weblate (Catalan) + +Currently translated at 100.0% (1806 of 1806 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/ca/ + +- Chore: Merge branch 'origin/develop' into Weblate. + +- Chore: apply to all swipers + +- Chore: format + +- Chore: :art: format + +- Chore: Translated using Weblate (Catalan) + +Currently translated at 100.0% (1807 of 1807 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/ca/ + +- Chore: Merge branch 'origin/develop' into Weblate. + +- Chore: Translated using Weblate (Spanish) + +Currently translated at 87.2% (1582 of 1813 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/es/ + +- Chore: Merge branch 'origin/develop' into Weblate. + +- Update helm config template + +- Chore: :art: format, add scss to prettier formatting + +- Chore: Translated using Weblate (Catalan) + +Currently translated at 100.0% (1814 of 1814 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/ca/ + +- Chore: :art: format + +- Chore: Translated using Weblate (Catalan) + +Currently translated at 100.0% (1815 of 1815 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/ca/ + +- Chore: :busts_in_silhouette: patrons + +- Chore: :art: format + +- Chore: 👥 patrons + +- Chore: 👥 patrons + +- Chore: :busts_in_silhouette: patrons + +- Chore: :busts_in_silhouette: patrons + +- Chore: :busts_in_silhouette: patrons + +- Chore: :busts_in_silhouette: patrons + +- Chore: :busts_in_silhouette: patrons + +- Chore: Translated using Weblate (German) + +Currently translated at 98.7% (1793 of 1815 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/de/ + +- Chore: Merge branch 'origin/develop' into Weblate. + +- Chore: 🎨 format + +- Chore: :busts_in_silhouette: patrons + +- Chore: :busts_in_silhouette: patrons + +- Chore: :art: format + +- Chore: Translated using Weblate (Catalan) + +Currently translated at 100.0% (1814 of 1814 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/ca/ + +- Chore: Translated using Weblate (Chinese (Simplified)) + +Currently translated at 88.2% (1600 of 1814 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/zh_Hans/ + +- Chore: Translated using Weblate (Chinese (Simplified)) + +Currently translated at 88.2% (1600 of 1814 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/zh_Hans/ + +- Chore: Translated using Weblate (Chinese (Simplified)) + +Currently translated at 91.0% (1651 of 1814 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/zh_Hans/ + +- Chore: Translated using Weblate (Chinese (Simplified)) + +Currently translated at 91.0% (1651 of 1814 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/zh_Hans/ + +- Chore: Translated using Weblate (Chinese (Simplified)) + +Currently translated at 94.7% (1719 of 1814 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/zh_Hans/ + +- Chore: Translated using Weblate (Chinese (Simplified)) + +Currently translated at 94.7% (1719 of 1814 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/zh_Hans/ + +- Chore: 👥 patrons + +- Chore: Translated using Weblate (Chinese (Simplified)) + +Currently translated at 96.8% (1757 of 1814 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/zh_Hans/ + +- Chore: Translated using Weblate (Chinese (Simplified)) + +Currently translated at 96.8% (1757 of 1814 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/zh_Hans/ + +- Chore: Merge branch 'origin/develop' into Weblate. + +- Chore: Merge branch 'origin/develop' into Weblate. + +- Chore: Translated using Weblate (Chinese (Simplified)) + +Currently translated at 97.4% (1769 of 1816 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/zh_Hans/ + +- Chore: Merge branch 'origin/develop' into Weblate. + +- Chore: Translated using Weblate (Chinese (Simplified)) + +Currently translated at 100.0% (1816 of 1816 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/zh_Hans/ + +- Chore: Translated using Weblate (Chinese (Simplified)) + +Currently translated at 100.0% (1816 of 1816 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/zh_Hans/ + +- Chore: :art: format + +- Chore: :busts_in_silhouette: patrons + +- Chore: 👥 patrons + +- Chore: Translated using Weblate (Catalan) + +Currently translated at 100.0% (1816 of 1816 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/ca/ + +- Chore: Translated using Weblate (Chinese (Simplified)) + +Currently translated at 100.0% (1816 of 1816 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/zh_Hans/ + +- Chore: Merge branch 'origin/develop' into Weblate. + +- Chore: 👥 patrons + +- Chore: Merge branch 'origin/develop' into Weblate. + +- Chore: Translated using Weblate (Chinese (Simplified)) + +Currently translated at 100.0% (1816 of 1816 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/zh_Hans/ + +- Chore: :arrow_up: up prettier, pnpm + +- Chore: :busts_in_silhouette: patrons + +- Chore: Translated using Weblate (French) + +Currently translated at 88.7% (1612 of 1816 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/fr/ + +- Chore: Translated using Weblate (Chinese (Simplified)) + +Currently translated at 100.0% (1816 of 1816 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/zh_Hans/ + +- Chore: Translated using Weblate (Chinese (Traditional)) + +Currently translated at 93.9% (1707 of 1816 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/zh_Hant/ + +- Chore: :art: format + +- Chore: Merge branch 'origin/develop' into Weblate. + +- Chore: add megalodon to cleaning scripts + +- Chore: Translated using Weblate (Japanese) + +Currently translated at 100.0% (1816 of 1816 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/ja/ + +- Chore: Translated using Weblate (Chinese (Traditional)) + +Currently translated at 94.3% (1714 of 1816 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/zh_Hant/ + +- Chore: Use css module + +- Chore: Translated using Weblate (Turkish) + +Currently translated at 3.9% (71 of 1816 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/tr/ + +- Chore: Translated using Weblate (Chinese (Traditional)) + +Currently translated at 95.0% (1726 of 1816 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/zh_Hant/ + +- Chore: Merge branch 'origin/develop' into Weblate. + +- Chore: :art: format + +- Chore: Translated using Weblate (Catalan) + +Currently translated at 100.0% (1819 of 1819 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/ca/ + +- Chore: :art: format + +- Chore: :busts_in_silhouette: patrons + +- Chore: vite conf changes + +- Chore: :page_facing_up: LICENSE for configuration directories + +Closes #10470 + +- Chore: change account + +- Chore: :art: format + +- Chore: forgot to remove a debug print stmt? + +- Chore: Added translation using Weblate (Galician) + +- Chore: Translated using Weblate (Catalan) + +Currently translated at 100.0% (1827 of 1827 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/ca/ + +- Chore: Merge branch 'origin/develop' into Weblate. + +- Chore: format + +- Chore: :art: format + +- Chore: :art: format + +- Chore: :art: format + +- Chore: Translated using Weblate (Catalan) + +Currently translated at 100.0% (1830 of 1830 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/ca/ + +- Chore: Translated using Weblate (Norwegian Bokmål) + +Currently translated at 4.3% (79 of 1830 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/nb_NO/ + +- Chore: Translated using Weblate (Portuguese (Brazil)) + +Currently translated at 5.6% (103 of 1830 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/pt_BR/ + +- Chore: Translated using Weblate (Portuguese (Brazil)) + +Currently translated at 5.6% (103 of 1830 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/pt_BR/ + +- Chore: Translated using Weblate (Galician) + +Currently translated at 0.8% (15 of 1830 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/gl/ + +- Chore: Merge branch 'origin/develop' into Weblate. + +- Chore: :art: format + +- Chore: Translated using Weblate (Catalan) + +Currently translated at 100.0% (1830 of 1830 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/ca/ + +- Chore: Translated using Weblate (Japanese) + +Currently translated at 100.0% (1830 of 1830 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/ja/ + +- Chore: Translated using Weblate (Turkish) + +Currently translated at 54.4% (996 of 1830 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/tr/ + +- Chore: Translated using Weblate (Ukrainian) + +Currently translated at 74.9% (1372 of 1830 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/uk/ + +- Chore: Merge branch 'origin/develop' into Weblate. + +- Chore: :busts_in_silhouette: patrons + +- Chore: :busts_in_silhouette: patrons + +- Chore: Translated using Weblate (Turkish) + +Currently translated at 56.5% (1035 of 1830 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/tr/ + +- Chore: Merge branch 'origin/develop' into Weblate. + +- Chore: :art: format + +- Chore: Translated using Weblate (Catalan) + +Currently translated at 100.0% (1831 of 1831 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/ca/ + +- Chore: Translated using Weblate (Turkish) + +Currently translated at 100.0% (1831 of 1831 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/tr/ + +- Chore: Translated using Weblate (Spanish) + +Currently translated at 86.9% (1592 of 1831 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/es/ + +- Chore: Translated using Weblate (Turkish) + +Currently translated at 100.0% (1831 of 1831 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/tr/ + +- 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/ + +- Chore: Merge branch 'origin/develop' into Weblate. + +- Chore: :art: format + +- 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/ + +- 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/ + +- Chore: Merge branch 'origin/develop' into Weblate. + +- Chore: Translated using Weblate (Catalan) + +Currently translated at 100.0% (1832 of 1832 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/ca/ + +- Chore: Translated using Weblate (Ukrainian) + +Currently translated at 100.0% (1832 of 1832 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/uk/ + +- Chore: :globe_with_meridians: (only outbox.json) no longer needed + +- Chore: Translated using Weblate (Catalan) + +Currently translated at 100.0% (1832 of 1832 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/ca/ + +- Chore: Merge branch 'origin/develop' into Weblate. + +- Chore: Added translation using Weblate (Bulgarian (bul_BG)) + +- Chore: Translated using Weblate (Catalan) + +Currently translated at 100.0% (1832 of 1832 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/ca/ + +- Chore: Merge branch 'origin/develop' into Weblate. + +- Chore: :art: format + +- Chore: :art: format + +- Chore: make contributors consistent with the website + +- Chore: Translated using Weblate (Catalan) + +Currently translated at 100.0% (1836 of 1836 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/ca/ + +- Chore: Translated using Weblate (Russian) + +Currently translated at 92.2% (1694 of 1836 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/ru/ + +- Chore: Translated using Weblate (Chinese (Simplified)) + +Currently translated at 100.0% (1836 of 1836 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/zh_Hans/ + + +### Performance + +- Perf: use msgpackr to encode and decode + +- Perf: :zap: use fast-blurhash for blurhash decoding, up deps + + +### Refactor + +- Refactor: :lipstick: improve miauth style + +- Refactor: :wrench: allow redis user to be configured + +Follow up #10366, 3df3c97deb284ecbf3363b90a45c6501957d1e98 + +- Refactor: simplify getSignatureUser + +- Refactor: :recycle: force patrons update on about-calckey + +- Refactor: no url instantiation + +- Refactor: use redis-semaphore for mutex across workers + +- Refactor: examine by get instead of exists + +- Refactor: :globe_with_meridians: patrons description + +- Refactor: :triangular_flag_on_post: post editing is no longer experimental + +- Refactor: ⚡ make identicons and server metrics optional + +Co-authored-by: Kainoa Kanter + +- Refactor: ⚡ make identicons and server metrics optional + +Co-authored-by: Kainoa Kanter + +- Refactor: ⚡ make identicons and server metrics optional + +Co-authored-by: Kainoa Kanter + +- Refactor: combine MediaVideo & MediaImage components + +- Refactor: :recycle: read patrons from local file for fallback instead of empty array + +- Refactor: 💫 tweak photoswipe animation speed + +- Refactor: :lock: password input for object storage secret key + +- Refactor: cache relays for a longer time + +- Refactor: ⚡ antenna notes in cache + +Co-authored-by: Kainoa Kanter + +- Refactor: :recycle: MkPostFormAttachees setup syntax + +- Refactor: :recycle: donation link logic, add link to /about, fix typo + +- Refactor: :lipstick: max instance name length 37, gradate specific server dono button + +- Refactor: :recycle: simplify shift key logic + +- Refactor: :wheelchair: add aria labels to buttons + +- Refactor: :recycle: substr -> slice + +- Refactor(backend): Use `exist` to check existence + +* refactor(backend): 存在確認の`findOneBy`を`exist`に置き換え + +* cleanup + +- Refactor: use copy_limit if greater than 0 + +- Refactor: use new card design in user popup + +- Refactor: :lipstick: relay icon + +- Refactor: :lipstick: privacy icon + +- Refactor: :recycle: no autobind + +- Refactor: :zap: use blurhash-as for encoding and decoding + +- Refactor: remove tinycolor from MkFolder + a11y + +- Refactor: :children_crossing: filter out https on recommended instances + +- Refactor: :recycle: correct params for notes/children+conversion + +- Refactor: :wastebasket: deprecate DB_MAX_NOTE_TEXT_LENGTH + +- Refactor: :rotating_light: linting fix + +- Refactor: ⚡ improve performance of contextmenu + + +### Styling + +- Style: New posts button + +- Style: visible button w/out hovering in url preview + +- Style: accent color in plyr + +- Style: add back scrollbar-color for firefox + +- Style: truncate long URL's + +- Style: make background banner blur static + +- Style; proper styling for url preview + +- Style fix + +- Style: link underlines + +- Style: link underlines, attempt two™ + +- Style: use muted repeat icon instead of forbidden for disabled boosts + +- Style: don't truncate URL's + +- Style: underline-offset tweak + +- Style: consistent link underline thickness + +- Style: user card design + + +## [14.0.0-rc3] - 2023-06-24 + +### Bug Fixes + +- Fix: editing caption accuracy + +- Fix: editing caption accuracy + +- Fix: only show meili in metrics if available + +- Fix server metric iteration + +- Fix inbox stall + +- Fixes #10284, fixes #10208; passing in all pugVariables needed in base.pug, fixes csp + +- Fix: unread message bgcolor + +- Fix logic + +- Fixes + +- Fix width + +- Fix boost mutes + +- Fix typo + +- Fix search features + +- Fix + +- Fix: :bug: properly enter date + +- Fix math + +- Fix + +- Fix back button display + +- Fix + +- Fix: "24"th hour doesn't exist, it's 0 + +- Fix: :adhesive_bandage: YYYYMMDD with dashes + +- Fix: :rotating_light: fix unused import + +- Fix: :adhesive_bandage: day isn't decreased by 1 + +- Fix: 🚸 make "show replies in timeline" work as expected + +Co-authored-by: Syuilo + +- Fix: :ambulance: fix stream.ts + +- Fix: :bug: sonic logged connection despite not existing + +- Fix: :ambulance: fix switch import + +- Fix: :lipstick: fix sign-in 2fa token style + +- Fix: :bug: 2FA dialog + +- Fix: :bug: use correct 2fa value + +- Fix: :adhesive_bandage: convert numeric input to string + +- Fix aode-relay compatibility + +- Fix: :bug: display punishments on desktop + +- Fix + +- Fix + +- Fix overflow + +- Fix user preview menu color + +- Fix: :lipstick: white foreground on forced black background + +Remedies the problem introduced by 020c4f578827e2391b35cd102ee197cc037c0382 causing black text to appear over a black-ish background + +- Fix: :globe_with_meridians: correct "clear" + +- Fix: 🐛 don't allow editing a post on another account + +- Fix: 🐛 when editing polls, keep votes for unmodified choices + +- Fix: :bug: properly index edited post + +- Fix: :adhesive_bandage: duplicate update + +- Fix: :lipstick: badge style on mobile + +- Fix UI sometimes being offset on mobile + +- Fix: 🐛 empty fs stat + +- Fix + +- Fix build and clean scripts + +- Fix + +- Fix: :bug: only collapsed reply if notification is reply + +- Fix: 🐛 proper isDuplicateKeyValueError handling + +Closes #10340 +Co-authored-by: Kainoa Kanter + +- Fix: :bug: collapse reply if type is a mention and it has a reply + +- Fix: :pencil2: typo in API docs + errors + +Co-authored-by: naskya + +- Fix: hide tooltip on page change + +- Fix: don't use cache on autocomplete for now + +- Fix: :lipstick: consistent emoji styling + +- Fix: :adhesive_bandage: disable Unicode 15 emojis + +https://github.com/jdecked/twemoji/pull/43 + +- Fix: :bug: pull up instance window instead of search field + +- Fix: autocomplete not being focused properly + +- Fix: mobile note spacing + +- Fix: 🐛 race condition between workers when creating note + +Closes #10345 +Discovered here: https://codeberg.org/calckey/calckey/issues/10345#issuecomment-950475 + +- Fix: :bug: non-duplicate skin tone selection + +- Fix: :ambulance: disable lightningcss transformer for now + +- Fix: typo + +- Fix: :arrow_down: downgrade chalk + +- Fix: :bug: start transaction with multi + +- Fix: :pencil2: fix tab characters + +thanks, yaml. ugh. + +- Fix: :bug: remove cw in post edit + +Closes #10353 + +- Fix: :construction_worker: fix format run + +- Fix compile error + +- Fix: jump to top of page when opening modals + +I want to do this probably later, for now it will still focus inside the window when pressing tab + +- Fix: :alembic: ensure splash is removed upon load + +https://codeberg.org/calckey/calckey/pulls/10285#issuecomment-951231 + +- Fix: focus first element inside modal + +- Fix: :adhesive_bandage: make cacheRemoteFiles false by default for new instances + +- Fix: basically just undo my previous modal changes + + +### Documentation + +- Docs: 📝 tips + +- Docs: 📝 changelog + +- Docs: :memo: rudamentary sea-orm-cli instructions + +- Docs: :memo: sea orm migration "Setting Up Migration" doc link + +- Docs: 📝 fix formatting + +- Docs: :memo: min rust ver + +- Docs: :memo: changelog + +- Docs: :memo: update links + +- Docs: :memo: changelog + +- Docs: :memo: API documentation generation + +- Docs: :memo: add symlink for api docs in docs/ + +- Docs: 📝 use document instead of symlink + +- Docs: :memo: document packages dir + +- Docs: :memo: clearer package docs + +- Docs: :memo: mention libvips requirement + +#10352 + +- Docs: :memo: v14.0.0-rc3 changelog + + +### Features + +- Feat: ✨ searchFilters meta property + +- Feat: ✨ patron labels + +- Feat: channel column in deck view + +- Feat: :sparkles: delay function in animated MFM + +- Feat: :monocle_face: bring back misskey's moderation displays on profile + +- Feat: 🔒 Improve 2FA/keypass experience + +Co-authored-by: Tamania +Co-authored-by: Syuilo + +- Feat: :lipstick: button icons for security + +- Feat: :sparkles: 2FA input dialog + +- Add comments + +- Add faded edges to swiper + shadows :3 + +- Add refresh button to poll + +- Add environment variable + +- Feat: :sparkles: display remaining time on ratelimits + +- Feat: :sparkles: $[small ] and $[center ] MFM syntax + +- Feat: :sparkles: clickable domains on job queue + +https://post.naskya.net/notes/9gbfos2mv5iz6g63 + +- Feat: :sparkles: emoji skin tone + +Closes #9959 + +- Feat: :sparkles: skin tone selector in category + +- Feat: :lock: expand /api/v1/instance/peers to proper endpoint and check for private mode + +Closes #10358 + +- Feat: :bookmark: v14.0.0-rc3 + + +### Miscellaneous Tasks + +- Chore: update patrons + +- Chore: lint sw + +- Chore: update patrons + +- Chore: update patrons + +- Chore: Translated using Weblate (Catalan) + +Currently translated at 100.0% (1770 of 1770 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/ca/ + +- Chore: Translated using Weblate (Japanese) + +Currently translated at 100.0% (1770 of 1770 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/ja/ + +- Chore: Merge branch 'origin/develop' into Weblate. + +- Chore: formatting + +- Chore: update patrons + +- Chore: Translated using Weblate (Catalan) + +Currently translated at 100.0% (1772 of 1772 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/ca/ + +- Chore: Merge branch 'origin/develop' into Weblate. + +- Chore: Merge branch 'origin/develop' into Weblate. + +- Update cheat sheet with delay + +- Chore: Translated using Weblate (Catalan) + +Currently translated at 100.0% (1777 of 1777 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/ca/ + +- Chore: Translated using Weblate (Polish) + +Currently translated at 96.4% (1714 of 1777 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/pl/ + +- Chore: Translated using Weblate (Italian) + +Currently translated at 71.0% (1262 of 1777 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/it/ + +- Chore: formatting + +- Chore: formatting + +- Chore: format + +- Chore: Translated using Weblate (Catalan) + +Currently translated at 100.0% (1787 of 1787 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/ca/ + +- Chore: Translated using Weblate (German) + +Currently translated at 97.4% (1741 of 1787 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/de/ + +- Chore: Translated using Weblate (German) + +Currently translated at 97.4% (1741 of 1787 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/de/ + +- Chore: formatting + +- Chore: :passport_control: improve gitea templates + +- Chore: :passport_control: conventional commits in body, not checkbox + +- Chore: :arrow_up: up pnpm + +- Chore: Added translation using Weblate (Portuguese (Brazil)) + +- Chore: Translated using Weblate (German) + +Currently translated at 98.6% (1762 of 1787 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/de/ + +- Chore: Translated using Weblate (Portuguese (Brazil)) + +Currently translated at 0.6% (12 of 1787 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/pt_BR/ + +- Chore: :art: format + +- Chore: Translated using Weblate (Japanese) + +Currently translated at 100.0% (1787 of 1787 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/ja/ + +- Chore: Translated using Weblate (Chinese (Traditional)) + +Currently translated at 95.6% (1709 of 1787 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/zh_Hant/ + +- Chore: Merge branch 'origin/develop' into Weblate. + +- Chore: Translated using Weblate (Catalan) + +Currently translated at 100.0% (1803 of 1803 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/ca/ + +- Chore: Translated using Weblate (Portuguese (Brazil)) + +Currently translated at 4.4% (81 of 1803 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/pt_BR/ + +- Chore: Merge branch 'origin/develop' into Weblate. + +- Chore: Translated using Weblate (Catalan) + +Currently translated at 100.0% (1804 of 1804 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/ca/ + +- Chore: Translated using Weblate (Polish) + +Currently translated at 95.1% (1716 of 1804 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/pl/ + +- Chore: Merge branch 'origin/develop' into Weblate. + +- Chore: formatting + +- Chore: Translated using Weblate (Catalan) + +Currently translated at 100.0% (1804 of 1804 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/ca/ + +- Chore: Translated using Weblate (German) + +Currently translated at 99.2% (1791 of 1804 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/de/ + +- Update to node 20 + +- Chore: format + +- Chore: Translated using Weblate (Italian) + +Currently translated at 69.9% (1262 of 1804 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/it/ + +- Chore: Merge branch 'origin/develop' into Weblate. + +- Chore: Merge branch 'origin/develop' into Weblate. + +- Chore: :art: format + +- Chore: :bookmark: dev52 + +- Chore: :art: format + +- Chore: update bug report template + +- Chore: :memo: links in bug template + +- Chore: :memo: bring bug template changes to feature template + +- Chore: :memo: deployment method in bug report + +- Chore: :memo: fix duplicate, add emojis + +- Chore: :memo: add emojis to issue templates + +Because everything's better with emojis! + +- Chore: :memo: emojis in issue label + +- Chore: :art: format + +- Chore: :memo: too many emojis + +- Chore: :coffin: remove vim settings + +- Chore: :arrow_up: up emojilib + +- Chore: Translated using Weblate (Catalan) + +Currently translated at 100.0% (1805 of 1805 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/ca/ + + +### Performance + +- Perf: limit number of antennas + +- Perf: set patrons in redis + +- Perf: use charts data for stats endpoint + +- Perf: ⚡ update emojis, cache in IndexedDb + +Closes #9959 +May fix #9724 + +- Perf: ⚡ use setInterval instead of setTimeout chain in MkTime + + +### Refactor + +- Refactor: client assets + +- Refactor: client assets + +- Refactor: :coffin: remove old db calls, add todo + +- Refactor: :recycle: import from @/db + +- Refactor: 💄 reverse pie chart color on indexing + +Co-authored-by: PrivateGER + +- Refactor: :recycle: use parent/child selector for attachment + +- Refactor: :lipstick: style punishments + +- Refactor: :arrow_up: use custom version of vue3-otp-input + +This enables the use of number inputs without the slider + +- Refactor: :safety_vest: replace js-yaml with yaml + +Technically mitigates CVE-2023-2251, but users never input YAML to Calckey. Still, this does no harm, and it's a good idea to keep dependencies like these up-to-date, as js-yaml was last updated 2 years ago. + +- Refactor: :coffin: unused import + +- Refactor: :art: locale loader + +- Refactor: :recycle: better edited timestamp display + +- Refactor: :recycle: reorganize note menu + +translate just above view remote + +- Refactor: ♻️ open instance as lookup window + +- Refactor: :pushpin: use own emoji descriptions + +- Refactor: :recycle: refactor MkModalWindow for TS safety + +- Refactor: :recycle: simplify null check + +- Refactor: :recycle: make skin tones modular + +Could possibly be for future custom emoji sets that support custom skin tones? (i.e. Mutant Standard) + +- Refactor: :recycle: url preview + +- Refactor: :label: add antenna type to streaming types + +- Enhance(frontend): improve ux of deck scroll + + +### Styling + +- Style: 💄 full follow button for userinfo in userlist + +- Style: :lipstick: margin on user card follow btn + +- Style: :lipstick: 2fa dialog styling + + +## [14.0.0-rc2c] - 2023-06-06 + +### Bug Fixes + +- Fix: post editing meta + + +### Miscellaneous Tasks + +- Chore: Translated using Weblate (Japanese) + +Currently translated at 100.0% (1753 of 1753 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/ja/ + + +## [14.0.0-rc2b] - 2023-06-06 + +## [14.0.0-rc2a] - 2023-06-06 + +### Bug Fixes + +- Fix cw button pos... oops + + +### Miscellaneous Tasks + +- Update cargo.lock + + +## [14.0.0-rc2] - 2023-06-06 + +### Bug Fixes + +- Fix translation box + +- Fix: correctly display links to self instance URL + +Closes #9270 + +Co-authored-by: GitHub + +- Fix typo + +- Fix tiny text on mobile + +- Fix: external link + +- Fix: dont stream hidden posts over websocket + +- Fix: only show signupsDisabled if signups disabled + +- Fix: hidden post federation + +- Fix: make userId optional cause its not used and +should not be used lmao + +- Fix font size + remove unecessary class + +- Fix instance-info if moderator but not admin + +- Fix incomplete revert + +- Fix: only fetch admin/meta if admin + +- Fix hidden post behavior + +- Fix: Detach push notifications.. +..from "isRead" check. Apps will handle that theself. + +- Fix: dont stream hidden posts + +- Fix padding + +- Fix clicking audio & search mfm opening post + +- Fix click.stop + +- Fix: run post import async + +- Fix: allow disabled radio buttons when created from radios.vue + +- Fix: :bug: add zip/unzip to docker container + +Closes #9843 + +- Fix quote attached styling, will prob improve later + +- Fix opening info button + +- Fix button + +- Fix typo + +- Fix + +- Fix + +- Fix typo + +- Fix meta update + +- Fix image not displaying if no text + +- Fix mobile navbar + +- Fix quote + +- Fix show replies toggle not saving + +- Fix noBg timeline BG's when wallpaper set + +- Fix border-radius of folder + +- Fix: dont show cheat sheet when not needed + +- Fix loading icon for boosts tab + +- Fix null error + +- Fix depth + +- Fix + +- Fix: show follow requests even if not locked + +- Fix api doc? + +- Fix import + +- Fix + +- Fixes!! + +- Fix avatar pos + +- Fix fade + +- Fix rogue :global() (would prevent text in toggles from wrapping) + +- Fix edit icon + +- Fix: Escape SQL LIKE + +* SQL LIKE escape + +- Fix + +- Fix + +- Fix z-index + +- Fix z-index + +- Fix: :lipstick: margin on expand tweet button + +- Fix style + +- Fix font size + +- Fix + +- Fix: MFM crop percentage parsing + +- Fix + +- Fix + +- Fix position + +- Fix: move isRenote check to note.vue + +- Fix: move isRenote check to note.vue + +- Fix + +- Fix example config format + +- Fix + +- Fixes + +- Fix: server info widget images + +- Fix query + +- Fix close button pos in compose box + +- Fix + +- Fix border w/ wallpaper + +- Fix + +- Fix + +- Fix: locale key + +- Fix: show message on error alert if text is null + +- Fix gap + +- Fix: :recycle: use locale for error + +https://calckey.social/notes/9fippqiwhl287b5m + +- Fix mfm-cheat-sheet styling + +- Fix: vue-plyr audio tag + +Co-authored-by: mappi + +- Fix features + +- Fix varchar array + +- Fix primary key specifier + +- Fix unit test + +- Fix unit test + +- Fix native import + +- Fix unit tests + +- Fix migration + +- Fix: remove unessicary extra line in note menu + +- Fix tutorial + +- Fix: summary if 1 attachment + +- Fix outdated docker deps + +- Fix background of mentions + +- Fix: :bug: prevent null date insertion + +https://calckey.aokaga.work/notes/9f6ksv2oov + +- Fix in cheat sheet also, I've realized I will need to do it a different at some point but this works for now. + +- Fix collapsed height + +- Fix + +- Fix + + +### Documentation + +- Docs: links + +- Docs: 📝 pm2 logrotate + +- Docs: fix k8s link + +- Docs: add opencollective + +- Docs: cleanup apache + +- Docs: Add configuration for Caddy + +- Docs: cleanup apache + +- Docs: notes + +- Docs: develop by default + +- Docs: searc providers + +- Docs: deps + +- Docs: 📝 versions + +- Docs: 📝 typos + +- Docs: 📝 full git clone + +- Docs: 📝 rust version + + +### Features + +- Add catppuccin latte + +- Add migration patches and fix commands + +- Feat: show message if signups are disabled + +- Feat: チャンネルの検索用ページとAPIの追加 + +* add channel search + +* move channel search to channel list page + +--------- + +Co-authored-by: tamaina +Co-authored-by: syuilo +Co-authored-by: atsuchan <83960488+atsu1125@users.noreply.github.com> +Co-authored-by: Masaya Suzuki <15100604+massongit@users.noreply.github.com> +Co-authored-by: Kagami Sascha Rosylight +Co-authored-by: taiy <53635909+taiyme@users.noreply.github.com> +Co-authored-by: xianon +Co-authored-by: kabo2468 <28654659+kabo2468@users.noreply.github.com> +Co-authored-by: YS <47836716+yszkst@users.noreply.github.com> +Co-authored-by: Khsmty +Co-authored-by: Soni L +Co-authored-by: mei23 +Co-authored-by: daima3629 <52790780+daima3629@users.noreply.github.com> +Co-authored-by: Windymelt <1113940+windymelt@users.noreply.github.com> + +- Feat: Add Nix development flake with flake-parts + +- Add back #10067 except for media change + +- Feat: :sparkles: ability for moderators to send mod mail + +- Adding calckey helm chart + +- Adding example config + +- Feat: 投稿したコンテンツのAIによる学習を軽減するオプションを追加 + +Co-authored-by: GitHub + +- Add (back) pwa install button to help menu + +- Add initial button + +- Add experimental feature gate + +- Feat: allow horizontal scaling + +- Add touch events for tooltip on range input + +- Add withChart prop to UserCardMini + +- Add back icons + +- Add ::before & ::after to themeChanging class + +- Add fade description + +- Add fade to cheat sheet + +- Add fade to MFM options + +- Feat: :necktie: add link to TOS in info icon + +Address #10117 + +- Add sonic back to compose + +- Add semicolon after property + +- Add advanced search parameters in search popup + +- Add ability to crop content + +- Add channel federation warn + +- Feat: ✨ server info widget + +Co-authored-by: Syuilo + +- Add pie chart to meili stats + +- Add tooltip to meili pie chart + +- Feat: :sparkles: server metrics in admin overview + +- Add entities and two schemas + +- Add repository trait + +- Add mock database + +- Add utility crate + +- Add random string generator + +- Add integration test in model + +- Add tests + +- Add newtype + +- Add abstraction of string array type + +- Add migration to convert array to jsonb + +- Add default values + +- Add pack_by_id + +- Add napi schema + +- Add native calls + +- Add test + +- Add format script + +- Add unit test + +- Add integration test of antenna + +- Add cargo test script + +- Add rust to the runtime container for migrations + + +### Miscellaneous Tasks + +- Updates to include alt text editing + +- Update file sensitivity on note edit + +- Chore: Translated using Weblate (Catalan) + +Currently translated at 100.0% (1739 of 1739 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/ca/ + +- Chore: Translated using Weblate (Chinese (Traditional)) + +Currently translated at 96.8% (1685 of 1739 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/zh_Hant/ + +- Chore: Translated using Weblate (Finnish) + +Currently translated at 21.1% (367 of 1739 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/fi/ + +- Chore: Merge branch 'origin/develop' into Weblate. + +- Chore: Translated using Weblate (English) + +Currently translated at 100.0% (1742 of 1742 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/en/ + +- Chore: Translated using Weblate (Catalan) + +Currently translated at 100.0% (1742 of 1742 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/ca/ + +- Chore: Translated using Weblate (Catalan) + +Currently translated at 100.0% (1742 of 1742 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/ca/ + +- Chore: Translated using Weblate (German) + +Currently translated at 94.0% (1639 of 1742 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/de/ + +- Chore: Translated using Weblate (German) + +Currently translated at 94.0% (1639 of 1742 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/de/ + +- Chore: Translated using Weblate (Finnish) + +Currently translated at 43.4% (757 of 1742 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/fi/ + +- Chore: Translated using Weblate (Catalan) + +Currently translated at 100.0% (1742 of 1742 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/ca/ + +- Chore: Translated using Weblate (Catalan) + +Currently translated at 100.0% (1742 of 1742 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/ca/ + +- Chore: Translated using Weblate (Finnish) + +Currently translated at 43.5% (759 of 1742 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/fi/ + +- Chore: Translated using Weblate (Catalan) + +Currently translated at 100.0% (1742 of 1742 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/ca/ + +- Chore: Translated using Weblate (German) + +Currently translated at 94.6% (1649 of 1742 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/de/ + +- Chore: Translated using Weblate (Finnish) + +Currently translated at 48.9% (853 of 1742 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/fi/ + +- Chore: Merge branch 'origin/develop' into Weblate. + +- Chore: Merge branch 'origin/develop' into Weblate. + +- Chore: Translated using Weblate (Catalan) + +Currently translated at 100.0% (1744 of 1744 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/ca/ + +- Chore: Translated using Weblate (German) + +Currently translated at 96.3% (1681 of 1744 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/de/ + +- Chore: Translated using Weblate (German) + +Currently translated at 96.5% (1683 of 1744 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/de/ + +- Chore: Translated using Weblate (German) + +Currently translated at 96.5% (1683 of 1744 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/de/ + +- Chore: Translated using Weblate (German) + +Currently translated at 96.5% (1683 of 1744 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/de/ + +- Chore: Translated using Weblate (Catalan) + +Currently translated at 100.0% (1744 of 1744 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/ca/ + +- Chore: Merge branch 'origin/develop' into Weblate. + +- Chore: format + +- Chore: patrons + +- Update tag + +- Chore: patrons + +- Chore: patrons + +- Chore: formatting + +- Chore: patrons + +- Chore: Translated using Weblate (Catalan) + +Currently translated at 100.0% (1747 of 1747 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/ca/ + +- Chore: Translated using Weblate (German) + +Currently translated at 96.5% (1687 of 1747 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/de/ + +- Chore: Translated using Weblate (German) + +Currently translated at 96.5% (1687 of 1747 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/de/ + +- Chore: Translated using Weblate (Finnish) + +Currently translated at 51.6% (902 of 1747 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/fi/ + +- Chore: Translated using Weblate (German) + +Currently translated at 96.6% (1688 of 1747 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/de/ + +- Chore: Translated using Weblate (Catalan) + +Currently translated at 100.0% (1750 of 1750 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/ca/ + +- Chore: Translated using Weblate (German) + +Currently translated at 96.5% (1688 of 1748 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/de/ + +- Chore: Merge branch 'origin/develop' into Weblate. + +- Chore: formatting + +- Chore: pnpm 8.4.0 + +- Chore: Translated using Weblate (German) + +Currently translated at 96.6% (1690 of 1749 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/de/ + +- Chore: format + +- Chore: Translated using Weblate (German) + +Currently translated at 97.3% (1703 of 1749 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/de/ + +- Chore: Merge branch 'origin/develop' into Weblate. + +- Update import-export options + +- Chore: formatting + +- Chore: formatting + +- Chore: patrons + +- Chore: patrons + +- Chore: formatting + +- Chore: Translated using Weblate (English) + +Currently translated at 100.0% (1734 of 1734 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/en/ + +- Chore: Translated using Weblate (Catalan) + +Currently translated at 99.9% (1733 of 1734 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/ca/ + +- Chore: Translated using Weblate (Czech) + +Currently translated at 50.6% (879 of 1734 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/cs/ + +- Chore: Translated using Weblate (German) + +Currently translated at 97.6% (1693 of 1734 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/de/ + +- Chore: Translated using Weblate (Spanish) + +Currently translated at 92.5% (1605 of 1734 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/es/ + +- Chore: Translated using Weblate (French) + +Currently translated at 95.4% (1655 of 1734 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/fr/ + +- Chore: Translated using Weblate (French) + +Currently translated at 95.4% (1655 of 1734 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/fr/ + +- Chore: Translated using Weblate (Indonesian) + +Currently translated at 84.0% (1457 of 1734 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/id/ + +- Chore: Translated using Weblate (Polish) + +Currently translated at 99.4% (1725 of 1734 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/pl/ + +- Chore: Merge branch 'origin/develop' into Weblate. + +- Chore: Merge branch 'origin/develop' into Weblate. + +- Chore: Translated using Weblate (Catalan) + +Currently translated at 100.0% (1735 of 1735 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/ca/ + +- Chore: Translated using Weblate (German) + +Currently translated at 97.5% (1693 of 1735 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/de/ + +- Chore: Merge branch 'origin/develop' into Weblate. + +- Chore: Merge branch 'origin/develop' into Weblate. + +- Chore: Translated using Weblate (German) + +Currently translated at 99.3% (1725 of 1737 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/de/ + +- Chore: Translated using Weblate (German) + +Currently translated at 99.3% (1725 of 1737 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/de/ + +- Chore: Translated using Weblate (Polish) + +Currently translated at 99.3% (1725 of 1737 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/pl/ + +- Chore: Merge branch 'origin/develop' into Weblate. + +- Chore: Merge branch 'origin/develop' into Weblate. + +- Chore: up pnpm + +- Update locale + +- Chore: Translated using Weblate (Catalan) + +Currently translated at 100.0% (1738 of 1738 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/ca/ + +- Chore: Translated using Weblate (German) + +Currently translated at 99.4% (1729 of 1738 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/de/ + +- Chore: Merge branch 'origin/develop' into Weblate. + +- Chore: Merge branch 'origin/develop' into Weblate. + +- Chore: Merge branch 'origin/develop' into Weblate. + +- Chore: formatting + +- Update translation + +- Chore: Translated using Weblate (Catalan) + +Currently translated at 99.9% (1746 of 1747 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/ca/ + +- Chore: Translated using Weblate (French) + +Currently translated at 96.2% (1682 of 1747 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/fr/ + +- Chore: Translated using Weblate (French) + +Currently translated at 96.2% (1682 of 1747 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/fr/ + +- Chore: Translated using Weblate (French) + +Currently translated at 96.2% (1682 of 1747 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/fr/ + +- Chore: Translated using Weblate (Catalan) + +Currently translated at 100.0% (1747 of 1747 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/ca/ + +- Chore: Translated using Weblate (German) + +Currently translated at 99.0% (1730 of 1747 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/de/ + +- Chore: Translated using Weblate (French) + +Currently translated at 96.2% (1682 of 1747 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/fr/ + +- Chore: Merge branch 'origin/develop' into Weblate. + +- Chore: Translated using Weblate (English) + +Currently translated at 100.0% (1744 of 1744 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/en/ + +- Chore: Translated using Weblate (Japanese) + +Currently translated at 99.9% (1743 of 1744 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/ja/ + +- Chore: Translated using Weblate (Japanese) + +Currently translated at 99.9% (1743 of 1744 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/ja/ + +- Chore: Translated using Weblate (Russian) + +Currently translated at 98.1% (1711 of 1744 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/ru/ + +- Chore: Translated using Weblate (Danish) + +Currently translated at 11.4% (200 of 1744 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/da/ + +- Chore: Translated using Weblate (German) + +Currently translated at 99.1% (1729 of 1744 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/de/ + +- Chore: Merge branch 'origin/develop' into Weblate. + +- Chore: Translated using Weblate (Catalan) + +Currently translated at 100.0% (1745 of 1745 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/ca/ + +- Chore: Merge branch 'origin/develop' into Weblate. + +- Chore: formatting + +- Chore: Translated using Weblate (Catalan) + +Currently translated at 100.0% (1742 of 1742 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/ca/ + +- Chore: Translated using Weblate (Catalan) + +Currently translated at 100.0% (1743 of 1743 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/ca/ + +- Chore: Merge branch 'origin/develop' into Weblate. + +- Chore: Translated using Weblate (Chinese (Traditional)) + +Currently translated at 98.6% (1719 of 1743 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/zh_Hant/ + +- Chore: Translated using Weblate (Dutch) + +Currently translated at 19.7% (344 of 1743 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/nl/ + +- Chore: Merge branch 'origin/develop' into Weblate. + +- Chore: formatting + +- Chore: Translated using Weblate (Catalan) + +Currently translated at 100.0% (1746 of 1746 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/ca/ + +- Chore: Merge branch 'origin/develop' into Weblate. + +- Chore: formatting + +- Chore: Translated using Weblate (Catalan) + +Currently translated at 100.0% (1747 of 1747 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/ca/ + +- Chore: Translated using Weblate (Dutch) + +Currently translated at 23.2% (407 of 1747 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/nl/ + +- Chore: formatting + +- Chore: up mfm-js in backend + +- Chore: Translated using Weblate (Catalan) + +Currently translated at 100.0% (1747 of 1747 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/ca/ + +- Chore: Translated using Weblate (Dutch) + +Currently translated at 24.9% (436 of 1747 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/nl/ + +- Chore: Translated using Weblate (Dutch) + +Currently translated at 24.9% (436 of 1747 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/nl/ + +- Chore: Merge branch 'origin/develop' into Weblate. + +- Chore: upgrade swiper + +- Chore: formatting + +- Chore: up pnpm + +- Chore: Translated using Weblate (Catalan) + +Currently translated at 100.0% (1748 of 1748 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/ca/ + +- Chore: Translated using Weblate (Dutch) + +Currently translated at 25.0% (438 of 1748 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/nl/ + +- Chore: Merge branch 'origin/develop' into Weblate. + +- Chore: formatting + +- Chore: Translated using Weblate (Catalan) + +Currently translated at 100.0% (1750 of 1750 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/ca/ + +- Chore: Merge branch 'origin/develop' into Weblate. + +- Chore: Translated using Weblate (Catalan) + +Currently translated at 100.0% (1753 of 1753 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/ca/ + +- Chore: Translated using Weblate (Dutch) + +Currently translated at 30.8% (541 of 1753 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/nl/ + +- Chore: Translated using Weblate (German) + +Currently translated at 95.6% (1676 of 1753 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/de/ + +- Chore: Translated using Weblate (German) + +Currently translated at 95.6% (1676 of 1753 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/de/ + +- Chore: Translated using Weblate (Dutch) + +Currently translated at 31.6% (554 of 1753 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/nl/ + +- Chore: formatting + +- Update post import + +- Update inbox import timeout + +- Chore: Translated using Weblate (Dutch) + +Currently translated at 32.1% (564 of 1753 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/nl/ + +- Chore: formatting + +- Chore: format + +- Chore: rebase to v13 MkFolder + +Co-authored-by: Syuilo + +- Chore: Translated using Weblate (English) + +Currently translated at 100.0% (1753 of 1753 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/en/ + +- Chore: Translated using Weblate (Japanese) + +Currently translated at 98.8% (1732 of 1753 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/ja/ + +- Chore: Translated using Weblate (Dutch) + +Currently translated at 32.2% (566 of 1753 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/nl/ + +- Chore: Translated using Weblate (Dutch) + +Currently translated at 32.3% (567 of 1753 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/nl/ + +- Chore: Translated using Weblate (Catalan) + +Currently translated at 100.0% (1753 of 1753 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/ca/ + +- Chore: Translated using Weblate (German) + +Currently translated at 97.6% (1711 of 1753 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/de/ + +- Chore: Translated using Weblate (German) + +Currently translated at 98.1% (1720 of 1753 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/de/ + +- Chore: Translated using Weblate (German) + +Currently translated at 98.1% (1720 of 1753 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/de/ + +- Chore: Translated using Weblate (German) + +Currently translated at 98.1% (1720 of 1753 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/de/ + +- Chore: Translated using Weblate (German) + +Currently translated at 98.1% (1720 of 1753 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/de/ + +- Chore: Translated using Weblate (German) + +Currently translated at 98.9% (1734 of 1753 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/de/ + +- Chore: Translated using Weblate (Dutch) + +Currently translated at 34.2% (601 of 1753 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/nl/ + +- Chore: Translated using Weblate (German) + +Currently translated at 98.9% (1734 of 1753 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/de/ + +- Chore: Translated using Weblate (German) + +Currently translated at 98.9% (1734 of 1753 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/de/ + +- Chore: formatting + +- Chore: :arrow_up: up pnpm + +- Chore: :bulb: meili + +- Chore: Translated using Weblate (German) + +Currently translated at 98.9% (1734 of 1753 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/de/ + +- Chore: format + +- Chore: format + +- Chore: :technologist: pull request template + +- Chore: formatting + +- Chore: up bull-board deps + +- Chore: :arrow_up: up bull + +- Chore: Translated using Weblate (Danish) + +Currently translated at 11.5% (203 of 1753 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/da/ + +- Chore: formatting + +- Chore: Translated using Weblate (German) + +Currently translated at 98.9% (1734 of 1753 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/de/ + +- Chore: Translated using Weblate (German) + +Currently translated at 99.5% (1745 of 1753 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/de/ + +- Chore: formatting + +- Chore: update example config + +- Chore: formatting + +- Chore: formatting + +- Chore: :arrow_up: up various deps + +- Chore: Translated using Weblate (Catalan) + +Currently translated at 100.0% (1753 of 1753 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/ca/ + +- Chore: format + +- Chore: formatting + +- Chore: :arrow_up: up deps + +- Chore: formatting + +- Chore: format + + +### Performance + +- Perf: インスタンスデフォルトテーマを予めjson5 -> jsonに変換しておくことでjson5を初期バンドルに含めずに済むように + +Co-authored-by: Syuilo + + +### Refactor + +- Refactor: remove internal apps page + +- Enhance: emoji width and height + +- Refactor: make post imports an experiment + +- Refactor: :coffin: remove old metrics view + +- Refactor: add back old info display from mkv12 + +- Refactor: :recycle: ssr views + +Correct og:type for users, format docs, deprecate _info_card_ + +- Refactor: :recycle: sync note summaries + +- Refactor: remove mk remnants + + +### Styling + +- Style: 💄 server metrics widgets + + +## [14.0.0-rc] - 2023-05-02 + +### Bug Fixes + +- BlockMath is not necessarily multi-line (is this copy-pasted from blockCode?) + +- Fix poll voting causing edit revisions. + +- Fix(ap): Use unique identifier for each follow request + +Closes #9677 + +Co-authored-by: GitHub + +- Fix meta fetch + +- Fix params + +- Fix email validation + +- Fix: Commit CI not running because cargo is not installed + +- Fix: Switch to node alpine image + +- Fix db migration + +- Fix lang + +- Fix show more import + + +### Features + +- Add toggler + +- Add blockMath + +- Add silenced colour + +- Add db migration + + +### Miscellaneous Tasks + +- Chore: Translated using Weblate (English) + +Currently translated at 100.0% (1735 of 1735 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/en/ + +- Chore: Translated using Weblate (Catalan) + +Currently translated at 37.1% (644 of 1735 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/ca/ + +- Chore: Translated using Weblate (Finnish) + +Currently translated at 11.7% (204 of 1735 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/fi/ + +- Chore: Translated using Weblate (Finnish) + +Currently translated at 11.7% (204 of 1735 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/fi/ + +- Chore: Translated using Weblate (Japanese) + +Currently translated at 99.3% (1724 of 1735 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/ja/ + +- Chore: Merge branch 'origin/develop' into Weblate. + +- Chore: up browser-image-resizer + +- Chore: format + +- Chore: theme refactor + +- Update patrons + +- Chore: Translated using Weblate (Catalan) + +Currently translated at 69.2% (1204 of 1739 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/ca/ + +- Chore: patrons + +- Chore: formatting + +- Chore: Translated using Weblate (Catalan) + +Currently translated at 100.0% (1739 of 1739 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/ca/ + +- Chore: Merge branch 'origin/develop' into Weblate. + + +## [13.2.0-beta9h] - 2023-04-30 + +## [13.2.0-beta9] - 2023-04-29 + +### Bug Fixes + +- Fix: add route + +- Fix? + +- Fix: style email with inline styles + +- Fix disabled, formatting + +- Fix: Make statusModel `created_at` fields be ISO 8601 strings + +This makes the 'Reactions to this post' status (seen when viewing +a status context) send the correct data type for `created_at` fields. + +https://docs.joinmastodon.org/entities/Account/#created_at +https://docs.joinmastodon.org/entities/Status/#created_at + +- Fix: Get list titles from Form data when creating and updating lists + +This change will actually make it possible for Mastodon clients to +create and rename lists, as they send the title in a Form data instead +of a query string. + +https://docs.joinmastodon.org/methods/lists/#form-data-parameters + +- Fix: Declare /api/v1/accounts/relationships before /api/v1/accounts/:id + +Previously the 'relationships' part was considered to be an account id +and was handled by completely different API endpoint. + +- Fixes + +- Fixes? + +- Fix subnote + +- Fix + +- Fix: centering block math ([#9946](https://github.com/orhun/git-cliff/issues/9946)) + +Similar to `inlineCode` and `blockCode`, MFM provides two types of formula syntax, `mathInline` and `mathBlock` (I'm curious why these aren't called `inlineMath`/`blockMath`, but oh well) + +Other platforms, like GitHub, **Math**todon, my blog, etc., also support these two types of formula representation, and math blocks are centered on (maybe) all such platforms. + +![](https://cdn.discordapp.com/attachments/823878222897741868/1101837026304720997/2023-04-29_201943.png) + +But Calckey (Misskey v12) don't center math blocks. I'd say this is a bug, and this makes `blockMath` useless (it's just `inlineMath` in a new line). + +![](https://cdn.discordapp.com/attachments/823878222897741868/1101837026027917342/2023-04-29_202008.png) + +So I fixed this. + +![](https://cdn.discordapp.com/attachments/823878222897741868/1101837183574355978/2023-04-29_202854.png) + +Co-authored-by: naskya +Reviewed-on: https://codeberg.org/calckey/calckey/pulls/9946 +Co-authored-by: naskya +Co-committed-by: naskya + + +### Documentation + +- Docs + + +### Features + +- Feat: :sparkles: frontend interface for post-account creation email verification + +- Add kaiteki to example proxyBypassHosts + +- Add additional information & show more button in user preview popup + +- Add the focus trap thingies again + + +### Miscellaneous Tasks + +- Chore: more rpine for server activity widget + +- Chore: update examples + +- Update patrons + +- Chore: Translated using Weblate (Catalan) + +Currently translated at 35.0% (606 of 1727 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/ca/ + +- Chore: Translated using Weblate (Russian) + +Currently translated at 100.0% (1727 of 1727 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/ru/ + +- Chore: Translated using Weblate (Chinese (Traditional)) + +Currently translated at 97.4% (1683 of 1727 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/zh_Hant/ + +- Chore: Merge branch 'origin/develop' into Weblate. + +- Chore: Merge branch 'origin/develop' into Weblate. + +- Chore: Merge branch 'origin/develop' into Weblate. + +- Chore: update icons on post form + +- Chore: Added translation using Weblate (Finnish) + +- Chore: Translated using Weblate (Finnish) + +Currently translated at 2.4% (43 of 1735 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/fi/ + +- Chore: upgrade megalodon + + +### Refactor + +- Refactor: change import type to radio + + +## [13.2.0-beta8] - 2023-04-26 + +### Bug Fixes + +- Fix an instance ticker bug + + +### Features + +- Feat: heatmap option for activity widget + +- Feat: reserved usernames ([#9917](https://github.com/orhun/git-cliff/issues/9917)) + +This PR adds a feature to prevent users from creating a new account with a reserved username such as root, admin, system, proxy, info, etc... + +Reserved usernames can be configured via the config file. + +The administrator can create an account with a reserved username via the first setup screen or the control panel. + +The existing account of reserved usernames will not be affected. + +Co-authored-by: Namekuji +Reviewed-on: https://codeberg.org/calckey/calckey/pulls/9917 +Co-authored-by: Namekuji +Co-committed-by: Namekuji + + +### Miscellaneous Tasks + +- Chore: Translated using Weblate (Catalan) + +Currently translated at 22.9% (396 of 1727 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/ca/ + +- Chore: Translated using Weblate (German) + +Currently translated at 94.6% (1634 of 1727 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/de/ + +- Chore: Translated using Weblate (German) + +Currently translated at 94.6% (1634 of 1727 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/de/ + +- Chore: Translated using Weblate (Spanish) + +Currently translated at 92.2% (1594 of 1727 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/es/ + +- Chore: Translated using Weblate (French) + +Currently translated at 95.6% (1652 of 1727 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/fr/ + +- Chore: Translated using Weblate (Russian) + +Currently translated at 100.0% (1727 of 1727 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/ru/ + +- Chore: Translated using Weblate (Chinese (Traditional)) + +Currently translated at 97.2% (1680 of 1727 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/zh_Hant/ + +- Chore: formatting + + +## [13.2.0-beta7] - 2023-04-25 + +### Bug Fixes + +- Fix: :lock: don't show notes with CW on welcome screen + +Closes #9849 + +- Prevent crashes due to timezone === null + +- Fix a bug + +- Fix: disable "Search" keyword ([#9856](https://github.com/orhun/git-cliff/issues/9856)) + +Related: #9816 #9830 +I was so careless that I didn't know "Search" was also a keyword. I disabled that and fixed a minor bug. + +Co-authored-by: naskya +Reviewed-on: https://codeberg.org/calckey/calckey/pulls/9856 +Co-authored-by: naskya +Co-committed-by: naskya + +- Fix + +- Fix tag on explore + +- Fix header tabs + +- Fix: ドライブアップロードで413が返ってきたときにエラーメッセージを表示 ([#10680](https://github.com/orhun/git-cliff/issues/10680)) + +- Fix: boost muting in the recommended timeline ([#9906](https://github.com/orhun/git-cliff/issues/9906)) + +Closes: #9905 +Co-authored-by: naskya +Reviewed-on: https://codeberg.org/calckey/calckey/pulls/9906 +Co-authored-by: naskya +Co-committed-by: naskya + + +### Documentation + +- Docs: rm yunohost + +has been broken for months, I don't maintain it either. + + +### Features + +- Feat: rename workspaces + +- Feat: :sparkles: software name on hover icon in instance ticker + +- Feat: add an option to disable emoji reactions ([#9878](https://github.com/orhun/git-cliff/issues/9878)) + +Closes: #9865 +Co-authored-by: naskya +Reviewed-on: https://codeberg.org/calckey/calckey/pulls/9878 +Co-authored-by: naskya +Co-committed-by: naskya + +- Feat: make it toggleable whether to disable emojis in notifications ([#9880](https://github.com/orhun/git-cliff/issues/9880)) + +I talked about feature #9865 on my fedi account and received a comment like, "I don't care about emoji reactions in my timelines, but I do care what reactions I get!" + +Adding too many options is bad, but I agreed that making it toggleable whether to disable emojis in notifications is helpful, so I added this feature. This allows you to check emoji reactions to your posts in notifications while using the simple UI. I'd say this provides an experience that neither Mastodon nor Misskey has. + +The new setting item shows up only when you disable emoji reactions. + +Co-authored-by: naskya +Reviewed-on: https://codeberg.org/calckey/calckey/pulls/9880 +Co-authored-by: naskya +Co-committed-by: naskya + +- Feat: Implement reading Announcements from MastoAPI + + +### Miscellaneous Tasks + +- Chore: up swc + +- Update locale + +- Chore: formatting + +- Chore: add weblate config file + +- Chore: Translated using Weblate (Polish) + +Currently translated at 99.7% (1720 of 1724 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/pl/ + +- Chore: Merge branch 'origin/develop' into Weblate. + +- Chore: formatting + +- Update locales for calckey + +- Chore: Translated using Weblate (Greek) + +Currently translated at 31.6% (545 of 1724 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/el/ + +- Chore: Translated using Weblate (English) + +Currently translated at 100.0% (1724 of 1724 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/en/ + +- Chore: Translated using Weblate (Spanish) + +Currently translated at 92.0% (1587 of 1724 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/es/ + +- Chore: Translated using Weblate (Polish) + +Currently translated at 100.0% (1724 of 1724 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/pl/ + +- Chore: Translated using Weblate (Russian) + +Currently translated at 90.1% (1554 of 1724 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/ru/ + +- Chore: Translated using Weblate (Greek) + +Currently translated at 33.2% (574 of 1724 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/el/ + +- Chore: Translated using Weblate (Spanish) + +Currently translated at 92.0% (1587 of 1724 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/es/ + +- Chore: Translated using Weblate (Greek) + +Currently translated at 41.4% (714 of 1724 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/el/ + +- Chore: Translated using Weblate (Greek) + +Currently translated at 43.2% (745 of 1724 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/el/ + +- Chore: Translated using Weblate (Spanish) + +Currently translated at 92.5% (1595 of 1724 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/es/ + +- Chore: Translated using Weblate (Russian) + +Currently translated at 93.6% (1615 of 1724 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/ru/ + +- Chore: Translated using Weblate (Greek) + +Currently translated at 43.2% (745 of 1724 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/el/ + +- Chore: Merge branch 'origin/develop' into Weblate. + +- Chore: formatting + +- Chore: update summaly + +- Chore: Translated using Weblate (Japanese) + +Currently translated at 100.0% (1726 of 1726 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/ja/ + +- Chore: Translated using Weblate (Polish) + +Currently translated at 100.0% (1726 of 1726 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/pl/ + +- Chore: Translated using Weblate (Russian) + +Currently translated at 95.0% (1640 of 1726 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/ru/ + +- Chore: Translated using Weblate (Russian) + +Currently translated at 95.2% (1644 of 1726 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/ru/ + +- Chore: Translated using Weblate (Russian) + +Currently translated at 100.0% (1726 of 1726 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/ru/ + +- Chore: Merge branch 'origin/develop' into Weblate. + +- Chore: Merge branch 'origin/develop' into Weblate. + +- Chore: formatting + +- Chore: Merge branch 'origin/develop' into Weblate. + +- Chore: Translated using Weblate (English) + +Currently translated at 100.0% (1727 of 1727 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/en/ + +- Chore: Translated using Weblate (Russian) + +Currently translated at 99.8% (1724 of 1727 strings) + +Translation: Calckey/locales +Translate-URL: https://hosted.weblate.org/projects/calckey/locales/ru/ + +- Chore: up pakcages + + +### Styling + +- Style announcement image + + +## [13.2.0-beta6] - 2023-04-13 + +### Bug Fixes + +- Fix: add cargo to DOCKERFILE + +- Fix: add cargo to DOCKERFILE + +- Fix #9784 + +- Fix #9784 + +- Fix help button alignment iconsOnly + +- Fix indexing description + +- Fix: :passport_control: no longer need 2fa for webauthn + +- Fix import + +- Fix button alignments + +- Fix: dialogs not coming up + +- Fix: dockerfile + +- Fix: add copy for build from native-utils + +- Fix: changing passwords, 2fa, and password resets. + +The argon2 usage was only implemented for sign-ins which broke a bunch of other +endpoints and features. + +- Fix help button alignment iconsOnly + +- Fix indexing description + +- Fix: :passport_control: no longer need 2fa for webauthn + +- Fix import + +- Fix button alignments + +- Fix: dialogs not coming up + +- Fix: dockerfile + +- Fix: add copy for build from native-utils + +- Fix: changing passwords, 2fa, and password resets. + +The argon2 usage was only implemented for sign-ins which broke a bunch of other +endpoints and features. + +- Fix: buttons not showing + +- Fix: :bug: go to last timeline selected + +- Fix: toggling the blocking state from the instance-info admin view ([#9809](https://github.com/orhun/git-cliff/issues/9809)) + +Because the admin meta information was never loaded on this page, no amount of toggling the block or suspend sliders on the instance-info page (e.g. `https://calckey.example.com/instance-info/instance.tld`) will result in the instance actually being added to the blocklist. You could still do it from the bulk blocklist management page, but that can get unwieldy quickly if you just want to do a quick block of an instance. + +Co-authored-by: amy bones +Reviewed-on: https://codeberg.org/calckey/calckey/pulls/9809 +Co-authored-by: amybones +Co-committed-by: amybones + +- Fix + +- Fix(client): userpage ui ([#9179](https://github.com/orhun/git-cliff/issues/9179)) + +* fix(unverified): clip pages ui + +* fix(unverified): user page width + +Co-authored-by: syuilo + +- Fix id of move activity + +- Fix move inbox + +- Fix: format script; chore: format + +- Fix: typo + +- Fix + +- Fix chat metadata + +- Fix lookup instance + + +### Documentation + +- Docs: hyperlink foundkey commits + +- Docs: hyperlink foundkey commits + + +### Features + +- Add VIPS to dockerfile + +- Add "speak as cat" setting to ja-jp + +- Add VIPS to dockerfile + +- Add "speak as cat" setting to ja-jp + +- Feat: :sparkles: push notifs button + +Co-authored-by: Tamania + +- Feat: give reason for soft mutes + +Bad UX when a post is muted and it just says "Some chick said something". Now +provide some context too to help people decide if they want to view something +potentially triggering. + +- Feat: blur muted text + +- Feat: add hidden hashtags management page + +This simply adds a basic admin UI to blocklist some hashtags from displaying in +the trending widget. The facility existed already in the backend, but there was +no UI to manipulate the list save for executing raw SQL or API calls. + +- Feat: per-user boost muting ([#9825](https://github.com/orhun/git-cliff/issues/9825)) + +Cherry-picked from FoundKey/c414f24a2c ([commit](https://akkoma.dev/FoundKeyGang/FoundKey/commit/c414f24a2c123774246c7eca65edda4d3afaf8b3)) + +This allows us to hide specified users' boosts from the timelines (the boosts will still be visible on their user page). + +Co-authored-by: Hélène +Co-authored-by: naskya +Reviewed-on: https://codeberg.org/calckey/calckey/pulls/9825 +Co-authored-by: naskya +Co-committed-by: naskya + +- Feat: messaging room banner + +- Feat: mark all as read action in chat + +- Feat: admin lookup files/instance + +- Feat: index posts action + +- Feat: lookup post action + + +### Miscellaneous Tasks + +- Chore: :art: format + +- Chore: remove okteto + +- Chore: update Japanese locale ([#9802](https://github.com/orhun/git-cliff/issues/9802)) + +- Chore: :art: format + +- Chore: remove okteto + +- Chore: update patrons + +- Chore: pnpm 8.1.1 + +- Chore: back button in control panel + +- Chore: back button in more places + +- Chore: rome formatting + +- Chore: formatting + +- Chore: up pnpm + +- Chore: formatting + +- Chore: update mfm-js version ([#9844](https://github.com/orhun/git-cliff/issues/9844)) + +This resolves #9757. + +Co-authored-by: naskya +Reviewed-on: https://codeberg.org/calckey/calckey/pulls/9844 +Co-authored-by: naskya +Co-committed-by: naskya + + +### Refactor + +- Refactor + + +### Styling + +- Style + +- Style + + +## [13.2.0-beta4] - 2023-04-01 + +### Bug Fixes + +- Fix + +- Fix + +- Fix callback url + +- Fix japanese locale + +- Fix? + +- Fix: allow announces with followers visibility + +- Fix: :bug: formlink -> button + +fixes notifications and drive in settings + +- Fix sounds settings + +- Fix doc link + +- Fix: direct boost ([#9783](https://github.com/orhun/git-cliff/issues/9783)) + +Sorry to create PR multiple times. I should have included this in #9778. + +Co-authored-by: naskya +Reviewed-on: https://codeberg.org/calckey/calckey/pulls/9783 +Co-authored-by: naskya +Co-committed-by: naskya + +- Fix: don't nyaize quoted text + +- Fix: don't nyaize quoted text ([#9791](https://github.com/orhun/git-cliff/issues/9791)) + +- Fix search import + +- Fix migration + +- Fix: :bug: make recently used group + +Closes #9784 + + +### Documentation + +- Docs: add note about reverse migration + + +### Features + +- Feat: :sparkles: post metadata uses full @ + +Closes #9660 + +- Feat: new chat button on mobile + +- Feat: experimental post import + +- Add info + +- Feat: :sparkles: button in admin dash to index posts + +- Feat: custom KaTeX macro ([#9779](https://github.com/orhun/git-cliff/issues/9779)) + +Closes: #9759 +Co-authored-by: naskya +Reviewed-on: https://codeberg.org/calckey/calckey/pulls/9779 +Co-authored-by: naskya +Co-committed-by: naskya + +- Add rust to docker + +- Feat: :lock: add argon2 support + +Passwords will be automatically re-hashed on sign-in. All new password hashes will be argon2 by default. This uses argon2id and is not configurable. In the very unlikely case someone has more specific needs, a fork is recommended. ChangeLog: Added Co-authored-by: Chloe Kudryavtsev + +Breaks Calckey -> Misskey migration, but fixes Foundkey -> Calckey migration + +- Add argon + +- Feat: add option to boost with Home and Followers-only visibility ([#9788](https://github.com/orhun/git-cliff/issues/9788)) + +Closes: #9777 + +This pull request includes UI changes (please check the attached images). + +Co-authored-by: naskya +Reviewed-on: https://codeberg.org/calckey/calckey/pulls/9788 +Co-authored-by: naskya +Co-committed-by: naskya + +- Feat: :sparkles: search now searches posts and users + +- Feat: :sparkles: help menu in navbar + +- Add top margin to help button + + +### Miscellaneous Tasks + +- Chore: update patron list + +- Chore: update patrons + +- Chore: bump pnpm + +- Update post job + +- Chore: :globe_with_meridians: locale changes + +Closes #9781 #9773 + +- Chore: :globe_with_meridians: locale changes + +Closes #9781 #9773 + +- Chore: update patron list + +- Chore: up pnpm + +- Chore: add cleanup migration + + +## [13.2.0-beta31] - 2023-03-24 + +### Bug Fixes + +- Fix: max user profile length to db field length + +Resolves: #9749 + +- Fix: :bug: no nyaizing undefined text + +Closes #9752 + +- Fix: a bug in ads + +- Fix color in follow button + +- Fix: relay signature handling + +A change sometime ago moved to setting some signature fields in the incoming +object to undefined as opposed to deleting them. The trouble is that downstream +code checks against existence, not undefinedness and rejects the message. + +Resolves: #9665 + + +### Documentation + +- Docs: sonic + + +### Features + +- Feat: swap home timeline with social's functionality ([#9597](https://github.com/orhun/git-cliff/issues/9597)) + +The Home timeline functionality is swapped with social's. Meaning that Home timeline now consists of followee's and local posts. Social from now on will contain only followee's posts. See more info in the attached ticket. + +Some changes applied in english locales as well. Probably the rest of the languages need to be fixed though. + +This PR closes the ticket: https://codeberg.org/calckey/calckey/issues/9552 + +Co-authored-by: yawhn +Co-authored-by: ThatOneCalculator +Reviewed-on: https://codeberg.org/calckey/calckey/pulls/9597 +Co-authored-by: yawhn +Co-committed-by: yawhn + +- Feat: add sonic to docker + +- Feat: masto api add display name + + +### Miscellaneous Tasks + +- Chore: :package: upgrade megalodon + + +## [13.2.0-beta2] - 2023-03-21 + +### Bug Fixes + +- Fix deck view margins + +- Fix: Parse mastoAPI `limit` argument in more places & Improve converting arguments to boolean ([#9716](https://github.com/orhun/git-cliff/issues/9716)) + +Reviewed-on: https://codeberg.org/calckey/calckey/pulls/9716 +Authored-by: fruye +Signed-off-by: Cleo John +Co-authored-by: fruye +Co-committed-by: fruye + +- Fix: send button + +- Fix: intermediarily convert ids + +- Fix: mobile button alignment + +- Fix: when count is actually 0 + +- Fix cli more + +- Fix: don't show smartphone UI when settings icon is double-clicked + +- Fix: unicode aliases + +- Fix: repo url + +- Fix: :bug: can't send blank messages + +Closes #9661 + +- Fix: make sure cw button is on new line + +- Fix: local time for users + + +### Documentation + +- Docs: changes + +- Docs: 📝 sonic instructions + +- Docs: 📝 sonic + + +### Features + +- Feat: Make follower counts for remote users correct ([#9705](https://github.com/orhun/git-cliff/issues/9705)) + +#9293 + +Not sure if this is the right approach for this + +Co-authored-by: s1idewhist1e +Reviewed-on: https://codeberg.org/calckey/calckey/pulls/9705 +Co-authored-by: s1idewhist1e +Co-committed-by: s1idewhist1e + +- Add 404 as replacements + +- Feat: set license information for custom emojis ([#9719](https://github.com/orhun/git-cliff/issues/9719)) + +Closes: #9711 (please check this issue first) + +I cherry-picked two commits ([1](https://github.com/misskey-dev/misskey/commit/8ae9d2eaa8b0842671558370f787902e94b7f5a3), [2](https://github.com/misskey-dev/misskey/commit/ed51209172441927d24339f0759a5badbee3c9b6)) from [Misskey](https://github.com/misskey-dev/misskey) and made a few changes. +「ライセンス」should be written as "License" in the following screenshots, but it has not yet been translated. + +It would be nice if we could include multiple lines of text, but I just ported what's been implemented so far in Misskey not to mess things up. + +This is my first pull request (aside from typo correction). Feel free to point out any issues! + +![](https://cdn.discordapp.com/attachments/823878222897741868/1086372711841935440/2023-03-18_042011.png) +![](https://cdn.discordapp.com/attachments/823878222897741868/1086373178214981853/01.png) +![](https://cdn.discordapp.com/attachments/823878222897741868/1086373336709341246/2023-03-18_042629.png) + +Co-authored-by: syuilo +Co-authored-by: naskya +Reviewed-on: https://codeberg.org/calckey/calckey/pulls/9719 +Co-authored-by: naskya +Co-committed-by: naskya + +- Feat: compile time compression + +- Feat: spruce up CLI + +- Feat: :sparkles: more themes! + + +### Miscellaneous Tasks + +- Chore: update megalodon + +- Chore: update package locks + +- Chore: bump pnpm version + +- Chore: add emoji aliases + + +### Refactor + +- Refactor: nyaize on the frontend + + +## [13.2.0-beta3] - 2023-03-16 + +### Documentation + +- Docs: :memo: accurate update instructions + +Closes #9709 + +- Docs: :memo: accurate update instructions + +Closes #9709 + + +## [13.1.4.1] - 2023-03-16 + +## [13.1.4] - 2023-03-16 + +### Bug Fixes + +- Fix: Isolate unicode characters in display names ([#9702](https://github.com/orhun/git-cliff/issues/9702)) + +This fixes a 'Follows you' badge on a profile page and account addresses in threads from being drawn backwards when an account has some special Unicode characters that change the direction of text in their name (i.e. U+202E RIGHT-TO-LEFT OVERRIDE). + +Co-authored-by: fruye +Reviewed-on: https://codeberg.org/calckey/calckey/pulls/9702 +Co-authored-by: fruye +Co-committed-by: fruye + +- Fix: Run to boolean conversion in mastoAPI public and hashtag timelines + +The `only_media` query parameter in `/api/v1/timelines/public` and +`/api/v1/timelines/tag/:hashtag` was previously passed directly as-is to +the Misskey API, which made it pretty upset because it was receiving a +string named 'true' instead of the value 'true'. + +Needed for pleromaFE to display a timeline. + +- Fix: Run to boolean conversion in mastoAPI public and hashtag timelines ([#9710](https://github.com/orhun/git-cliff/issues/9710)) + +- Fix footer icons + +- Fix line in boosted text + +- Fix line alignment in smaller windows + + +## [13.2.0-beta] - 2023-03-15 + +## [13.1.3] - 2023-03-14 + +### Bug Fixes + +- Fix iconOnly for home tl + +- Fix import + +- Fix: some Masotdon API compat issues ([#9592](https://github.com/orhun/git-cliff/issues/9592)) +Co-authored-by: GeopJr +Co-committed-by: GeopJr + +- Fix(client): validate urls to improve security + +- Fix: :lock: prevent issues + +- Fix(client): use proxied image for instance icon + +- Fix(client): use proxied image for instance icon + +- Fix: 🐛 100vh body background color + +- Fix timelines + +- Fix: correct megalodon import + +- Fix navbar hover thingy ([#9616](https://github.com/orhun/git-cliff/issues/9616)) + +Co-authored-by: Freeplay +Reviewed-on: https://codeberg.org/calckey/calckey/pulls/9616 +Co-authored-by: Free +Co-committed-by: Free + +- Fix + +- Fix + +- Fix submenu positioning + +- Fix: :bug: first user gets admin + +Closes #9620 + +Co-authored-by: @Johann150 + +- Fix + +- Fix wrong import + +- Fix: correctly use note.emojis + +- Fix import + +- Fix + +- Fix import + +- Fix: :lipstick: admin overview style + +- Fix: :bug: pagination for "/api/channels/followed" + +Co-authored-by: takonomura <@takonomura@github.com> + +- Fix: more readable icon bgs + +- Fix + +- Fix + +- Fix oopsie + +- Fix: city validation + +- Fix: :bug: Don't show image previews if NSFW + +Closes #9636 + +- Fix: dialog + +- Fix? + +- Fix?? + +- Fix + +- Fix + +- Fix MkUpdated + +- Fix broken style + +- Fix: :bug: url slicing + +- Fix: visibility picker + +- Fix mastodon api stats + +- Fixed what ever calc did here, masto app didnt + +- Fix click events + +- Fix not being able to click around there are new posts button + +- Fix not being able to click around there are new posts button + +- Fix: multiple Ads' bugs. +feat: Ads widget + +https://codeberg.org/calckey/calckey/issues/9138 +https://codeberg.org/calckey/calckey/issues/9080 + +- Fix alignment + +- Fix line alignment + +- Fix subnote body clip area™ + +- Fix small window sizes + +- Fix indents on this ? + +- Fix errors + +- Fix import + +- Fix: post button text alignment + +- Fix mobile navbar + +- Fix? + +- Fix: mona is free + + +### Documentation + +- Docs: 📝 custom assets + +- Docs: 📝 migration from mk + +- Docs: get patch + +- Docs: run mig step + + +### Features + +- Feat: ✨ don't depend on an external service for urn:ietf:wg:oauth:2.0:oob ([#9602](https://github.com/orhun/git-cliff/issues/9602)) +Co-authored-by: GeopJr +Co-committed-by: GeopJr + +- Add account lookup + +- Add webpack config to compile sw.js for browser + +- Feat: :sparkles: remote featured notes + +- Feat: ✨ Add in Misskey v13's reacted users view + +- Feat: :sparkles: add position, scale , fg, and bg MFM from v13 + +- Feat: #9614 + +- Add debug to error + +- Feat: :sparkles: new admin panel data from Mk v13 + +- Add needed script + +- Add isActiveTab method + +- Feat: :sparkles: Show time for users + +- Feat: new modal + +- Add reply connectors, a + +- Add footer hover thingy to subnote too + +- Add some animations to reactions + +- Feat: 🔖 13.1.3 + + +### Miscellaneous Tasks + +- Update pnpm locks + +- Chore: :art: new dummy images + +- Chore: Rome Formatting + +- Chore: up calckey.js + +- Chore: calckey megalodon + +- Chore: tag dev1 + +- Chore: formatting + +- Chore: formatting + +- Chore: formatting + +- Chore: remove hard-to-see gradient + +- Chore: formatting + +- Chore: formatting + +- Chore: :fire: remove ability to add Twitter integration + +- Chore: rm dead code + +- Update readme + +- Update thingy? + +- Chore: update Japanese locale ([#9673](https://github.com/orhun/git-cliff/issues/9673)) +Co-authored-by: Namekuji +Co-committed-by: Namekuji + +- Chore: notes --> posts + +- Chore: apps + +- Chore: phosphor 2.0.2 + +adds woff2 as of 2.0.2 (my pr) + +- Chore: update patrons list + +- Chore: up icons + + +### Performance + +- Perf: :zap: emoji lib performance fix + +- Perf: :zap: MkPageHeader perf + +- Perf: :zap: emoji lib performance fix + + +### Refactor + +- Refactor please signin component + +- Refactor: use MkAvatars for mods + +- Refactor: max 5 url previews + +Closes #9654 + +- Refactor: :arrow_up: phosphor 2, sorta + +thank you sammy + + +### Styling + +- Style + +- Style view thread continuation button + fix link underline + +- Style fixes + +- Style: inlie-flex on ph-fw + + +### Testing + +- Test + +- Testing + + +## [13.1.3-rc] - 2023-02-09 + +### Bug Fixes + +- Fix some ctx stuff + +- Fixes + - Fix: Hide unmute option when the user is blocked +- Fix: Use theme --bg instead of a hardcoded color + + +### Documentation + +- Docs: :memo: changelog + +- Docs: 📝 branches + + ### Features - Feat: Mute and unfollow when blocking a user - Feat: Unblock with follow button -- Refresh user when changed +* refresh user when changed + +- Feat: ✨ v1 Mastodon API +This commit adds (maybe unstable) support for Mastodons v1 api +also some v2 endpoints, maybe I miss stuff, I dont know. +We will need to test this but it should be kinda stable +and work like (old) butter. + +Co-authored-by: Natty +Co-authored-by: cutls - Feature/help_menu ([#9587](https://github.com/orhun/git-cliff/issues/9587)) @@ -31,6 +4359,8 @@ Reviewed-on: https://codeberg.org/calckey/calckey/pulls/9587 ## [13.1.2] - 2023-02-06 +## [13.1.1] - 2023-02-05 + ### Bug Fixes - Fix: :bug: federate fedibird quote properly @@ -130,6 +4460,10 @@ Closes #9544 - Fix: reactions using unicode weren't processed +- Fixing a git merge error. + +- Fix: docker tags + ### Documentation diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 44e1e220c7..464eb0fc66 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -61,8 +61,8 @@ representative at an online or offline event. Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at -@thatonecalculator on Codeberg, -`@thatonecalculator@stop.voring.me` or `@t1c@i.calckey.cloud` on the Fediverse, +@t1c on Prometheus Gitlab, +`@kainoa@firefish.social` on the Fediverse, or kainoa@t1c.dev via email. All complaints will be reviewed and investigated promptly and fairly. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4e143c411a..b85a9360d5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,26 +1,26 @@ # Contribution guide -We're glad you're interested in contributing Calckey! In this document you will find the information you need to contribute to the project. +We're glad you're interested in contributing Firefish! In this document you will find the information you need to contribute to the project. ## Translation (i18n) -Calckey uses [Weblate](hhttps://hosted.weblate.org/engage/calckey/) for translation and internationalization management. +Firefish uses [Weblate](hhttps://hosted.weblate.org/engage/firefish/) for translation and internationalization management. If your language is not listed in Weblate, please open an issue. You can contribute without knowing how to code by helping translate here: -[![Translation status](https://hosted.weblate.org/widgets/calckey/-/287x66-grey.png)](https://hosted.weblate.org/engage/calckey/) +[![Translation status](https://hosted.weblate.org/widgets/firefish/-/287x66-grey.png)](https://hosted.weblate.org/engage/firefish/) -[![Translation bars](https://hosted.weblate.org/widgets/calckey/-/multi-auto.svg)](https://hosted.weblate.org/engage/calckey/) +[![Translation bars](https://hosted.weblate.org/widgets/firefish/-/multi-auto.svg)](https://hosted.weblate.org/engage/firefish/) ## Roadmap -See [CALCKEY.md](./CALCKEY.md) +See [FIREFISH.md](./FIREFISH.md) ## Issues Before creating an issue, please check the following: - To avoid duplication, please search for similar issues before creating a new issue. - Do not use Issues to ask questions or troubleshooting. - Issues should only be used to feature requests, suggestions, and bug tracking. - - Please ask questions or troubleshooting in the [Matrix room](https://matrix.to/#/#calckey:matrix.fedibird.com). + - Please ask questions or troubleshooting in the [Matrix room](https://matrix.to/#/#firefish:matrix.fedibird.com). > **Warning** > Do not close issues that are about to be resolved. It should remain open until a commit that actually resolves it is merged. @@ -48,8 +48,8 @@ Thank you for your PR! Before creating a PR, please check the following: - If there is an Issue which will be resolved by this PR, please include a reference to the Issue in the text. Good examples include `Closing: #21` or `Resolves: #21` - Check if there are any documents that need to be created or updated due to this change. - If you have added a feature or fixed a bug, please add a test case if possible. -- Please make sure that tests and Lint are passed in advance. - - You can run it with `pnpm run test` and `pnpm run lint`. [See more info](#testing) +- Please make sure that formatting, tests and Lint are passed in advance. + - You can run it with `pnpm run format`, `pnpm run test` and `pnpm run lint`. [See more info](#testing) - If this PR includes UI changes, please attach a screenshot in the text. Thanks for your cooperation 🤗 diff --git a/COPYING b/COPYING index fb483b5235..27aeb01ac9 100644 --- a/COPYING +++ b/COPYING @@ -1,15 +1,24 @@ -Unless otherwise stated this repository is -Copyright © 2014-2022 syuilo and contributers -Copyright © 2022 thatonecalculator and contributers +Unless specified otherwise, the entirety of this repository is subject to the following: +Copyright © 2014-2023 syuilo and contributors +Copyright © 2022-2023 Kainoa Kanter and contributors And is distributed under The GNU Affero General Public License Version 3, you should have received a copy of the license file as LICENSE. +--- -Calckey includes several third-party Open-Source softwares. +These specific configuration directories: -Emoji keywords for Unicode 11 and below by Mu-An Chiou -License: MIT -https://github.com/muan/emojilib/blob/master/LICENSE +- .config/ +- custom/assets/ + +and their contents are +Copyright © 2022-2023 Kainoa Kanter and contributors + +And are distributed under The Apache License, Version 2.0, you should have received a copy of the license file as LICENSE in each specified directory. + +--- + +Firefish includes several third-party open-source softwares and software libraries. RsaSignature2017 implementation by Transmute Industries Inc License: MIT @@ -18,3 +27,7 @@ https://github.com/transmute-industries/RsaSignature2017/blob/master/LICENSE Machine learning model for sensitive images by Infinite Red, Inc. License: MIT https://github.com/infinitered/nsfwjs/blob/master/LICENSE + +Licenses for all softwares and software libraries installed via the Node Package Manager ("npm") can be found by running the following shell command in the root directory of this repository: + +pnpm licenses list diff --git a/Dockerfile b/Dockerfile index 03ede16c71..ec07bb2106 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,51 +1,73 @@ ## Install dev and compilation dependencies, build files -FROM node:19-alpine as build -WORKDIR /calckey +FROM alpine:3.18 as build +WORKDIR /firefish # Install compilation dependencies -RUN apk add --no-cache --no-progress git alpine-sdk python3 +RUN apk add --no-cache --no-progress git alpine-sdk python3 nodejs-current npm rust cargo vips + +# Copy only the cargo dependency-related files first, to cache efficiently +COPY packages/backend/native-utils/Cargo.toml packages/backend/native-utils/Cargo.toml +COPY packages/backend/native-utils/Cargo.lock packages/backend/native-utils/Cargo.lock +COPY packages/backend/native-utils/src/lib.rs packages/backend/native-utils/src/ +COPY packages/backend/native-utils/migration/Cargo.toml packages/backend/native-utils/migration/Cargo.toml +COPY packages/backend/native-utils/migration/src/lib.rs packages/backend/native-utils/migration/src/ + +# Install cargo dependencies +RUN cargo fetch --locked --manifest-path /firefish/packages/backend/native-utils/Cargo.toml # Copy only the dependency-related files first, to cache efficiently COPY package.json pnpm*.yaml ./ COPY packages/backend/package.json packages/backend/package.json COPY packages/client/package.json packages/client/package.json COPY packages/sw/package.json packages/sw/package.json +COPY packages/firefish-js/package.json packages/firefish-js/package.json +COPY packages/megalodon/package.json packages/megalodon/package.json +COPY packages/backend/native-utils/package.json packages/backend/native-utils/package.json +COPY packages/backend/native-utils/npm/linux-x64-musl/package.json packages/backend/native-utils/npm/linux-x64-musl/package.json +COPY packages/backend/native-utils/npm/linux-arm64-musl/package.json packages/backend/native-utils/npm/linux-arm64-musl/package.json -# Configure corepack and pnpm -RUN corepack enable -RUN corepack prepare pnpm@latest --activate +# Configure corepack and pnpm, and install dev mode dependencies for compilation +RUN corepack enable && corepack prepare pnpm@latest --activate && pnpm i --frozen-lockfile -# Install dev mode dependencies for compilation -RUN pnpm i --frozen-lockfile +# Copy in the rest of the native-utils rust files +COPY packages/backend/native-utils packages/backend/native-utils/ -# Copy in the rest of the files, to compile from TS to JS +# Compile native-utils +RUN pnpm run --filter native-utils build + +# Copy in the rest of the files to compile COPY . ./ -RUN pnpm run build +RUN env NODE_ENV=production sh -c "pnpm run --filter '!native-utils' build && pnpm run gulp" -# Trim down the dependencies to only the prod deps +# Trim down the dependencies to only those for production RUN pnpm i --prod --frozen-lockfile - ## Runtime container -FROM node:19-alpine -WORKDIR /calckey +FROM alpine:3.18 +WORKDIR /firefish # Install runtime dependencies -RUN apk add --no-cache --no-progress tini ffmpeg +RUN apk add --no-cache --no-progress tini ffmpeg vips-dev zip unzip nodejs-current COPY . ./ +COPY --from=build /firefish/packages/megalodon /firefish/packages/megalodon + # Copy node modules -COPY --from=build /calckey/node_modules /calckey/node_modules -COPY --from=build /calckey/packages/backend/node_modules /calckey/packages/backend/node_modules -COPY --from=build /calckey/packages/sw/node_modules /calckey/packages/sw/node_modules -COPY --from=build /calckey/packages/client/node_modules /calckey/packages/client/node_modules +COPY --from=build /firefish/node_modules /firefish/node_modules +COPY --from=build /firefish/packages/backend/node_modules /firefish/packages/backend/node_modules +COPY --from=build /firefish/packages/sw/node_modules /firefish/packages/sw/node_modules +COPY --from=build /firefish/packages/client/node_modules /firefish/packages/client/node_modules +COPY --from=build /firefish/packages/firefish-js/node_modules /firefish/packages/firefish-js/node_modules # Copy the finished compiled files -COPY --from=build /calckey/built /calckey/built -COPY --from=build /calckey/packages/backend/built /calckey/packages/backend/built -COPY --from=build /calckey/packages/backend/assets/instance.css /calckey/packages/backend/assets/instance.css +COPY --from=build /firefish/built /firefish/built +COPY --from=build /firefish/packages/backend/built /firefish/packages/backend/built +COPY --from=build /firefish/packages/backend/assets/instance.css /firefish/packages/backend/assets/instance.css +COPY --from=build /firefish/packages/backend/native-utils/built /firefish/packages/backend/native-utils/built -RUN corepack enable +RUN corepack enable && corepack prepare pnpm@latest --activate +ENV NODE_ENV=production +VOLUME "/firefish/files" ENTRYPOINT [ "/sbin/tini", "--" ] CMD [ "pnpm", "run", "migrateandstart" ] diff --git a/CALCKEY.md b/FIREFISH.md similarity index 76% rename from CALCKEY.md rename to FIREFISH.md index 67d317f435..cd5f85ecbb 100644 --- a/CALCKEY.md +++ b/FIREFISH.md @@ -1,42 +1,29 @@ -# All the changes to Calckey from stock Misskey +# All the changes to Firefish from stock Misskey + +> **Warning** +> This list is incomplete. Please check the [Releases](https://gitlab.prometheus.systems/firefish/firefish/releases) and [Changelog](https://gitlab.prometheus.systems/firefish/firefish/src/branch/develop/CHANGELOG.md) for a more complete list of changes. There have been [>4000 commits (laggy link)](https://gitlab.prometheus.systems/firefish/firefish/compare/700a7110f7e34f314b070987aa761c451ec34efc...develop) since we forked Misskey! ## Planned - Stucture - - [Sonic](https://crates.io/crates/sonic-server) support as an ElasticSearch alternative - - [DragonflyDB](https://dragonflydb.io/) support as a Redis alternative - - Optionally use [ScyllaDB](https://www.scylladb.com/open-source-nosql-database/) for storing notes - - Rewrite backend in Rust and [Axum](https://github.com/tokio-rs/axum) + - Rewrite backend in Rust and [Rocket](https://rocket.rs/) - Function - - Federate with note edits - - Admin customizable max note length (100-8000) - - User "choices" (recommended users) like Mastodon and Soapbox + - User "choices" (recommended users) and featured hashtags like Mastodon and Soapbox - Join Reason system like Mastodon/Pleroma - - Option to publicize instance blocks - - Backfill remote users - - Build flag to remove NSFW/AI stuff - - Timeline filters - - Filter notifications by user - - Non-nyaify cat mode - - Exclude self from antenna -- Form - - MFM button - - Personal notes for all accounts - - Fully revamp non-logged-in screen - - Classic mode make instance icon bring up new context menu - - [Rat mode?](https://stop.voring.me/notes/933fx97bmd) + - Option to publicize server blocks + - More antenna options + - Groups ## Work in progress -- Weblate project -- Customizable max note length -- Link verification - Better Messaging UI - Better API Documentation - Remote follow button -- Admin custom CSS -- Add back time machine (jump to date) - Improve accesibility +- Timeline filters +- Events +- Fully revamp non-logged-in screen +- Optionally use [ScyllaDB](https://www.scylladb.com/open-source-nosql-database/) for storing notes ## Implemented @@ -46,7 +33,7 @@ - Upgrade packages with security vunrabilities - Saner defaults - Fediverse account migration -- Recommended instances timeline +- Recommended servers timeline - OCR image captioning - Improve mobile UX - Swipe through pages on mobile @@ -74,10 +61,9 @@ - Better welcome screen (not logged in) - vue-plyr as video/audio player - Ability to turn off "Connection lost" message -- Raw instance info only for moderators +- Raw server info only for moderators - New spinner animation - Spinner instead of "Loading..." -- SearchX instead of Google - Always signToActivityPubGet - Spacing on group items - Quotes have solid border @@ -99,16 +85,43 @@ - Undo renote button inside original note - Custom locales - Obliteration of Ai-chan -- Switch to [Calckey.js](https://codeberg.org/calckey/calckey.js) +- Switch to [Firefish.js](https://gitlab.prometheus.systems/firefish/firefish.js) - Woozy mode 🥴 -- Improve blocking instances +- Improve blocking servers - Release notes - New post style - Admins set default reaction emoji - Allows custom emoji - Fix lint errors - Use Rome instead of ESLint -- Keyboard accesibility +- Mastodon API support +- More antenna options +- New dashboard +- Backfill follower counts +- Compile time compression +- Sonic search +- Popular color schemes, including Nord, Gruvbox, and Catppuccin +- Non-nyaify cat mode +- Post imports from other Firefish/Misskey/Mastodon/Pleroma/Akkoma servers +- Improve Classic mode +- Proper Helm/Kubernetes config +- Multiple boost visibilities +- Improve system emails +- Mod mail +- Focus trapping and button labels +- Meilisearch with filters +- Post editing +- Display remaining time on rate-limits +- Proper 2FA input dialog +- Let moderators see moderation nodes +- Non-mangled unicode emojis + - Skin tone selection support +- [DragonflyDB](https://dragonflydb.io/) support as a Redis alternative +- Link verification +- Importing posts from other Firefish/Misskey/Mastodon/Akkoma/Pleroma instances + +## Implemented (remote) + - MissV: [fix Misskey Forkbomb](https://code.vtopia.live/Vtopia/MissV/commit/40b23c070bd4adbb3188c73546c6c625138fb3c1) - [Make showing ads optional](https://github.com/misskey-dev/misskey/pull/8996) - [Tapping avatar in mobile opens account modal](https://github.com/misskey-dev/misskey/pull/9056) @@ -121,7 +134,7 @@ - 👍 also triggers generic like/favorite - [Add additional background for acrylic popups if backdrop-filter is unsupported](https://github.com/misskey-dev/misskey/pull/8671) - [Add parameters to MFM rotate](https://github.com/misskey-dev/misskey/pull/8549) -- Many changes from [Foundkey](https://akkoma.dev/FoundKeyGang/Foundkey) +- Many changes from [FoundKey](https://akkoma.dev/FoundKeyGang/FoundKey) - https://akkoma.dev/FoundKeyGang/FoundKey/commit/0ece67b04c3f0365057624c1068808276ccab981: refactor pages/auth.form.vue to composition API - https://akkoma.dev/FoundKeyGang/FoundKey/commit/4bc9610d8bf5af736b5e89e4782395705de45d7d: remove unnecessary joins - https://akkoma.dev/FoundKeyGang/FoundKey/commit/9ee609d70082f7a6dc119a5d83c0e7c5e1208676: enhance privacy of notes @@ -150,3 +163,4 @@ - https://akkoma.dev/FoundKeyGang/FoundKey/commit/ca90cedba0a0704b503c2778694230f5a7dfbace: server: reduce dead instance detection to 7 days - https://akkoma.dev/FoundKeyGang/FoundKey/commit/e9ab42c10afb4e27516c2d2b5e3e06630efe9edd: Alt text in image viewer - https://akkoma.dev/FoundKeyGang/FoundKey/commit/ed9d4023d41bba7c4ac53a1a3422246feed37de2: add argon2 support + - https://akkoma.dev/FoundKeyGang/FoundKey/commit/c414f24a2c123774246c7eca65edda4d3afaf8b3: feat: per-user renote muting diff --git a/README.md b/README.md index 68e2489b87..468bdf16a4 100644 --- a/README.md +++ b/README.md @@ -1,41 +1,50 @@ +# ️:warning: Repo has moved! + +https://gitlab.prometheus.systems/firefish/firefish/ +
- - Calckey logo + + Firefish logo -**🌎 **[Calckey](https://i.calckey.cloud/)** is an open source, decentralized social media platform that's free forever! 🚀** +**🌎 **[Firefish](https://joinfirefish.org/)** is an open source, decentralized social media platform that's free forever! 🚀** [![no github badge](https://nogithub.codeberg.page/badge.svg)](https://nogithub.codeberg.page/) -[![status badge](https://ci.codeberg.org/api/badges/calckey/calckey/status.svg)](https://ci.codeberg.org/calckey/calckey) + +[![opencollective badge](https://opencollective.com/firefish/tiers/badge.svg)](https://opencollective.com/Firefish) [![liberapay badge](https://img.shields.io/liberapay/receives/ThatOneCalculator?logo=liberapay)](https://liberapay.com/ThatOneCalculator) -[![translate-badge](https://hosted.weblate.org/widgets/calckey/-/svg-badge.svg)](https://hosted.weblate.org/engage/calckey/) -[![docker badge](https://img.shields.io/docker/pulls/thatonecalculator/calckey?logo=docker)](https://hub.docker.com/r/thatonecalculator/calckey) +[![translate-badge](https://hosted.weblate.org/widgets/firefish/-/svg-badge.svg)](https://hosted.weblate.org/engage/firefish/) + [![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-2.1-4baaaa.svg)](./CODE_OF_CONDUCT.md) -[![Codeberg badge](https://custom-icon-badges.demolab.com/badge/hosted%20on-codeberg-4793CC.svg?logo=codeberg&logoColor=white)](https://codeberg.org/calckey/calckey/)
-Calc (the Calckey mascot) smoking a fat dart + -# ✨ About Calckey +# ✨ About Firefish -- Calckey is based off of Misskey, a powerful microblogging server on ActivityPub with features such as emoji reactions, a customizable web UI, rich chatting, and much more! -- Calckey adds many quality of life changes and bug fixes for users and instance admins alike. -- Read **[this document](./CALCKEY.md)** all for current and future differences. +- Firefish is based off of Misskey, a powerful microblogging server on ActivityPub with features such as emoji reactions, a customizable web UI, rich chatting, and much more! +- Firefish adds many quality of life changes and bug fixes for users and server admins alike. +- Read **[this document](./FIREFISH.md)** all for current and future differences. - Notable differences: - Improved UI/UX (especially on mobile) + - Post editing + - Content importing - Improved notifications - - Fediverse account migration - - Improved instance security + - Improved server security - Improved accessibility - - Recommended Instances timeline + - Improved threads + - Recommended Servers timeline - OCR image captioning - New and improved Groups - Better intro tutorial + - Compatibility with Mastodon clients/apps + - Backfill user information + - Advanced search - Many more user and admin settings - - [So much more!](./CALCKEY.md) + - [So much more!](./FIREFISH.md)
@@ -43,48 +52,78 @@ # 🥂 Links -- 💸 Liberapay: +### Want to get involved? Great! + +- If you have the means to, [donations](https://opencollective.com/Firefish) are a great way to keep us going. +- If you know how to program in TypeScript, Vue, or Rust, read the [contributing](./CONTRIBUTING.md) document. +- If you know a non-English language, translating Firefish on [Weblate](https://hosted.weblate.org/engage/firefish/) help bring Firefish to more people. No technical experience needed! +- Want to write/report about us, have any professional inquiries, or just have questions to ask? Contact us [here!](https://joinfirefish.org/contact/) + +### All links + +- 🌐 Homepage: +- 💸 Donations: + - OpenCollective: + - Liberapay: - Donate publicly to get your name on the Patron list! -- 🚢 Flagship instance: -- 📣 Official account: -- 💁 Matrix support room: -- 📜 Instance list: -- 📖 JoinFediverse Wiki: -- 🐋 Docker Hub: -- ✍️ Weblate: -- 📦 Yunohost: +- 🚢 Flagship server: +- 💁 Matrix support room: +- 📣 Official account: +- 📜 Server list: +- ✍️ Weblate: +- ️️📬 Contact: # 🌠 Getting started +Want to just join a Firefish server? View the list here, pick one, and join: + +### https://joinfirefish.org/join + +--- + +Want to make your own? Keep reading! + This guide will work for both **starting from scratch** and **migrating from Misskey**. ## 🔰 Easy installers If you have access to a server that supports one of the sources below, I recommend you use it! Note that these methods *won't* allow you to migrate from Misskey without manual intervention. -[![Install on Ubuntu](https://pool.jortage.com/voringme/misskey/3b62a443-1b44-45cf-8f9e-f1c588f803ed.png)](https://codeberg.org/calckey/ubuntu-bash-install)  [![Install on the Arch User Repository](https://pool.jortage.com/voringme/misskey/ba2a5c07-f078-43f1-8483-2e01acca9c40.png)](https://aur.archlinux.org/packages/calckey)  [![Install Calckey with YunoHost](https://install-app.yunohost.org/install-with-yunohost.svg)](https://install-app.yunohost.org/?app=calckey) + -### 🐋 Docker +[![Install on Ubuntu](https://pool.jortage.com/voringme/misskey/3b62a443-1b44-45cf-8f9e-f1c588f803ed.png)](https://gitlab.prometheus.systems/firefish/ubuntu-bash-install)  [![Install Firefish with YunoHost](https://install-app.yunohost.org/install-with-yunohost.svg)](https://install-app.yunohost.org/?app=firefish) -[How to run Calckey with Docker](https://codeberg.org/calckey/calckey/src/branch/develop/docs/docker.md). +## 🛳️ Containerization + +- [🐳 How to run Firefish with Docker](https://gitlab.prometheus.systems/firefish/firefish/src/branch/develop/docs/docker.md) +- [🛞 How to run Firefish with Kubernetes/Helm](https://gitlab.prometheus.systems/firefish/firefish/src/branch/develop/docs/kubernetes.md) ## 🧑‍💻 Dependencies -- 🐢 At least [NodeJS](https://nodejs.org/en/) v18.12.1 (v19 recommended) - - Install with [nvm](https://github.com/nvm-sh/nvm) -- 🐘 At least [PostgreSQL](https://www.postgresql.org/) v12 -- 🍱 At least [Redis](https://redis.io/) v6 (v7 recommend) +- 🐢 At least [NodeJS](https://nodejs.org/en/) v18.16.0 (v20 recommended) +- 🐘 At least [PostgreSQL](https://www.postgresql.org/) v12 (v14 recommended) +- 🍱 At least [Redis](https://redis.io/) v6 (v7 recommended) - Web Proxy (one of the following) - 🍀 Nginx (recommended) + - 🦦 Caddy - 🪶 Apache +- ⚡ [libvips](https://www.libvips.org/) ### 😗 Optional dependencies - [FFmpeg](https://ffmpeg.org/) for video transcoding -- [ElasticSearch](https://www.elastic.co/elasticsearch/) for full-text search +- Full text search (one of the following) + - 🦔 [Sonic](https://crates.io/crates/sonic-server) + - [MeiliSearch](https://www.meilisearch.com/) + - [ElasticSearch](https://www.elastic.co/elasticsearch/) +- Caching server (one of the following) + - 🐲 [DragonflyDB](https://www.dragonflydb.io/) (recommended) + - 👻 [KeyDB](https://keydb.dev/) + - 🍱 Another [Redis](https://redis.io/) server ### 🏗️ Build dependencies +- 🦀 At least [Rust](https://www.rust-lang.org/) v1.68.0 - 🦬 C/C++ compiler & build tools - `build-essential` on Debian/Ubuntu Linux - `base-devel` on Arch Linux @@ -93,11 +132,12 @@ If you have access to a server that supports one of the sources below, I recomme ## 👀 Get folder ready ```sh -git clone --depth 1 https://codeberg.org/calckey/calckey.git -cd calckey/ +git clone https://gitlab.prometheus.systems/firefish/firefish.git +cd firefish/ ``` -By default, you're on the development branch. Run `git checkout beta` or `git checkout main` to switch to the Beta/Main branches. +> **Note** +> By default, you're on the develop branch. Run `git checkout main` or `git checkout beta` to switch to the Main/Beta branches. ## 📩 Install dependencies @@ -109,68 +149,127 @@ corepack prepare pnpm@latest --activate pnpm i # --no-optional ``` +### pm2 + +To install pm2 run: + +``` +npm i -g pm2 +pm2 install pm2-logrotate +``` + +> **Note** +> [`pm2-logrotate`](https://github.com/keymetrics/pm2-logrotate/blob/master/README.md) ensures that log files don't infinitely gather size, as Firefish produces a lot of logs. + ## 🐘 Create database -Assuming you set up PostgreSQL correctly, all you have to run is: +In PostgreSQL (`psql`), run the following command: + +```sql +CREATE DATABASE firefish WITH encoding = 'UTF8'; +``` + +or run the following from the command line: ```sh -psql postgres -c "create database calckey with encoding = 'UTF8';" +psql postgres -c "create database firefish with encoding = 'UTF8';" ``` +In Firefish's directory, fill out the `db` section of `.config/default.yml` with the correct information, where the `db` key is `firefish`. + +## 💰 Caching server + +If you experience a lot of traffic, it's a good idea to set up another Redis-compatible caching server. If you don't set one one up, it'll fall back to the mandatory Redis server. DragonflyDB is the recommended option due to its unrivaled performance and ease of use. + +## 🔎 Set up search + +### 🦔 Sonic + +Sonic is better suited for self hosters with smaller deployments. It uses almost no resources, barely any any disk space, and is relatively fast. + +Follow sonic's [installation guide](https://github.com/valeriansaliou/sonic#installation) + +> **Note** +> If you use IPv4: in Sonic's directory, edit the `config.cfg` file to change `inet` to `"0.0.0.0:1491"`. + +In Firefish's directory, fill out the `sonic` section of `.config/default.yml` with the correct information. + +### Meilisearch + +Meilisearch is better suited for larger deployments. It's faster but uses far more resources and disk space. + +Follow Meilisearch's [quick start guide](https://www.meilisearch.com/docs/learn/getting_started/quick_start) + +In Firefish's directory, fill out the `meilisearch` section of `.config/default.yml` with the correct information. + +### ElasticSearch + +Please don't use ElasticSearch unless you already have an ElasticSearch setup and want to continue using it for Firefish. ElasticSearch is slow, heavy, and offers very few benefits over Sonic/Meilisearch. + ## 💅 Customize - To add custom CSS for all users, edit `./custom/assets/instance.css`. -- To add static assets (such as images for the splash screen), place them in the `./custom/assets/` directory. They'll then be available on `https://yourinstance.tld/static-assets/filename.ext`. +- To add static assets (such as images for the splash screen), place them in the `./custom/assets/` directory. They'll then be available on `https://yourserver.tld/static-assets/filename.ext`. - To add custom locales, place them in the `./custom/locales/` directory. If you name your custom locale the same as an existing locale, it will overwrite it. If you give it a unique name, it will be added to the list. Also make sure that the first part of the filename matches the locale you're basing it on. (Example: `en-FOO.yml`) - To add custom error images, place them in the `./custom/assets/badges` directory, replacing the files already there. - To add custom sounds, place only mp3 files in the `./custom/assets/sounds` directory. - To update custom assets without rebuilding, just run `pnpm run gulp`. -## 🧑‍🔬 Configuring a new instance +## 🧑‍🔬 Configuring a new server - Run `cp .config/example.yml .config/default.yml` - Edit `.config/default.yml`, making sure to fill out required fields. - Also copy and edit `.config/docker_example.env` to `.config/docker.env` if you're using Docker. -## 🚚 Migrating from Misskey to Calckey +## 🚚 Migrating from Misskey/FoundKey to Firefish -For migrating from Misskey v13, Misskey v12, and Foundkey, read [this document](https://codeberg.org/calckey/calckey/src/branch/develop/docs/migrate.md). +For migrating from Misskey v13, Misskey v12, and FoundKey, read [this document](https://gitlab.prometheus.systems/firefish/firefish/src/branch/develop/docs/migrate.md). ## 🌐 Web proxy ### 🍀 Nginx (recommended) -- Run `sudo cp ./calckey.nginx.conf /etc/nginx/sites-available/ && cd /etc/nginx/sites-available/` -- Edit `calckey.nginx.conf` to reflect your instance properly -- Run `sudo ln -s ./calckey.nginx.conf ../sites-enabled/calckey.nginx.conf` +- Run `sudo cp ./firefish.nginx.conf /etc/nginx/sites-available/ && cd /etc/nginx/sites-available/` +- Edit `firefish.nginx.conf` to reflect your server properly +- Run `sudo ln -s ./firefish.nginx.conf ../sites-enabled/firefish.nginx.conf` - Run `sudo nginx -t` to validate that the config is valid, then restart the NGINX service. +### 🦦 Caddy + +- Add the following block to your `Caddyfile`, replacing `example.tld` with your own domain: +```caddy +example.tld { + reverse_proxy http://127.0.0.1:3000 +} +``` +- Reload your caddy configuration + ### 🪶 Apache -- Run `sudo cp ./calckey.apache.conf /etc/apache2/sites-available/ && cd /etc/apache2/sites-available/` -- Edit `calckey.apache.conf` to reflect your instance properly -- Run `sudo a2ensite calckey.apache` to enable the site +> **Warning** +> Apache has some known problems with Firefish. Only use it if you have to. + +- Run `sudo cp ./firefish.apache.conf /etc/apache2/sites-available/ && cd /etc/apache2/sites-available/` +- Edit `firefish.apache.conf` to reflect your server properly +- Run `sudo a2ensite firefish.apache` to enable the site - Run `sudo service apache2 restart` to reload apache2 configuration - - - ## 🚀 Build and launch! ### 🐢 NodeJS + pm2 -#### `git pull` and run these steps to update Calckey in the future! +#### `git pull` and run these steps to update Firefish in the future! ```sh # git pull pnpm install NODE_ENV=production pnpm run build && pnpm run migrate -pm2 start "NODE_ENV=production pnpm run start" --name Calckey +pm2 start "NODE_ENV=production pnpm run start" --name Firefish ``` ## 😉 Tips & Tricks -- When editing the config file, please don't fill out the settings at the bottom. They're designed *only* for managed hosting, not self hosting. Those settings are much better off being set in Calckey's control panel. -- Port 3000 (used in the default config) might be already used on your server for something else. To find an open port for Calckey, run `for p in {3000..4000}; do ss -tlnH | tr -s ' ' | cut -d" " -sf4 | grep -q "${p}$" || echo "${p}"; done | head -n 1`. Replace 3000 with the minimum port and 4000 with the maximum port if you need it. +- When editing the config file, please don't fill out the settings at the bottom. They're designed *only* for managed hosting, not self hosting. Those settings are much better off being set in Firefish's control panel. +- Port 3000 (used in the default config) might be already used on your server for something else. To find an open port for Firefish, run `for p in {3000..4000}; do ss -tlnH | tr -s ' ' | cut -d" " -sf4 | grep -q "${p}$" || echo "${p}"; done | head -n 1`. Replace 3000 with the minimum port and 4000 with the maximum port if you need it. - I'd recommend you use a S3 Bucket/CDN for Object Storage, especially if you use Docker. - I'd ***strongly*** recommend against using CloudFlare, but if you do, make sure to turn code minification off. - For push notifications, run `npx web-push generate-vapid-keys`, then put the public and private keys into Control Panel > General > ServiceWorker. @@ -178,6 +277,6 @@ pm2 start "NODE_ENV=production pnpm run start" --name Calckey - To add another admin account: - Go to the user's page > 3 Dots > About > Moderation > turn on "Moderator" - Go back to Overview > click the clipboard icon next to the ID - - Run `psql -d calckey` (or whatever the database name is) + - Run `psql -d firefish` (or whatever the database name is) - Run `UPDATE "user" SET "isAdmin" = true WHERE id='999999';` (replace `999999` with the copied ID) - - Have the new admin log out and log back in + - Restart your Firefish server diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md new file mode 100644 index 0000000000..0bad991a36 --- /dev/null +++ b/RELEASE_NOTES.md @@ -0,0 +1,119 @@ +# Firefish + +Welcome to the new era of FIREFISH! + + + +# Changelog + +## Major changes from last release candidate + +- Firefish branding and [new repo](https://gitlab.prometheus.systems/firefish/firefish)! +- Far better Mastodon API support +- Edits are now non-experimental +- Support for secondary cache server +- Link verification with `rel=me` +- Store antennas in cache +- Post imports with media +- Sytle fixes +- More translations +- Performance upgrades +- Bug fixes +- Faster build +- [FoundKey](https://genau.qwertqwefsday.eu/notes/9h0lqlg05m) -> Firefish migration fixes + +## Major changes from stable + +All of the above, plus: + +- Post editing +- Post imports +- New post design +- New header design +- Better accessibility +- Server silences +- Modmail +- New MFM effects +- Meilisearch search engine +- Channel search +- Improved system emails +- cuid2 IDs +- Emoji skin tones +- New 2FA flow +- Reduced visual clutter +- Deck view improvements + +# Upgrading + +## If upgrading from v13 (old stable) + +**In addition to the rest of the steps after this**: + +- Install the Rust toolchain (v1.68.0 or higher): + +- (Optional) install Meilisearch to use as a search engine instead of Sonic: + +- Replace your config file (`.config/default.yml`) with a blank version of the example (`.config/example.yml`) and re-enter the information. This will make things easier. + +## Dependencies + +- Upgrade to at least Node v20.3.1 (v20.4.0 recommended). + +- (Optional, recommended) install DragonflyDB and configure under `cacheServer`: + +## Set new repo and pull + +```sh +git remote set-url origin https://gitlab.prometheus.systems/firefish/firefish.git +git pull --ff +``` + +In case you get an error like: +``` +error: The following untracked working tree files would be overwritten by merge: + packages/backend/assets/LICENSE +Please move or remove them before you merge. +Aborting +``` + +Run: +```sh +rm ./packages/backend/assets/LICENSE +git pull --ff +``` + +## Upgrade packages + +```sh +corepack enable +pnpm i +``` + +## Build + +```sh +NODE_ENV=production pnpm run buld +``` + +## Migrate + +There are 3 new envoriment variables for this upgrade only, because antennas have been moved from the database to the cache. + +- `ANTENNA_MIGRATION_SKIP`: skips copying antennas to cache if `true`. Default is `false` (will clear all antennas if skipped). +- `ANTENNA_MIGRATION_COPY_LIMIT`: limits how many entries are copied to cache. Default is `0` (no limit). +- `ANTENNA_MIGRATION_READ_LIMIT`: limits how many entires are read from the database +in each iteration of migration. Large value may result in faster migration, but also may consume more memory. Default is `10000`. + +With default options: + +```sh +NODE_ENV=production pnpm run migrate +``` + +With custom options (feel free to only use some): + +```sh +NODE_ENV=production ANTENNA_MIGRATION_SKIP=false ANTENNA_MIGRATION_COPY_LIMIT=0 ANTENNA_MIGRATION_READ_LIMIT=1000 pnpm run migrate +``` + +And then restart Calckey...uh... Firefish! diff --git a/SECURITY.md b/SECURITY.md index 848fa7cb7c..fae9536b83 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -2,7 +2,7 @@ ## Minor Security Issues -If you discover a minor security issue in Calckey, please report it by sending an +If you discover a minor security issue in Firefish, please report it by sending an email to [kainoa@t1c.dev](mailto:kainoa@t1c.dev). ## High Security Issues @@ -11,6 +11,6 @@ If you discover a security issue, which is so high risk, that too much is affect This will allow us to assess the risk, and make a fix available before we add a -bug report to the Codeberg repository. +bug report to the Gitlab repository. -Thanks for helping make Calckey safe for everyone. +Thanks for helping make Firefish safe for everyone. diff --git a/animated.svg b/animated.svg new file mode 100644 index 0000000000..7b36f4d77e --- /dev/null +++ b/animated.svg @@ -0,0 +1,153 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/chart/Chart.yaml b/chart/Chart.yaml index dfd476dadc..8b58323865 100644 --- a/chart/Chart.yaml +++ b/chart/Chart.yaml @@ -1,6 +1,6 @@ apiVersion: v2 -name: calckey -description: A fun, new, open way to experience social media https://calckey.org +name: firefish +description: A fun, new, open way to experience social media https://joinfirefish.org # A chart can be either an 'application' or a 'library' chart. # @@ -15,7 +15,7 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.1.0 +version: 0.1.2 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to diff --git a/chart/README.md b/chart/README.md index a04b3d29a8..0833eb9a83 100644 --- a/chart/README.md +++ b/chart/README.md @@ -1,8 +1,8 @@ -# calckey +# firefish -![Version: 0.1.0](https://img.shields.io/badge/Version-0.1.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: rc](https://img.shields.io/badge/AppVersion-rc-informational?style=flat-square) +![Version: 0.1.2](https://img.shields.io/badge/Version-0.1.2-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: rc](https://img.shields.io/badge/AppVersion-rc-informational?style=flat-square) -A fun, new, open way to experience social media https://calckey.org +A fun, new, open way to experience social media https://joinfirefish.org ## Requirements @@ -21,33 +21,39 @@ A fun, new, open way to experience social media https://calckey.org | autoscaling.maxReplicas | int | `100` | | | autoscaling.minReplicas | int | `1` | | | autoscaling.targetCPUUtilizationPercentage | int | `80` | | -| calckey.allowedPrivateNetworks | list | `[]` | If you want to allow calckey to connect to private ips, enter the cidrs here. | -| calckey.domain | string | `"calckey.local"` | | -| calckey.isManagedHosting | bool | `true` | | -| calckey.objectStorage.access_key | string | `""` | | -| calckey.objectStorage.access_secret | string | `""` | | -| calckey.objectStorage.baseUrl | string | `""` | | -| calckey.objectStorage.bucket | string | `""` | | -| calckey.objectStorage.endpoint | string | `""` | | -| calckey.objectStorage.managed | bool | `true` | | -| calckey.objectStorage.prefix | string | `"files"` | | -| calckey.objectStorage.region | string | `""` | | -| calckey.reservedUsernames[0] | string | `"root"` | | -| calckey.reservedUsernames[1] | string | `"admin"` | | -| calckey.reservedUsernames[2] | string | `"administrator"` | | -| calckey.reservedUsernames[3] | string | `"me"` | | -| calckey.reservedUsernames[4] | string | `"system"` | | -| calckey.smtp.from_address | string | `"notifications@example.com"` | | -| calckey.smtp.login | string | `""` | | -| calckey.smtp.managed | bool | `true` | | -| calckey.smtp.password | string | `""` | | -| calckey.smtp.port | int | `587` | | -| calckey.smtp.server | string | `"smtp.mailgun.org"` | | -| calckey.smtp.useImplicitSslTls | bool | `false` | | -| elasticsearch | object | `{"auth":null,"enabled":false,"hostname":"","port":9200,"ssl":false}` | https://github.com/bitnami/charts/tree/master/bitnami/elasticsearch#parameters | +| firefish.allowedPrivateNetworks | list | `[]` | If you want to allow firefish to connect to private ips, enter the cidrs here. | +| firefish.deepl.authKey | string | `""` | | +| firefish.deepl.isPro | bool | `false` | | +| firefish.deepl.managed | bool | `false` | | +| firefish.domain | string | `"firefish.local"` | | +| firefish.isManagedHosting | bool | `true` | | +| firefish.libreTranslate.apiKey | string | `""` | | +| firefish.libreTranslate.apiUrl | string | `""` | | +| firefish.libreTranslate.managed | bool | `false` | | +| firefish.objectStorage.access_key | string | `""` | | +| firefish.objectStorage.access_secret | string | `""` | | +| firefish.objectStorage.baseUrl | string | `""` | | +| firefish.objectStorage.bucket | string | `""` | | +| firefish.objectStorage.endpoint | string | `""` | | +| firefish.objectStorage.managed | bool | `true` | | +| firefish.objectStorage.prefix | string | `"files"` | | +| firefish.objectStorage.region | string | `""` | | +| firefish.reservedUsernames[0] | string | `"root"` | | +| firefish.reservedUsernames[1] | string | `"admin"` | | +| firefish.reservedUsernames[2] | string | `"administrator"` | | +| firefish.reservedUsernames[3] | string | `"me"` | | +| firefish.reservedUsernames[4] | string | `"system"` | | +| firefish.smtp.from_address | string | `"notifications@example.com"` | | +| firefish.smtp.login | string | `""` | | +| firefish.smtp.managed | bool | `true` | | +| firefish.smtp.password | string | `""` | | +| firefish.smtp.port | int | `587` | | +| firefish.smtp.server | string | `"smtp.mailgun.org"` | | +| firefish.smtp.useImplicitSslTls | bool | `false` | | +| elasticsearch | object | `{"auth":{},"enabled":false,"hostname":"","port":9200,"ssl":false}` | https://github.com/bitnami/charts/tree/master/bitnami/elasticsearch#parameters | | fullnameOverride | string | `""` | | | image.pullPolicy | string | `"IfNotPresent"` | | -| image.repository | string | `"docker.io/thatonecalculator/calckey"` | | +| image.repository | string | `"docker.io/thatonecalculator/firefish"` | | | image.tag | string | `""` | | | imagePullSecrets | list | `[]` | | | ingress.annotations | object | `{}` | | @@ -61,9 +67,9 @@ A fun, new, open way to experience social media https://calckey.org | nodeSelector | object | `{}` | | | podAnnotations | object | `{}` | | | podSecurityContext | object | `{}` | | -| postgresql.auth.database | string | `"calckey_production"` | | +| postgresql.auth.database | string | `"firefish_production"` | | | postgresql.auth.password | string | `""` | | -| postgresql.auth.username | string | `"calckey"` | | +| postgresql.auth.username | string | `"firefish"` | | | postgresql.enabled | bool | `true` | disable if you want to use an existing db; in which case the values below must match those of that external postgres instance | | redis.auth.password | string | `""` | you must set a password; the password generated by the redis chart will be rotated on each upgrade: | | redis.enabled | bool | `true` | | diff --git a/chart/templates/NOTES.txt b/chart/templates/NOTES.txt index d3e4f2f208..e84ee93cf4 100644 --- a/chart/templates/NOTES.txt +++ b/chart/templates/NOTES.txt @@ -6,16 +6,16 @@ {{- end }} {{- end }} {{- else if contains "NodePort" .Values.service.type }} - export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "calckey.fullname" . }}) + export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "firefish.fullname" . }}) export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") echo http://$NODE_IP:$NODE_PORT {{- else if contains "LoadBalancer" .Values.service.type }} NOTE: It may take a few minutes for the LoadBalancer IP to be available. - You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "calckey.fullname" . }}' - export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "calckey.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}") + You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "firefish.fullname" . }}' + export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "firefish.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}") echo http://$SERVICE_IP:{{ .Values.service.port }} {{- else if contains "ClusterIP" .Values.service.type }} - export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "calckey.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") + export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "firefish.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}") echo "Visit http://127.0.0.1:8080 to use your application" kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT diff --git a/chart/templates/_helpers.tpl b/chart/templates/_helpers.tpl index 00702ec34d..a7b4b9d328 100644 --- a/chart/templates/_helpers.tpl +++ b/chart/templates/_helpers.tpl @@ -1,7 +1,7 @@ {{/* Expand the name of the chart. */}} -{{- define "calckey.name" -}} +{{- define "firefish.name" -}} {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} {{- end }} @@ -10,7 +10,7 @@ Create a default fully qualified app name. We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). If release name contains chart name it will be used as a full name. */}} -{{- define "calckey.fullname" -}} +{{- define "firefish.fullname" -}} {{- if .Values.fullnameOverride }} {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} {{- else }} @@ -26,16 +26,16 @@ If release name contains chart name it will be used as a full name. {{/* Create chart name and version as used by the chart label. */}} -{{- define "calckey.chart" -}} +{{- define "firefish.chart" -}} {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} {{- end }} {{/* Common labels */}} -{{- define "calckey.labels" -}} -helm.sh/chart: {{ include "calckey.chart" . }} -{{ include "calckey.selectorLabels" . }} +{{- define "firefish.labels" -}} +helm.sh/chart: {{ include "firefish.chart" . }} +{{ include "firefish.selectorLabels" . }} {{- if .Chart.AppVersion }} app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} {{- end }} @@ -45,17 +45,17 @@ app.kubernetes.io/managed-by: {{ .Release.Service }} {{/* Selector labels */}} -{{- define "calckey.selectorLabels" -}} -app.kubernetes.io/name: {{ include "calckey.name" . }} +{{- define "firefish.selectorLabels" -}} +app.kubernetes.io/name: {{ include "firefish.name" . }} app.kubernetes.io/instance: {{ .Release.Name }} {{- end }} {{/* Create the name of the service account to use */}} -{{- define "calckey.serviceAccountName" -}} +{{- define "firefish.serviceAccountName" -}} {{- if .Values.serviceAccount.create }} -{{- default (include "calckey.fullname" .) .Values.serviceAccount.name }} +{{- default (include "firefish.fullname" .) .Values.serviceAccount.name }} {{- else }} {{- default "default" .Values.serviceAccount.name }} {{- end }} @@ -65,31 +65,31 @@ Create the name of the service account to use Create a default fully qualified name for dependent services. We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). */}} -{{- define "calckey.elasticsearch.fullname" -}} +{{- define "firefish.elasticsearch.fullname" -}} {{- printf "%s-%s" .Release.Name "elasticsearch" | trunc 63 | trimSuffix "-" -}} {{- end -}} -{{- define "calckey.redis.fullname" -}} +{{- define "firefish.redis.fullname" -}} {{- printf "%s-%s" .Release.Name "redis" | trunc 63 | trimSuffix "-" -}} {{- end -}} -{{- define "calckey.postgresql.fullname" -}} +{{- define "firefish.postgresql.fullname" -}} {{- printf "%s-%s" .Release.Name "postgresql" | trunc 63 | trimSuffix "-" -}} {{- end -}} {{/* config/default.yml content */}} -{{- define "calckey.configDir.default.yml" -}} +{{- define "firefish.configDir.default.yml" -}} #━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -# Calckey configuration +# Firefish configuration #━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ # ┌─────┐ #───┘ URL └───────────────────────────────────────────────────── # Final accessible URL seen by a user. -url: "https://{{ .Values.calckey.domain }}/" +url: "https://{{ .Values.firefish.domain }}/" # ONCE YOU HAVE STARTED THE INSTANCE, DO NOT CHANGE THE # URL SETTINGS AFTER THAT! @@ -118,7 +118,7 @@ port: 3000 db: {{- if .Values.postgresql.enabled }} - host: {{ template "calckey.postgresql.fullname" . }} + host: {{ template "firefish.postgresql.fullname" . }} port: '5432' {{- else }} host: {{ .Values.postgresql.postgresqlHostname }} @@ -137,14 +137,16 @@ db: # Extra Connection options #extra: - # ssl: true + # ssl: + # host: localhost + # rejectUnauthorized: false # ┌─────────────────────┐ #───┘ Redis configuration └───────────────────────────────────── redis: {{- if .Values.redis.enabled }} - host: {{ template "calckey.redis.fullname" . }}-master + host: {{ template "firefish.redis.fullname" . }}-master {{- else }} host: {{ required "When the redis chart is disabled .Values.redis.hostname is required" .Values.redis.hostname }} {{- end }} @@ -153,6 +155,10 @@ redis: pass: {{ .Values.redis.auth.password | quote }} #prefix: example-prefix #db: 1 + #user: default + #tls: + # host: localhost + # rejectUnauthorized: false # ┌─────────────────────┐ #───┘ Sonic configuration └───────────────────────────────────── @@ -212,7 +218,7 @@ id: 'aid' # Reserved usernames that only the administrator can register with reservedUsernames: -{{ .Values.calckey.reservedUsernames | toYaml }} +{{ .Values.firefish.reservedUsernames | toYaml }} # Whether disable HSTS #disableHsts: true @@ -260,7 +266,7 @@ reservedUsernames: #proxyRemoteFiles: true allowedPrivateNetworks: -{{ .Values.calckey.allowedPrivateNetworks | toYaml }} +{{ .Values.firefish.allowedPrivateNetworks | toYaml }} # TWA #twa: @@ -280,29 +286,34 @@ allowedPrivateNetworks: # If you mess this up, that's on you, you've been warned... #maxUserSignups: 100 -isManagedHosting: {{ .Values.calckey.isManagedHosting }} +isManagedHosting: {{ .Values.firefish.isManagedHosting }} deepl: - managed: false -# authKey: '' -# isPro: false -# + managed: {{ .Values.firefish.deepl.managed }} + authKey: {{ .Values.firefish.deepl.authKey | quote}} + isPro: {{ .Values.firefish.deepl.isPro }} + +libreTranslate: + managed: {{ .Values.firefish.libreTranslate.managed }} + apiUrl: {{ .Values.firefish.libreTranslate.apiUrl | quote }} + apiKey: {{ .Values.firefish.libreTranslate.apiKey | quote }} + email: - managed: {{ .Values.calckey.smtp.managed }} - address: {{ .Values.calckey.smtp.from_address | quote }} - host: {{ .Values.calckey.smtp.server | quote }} - port: {{ .Values.calckey.smtp.port }} - user: {{ .Values.calckey.smtp.login | quote }} - pass: {{ .Values.calckey.smtp.password | quote }} - useImplicitSslTls: {{ .Values.calckey.smtp.useImplicitSslTls }} + managed: {{ .Values.firefish.smtp.managed }} + address: {{ .Values.firefish.smtp.from_address | quote }} + host: {{ .Values.firefish.smtp.server | quote }} + port: {{ .Values.firefish.smtp.port }} + user: {{ .Values.firefish.smtp.login | quote }} + pass: {{ .Values.firefish.smtp.password | quote }} + useImplicitSslTls: {{ .Values.firefish.smtp.useImplicitSslTls }} objectStorage: - managed: {{ .Values.calckey.objectStorage.managed }} - baseUrl: {{ .Values.calckey.objectStorage.baseUrl | quote }} - bucket: {{ .Values.calckey.objectStorage.bucket | quote }} - prefix: {{ .Values.calckey.objectStorage.prefix | quote }} - endpoint: {{ .Values.calckey.objectStorage.endpoint | quote }} - region: {{ .Values.calckey.objectStorage.region | quote }} - accessKey: {{ .Values.calckey.objectStorage.access_key | quote }} - secretKey: {{ .Values.calckey.objectStorage.access_secret | quote }} + managed: {{ .Values.firefish.objectStorage.managed }} + baseUrl: {{ .Values.firefish.objectStorage.baseUrl | quote }} + bucket: {{ .Values.firefish.objectStorage.bucket | quote }} + prefix: {{ .Values.firefish.objectStorage.prefix | quote }} + endpoint: {{ .Values.firefish.objectStorage.endpoint | quote }} + region: {{ .Values.firefish.objectStorage.region | quote }} + accessKey: {{ .Values.firefish.objectStorage.access_key | quote }} + secretKey: {{ .Values.firefish.objectStorage.access_secret | quote }} useSsl: true connnectOverProxy: false setPublicReadOnUpload: true diff --git a/chart/templates/deployment.yaml b/chart/templates/deployment.yaml index 5bcf8851a4..ca63167632 100644 --- a/chart/templates/deployment.yaml +++ b/chart/templates/deployment.yaml @@ -1,16 +1,16 @@ apiVersion: apps/v1 kind: Deployment metadata: - name: {{ include "calckey.fullname" . }} + name: {{ include "firefish.fullname" . }} labels: - {{- include "calckey.labels" . | nindent 4 }} + {{- include "firefish.labels" . | nindent 4 }} spec: {{- if not .Values.autoscaling.enabled }} replicas: {{ .Values.replicaCount }} {{- end }} selector: matchLabels: - {{- include "calckey.selectorLabels" . | nindent 6 }} + {{- include "firefish.selectorLabels" . | nindent 6 }} template: metadata: annotations: @@ -19,31 +19,35 @@ spec: {{- toYaml . | nindent 8 }} {{- end }} labels: - {{- include "calckey.selectorLabels" . | nindent 8 }} + {{- include "firefish.selectorLabels" . | nindent 8 }} spec: {{- with .Values.imagePullSecrets }} imagePullSecrets: {{- toYaml . | nindent 8 }} {{- end }} - serviceAccountName: {{ include "calckey.serviceAccountName" . }} + serviceAccountName: {{ include "firefish.serviceAccountName" . }} securityContext: {{- toYaml .Values.podSecurityContext | nindent 8 }} volumes: - name: config-volume secret: - secretName: {{ template "calckey.fullname" . }}-config + secretName: {{ template "firefish.fullname" . }}-config containers: - name: {{ .Chart.Name }} securityContext: {{- toYaml .Values.securityContext | nindent 12 }} image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" imagePullPolicy: {{ .Values.image.pullPolicy }} + command: + - pnpm + - run + - start env: - name: "NODE_ENV" value: "production" volumeMounts: - name: config-volume - mountPath: /calckey/.config + mountPath: /firefish/.config ports: - name: http containerPort: 3000 diff --git a/chart/templates/hpa.yaml b/chart/templates/hpa.yaml index 4cdd2b6255..db19534a87 100644 --- a/chart/templates/hpa.yaml +++ b/chart/templates/hpa.yaml @@ -2,14 +2,14 @@ apiVersion: autoscaling/v2beta1 kind: HorizontalPodAutoscaler metadata: - name: {{ include "calckey.fullname" . }} + name: {{ include "firefish.fullname" . }} labels: - {{- include "calckey.labels" . | nindent 4 }} + {{- include "firefish.labels" . | nindent 4 }} spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment - name: {{ include "calckey.fullname" . }} + name: {{ include "firefish.fullname" . }} minReplicas: {{ .Values.autoscaling.minReplicas }} maxReplicas: {{ .Values.autoscaling.maxReplicas }} metrics: diff --git a/chart/templates/ingress.yaml b/chart/templates/ingress.yaml index 212c40e4b2..65caa43e89 100644 --- a/chart/templates/ingress.yaml +++ b/chart/templates/ingress.yaml @@ -1,5 +1,5 @@ {{- if .Values.ingress.enabled -}} -{{- $fullName := include "calckey.fullname" . -}} +{{- $fullName := include "firefish.fullname" . -}} {{- $svcPort := .Values.service.port -}} {{- if and .Values.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }} {{- if not (hasKey .Values.ingress.annotations "kubernetes.io/ingress.class") }} @@ -17,7 +17,7 @@ kind: Ingress metadata: name: {{ $fullName }} labels: - {{- include "calckey.labels" . | nindent 4 }} + {{- include "firefish.labels" . | nindent 4 }} {{- with .Values.ingress.annotations }} annotations: {{- toYaml . | nindent 4 }} diff --git a/chart/templates/job-db-migrate.yaml b/chart/templates/job-db-migrate.yaml new file mode 100644 index 0000000000..1aedeab5b1 --- /dev/null +++ b/chart/templates/job-db-migrate.yaml @@ -0,0 +1,59 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ include "firefish.fullname" . }}-db-migrate + labels: + {{- include "firefish.labels" . | nindent 4 }} + annotations: + "helm.sh/hook": post-install,pre-upgrade + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded + "helm.sh/hook-weight": "-2" +spec: + template: + metadata: + name: {{ include "firefish.fullname" . }}-db-migrate + {{- with .Values.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + restartPolicy: Never + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "firefish.serviceAccountName" . }} + securityContext: + {{- toYaml .Values.podSecurityContext | nindent 8 }} + volumes: + - name: config-volume + secret: + secretName: {{ template "firefish.fullname" . }}-config + containers: + - name: {{ .Chart.Name }} + securityContext: + {{- toYaml .Values.securityContext | nindent 12 }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + command: + - pnpm + - run + - migrate + env: + - name: "NODE_ENV" + value: "production" + volumeMounts: + - name: config-volume + mountPath: /firefish/.config + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} diff --git a/chart/templates/secret-config.yaml b/chart/templates/secret-config.yaml index 2dad134c56..9f683cfcd8 100644 --- a/chart/templates/secret-config.yaml +++ b/chart/templates/secret-config.yaml @@ -1,9 +1,9 @@ apiVersion: v1 kind: Secret metadata: - name: {{ template "calckey.fullname" . }}-config + name: {{ template "firefish.fullname" . }}-config labels: - {{- include "calckey.labels" . | nindent 4 }} + {{- include "firefish.labels" . | nindent 4 }} type: Opaque data: - default.yml: {{ include "calckey.configDir.default.yml" . | b64enc }} + default.yml: {{ include "firefish.configDir.default.yml" . | b64enc }} diff --git a/chart/templates/service.yaml b/chart/templates/service.yaml index d46067a406..f49d9cd5d7 100644 --- a/chart/templates/service.yaml +++ b/chart/templates/service.yaml @@ -1,9 +1,9 @@ apiVersion: v1 kind: Service metadata: - name: {{ include "calckey.fullname" . }} + name: {{ include "firefish.fullname" . }} labels: - {{- include "calckey.labels" . | nindent 4 }} + {{- include "firefish.labels" . | nindent 4 }} spec: type: {{ .Values.service.type }} ports: @@ -12,4 +12,4 @@ spec: protocol: TCP name: http selector: - {{- include "calckey.selectorLabels" . | nindent 4 }} + {{- include "firefish.selectorLabels" . | nindent 4 }} diff --git a/chart/templates/serviceaccount.yaml b/chart/templates/serviceaccount.yaml index f269ad028b..d8db3a3ae2 100644 --- a/chart/templates/serviceaccount.yaml +++ b/chart/templates/serviceaccount.yaml @@ -2,9 +2,9 @@ apiVersion: v1 kind: ServiceAccount metadata: - name: {{ include "calckey.serviceAccountName" . }} + name: {{ include "firefish.serviceAccountName" . }} labels: - {{- include "calckey.labels" . | nindent 4 }} + {{- include "firefish.labels" . | nindent 4 }} {{- with .Values.serviceAccount.annotations }} annotations: {{- toYaml . | nindent 4 }} diff --git a/chart/templates/tests/test-connection.yaml b/chart/templates/tests/test-connection.yaml index b8db3d9a17..7abe03611a 100644 --- a/chart/templates/tests/test-connection.yaml +++ b/chart/templates/tests/test-connection.yaml @@ -1,9 +1,9 @@ apiVersion: v1 kind: Pod metadata: - name: "{{ include "calckey.fullname" . }}-test-connection" + name: "{{ include "firefish.fullname" . }}-test-connection" labels: - {{- include "calckey.labels" . | nindent 4 }} + {{- include "firefish.labels" . | nindent 4 }} annotations: "helm.sh/hook": test spec: @@ -11,5 +11,5 @@ spec: - name: wget image: busybox command: ['wget'] - args: ['{{ include "calckey.fullname" . }}:{{ .Values.service.port }}'] + args: ['{{ include "firefish.fullname" . }}:{{ .Values.service.port }}'] restartPolicy: Never diff --git a/chart/values.yaml b/chart/values.yaml index 1f8f8c8f77..3bb050441a 100644 --- a/chart/values.yaml +++ b/chart/values.yaml @@ -1,18 +1,28 @@ -# Default values for calckey. +# Default values for firefish. # This is a YAML-formatted file. # Declare variables to be passed into your templates. replicaCount: 1 image: - repository: docker.io/thatonecalculator/calckey + repository: docker.io/thatonecalculator/firefish pullPolicy: IfNotPresent # Overrides the image tag whose default is the chart appVersion. tag: "" -calckey: +firefish: isManagedHosting: true - domain: calckey.local + domain: firefish.local + + deepl: + managed: false + authKey: "" + isPro: false + + libreTranslate: + managed: false + apiUrl: "" + apiKey: "" smtp: managed: true @@ -33,7 +43,7 @@ calckey: endpoint: "" # e.g. "nyc3.digitaloceanspaces.com:443" region: "" # e.g. "nyc3" - # -- If you want to allow calckey to connect to private ips, enter the cidrs here. + # -- If you want to allow firefish to connect to private ips, enter the cidrs here. allowedPrivateNetworks: [] # - "10.0.0.0/8" @@ -52,8 +62,8 @@ postgresql: # postgresqlHostname: preexisting-postgresql # postgresqlPort: 5432 auth: - database: calckey_production - username: calckey + database: firefish_production + username: firefish # you must set a password; the password generated by the postgresql chart will # be rotated on each upgrade: # https://github.com/bitnami/charts/tree/master/bitnami/postgresql#upgrade diff --git a/cliff.toml b/cliff.toml index 394b845774..986ce68f20 100644 --- a/cliff.toml +++ b/cliff.toml @@ -4,7 +4,7 @@ # changelog header header = """ # Changelog\n -All changes from v13.0.0 onwards, for a full list of differences read CALCKEY.md\n +All changes from v13.0.0 onwards, for a list of differences read FIREFISH.md\n """ # template for the changelog body # https://tera.netlify.app/docs/#introduction @@ -35,10 +35,6 @@ conventional_commits = false filter_unconventional = true # process each line of a commit as an individual commit split_commits = false -# regex for preprocessing the commit messages -commit_preprocessors = [ - { pattern = '\((\w+\s)?#([0-9]+)\)', replace = "([#${2}](https://github.com/orhun/git-cliff/issues/${2}))"}, -] # regex for parsing and grouping commits commit_parsers = [ { message = "^feat", group = "Features"}, diff --git a/custom/assets/LICENSE b/custom/assets/LICENSE new file mode 100644 index 0000000000..cb57aef954 --- /dev/null +++ b/custom/assets/LICENSE @@ -0,0 +1,13 @@ +Copyright 2023 Firefish + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/custom/assets/badges/error.png b/custom/assets/badges/error.png new file mode 100644 index 0000000000..e90912b405 Binary files /dev/null and b/custom/assets/badges/error.png differ diff --git a/custom/assets/badges/info.png b/custom/assets/badges/info.png new file mode 100644 index 0000000000..ac70544e4a Binary files /dev/null and b/custom/assets/badges/info.png differ diff --git a/custom/assets/badges/not-found.png b/custom/assets/badges/not-found.png new file mode 100644 index 0000000000..73d611e0e1 Binary files /dev/null and b/custom/assets/badges/not-found.png differ diff --git a/cypress.config.ts b/cypress.config.ts index e390c41a54..25ff2aa075 100644 --- a/cypress.config.ts +++ b/cypress.config.ts @@ -1,12 +1,12 @@ -import { defineConfig } from 'cypress' +import { defineConfig } from "cypress"; export default defineConfig({ - e2e: { - // We've imported your old cypress plugins here. - // You may want to clean this up later by importing these. - setupNodeEvents(on, config) { - return require('./cypress/plugins/index.js')(on, config) - }, - baseUrl: 'http://localhost:61812', - }, -}) + e2e: { + // We've imported your old cypress plugins here. + // You may want to clean this up later by importing these. + setupNodeEvents(on, config) { + return require("./cypress/plugins/index.js")(on, config); + }, + baseUrl: "http://localhost:61812", + }, +}); diff --git a/cypress/e2e/basic.cy.js b/cypress/e2e/basic.cy.js index eb5195c4b2..f73a25efbc 100644 --- a/cypress/e2e/basic.cy.js +++ b/cypress/e2e/basic.cy.js @@ -1,4 +1,4 @@ -describe('Before setup instance', () => { +describe("Before setup instance", () => { beforeEach(() => { cy.resetState(); }); @@ -9,31 +9,31 @@ describe('Before setup instance', () => { cy.wait(1000); }); - it('successfully loads', () => { - cy.visit('/'); - }); + it("successfully loads", () => { + cy.visit("/"); + }); - it('setup instance', () => { - cy.visit('/'); + it("setup instance", () => { + cy.visit("/"); - cy.intercept('POST', '/api/admin/accounts/create').as('signup'); - - cy.get('[data-cy-admin-username] input').type('admin'); - cy.get('[data-cy-admin-password] input').type('admin1234'); - cy.get('[data-cy-admin-ok]').click(); + cy.intercept("POST", "/api/admin/accounts/create").as("signup"); + + cy.get("[data-cy-admin-username] input").type("admin"); + cy.get("[data-cy-admin-password] input").type("admin1234"); + cy.get("[data-cy-admin-ok]").click(); // なぜか動かない //cy.wait('@signup').should('have.property', 'response.statusCode'); - cy.wait('@signup'); - }); + cy.wait("@signup"); + }); }); -describe('After setup instance', () => { +describe("After setup instance", () => { beforeEach(() => { cy.resetState(); // インスタンス初期セットアップ - cy.registerUser('admin', 'pass', true); + cy.registerUser("admin", "pass", true); }); afterEach(() => { @@ -42,34 +42,34 @@ describe('After setup instance', () => { cy.wait(1000); }); - it('successfully loads', () => { - cy.visit('/'); - }); + it("successfully loads", () => { + cy.visit("/"); + }); - it('signup', () => { - cy.visit('/'); + it("signup", () => { + cy.visit("/"); - cy.intercept('POST', '/api/signup').as('signup'); + cy.intercept("POST", "/api/signup").as("signup"); - cy.get('[data-cy-signup]').click(); - cy.get('[data-cy-signup-username] input').type('alice'); - cy.get('[data-cy-signup-password] input').type('alice1234'); - cy.get('[data-cy-signup-password-retype] input').type('alice1234'); - cy.get('[data-cy-signup-submit]').click(); + cy.get("[data-cy-signup]").click(); + cy.get("[data-cy-signup-username] input").type("alice"); + cy.get("[data-cy-signup-password] input").type("alice1234"); + cy.get("[data-cy-signup-password-retype] input").type("alice1234"); + cy.get("[data-cy-signup-submit]").click(); - cy.wait('@signup'); - }); + cy.wait("@signup"); + }); }); -describe('After user signup', () => { +describe("After user signup", () => { beforeEach(() => { cy.resetState(); // インスタンス初期セットアップ - cy.registerUser('admin', 'pass', true); + cy.registerUser("admin", "pass", true); // ユーザー作成 - cy.registerUser('alice', 'alice1234'); + cy.registerUser("alice", "alice1234"); }); afterEach(() => { @@ -78,51 +78,53 @@ describe('After user signup', () => { cy.wait(1000); }); - it('successfully loads', () => { - cy.visit('/'); - }); + it("successfully loads", () => { + cy.visit("/"); + }); - it('signin', () => { - cy.visit('/'); + it("signin", () => { + cy.visit("/"); - cy.intercept('POST', '/api/signin').as('signin'); + cy.intercept("POST", "/api/signin").as("signin"); - cy.get('[data-cy-signin]').click(); - cy.get('[data-cy-signin-username] input').type('alice'); + cy.get("[data-cy-signin]").click(); + cy.get("[data-cy-signin-username] input").type("alice"); // Enterキーでサインインできるかの確認も兼ねる - cy.get('[data-cy-signin-password] input').type('alice1234{enter}'); + cy.get("[data-cy-signin-password] input").type("alice1234{enter}"); - cy.wait('@signin'); - }); + cy.wait("@signin"); + }); - it('suspend', function() { - cy.request('POST', '/api/admin/suspend-user', { + it("suspend", function () { + cy.request("POST", "/api/admin/suspend-user", { i: this.admin.token, userId: this.alice.id, }); - cy.visit('/'); + cy.visit("/"); - cy.get('[data-cy-signin]').click(); - cy.get('[data-cy-signin-username] input').type('alice'); - cy.get('[data-cy-signin-password] input').type('alice1234{enter}'); + cy.get("[data-cy-signin]").click(); + cy.get("[data-cy-signin-username] input").type("alice"); + cy.get("[data-cy-signin-password] input").type("alice1234{enter}"); // TODO: cypressにブラウザの言語指定できる機能が実装され次第英語のみテストするようにする - cy.contains(/アカウントが凍結されています|This account has been suspended due to/gi); + cy.contains( + /アカウントが凍結されています|This account has been suspended due to/gi, + ); }); }); -describe('After user singed in', () => { +describe("After user singed in", () => { beforeEach(() => { cy.resetState(); // インスタンス初期セットアップ - cy.registerUser('admin', 'pass', true); + cy.registerUser("admin", "pass", true); // ユーザー作成 - cy.registerUser('alice', 'alice1234'); + cy.registerUser("alice", "alice1234"); - cy.login('alice', 'alice1234'); + cy.login("alice", "alice1234"); }); afterEach(() => { @@ -131,17 +133,17 @@ describe('After user singed in', () => { cy.wait(1000); }); - it('successfully loads', () => { - cy.get('[data-cy-open-post-form]').should('be.visible'); - }); + it("successfully loads", () => { + cy.get("[data-cy-open-post-form]").should("be.visible"); + }); - it('note', () => { - cy.get('[data-cy-open-post-form]').click(); - cy.get('[data-cy-post-form-text]').type('Hello, Misskey!'); - cy.get('[data-cy-open-post-form-submit]').click(); + it("note", () => { + cy.get("[data-cy-open-post-form]").click(); + cy.get("[data-cy-post-form-text]").type("Hello, Misskey!"); + cy.get("[data-cy-open-post-form-submit]").click(); - cy.contains('Hello, Misskey!'); - }); + cy.contains("Hello, Misskey!"); + }); }); // TODO: 投稿フォームの公開範囲指定のテスト diff --git a/cypress/e2e/widgets.cy.js b/cypress/e2e/widgets.cy.js index 9eea010bde..e3c9326db8 100644 --- a/cypress/e2e/widgets.cy.js +++ b/cypress/e2e/widgets.cy.js @@ -1,14 +1,14 @@ -describe('After user signed in', () => { +describe("After user signed in", () => { beforeEach(() => { cy.resetState(); - cy.viewport('macbook-16'); + cy.viewport("macbook-16"); // インスタンス初期セットアップ - cy.registerUser('admin', 'pass', true); + cy.registerUser("admin", "pass", true); // ユーザー作成 - cy.registerUser('alice', 'alice1234'); + cy.registerUser("alice", "alice1234"); - cy.login('alice', 'alice1234'); + cy.login("alice", "alice1234"); }); afterEach(() => { @@ -17,47 +17,47 @@ describe('After user signed in', () => { cy.wait(1000); }); - it('widget edit toggle is visible', () => { - cy.get('.mk-widget-edit').should('be.visible'); - }); + it("widget edit toggle is visible", () => { + cy.get(".mk-widget-edit").should("be.visible"); + }); - it('widget select should be visible in edit mode', () => { - cy.get('.mk-widget-edit').click(); - cy.get('.mk-widget-select').should('be.visible'); - }); + it("widget select should be visible in edit mode", () => { + cy.get(".mk-widget-edit").click(); + cy.get(".mk-widget-select").should("be.visible"); + }); - it('first widget should be removed', () => { - cy.get('.mk-widget-edit').click(); - cy.get('.customize-container:first-child .remove._button').click(); - cy.get('.customize-container').should('have.length', 2); + it("first widget should be removed", () => { + cy.get(".mk-widget-edit").click(); + cy.get(".customize-container:first-child .remove._button").click(); + cy.get(".customize-container").should("have.length", 2); }); function buildWidgetTest(widgetName) { it(`${widgetName} widget should get added`, () => { - cy.get('.mk-widget-edit').click(); - cy.get('.mk-widget-select select').select(widgetName, { force: true }); - cy.get('.bg._modalBg.transparent').click({ multiple: true, force: true }); - cy.get('.mk-widget-add').click({ force: true }); - cy.get(`.mkw-${widgetName}`).should('exist'); + cy.get(".mk-widget-edit").click(); + cy.get(".mk-widget-select select").select(widgetName, { force: true }); + cy.get(".bg._modalBg.transparent").click({ multiple: true, force: true }); + cy.get(".mk-widget-add").click({ force: true }); + cy.get(`.mkw-${widgetName}`).should("exist"); }); } - buildWidgetTest('memo'); - buildWidgetTest('notifications'); - buildWidgetTest('timeline'); - buildWidgetTest('calendar'); - buildWidgetTest('rss'); - buildWidgetTest('trends'); - buildWidgetTest('clock'); - buildWidgetTest('activity'); - buildWidgetTest('photos'); - buildWidgetTest('digitalClock'); - buildWidgetTest('federation'); - buildWidgetTest('postForm'); - buildWidgetTest('slideshow'); - buildWidgetTest('serverMetric'); - buildWidgetTest('onlineUsers'); - buildWidgetTest('jobQueue'); - buildWidgetTest('button'); - buildWidgetTest('aiscript'); + buildWidgetTest("memo"); + buildWidgetTest("notifications"); + buildWidgetTest("timeline"); + buildWidgetTest("calendar"); + buildWidgetTest("rss"); + buildWidgetTest("trends"); + buildWidgetTest("clock"); + buildWidgetTest("activity"); + buildWidgetTest("photos"); + buildWidgetTest("digitalClock"); + buildWidgetTest("federation"); + buildWidgetTest("postForm"); + buildWidgetTest("slideshow"); + buildWidgetTest("serverMetric"); + buildWidgetTest("onlineUsers"); + buildWidgetTest("jobQueue"); + buildWidgetTest("button"); + buildWidgetTest("aiscript"); }); diff --git a/cypress/plugins/index.js b/cypress/plugins/index.js index aa9918d215..3a4b6deb18 100644 --- a/cypress/plugins/index.js +++ b/cypress/plugins/index.js @@ -16,6 +16,6 @@ * @type {Cypress.PluginConfig} */ module.exports = (on, config) => { - // `on` is used to hook into various events Cypress emits - // `config` is the resolved Cypress config -} + // `on` is used to hook into various events Cypress emits + // `config` is the resolved Cypress config +}; diff --git a/cypress/support/commands.js b/cypress/support/commands.js index 95bfcf6855..3fe95b93d0 100644 --- a/cypress/support/commands.js +++ b/cypress/support/commands.js @@ -24,32 +24,34 @@ // -- This will overwrite an existing command -- // Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... }) -Cypress.Commands.add('resetState', () => { - cy.window(win => { - win.indexedDB.deleteDatabase('keyval-store'); +Cypress.Commands.add("resetState", () => { + cy.window((win) => { + win.indexedDB.deleteDatabase("keyval-store"); }); - cy.request('POST', '/api/reset-db').as('reset'); - cy.get('@reset').its('status').should('equal', 204); + cy.request("POST", "/api/reset-db").as("reset"); + cy.get("@reset").its("status").should("equal", 204); cy.reload(true); }); -Cypress.Commands.add('registerUser', (username, password, isAdmin = false) => { - const route = isAdmin ? '/api/admin/accounts/create' : '/api/signup'; +Cypress.Commands.add("registerUser", (username, password, isAdmin = false) => { + const route = isAdmin ? "/api/admin/accounts/create" : "/api/signup"; - cy.request('POST', route, { + cy.request("POST", route, { username: username, password: password, - }).its('body').as(username); + }) + .its("body") + .as(username); }); -Cypress.Commands.add('login', (username, password) => { - cy.visit('/'); +Cypress.Commands.add("login", (username, password) => { + cy.visit("/"); - cy.intercept('POST', '/api/signin').as('signin'); + cy.intercept("POST", "/api/signin").as("signin"); - cy.get('[data-cy-signin]').click(); - cy.get('[data-cy-signin-username] input').type(username); - cy.get('[data-cy-signin-password] input').type(`${password}{enter}`); + cy.get("[data-cy-signin]").click(); + cy.get("[data-cy-signin-username] input").type(username); + cy.get("[data-cy-signin-password] input").type(`${password}{enter}`); - cy.wait('@signin').as('signedIn'); + cy.wait("@signin").as("signedIn"); }); diff --git a/cypress/support/e2e.js b/cypress/support/e2e.js index 9185be344c..961c6ac888 100644 --- a/cypress/support/e2e.js +++ b/cypress/support/e2e.js @@ -14,19 +14,21 @@ // *********************************************************** // Import commands.js using ES2015 syntax: -import './commands' +import "./commands"; // Alternatively you can use CommonJS syntax: // require('./commands') -Cypress.on('uncaught:exception', (err, runnable) => { - if ([ - // Chrome - 'ResizeObserver loop limit exceeded', +Cypress.on("uncaught:exception", (err, runnable) => { + if ( + [ + // Chrome + "ResizeObserver loop limit exceeded", - // Firefox - 'ResizeObserver loop completed with undelivered notifications', - ].some(msg => err.message.includes(msg))) { + // Firefox + "ResizeObserver loop completed with undelivered notifications", + ].some((msg) => err.message.includes(msg)) + ) { return false; } }); diff --git a/dev/docker-compose.yml.example b/dev/docker-compose.yml.example index db235f7a7e..21779a57bf 100644 --- a/dev/docker-compose.yml.example +++ b/dev/docker-compose.yml.example @@ -2,9 +2,9 @@ version: "3" services: web: - image: docker.io/thatonecalculator/calckey + image: docker.io/thatonecalculator/firefish build: .. - container_name: calckey_web + container_name: firefish_web restart: always depends_on: - db @@ -16,12 +16,12 @@ services: - network # - web volumes: - - ../files:/calckey/files - - ../.config:/calckey/.config:ro + - ../files:/firefish/files + - ../.config:/firefish/.config:ro redis: restart: always - container_name: calckey_redis + container_name: firefish_redis image: docker.io/redis:7.0-alpine networks: - network @@ -31,7 +31,7 @@ services: db: restart: always image: docker.io/postgres:12.2-alpine - container_name: calckey_db + container_name: firefish_db networks: - network env_file: diff --git a/docker-compose.yml b/docker-compose.yml index f51cf1b9a9..720cb63271 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,13 +2,15 @@ version: "3" services: web: - image: docker.io/thatonecalculator/calckey - container_name: calckey_web + image: docker.io/thatonecalculator/firefish + container_name: firefish_web restart: unless-stopped depends_on: - db - redis -# - es +### Uncomment one of the following to use a search engine +# - meilisearch +# - sonic ports: - "3000:3000" networks: @@ -17,13 +19,13 @@ services: environment: NODE_ENV: production volumes: - - ./files:/calckey/files - - ./.config:/calckey/.config:ro + - ./files:/firefish/files + - ./.config:/firefish/.config:ro redis: restart: unless-stopped image: docker.io/redis:7.0-alpine - container_name: calckey_redis + container_name: firefish_redis networks: - calcnet volumes: @@ -32,7 +34,7 @@ services: db: restart: unless-stopped image: docker.io/postgres:12.2-alpine - container_name: calckey_db + container_name: firefish_db networks: - calcnet env_file: @@ -40,19 +42,33 @@ services: volumes: - ./db:/var/lib/postgresql/data -# es: -# restart: unless-stopped -# image: docker.elastic.co/elasticsearch/elasticsearch-oss:6.4.2 -# environment: -# - "ES_JAVA_OPTS=-Xms512m -Xmx512m" -# - "TAKE_FILE_OWNERSHIP=111" +### Only one of the below should be used. +### Meilisearch is better overall, but resource-intensive. Sonic is a very light full text search engine. + +# meilisearch: +# container_name: meilisearch +# image: getmeili/meilisearch:v1.1.1 +# environment: +# - MEILI_ENV=${MEILI_ENV:-development} +# ports: +# - "7700:7700" # networks: # - calcnet # volumes: -# - ./elasticsearch:/usr/share/elasticsearch/data +# - ./meili_data:/meili_data +# restart: unless-stopped + +# sonic: +# restart: unless-stopped +# image: docker.io/valeriansaliou/sonic:v1.4.0 +# networks: +# - calcnet +# volumes: +# - ./sonic:/var/lib/sonic/store +# - ./sonic/config.cfg:/etc/sonic.cfg networks: calcnet: - # web: - # external: - # name: web + # web: + # external: + # name: web diff --git a/docs/api-doc.md b/docs/api-doc.md new file mode 100644 index 0000000000..04fbaffd44 --- /dev/null +++ b/docs/api-doc.md @@ -0,0 +1,5 @@ +# API Documentation + +You can find interactive API documentation at any Firefish instance. https://firefish.social/api-doc + +You can also find auto-generated documentation for firefish-js [here](../packages/firefish-js/markdown/firefish-js.md). diff --git a/docs/development.md b/docs/development.md new file mode 100644 index 0000000000..31e6bf9994 --- /dev/null +++ b/docs/development.md @@ -0,0 +1,108 @@ +# 🌎 Firefish Developer Docs + +## Nix Dev Environment +The Firefish repo comes with a Nix-based shell environment to help make development as easy as possible! + +Please note, however, that this environment will not work on Windows outside of a WSL2 environment. + +### Prerequisites + +- Installed the [Nix Package Manager](https://nixos.org/download.html) (use the comman on their website) +- Installed [direnv](https://direnv.net/docs/installation.html) and added its hook to your shell. (package manager) + +Once the repo is cloned to your computer, follow these next few steps inside the Firefish folder: + +- Run `direnv allow`. This will build the environment and install all needed tools. +- Run `install-deps`, then `prepare-config`, to install the node dependencies and prepare the needed config files. +- In a second terminal, run `devenv up`. This will spawn a **Redis** server, a **Postgres** server, and the **Firefish** server in dev mode. +- Once you see the Firefish banner printed in your second terminal, run `migrate` in the first. +- Once migrations finish, open http://localhost:3000 in your web browser. +- You should now see the admin user creation screen! + +Note: When you want to restart a dev server, all you need to do is run `devenv up`, no other steps are necessary. + +# Possible Troubles with the dev enviroment +(this doesn't have to be done under normal conditions, this is for future reference) + +### direnv +If you have any trouble with `direnv allow` +Check that the contents of `.envrc` have the same version of nix-direnv that is specified here: +> nix-direnv under -> installation -> using direnv source url +> https://github.com/nix-community/nix-direnv#direnv-source_url + +there should be no errors during `direnv allow` + +### outdated nix packages +if `install-deps` or any subsequent command doesn't run due to versioning problems +`flake.nix` and `flake.lock` may be outdated + +delete `flake.lock`, or better, run `nix flake update --extra-experimental-features flakes --extra-experimental-features nix-command` +after that, run `direnv rebuild` + +if there are any errors, you might have to change `flake.nix` +(because the available options can change between versions - consider getting support in [the matrix channel](https://matrix.to/#/#firefish:matrix.fedibird.com)) + +### after changing a node version +in my case, i had to change the node version from 19, to 18 + +! before proceeding, make sure to delete all build artifacts! +remove `node_modules` and `built` folders, and maybe `.devenv` and `.direnv` as well +manually, or run `npm cache clean --force` and `pnpm cleanall` + +### Windows Subsystem for Linux +if `devenv up` terminates because of wrong folder permissions, + +create the file `/etc/wsl.conf` in your distro and add +```shell +[automount] +options = "metadata" +``` + +this allows `chmod` calls to actually have an effect. +the build scripts DO actually set the permissions, it just needs to work in wsl. + +### devenv up +devenv up may take a looong time. (some say this is fake news, maybe it was bad luck in my case) + +do not get spooked by this error: +``` +> firefish@14.0.0-dev32 start /mnt/.../firefish +> pnpm --filter backend run start + + +> backend@ start /mnt/.../firefish/packages/backend +> pnpm node ./built/index.js + +node:internal/modules/cjs/loader:1078 + throw err; + ^ + +Error: Cannot find module '/mnt/.../firefish/packages/backend/built/index.js' + at Module._resolveFilename (node:internal/modules/cjs/loader:1075:15) + at Module._load (node:internal/modules/cjs/loader:920:27) + at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12) + at node:internal/main/run_main_module:23:47 { + code: 'MODULE_NOT_FOUND', + requireStack: [] +} + +Node.js v18.16.0 +undefined +/mnt/.../firefish/packages/backend: + ERR_PNPM_RECURSIVE_RUN_FIRST_FAIL  backend@ start: `pnpm node ./built/index.js` +Exit status 1 + ELIFECYCLE  Command failed with exit code 1. +``` + +the script is designed to constantly try to start the server, while the build is still running. +this just means that the build isn't finished yet. + +at some point you should see a banner that says "Firefish" in big letters - +then you're good to go and can run `migrate` (in another terminal)! + +if you don't see the banner, +and it's for some reason stuck on `Finished 'build' after 917 ms` for a view minutes, + +just leave devenv running and open another terminal in the folder +run `migrate` and then `pnpm --filter backend run start` by yourself +the server should start diff --git a/docker-README.md b/docs/docker.md similarity index 61% rename from docker-README.md rename to docs/docker.md index 1119b34f12..b8a243a38a 100644 --- a/docker-README.md +++ b/docs/docker.md @@ -1,41 +1,42 @@ -# 🐳 Running a Calckey instance with Docker +# 🐳 Running a Firefish server with Docker ## Pre-built docker container -[thatonecalculator/calckey](https://hub.docker.com/r/thatonecalculator/calckey) +[thatonecalculator/firefish](https://hub.docker.com/r/thatonecalculator/firefish) ## `docker-compose` There is a `docker-compose.yml` in the root of the project that you can use to build the container from source - .config/docker.env (**db config settings**) -- .config/default.yml (**calckey instance settings**) +- .config/default.yml (**firefish server settings**) ## Configuring Rename the files: -`cp .config/default_example.yml .config/default.yml` +`cp .config/example.yml .config/default.yml` `cp .config/example.env .config/docker.env` then edit them according to your environment. You can configure `docker.env` with anything you like, but you will have to pay attention to the `default.yml` file: -- `url` should be set to the URL you will be hosting the web interface for the instance at. -- `host`, `db`, `user`, `pass` will have to be configured in the `PostgreSQL configuration` section - `host` is the name of the postgres container (eg: *calckey_db_1*), and the others should match your `docker.env`. -- `host`will need to be configured in the *Redis configuration* section - it is the name of the redis container (eg: *calckey_redis_1*) +- `url` should be set to the URL you will be hosting the web interface for the server at. +- `host`, `db`, `user`, `pass` will have to be configured in the `PostgreSQL configuration` section - `host` is the name of the postgres container (eg: *firefish_db_1*), and the others should match your `docker.env`. +- `host`will need to be configured in the *Redis configuration* section - it is the name of the redis container (eg: *firefish_redis_1*) +- `auth` will need to be configured in the *Sonic* section - cannot be the default `SecretPassword` Everything else can be left as-is. ## Running docker-compose -The [prebuilt container for calckey](https://hub.docker.com/r/thatonecalculator/calckey) is fairly large, and may take a few minutes to download and extract using docker. +The [prebuilt container for firefish](https://hub.docker.com/r/thatonecalculator/firefish) is fairly large, and may take a few minutes to download and extract using docker. Copy `docker-compose.yml` and the `config/` to a directory, then run the **docker-compose** command: `docker-compose up -d`. -NOTE: This will take some time to come fully online, even after download and extracting the container images, and it may emit some error messages before completing successfully. Specifically, the `db` container needs to initialize and so isn't available to the `web` container right away. Only once the `db` container comes online does the `web` container start building and initializing the calckey tables. +NOTE: This will take some time to come fully online, even after download and extracting the container images, and it may emit some error messages before completing successfully. Specifically, the `db` container needs to initialize and so isn't available to the `web` container right away. Only once the `db` container comes online does the `web` container start building and initializing the firefish tables. -Once the instance is up you can use a web browser to access the web interface at `http://serverip:3000` (where `serverip` is the IP of the server you are running the calckey instance on). +Once the server is up you can use a web browser to access the web interface at `http://serverip:3000` (where `serverip` is the IP of the server you are running the firefish server on). ## Docker for development diff --git a/docs/fk.patch b/docs/fk.patch new file mode 100644 index 0000000000..2d51512c96 --- /dev/null +++ b/docs/fk.patch @@ -0,0 +1,41 @@ +diff --git a/packages/backend/migration/1661376843000-remove-mentioned-remote-users-column.js b/packages/backend/migration/1661376843000-remove-mentioned-remote-users-column.js +index 42d79b5b5..1fd5e0f10 100644 +--- a/packages/backend/migration/1661376843000-remove-mentioned-remote-users-column.js ++++ b/packages/backend/migration/1661376843000-remove-mentioned-remote-users-column.js +@@ -7,6 +7,22 @@ export class removeMentionedRemoteUsersColumn1661376843000 { + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "note" ADD "mentionedRemoteUsers" TEXT NOT NULL DEFAULT '[]'::text`); +- await queryRunner.query(`UPDATE "note" SET "mentionedRemoteUsers" = (SELECT COALESCE(json_agg(row_to_json("data"))::text, '[]') FROM (SELECT "url", "uri", "username", "host" FROM "user" JOIN "user_profile" ON "user"."id" = "user_profile". "userId" WHERE "user"."host" IS NOT NULL AND "user"."id" = ANY("note"."mentions")) AS "data")`); ++ await queryRunner.query(` ++ CREATE TEMP TABLE IF NOT EXISTS "temp_mentions" AS ++ SELECT "id", "url", "uri", "username", "host" ++ FROM "user" ++ JOIN "user_profile" ON "user"."id" = "user_profile"."userId" WHERE "user"."host" IS NOT NULL ++ `); ++ ++ await queryRunner.query(` ++ CREATE UNIQUE INDEX "temp_mentions_id" ON "temp_mentions"("id") ++ `); ++ ++ await queryRunner.query(` ++ UPDATE "note" SET "mentionedRemoteUsers" = ( ++ SELECT COALESCE(json_agg(row_to_json("data")::jsonb - 'id')::text, '[]') FROM "temp_mentions" AS "data" ++ WHERE "data"."id" = ANY("note"."mentions") ++ ) ++ `); + } + } +diff --git a/packages/backend/migration/1663399074403-resize-comments-drive-file.js b/packages/backend/migration/1663399074403-resize-comments-drive-file.js +index a037f1655..0873aec9b 100644 +--- a/packages/backend/migration/1663399074403-resize-comments-drive-file.js ++++ b/packages/backend/migration/1663399074403-resize-comments-drive-file.js +@@ -9,6 +9,6 @@ export class resizeCommentsDriveFile1663399074403 { + } + + async down(queryRunner) { +- await queryRunner.query(`ALTER TABLE "drive_file" ALTER COLUMN "comment" TYPE character varying(512)`); +- } ++ console.log('This migration cannot be reverted, skipping...'); ++ } + } diff --git a/docs/kubernetes.md b/docs/kubernetes.md new file mode 100644 index 0000000000..5ec6b46ad2 --- /dev/null +++ b/docs/kubernetes.md @@ -0,0 +1,45 @@ +# Running a Firefish server with Kubernetes and Helm + +This is a [Helm](https://helm.sh/) chart directory in the root of the project +that you can use to deploy firefish to a Kubernetes cluster + +## Deployment + +1. Copy the example helm values and make your changes: +```shell +cp .config/helm_values_example.yml .config/helm_values.yml +``` + +2. Update helm dependencies: +```shell +cd chart +helm dependency list $dir 2> /dev/null | tail +2 | head -n -1 | awk '{ print "helm repo add " $1 " " $3 }' | while read cmd; do $cmd; done; +cd ../ +``` + +3. Create the firefish helm release (also used to update existing deployment): +```shell +helm upgrade \ + --install \ + --namespace firefish \ + --create-namespace \ + firefish chart/ \ + -f .config/helm_values.yml +``` + +4. Watch your firefish server spin up: +```shell +kubectl -n firefish get po -w +``` + +5. Initial the admin user and managed config: +```shell +export firefish_USERNAME="my_desired_admin_handle" && \ +export firefish_PASSWORD="myDesiredInitialPassword" && \ +export firefish_HOST="firefish.example.com" && \ +export firefish_TOKEN=$(curl -X POST https://$firefish_HOST/api/admin/accounts/create -H "Content-Type: application/json" -d "{ \"username\":\"$firefish_USERNAME\", \"password\":\"$firefish_PASSWORD\" }" | jq -r '.token') && \ +echo "Save this token: ${firefish_TOKEN}" && \ +curl -X POST -H "Authorization: Bearer $firefish_TOKEN" https://$firefish_HOST/api/admin/accounts/hosted +``` + +6. Enjoy! diff --git a/docs/migrate.md b/docs/migrate.md new file mode 100644 index 0000000000..005c911215 --- /dev/null +++ b/docs/migrate.md @@ -0,0 +1,106 @@ +# 🚚 Migrating from Misskey/FoundKey to Firefish + +All the guides below assume you're starting in the root of the repo directory. + +### Before proceeding + +- **Ensure you have stopped all master and worker processes of Misskey.** +- **Ensure you have backups of the database before performing any commands.** + +## Misskey v13 and above + +Tested with Misskey v13.11.3. + +If your Misskey v13 is older, we recommend updating your Misskey to v13.11.3. + +```sh +wget -O mkv13.patch https://gitlab.prometheus.systems/firefish/firefish/-/raw/develop/docs/mkv13.patch +wget -O mkv13_restore.patch https://gitlab.prometheus.systems/firefish/firefish/-/raw/develop/docs/mkv13_restore.patch +git apply mkv13.patch mkv13_restore.patch + +cd packages/backend + +LINE_NUM="$(pnpm typeorm migration:show -d ormconfig.js | grep -n activeEmailValidation1657346559800 | cut -d ':' -f 1)" +NUM_MIGRATIONS="$(pnpm typeorm migration:show -d ormconfig.js | tail -n+"$LINE_NUM" | grep '\[X\]' | wc -l)" + +for i in $(seq 1 $NUM_MIGRATIONS); do pnpm typeorm migration:revert -d ormconfig.js; done + +cd ../../ + +git remote set-url origin https://gitlab.prometheus.systems/firefish/firefish.git +git fetch origin +git stash push +rm -rf fluent-emojis misskey-assets +git switch main # or beta or develop +git pull --ff +wget -O renote_muting.patch https://gitlab.prometheus.systems/firefish/firefish/-/raw/develop/docs/renote_muting.patch +git apply renote_muting.patch + +pnpm install +NODE_ENV=production pnpm run build +pnpm run migrate +git stash push +``` + +Depending on the version you're migrating from, you may have to open Postgres with `psql -d your_database` and run the following commands: + +```sql +ALTER TABLE "meta" ADD COLUMN "disableLocalTimeline" boolean DEFAULT false; +ALTER TABLE "meta" ADD COLUMN "disableGlobalTimeline" boolean DEFAULT false; +ALTER TABLE "meta" ADD COLUMN "localDriveCapacityMb" integer DEFAULT 512; +ALTER TABLE "meta" ADD COLUMN "remoteDriveCapacityMb" integer DEFAULT 128; +ALTER TABLE "user" ADD COLUMN "isSilenced" boolean DEFAULT false; +ALTER TABLE "user" ADD COLUMN "isAdmin" boolean DEFAULT false; +ALTER TABLE "user" ADD COLUMN "isModerator" boolean DEFAULT false; +ALTER TABLE "user" ADD COLUMN "remoteDriveCapacityMb" integer DEFAULT 128; +ALTER TABLE "user" ADD COLUMN "driveCapacityOverrideMb" integer DEFAULT 128; +ALTER TABLE "instance" ADD COLUMN "caughtAt" date; +ALTER TABLE "instance" ADD COLUMN "latestRequestSentAt" date; +ALTER TABLE "instance" ADD COLUMN "latestStatus" character varying(512); +ALTER TABLE "instance" ADD COLUMN "lastCommunicatedAt" date; +``` + +then quit with `\q`, and restart Firefish. + +Note: Ignore errors of `column "xxx" of relation "xxx" already exists`. + +If no other errors happened, your Firefish is ready to launch! + +## Misskey v12.119 and before + +```sh +git remote set-url origin https://gitlab.prometheus.systems/firefish/firefish.git +git fetch +git checkout main # or beta or develop +git pull --ff + +NODE_ENV=production pnpm run migrate +# build using prefered method +``` + +## FoundKey + +```sh +wget -O fk.patch https://gitlab.prometheus.systems/firefish/firefish/-/raw/develop/docs/fk.patch +git apply fk.patch +cd packages/backend + +LINE_NUM="$(npx typeorm migration:show -d ormconfig.js | grep -n uniformThemecolor1652859567549 | cut -d ':' -f 1)" +NUM_MIGRATIONS="$(npx typeorm migration:show -d ormconfig.js | tail -n+"$LINE_NUM" | grep '\[X\]' | wc -l)" + +for i in $(seq 1 $NUM_MIGRATIONS); do + npx typeorm migration:revert -d ormconfig.js +done + +git remote set-url origin https://gitlab.prometheus.systems/firefish/firefish.git +git fetch +git checkout main # or beta or develop +git pull --ff + +NODE_ENV=production pnpm run migrate +# build using prefered method +``` + +## Reverse + +You ***cannot*** migrate back to Misskey from Firefish due to re-hashing passwords on signin with argon2. You can migrate from Calckey to FoundKey, although this is not recommended due to FoundKey being end-of-life, and may have some problems with alt-text. diff --git a/docs/mkv13.patch b/docs/mkv13.patch new file mode 100644 index 0000000000..e6106b16f2 --- /dev/null +++ b/docs/mkv13.patch @@ -0,0 +1,45 @@ +diff --git a/packages/backend/migration/1672704017999-remove-lastCommunicatedAt.js b/packages/backend/migration/1672704017999-remove-lastCommunicatedAt.js +index 38a676985..c4ae690e0 100644 +--- a/packages/backend/migration/1672704017999-remove-lastCommunicatedAt.js ++++ b/packages/backend/migration/1672704017999-remove-lastCommunicatedAt.js +@@ -6,6 +6,8 @@ export class removeLastCommunicatedAt1672704017999 { + } + + async down(queryRunner) { +- await queryRunner.query(`ALTER TABLE "instance" ADD "lastCommunicatedAt" TIMESTAMP WITH TIME ZONE NOT NULL`); ++ await queryRunner.query(`ALTER TABLE "instance" ADD "lastCommunicatedAt" TIMESTAMP WITH TIME ZONE`); ++ await queryRunner.query(`UPDATE "instance" SET "lastCommunicatedAt" = COALESCE("infoUpdatedAt", "caughtAt")`); ++ await queryRunner.query(`ALTER TABLE "instance" ALTER COLUMN "lastCommunicatedAt" SET NOT NULL`); + } + } +diff --git a/packages/backend/migration/1673336077243-PollChoiceLength.js b/packages/backend/migration/1673336077243-PollChoiceLength.js +index 810c626e0..5809528cb 100644 +--- a/packages/backend/migration/1673336077243-PollChoiceLength.js ++++ b/packages/backend/migration/1673336077243-PollChoiceLength.js +@@ -6,6 +6,6 @@ export class PollChoiceLength1673336077243 { + } + + async down(queryRunner) { +- await queryRunner.query(`ALTER TABLE "poll" ALTER COLUMN "choices" TYPE character varying(128) array`); ++ //await queryRunner.query(`ALTER TABLE "poll" ALTER COLUMN "choices" TYPE character varying(128) array`); + } + } +diff --git a/packages/backend/migration/1674118260469-achievement.js b/packages/backend/migration/1674118260469-achievement.js +index 131ab96f8..57a922f83 100644 +--- a/packages/backend/migration/1674118260469-achievement.js ++++ b/packages/backend/migration/1674118260469-achievement.js +@@ -18,12 +18,13 @@ export class achievement1674118260469 { + + async down(queryRunner) { + await queryRunner.query(`CREATE TYPE "public"."user_profile_mutingnotificationtypes_enum_old" AS ENUM('follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'receiveFollowRequest', 'followRequestAccepted', 'groupInvited', 'app', 'pollEnded')`); ++ await queryRunner.query(`CREATE TYPE "public"."notification_type_enum_old" AS ENUM('follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'pollEnded', 'receiveFollowRequest', 'followRequestAccepted', 'groupInvited', 'app')`); + await queryRunner.query(`ALTER TABLE "user_profile" ALTER COLUMN "mutingNotificationTypes" DROP DEFAULT`); + await queryRunner.query(`ALTER TABLE "user_profile" ALTER COLUMN "mutingNotificationTypes" TYPE "public"."user_profile_mutingnotificationtypes_enum_old"[] USING "mutingNotificationTypes"::"text"::"public"."user_profile_mutingnotificationtypes_enum_old"[]`); + await queryRunner.query(`ALTER TABLE "user_profile" ALTER COLUMN "mutingNotificationTypes" SET DEFAULT '{}'`); + await queryRunner.query(`DROP TYPE "public"."user_profile_mutingnotificationtypes_enum"`); + await queryRunner.query(`ALTER TYPE "public"."user_profile_mutingnotificationtypes_enum_old" RENAME TO "user_profile_mutingnotificationtypes_enum"`); +- await queryRunner.query(`CREATE TYPE "public"."notification_type_enum_old" AS ENUM('follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'pollEnded', 'receiveFollowRequest', 'followRequestAccepted', 'groupInvited', 'app')`); ++ await queryRunner.query(`DELETE FROM "public"."notification" WHERE "type" = 'achievementEarned'`); + await queryRunner.query(`ALTER TABLE "notification" ALTER COLUMN "type" TYPE "public"."notification_type_enum_old" USING "type"::"text"::"public"."notification_type_enum_old"`); + await queryRunner.query(`DROP TYPE "public"."notification_type_enum"`); + await queryRunner.query(`ALTER TYPE "public"."notification_type_enum_old" RENAME TO "notification_type_enum"`); diff --git a/docs/mkv13_restore.patch b/docs/mkv13_restore.patch new file mode 100644 index 0000000000..9ef9934edc --- /dev/null +++ b/docs/mkv13_restore.patch @@ -0,0 +1,127 @@ +diff --git a/packages/backend/migration/1680491187535-cleanup.js b/packages/backend/migration/1680491187535-cleanup.js +index 1e609ca06..0e6accf3e 100644 +--- a/packages/backend/migration/1680491187535-cleanup.js ++++ b/packages/backend/migration/1680491187535-cleanup.js +@@ -1,10 +1,40 @@ + export class cleanup1680491187535 { +- name = 'cleanup1680491187535' ++ name = "cleanup1680491187535"; + +- async up(queryRunner) { +- await queryRunner.query(`DROP TABLE "antenna_note" `); +- } ++ async up(queryRunner) { ++ await queryRunner.query(`DROP TABLE "antenna_note" `); ++ } + +- async down(queryRunner) { +- } ++ async down(queryRunner) { ++ await queryRunner.query( ++ `CREATE TABLE antenna_note ( id character varying(32) NOT NULL, "noteId" character varying(32) NOT NULL, "antennaId" character varying(32) NOT NULL, read boolean DEFAULT false NOT NULL)`, ++ ); ++ await queryRunner.query( ++ `COMMENT ON COLUMN antenna_note."noteId" IS 'The note ID.'`, ++ ); ++ await queryRunner.query( ++ `COMMENT ON COLUMN antenna_note."antennaId" IS 'The antenna ID.'`, ++ ); ++ await queryRunner.query( ++ `ALTER TABLE ONLY antenna_note ADD CONSTRAINT "PK_fb28d94d0989a3872df19fd6ef8" PRIMARY KEY (id)`, ++ ); ++ await queryRunner.query( ++ `CREATE INDEX "IDX_0d775946662d2575dfd2068a5f" ON antenna_note USING btree ("antennaId")`, ++ ); ++ await queryRunner.query( ++ `CREATE UNIQUE INDEX "IDX_335a0bf3f904406f9ef3dd51c2" ON antenna_note USING btree ("noteId", "antennaId")`, ++ ); ++ await queryRunner.query( ++ `CREATE INDEX "IDX_9937ea48d7ae97ffb4f3f063a4" ON antenna_note USING btree (read)`, ++ ); ++ await queryRunner.query( ++ `CREATE INDEX "IDX_bd0397be22147e17210940e125" ON antenna_note USING btree ("noteId")`, ++ ); ++ await queryRunner.query( ++ `ALTER TABLE ONLY antenna_note ADD CONSTRAINT "FK_0d775946662d2575dfd2068a5f5" FOREIGN KEY ("antennaId") REFERENCES antenna(id) ON DELETE CASCADE`, ++ ); ++ await queryRunner.query( ++ `ALTER TABLE ONLY antenna_note ADD CONSTRAINT "FK_bd0397be22147e17210940e125b" FOREIGN KEY ("noteId") REFERENCES note(id) ON DELETE CASCADE`, ++ ); ++ } + } +diff --git a/packages/backend/migration/1680582195041-cleanup.js b/packages/backend/migration/1680582195041-cleanup.js +index c587e456a..a91d6ff3c 100644 +--- a/packages/backend/migration/1680582195041-cleanup.js ++++ b/packages/backend/migration/1680582195041-cleanup.js +@@ -1,11 +1,64 @@ + export class cleanup1680582195041 { +- name = 'cleanup1680582195041' ++ name = "cleanup1680582195041"; + +- async up(queryRunner) { +- await queryRunner.query(`DROP TABLE "notification" `); +- } ++ async up(queryRunner) { ++ await queryRunner.query(`DROP TABLE "notification"`); ++ } + +- async down(queryRunner) { +- +- } ++ async down(queryRunner) { ++ await queryRunner.query( ++ `CREATE TABLE notification ( id character varying(32) NOT NULL, "createdAt" timestamp with time zone NOT NULL, "notifieeId" character varying(32) NOT NULL, "notifierId" character varying(32), "isRead" boolean DEFAULT false NOT NULL, "noteId" character varying(32), reaction character varying(128), choice integer, "followRequestId" character varying(32), type notification_type_enum NOT NULL, "customBody" character varying(2048), "customHeader" character varying(256), "customIcon" character varying(1024), "appAccessTokenId" character varying(32), achievement character varying(128))`, ++ ); ++ await queryRunner.query( ++ `COMMENT ON COLUMN notification."createdAt" IS 'The created date of the Notification.'`, ++ ); ++ await queryRunner.query( ++ `COMMENT ON COLUMN notification."notifieeId" IS 'The ID of recipient user of the Notification.'`, ++ ); ++ await queryRunner.query( ++ `COMMENT ON COLUMN notification."notifierId" IS 'The ID of sender user of the Notification.'`, ++ ); ++ await queryRunner.query( ++ `COMMENT ON COLUMN notification."isRead" IS 'Whether the Notification is read.'`, ++ ); ++ await queryRunner.query( ++ `COMMENT ON COLUMN notification.type IS 'The type of the Notification.'`, ++ ); ++ await queryRunner.query( ++ `ALTER TABLE ONLY notification ADD CONSTRAINT "PK_705b6c7cdf9b2c2ff7ac7872cb7" PRIMARY KEY (id)`, ++ ); ++ await queryRunner.query( ++ `CREATE INDEX "IDX_080ab397c379af09b9d2169e5b" ON notification USING btree ("isRead")`, ++ ); ++ await queryRunner.query( ++ `CREATE INDEX "IDX_33f33cc8ef29d805a97ff4628b" ON notification USING btree (type)`, ++ ); ++ await queryRunner.query( ++ `CREATE INDEX "IDX_3b4e96eec8d36a8bbb9d02aa71" ON notification USING btree ("notifierId")`, ++ ); ++ await queryRunner.query( ++ `CREATE INDEX "IDX_3c601b70a1066d2c8b517094cb" ON notification USING btree ("notifieeId")`, ++ ); ++ await queryRunner.query( ++ `CREATE INDEX "IDX_b11a5e627c41d4dc3170f1d370" ON notification USING btree ("createdAt")`, ++ ); ++ await queryRunner.query( ++ `CREATE INDEX "IDX_e22bf6bda77b6adc1fd9e75c8c" ON notification USING btree ("appAccessTokenId")`, ++ ); ++ await queryRunner.query( ++ `ALTER TABLE ONLY notification ADD CONSTRAINT "FK_3b4e96eec8d36a8bbb9d02aa710" FOREIGN KEY ("notifierId") REFERENCES "user"(id) ON DELETE CASCADE`, ++ ); ++ await queryRunner.query( ++ `ALTER TABLE ONLY notification ADD CONSTRAINT "FK_3c601b70a1066d2c8b517094cb9" FOREIGN KEY ("notifieeId") REFERENCES "user"(id) ON DELETE CASCADE`, ++ ); ++ await queryRunner.query( ++ `ALTER TABLE ONLY notification ADD CONSTRAINT "FK_769cb6b73a1efe22ddf733ac453" FOREIGN KEY ("noteId") REFERENCES note(id) ON DELETE CASCADE`, ++ ); ++ await queryRunner.query( ++ `ALTER TABLE ONLY notification ADD CONSTRAINT "FK_bd7fab507621e635b32cd31892c" FOREIGN KEY ("followRequestId") REFERENCES follow_request(id) ON DELETE CASCADE`, ++ ); ++ await queryRunner.query( ++ `ALTER TABLE ONLY notification ADD CONSTRAINT "FK_e22bf6bda77b6adc1fd9e75c8c9" FOREIGN KEY ("appAccessTokenId") REFERENCES access_token(id) ON DELETE CASCADE`, ++ ); ++ } + } diff --git a/docs/renote_muting.patch b/docs/renote_muting.patch new file mode 100644 index 0000000000..c5bd2818c6 --- /dev/null +++ b/docs/renote_muting.patch @@ -0,0 +1,23 @@ +diff --git a/packages/backend/migration/1665091090561-add-renote-muting.js b/packages/backend/migration/1665091090561-add-renote-muting.js +index 2c76aaff5..f8541c818 100644 +--- a/packages/backend/migration/1665091090561-add-renote-muting.js ++++ b/packages/backend/migration/1665091090561-add-renote-muting.js +@@ -4,18 +4,6 @@ export class addRenoteMuting1665091090561 { + } + + async up(queryRunner) { +- await queryRunner.query( +- `CREATE TABLE "renote_muting" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "muteeId" character varying(32) NOT NULL, "muterId" character varying(32) NOT NULL, CONSTRAINT "PK_renoteMuting_id" PRIMARY KEY ("id"))`, +- ); +- await queryRunner.query( +- `CREATE INDEX "IDX_renote_muting_createdAt" ON "muting" ("createdAt") `, +- ); +- await queryRunner.query( +- `CREATE INDEX "IDX_renote_muting_muteeId" ON "muting" ("muteeId") `, +- ); +- await queryRunner.query( +- `CREATE INDEX "IDX_renote_muting_muterId" ON "muting" ("muterId") `, +- ); + } + + async down(queryRunner) {} diff --git a/firefish.apache.conf b/firefish.apache.conf new file mode 100644 index 0000000000..b0c69d51df --- /dev/null +++ b/firefish.apache.conf @@ -0,0 +1,13 @@ +# Replace example.tld with your domain + + + ServerName example.tld + # For WebSocket + ProxyPass "/streaming" "ws://127.0.0.1:3000/streaming/" + # Proxy to Node + ProxyPass "/" "http://127.0.0.1:3000/" + ProxyPassReverse "/" "http://127.0.0.1:3000/" + ProxyPreserveHost On + # For files proxy + AllowEncodedSlashes On + \ No newline at end of file diff --git a/calckey.nginx.conf b/firefish.nginx.conf similarity index 100% rename from calckey.nginx.conf rename to firefish.nginx.conf diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000000..f1ff690415 --- /dev/null +++ b/flake.lock @@ -0,0 +1,294 @@ +{ + "nodes": { + "devenv": { + "inputs": { + "flake-compat": "flake-compat", + "nix": "nix", + "nixpkgs": "nixpkgs", + "pre-commit-hooks": "pre-commit-hooks" + }, + "locked": { + "lastModified": 1685521914, + "narHash": "sha256-0fdFP5IASLwJ0PSXrErW8PZon9TVYmi8VRF8OtjGkV4=", + "owner": "cachix", + "repo": "devenv", + "rev": "e206d8f2e3e8d6aa943656052f15bdfea8146b8d", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "devenv", + "type": "github" + } + }, + "fenix": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ], + "rust-analyzer-src": "rust-analyzer-src" + }, + "locked": { + "lastModified": 1685514167, + "narHash": "sha256-urRxF0ZGSNeZjM4kALNg3wTh7fBscbqQmS6S/HU7Wms=", + "owner": "nix-community", + "repo": "fenix", + "rev": "3abfea51663583186f687c49a157eab1639349ca", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "fenix", + "type": "github" + } + }, + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1673956053, + "narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-parts": { + "inputs": { + "nixpkgs-lib": "nixpkgs-lib" + }, + "locked": { + "lastModified": 1685457039, + "narHash": "sha256-bEFtQm+YyLxQjKQAaBHJyPN1z2wbhBnr2g1NJWSYjwM=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "80717d11615b6f42d1ad2e18ead51193fc15de69", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "flake-utils": { + "locked": { + "lastModified": 1667395993, + "narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "gitignore": { + "inputs": { + "nixpkgs": [ + "devenv", + "pre-commit-hooks", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1660459072, + "narHash": "sha256-8DFJjXG8zqoONA1vXtgeKXy68KdJL5UaXR8NtVMUbx8=", + "owner": "hercules-ci", + "repo": "gitignore.nix", + "rev": "a20de23b925fd8264fd7fad6454652e142fd7f73", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "gitignore.nix", + "type": "github" + } + }, + "lowdown-src": { + "flake": false, + "locked": { + "lastModified": 1633514407, + "narHash": "sha256-Dw32tiMjdK9t3ETl5fzGrutQTzh2rufgZV4A/BbxuD4=", + "owner": "kristapsdz", + "repo": "lowdown", + "rev": "d2c2b44ff6c27b936ec27358a2653caaef8f73b8", + "type": "github" + }, + "original": { + "owner": "kristapsdz", + "repo": "lowdown", + "type": "github" + } + }, + "nix": { + "inputs": { + "lowdown-src": "lowdown-src", + "nixpkgs": [ + "devenv", + "nixpkgs" + ], + "nixpkgs-regression": "nixpkgs-regression" + }, + "locked": { + "lastModified": 1676545802, + "narHash": "sha256-EK4rZ+Hd5hsvXnzSzk2ikhStJnD63odF7SzsQ8CuSPU=", + "owner": "domenkozar", + "repo": "nix", + "rev": "7c91803598ffbcfe4a55c44ac6d49b2cf07a527f", + "type": "github" + }, + "original": { + "owner": "domenkozar", + "ref": "relaxed-flakes", + "repo": "nix", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1678875422, + "narHash": "sha256-T3o6NcQPwXjxJMn2shz86Chch4ljXgZn746c2caGxd8=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "126f49a01de5b7e35a43fd43f891ecf6d3a51459", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-lib": { + "locked": { + "dir": "lib", + "lastModified": 1682879489, + "narHash": "sha256-sASwo8gBt7JDnOOstnps90K1wxmVfyhsTPPNTGBPjjg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "da45bf6ec7bbcc5d1e14d3795c025199f28e0de0", + "type": "github" + }, + "original": { + "dir": "lib", + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-regression": { + "locked": { + "lastModified": 1643052045, + "narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2", + "type": "github" + }, + "original": { + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2", + "type": "github" + } + }, + "nixpkgs-stable": { + "locked": { + "lastModified": 1678872516, + "narHash": "sha256-/E1YwtMtFAu2KUQKV/1+KFuReYPANM2Rzehk84VxVoc=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "9b8e5abb18324c7fe9f07cb100c3cd4a29cda8b8", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-22.11", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_2": { + "locked": { + "lastModified": 1685399834, + "narHash": "sha256-Lt7//5snriXSdJo5hlVcDkpERL1piiih0UXIz1RUcC4=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "58c85835512b0db938600b6fe13cc3e3dc4b364e", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "pre-commit-hooks": { + "inputs": { + "flake-compat": [ + "devenv", + "flake-compat" + ], + "flake-utils": "flake-utils", + "gitignore": "gitignore", + "nixpkgs": [ + "devenv", + "nixpkgs" + ], + "nixpkgs-stable": "nixpkgs-stable" + }, + "locked": { + "lastModified": 1682596858, + "narHash": "sha256-Hf9XVpqaGqe/4oDGr30W8HlsWvJXtMsEPHDqHZA6dDg=", + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "rev": "fb58866e20af98779017134319b5663b8215d912", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "type": "github" + } + }, + "root": { + "inputs": { + "devenv": "devenv", + "fenix": "fenix", + "flake-parts": "flake-parts", + "nixpkgs": "nixpkgs_2" + } + }, + "rust-analyzer-src": { + "flake": false, + "locked": { + "lastModified": 1685465261, + "narHash": "sha256-aJ2nUinUrNcFi+pb47bS5IIAeSiUEEPLJY8W4Q8Pcjk=", + "owner": "rust-lang", + "repo": "rust-analyzer", + "rev": "d2b3caa5b5694125fad04a9699e919444439f6a2", + "type": "github" + }, + "original": { + "owner": "rust-lang", + "ref": "nightly", + "repo": "rust-analyzer", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000000..60e2d54a25 --- /dev/null +++ b/flake.nix @@ -0,0 +1,86 @@ +{ + description = "Firefish development flake"; + + inputs = { + nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable"; + # Flake Parts framework(https://flake.parts) + flake-parts.url = "github:hercules-ci/flake-parts"; + # Devenv for better devShells(https://devenv.sh) + devenv.url = "github:cachix/devenv"; + # Fenix for rust development + fenix.url = "github:nix-community/fenix"; + fenix.inputs.nixpkgs.follows = "nixpkgs"; + }; + outputs = inputs@{ flake-parts, ... }: + flake-parts.lib.mkFlake { inherit inputs; } { + imports = [ + inputs.devenv.flakeModule + ]; + + # Define the systems that this works on. Only tested with x66_64-linux, add more if you test and it works. + systems = [ + "x86_64-linux" + ]; + # Expose these attributes for every system defined above. + perSystem = { config, pkgs, ... }: { + # Devenv shells + devenv = { + shells = { + # The default shell, used by nix-direnv + default = { + name = "firefish-dev-shell"; + # Add additional packages to our environment + packages = [ + pkgs.nodePackages.pnpm + + pkgs.python3 + ]; + # No need to warn on a new version, we'll update as needed. + devenv.warnOnNewVersion = false; + # Enable typescript support + languages.typescript.enable = true; + # Enable javascript for NPM and PNPM + languages.javascript.enable = true; + languages.javascript.package = pkgs.nodejs_18; + # Enable stable Rust for the backend + languages.rust.enable = true; + languages.rust.version = "stable"; + processes = { + dev-server.exec = "pnpm run dev"; + }; + scripts = { + build.exec = "pnpm run build"; + clean.exec = "pnpm run clean"; + clear-state.exec = "rm -rf .devenv/state/redis .devenv/state/postgres"; + format.exec = "pnpm run format"; + install-deps.exec = "pnpm install"; + migrate.exec = "pnpm run migrate"; + prepare-config.exec = "cp .config/devenv.yml .config/default.yml"; + }; + services = { + postgres = { + enable = true; + package = pkgs.postgresql_12; + initialDatabases = [{ + name = "firefish"; + }]; + initialScript = '' + CREATE USER firefish WITH PASSWORD 'firefish'; + ALTER USER firefish WITH SUPERUSER; + GRANT ALL ON DATABASE firefish TO firefish; + ''; + listen_addresses = "127.0.0.1"; + port = 5432; + }; + redis = { + enable = true; + bind = "127.0.0.1"; + port = 6379; + }; + }; + }; + }; + }; + }; + }; +} diff --git a/gulpfile.js b/gulpfile.js index 89a6acb839..7220d8421a 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -2,69 +2,98 @@ * Gulp tasks */ -const fs = require('fs'); -const gulp = require('gulp'); -const replace = require('gulp-replace'); -const terser = require('gulp-terser'); -const cssnano = require('gulp-cssnano'); +const fs = require("fs"); +const gulp = require("gulp"); +const replace = require("gulp-replace"); +const terser = require("gulp-terser"); +const cssnano = require("gulp-cssnano"); -const locales = require('./locales'); -const meta = require('./package.json'); +const locales = require("./locales"); +const meta = require("./package.json"); -gulp.task('copy:backend:views', () => - gulp.src('./packages/backend/src/server/web/views/**/*').pipe(gulp.dest('./packages/backend/built/server/web/views')) +gulp.task("copy:backend:views", () => + gulp + .src("./packages/backend/src/server/web/views/**/*") + .pipe(gulp.dest("./packages/backend/built/server/web/views")), ); -gulp.task('copy:backend:custom', () => - gulp.src('./custom/assets/*').pipe(gulp.dest('./packages/backend/assets/')) +gulp.task("copy:backend:custom", () => + gulp + .src("./custom/assets/**/*") + .pipe(gulp.dest("./packages/backend/assets/")), ); -gulp.task('copy:client:fonts', () => - gulp.src('./packages/client/node_modules/three/examples/fonts/**/*').pipe(gulp.dest('./built/_client_dist_/fonts/')) +gulp.task("copy:client:fonts", () => + gulp + .src("./packages/client/node_modules/three/examples/fonts/**/*") + .pipe(gulp.dest("./built/_client_dist_/fonts/")), ); -gulp.task('copy:client:phosphor', () => - gulp.src('./node_modules/phosphor-icons/src/fonts/*').pipe(gulp.dest('./built/_client_dist_/phosphor/')) -); +gulp.task("copy:client:locales", (cb) => { + fs.mkdirSync("./built/_client_dist_/locales", { recursive: true }); -gulp.task('copy:client:locales', cb => { - fs.mkdirSync('./built/_client_dist_/locales', { recursive: true }); - - const v = { '_version_': meta.version }; + const v = { _version_: meta.version }; for (const [lang, locale] of Object.entries(locales)) { - fs.writeFileSync(`./built/_client_dist_/locales/${lang}.${meta.version}.json`, JSON.stringify({ ...locale, ...v }), 'utf-8'); + fs.writeFileSync( + `./built/_client_dist_/locales/${lang}.${meta.version}.json`, + JSON.stringify({ ...locale, ...v }), + "utf-8", + ); } cb(); }); - -gulp.task('build:backend:script', () => { - return gulp.src(['./packages/backend/src/server/web/boot.js', './packages/backend/src/server/web/bios.js', './packages/backend/src/server/web/cli.js']) - .pipe(replace('LANGS', JSON.stringify(Object.keys(locales)))) - .pipe(terser({ - toplevel: true - })) - .pipe(gulp.dest('./packages/backend/built/server/web/')); +gulp.task("build:backend:script", () => { + return gulp + .src([ + "./packages/backend/src/server/web/boot.js", + "./packages/backend/src/server/web/bios.js", + "./packages/backend/src/server/web/cli.js", + ]) + .pipe(replace("LANGS", JSON.stringify(Object.keys(locales)))) + .pipe( + terser({ + toplevel: true, + }), + ) + .pipe(gulp.dest("./packages/backend/built/server/web/")); }); -gulp.task('build:backend:style', () => { - return gulp.src(['./packages/backend/src/server/web/style.css', './packages/backend/src/server/web/bios.css', './packages/backend/src/server/web/cli.css']) - .pipe(cssnano({ - zindex: false - })) - .pipe(gulp.dest('./packages/backend/built/server/web/')); +gulp.task("build:backend:style", () => { + return gulp + .src([ + "./packages/backend/src/server/web/style.css", + "./packages/backend/src/server/web/bios.css", + "./packages/backend/src/server/web/cli.css", + ]) + .pipe( + cssnano({ + zindex: false, + }), + ) + .pipe(gulp.dest("./packages/backend/built/server/web/")); }); -gulp.task('build', gulp.parallel( - 'copy:client:locales', 'copy:backend:views', 'copy:backend:custom', 'build:backend:script', 'build:backend:style', 'copy:client:fonts', 'copy:client:phosphor' -)); +gulp.task( + "build", + gulp.parallel( + "copy:client:locales", + "copy:backend:views", + "copy:backend:custom", + "build:backend:script", + "build:backend:style", + "copy:client:fonts", + ), +); -gulp.task('default', gulp.task('build')); +gulp.task("default", gulp.task("build")); -gulp.task('watch', () => { - gulp.watch([ - './packages/*/src/**/*', - ], { ignoreInitial: false }, gulp.task('build')); +gulp.task("watch", () => { + gulp.watch( + ["./packages/*/src/**/*"], + { ignoreInitial: false }, + gulp.task("build"), + ); }); diff --git a/issue_template/bug.yaml b/issue_template/bug.yaml index 3a21f1399a..0b6f5859cd 100644 --- a/issue_template/bug.yaml +++ b/issue_template/bug.yaml @@ -1,18 +1,28 @@ -name: Bug Report +name: 🐛 Bug Report about: File a bug report title: "[Bug]: " +blank_issues_enabled: true +contact_links: + - name: 💁 Support Matrix + url: https://matrix.to/#/%23firefish:matrix.fedibird.com + about: Having trouble with deployment? Ask the support chat. + - name: 🔒 Resposible Disclosure + url: https://gitlab.prometheus.systems/firefish/firefish/src/branch/develop/SECURITY.md + about: Found a security vulnerability? Please disclose it responsibly. body: - type: markdown attributes: value: | - Thanks for taking the time to fill out this bug report! + 💖 Thanks for taking the time to fill out this bug report! + 💁 Having trouble with deployment? [Ask the support chat.](https://matrix.to/#/%23firefish:matrix.fedibird.com) + 🔒 Found a security vulnerability? [Please disclose it responsibly.](https://gitlab.prometheus.systems/firefish/firefish/src/branch/develop/SECURITY.md) + 🤝 By submitting this issue, you agree to follow our [Contribution Guidelines.](https://gitlab.prometheus.systems/firefish/firefish/src/branch/develop/CONTRIBUTING.md) - type: textarea id: what-happened attributes: label: What happened? description: Please give us a brief description of what happened. placeholder: Tell us what you see! - value: "A bug happened!" validations: required: true - type: textarea @@ -21,31 +31,41 @@ body: label: What did you expect to happen? description: Please give us a brief description of what you expected to happen. placeholder: Tell us what you wish happened! - value: "Instead of x, y should happen instead!" validations: required: true - type: input id: version attributes: label: Version - description: What version of calckey is your instance running? You can find this by clicking your instance's logo at the bottom left and then clicking instance information. - placeholder: Calckey Version 13.0.4 + description: What version of firefish is your instance running? You can find this by clicking your instance's logo at the bottom left and then clicking instance information. + placeholder: v13.1.4.1 validations: required: true - type: input id: instance attributes: label: Instance - description: What instance of calckey are you using? - placeholder: stop.voring.me + description: What instance of firefish are you using? + placeholder: firefish.social validations: required: false - type: dropdown - id: browsers + id: issue-type attributes: - label: What browser are you using? + label: What type of issue is this? + description: If this happens on your device and has to do with the user interface, it's client-side. If this happens on either with the API or the backend, or you got a server-side error in the client, it's server-side. multiple: false options: + - Client-side + - Server-side + - Other (Please Specify) + - type: dropdown + id: browsers + attributes: + label: What browser are you using? (Client-side issues only) + multiple: false + options: + - N/A - Firefox - Chrome - Brave @@ -54,6 +74,50 @@ body: - Safari - Microsoft Edge - Other (Please Specify) + - type: dropdown + id: device + attributes: + label: What operating system are you using? (Client-side issues only) + multiple: false + options: + - N/A + - Windows + - MacOS + - Linux + - Android + - iOS + - Other (Please Specify) + - type: dropdown + id: deplotment-method + attributes: + label: How do you deploy Firefish on your server? (Server-side issues only) + multiple: false + options: + - N/A + - Manual + - Ubuntu Install Script + - Docker Compose + - Docker Prebuilt Image + - Helm Chart + - YunoHost + - AUR Package + - Other (Please Specify) + - type: dropdown + id: operating-system + attributes: + label: What operating system are you using? (Server-side issues only) + multiple: false + options: + - N/A + - Ubuntu >= 22.04 + - Ubuntu < 22.04 + - Debian + - Arch + - RHEL (CentOS/AlmaLinux/Rocky Linux) + - FreeBSD + - OpenBSD + - Android + - Other (Please Specify) - type: textarea id: logs attributes: @@ -64,7 +128,9 @@ body: id: terms attributes: label: Contribution Guidelines - description: By submitting this issue, you agree to follow our [Contribution Guidelines](https://codeberg.org/calckey/calckey/src/branch/develop/CONTRIBUTING.md) + description: By submitting this issue, you agree to follow our [Contribution Guidelines](https://gitlab.prometheus.systems/firefish/firefish/src/branch/develop/CONTRIBUTING.md) options: - label: I agree to follow this project's Contribution Guidelines required: true + - label: I have searched the issue tracker for similar issues, and this is not a duplicate. + required: true diff --git a/issue_template/feature.yaml b/issue_template/feature.yaml index 32f7f2c105..0be916fb31 100644 --- a/issue_template/feature.yaml +++ b/issue_template/feature.yaml @@ -1,18 +1,28 @@ -name: Feature Request +name: ✨ Feature Request about: Request a Feature title: "[Feature]: " +blank_issues_enabled: true +contact_links: + - name: 💁 Support Matrix + url: https://matrix.to/#/%23firefish:matrix.fedibird.com + about: Having trouble with deployment? Ask the support chat. + - name: 🔒 Resposible Disclosure + url: https://gitlab.prometheus.systems/firefish/firefish/src/branch/develop/SECURITY.md + about: Found a security vulnerability? Please disclose it responsibly. body: - type: markdown attributes: value: | - Thanks for taking the time to fill out this feature request! + 💖 Thanks for taking the time to fill out this feature request! + 💁 Having trouble with deployment? [Ask the support chat.](https://matrix.to/#/%23firefish:matrix.fedibird.com) + 🔒 Found a security vulnerability? [Please disclose it responsibly.](https://gitlab.prometheus.systems/firefish/firefish/src/branch/develop/SECURITY.md) + 🤝 By submitting this issue, you agree to follow our [Contribution Guidelines.](https://gitlab.prometheus.systems/firefish/firefish/src/branch/develop/CONTRIBUTING.md) - type: textarea id: what-feature attributes: label: What feature would you like implemented? description: Please give us a brief description of what you'd like. placeholder: Tell us what you want! - value: "x feature would be great!" validations: required: true - type: textarea @@ -21,50 +31,31 @@ body: label: Why should we add this feature? description: Please give us a brief description of why your feature is important. placeholder: Tell us why you want this feature! - value: "x feature is super useful because y!" validations: required: true - type: input id: version attributes: label: Version - description: What version of calckey is your instance running? You can find this by clicking your instance's logo at the bottom left and then clicking instance information. - placeholder: Calckey Version 13.0.4 + description: What version of firefish is your instance running? You can find this by clicking your instance's logo at the bottom left and then clicking instance information. + placeholder: Firefish Version 13.1.4.1 validations: required: true - type: input id: instance attributes: label: Instance - description: What instance of calckey are you using? - placeholder: stop.voring.me + description: What instance of firefish are you using? + placeholder: firefish.social validations: required: false - - type: dropdown - id: browsers - attributes: - label: What browser are you using? - multiple: false - options: - - Firefox - - Chrome - - Brave - - Librewolf - - Chromium - - Safari - - Microsoft Edge - - Other (Please Specify) - - type: textarea - id: logs - attributes: - label: Relevant log output - description: Please copy and paste any relevant log output. You can find your log by inspecting the page, and going to the "console" tab. This will be automatically formatted into code, so no need for backticks. - render: shell - type: checkboxes id: terms attributes: label: Contribution Guidelines - description: By submitting this issue, you agree to follow our [Contribution Guidelines](https://codeberg.org/calckey/calckey/src/branch/develop/CONTRIBUTING.md) + description: By submitting this issue, you agree to follow our [Contribution Guidelines](https://gitlab.prometheus.systems/firefish/firefish/src/branch/develop/CONTRIBUTING.md) options: - label: I agree to follow this project's Contribution Guidelines required: true + - label: I have searched the issue tracker for similar requests, and this is not a duplicate. + required: true diff --git a/kubernetes-README.md b/kubernetes-README.md deleted file mode 100644 index 710d0dee06..0000000000 --- a/kubernetes-README.md +++ /dev/null @@ -1,45 +0,0 @@ -# Running a Calckey instance with Kubernetes and Helm - -This is a [Helm](https://helm.sh/) chart directory in the root of the project -that you can use to deploy calckey to a Kubernetes cluster - -## Deployment - -1. Copy the example helm values and make your changes: -```shell -cp .config/helm_values_example.yml .config/helm_values.yml -``` - -2. Update helm dependencies: -```shell -cd chart -helm dependency list $dir 2> /dev/null | tail +2 | head -n -1 | awk '{ print "helm repo add " $1 " " $3 }' | while read cmd; do $cmd; done; -cd ../ -``` - -3. Create the calckey helm release (also used to update existing deployment): -```shell -helm upgrade \ - --install \ - --namespace calckey \ - --create-namespace \ - calckey chart/ \ - -f .config/helm_values.yml -``` - -4. Watch your calckey instance spin up: -```shell -kubectl -n calckey get po -w -``` - -5. Initial the admin user and managed config: -```shell -export CALCKEY_USERNAME="my_desired_admin_handle" && \ -export CALCKEY_PASSWORD="myDesiredInitialPassword" && \ -export CALCKEY_HOST="calckey.example.com" && \ -export CALCKEY_TOKEN=$(curl -X POST https://$CALCKEY_HOST/api/admin/accounts/create -H "Content-Type: application/json" -d "{ \"username\":\"$CALCKEY_USERNAME\", \"password\":\"$CALCKEY_PASSWORD\" }" | jq -r '.token') && \ -echo "Save this token: ${CALCKEY_TOKEN}" && \ -curl -X POST -H "Authorization: Bearer $CALCKEY_TOKEN" https://$CALCKEY_HOST/api/admin/accounts/hosted -``` - -6. Enjoy! diff --git a/locales/ar-SA.yml b/locales/ar-SA.yml index fc29623ddc..57a813a20b 100644 --- a/locales/ar-SA.yml +++ b/locales/ar-SA.yml @@ -176,7 +176,6 @@ operations: "الإجراءات" software: "البرمجية" version: "الإصدار" metadata: "البيانات الوصفية" -withNFiles: "{n} ملف (ملفات)" monitor: "شاشة التحكم" jobQueue: "قائمة الانتظار" cpuAndMemory: "وحدة المعالجة المركزية والذاكرة" @@ -197,7 +196,7 @@ noUsers: "ليس هناك مستخدمون" editProfile: "تعديل الملف التعريفي" noteDeleteConfirm: "هل تريد حذف هذه الملاحظة؟" pinLimitExceeded: "لا يمكنك تدبيس الملاحظات بعد الآن." -intro: "لقد انتهت عملية تنصيب Misskey. الرجاء إنشاء حساب إداري." +intro: "لقد انتهت عملية تنصيب Firefish. الرجاء إنشاء حساب إداري." done: "تمّ" processing: "المعالجة جارية" preview: "معاينة" @@ -372,7 +371,7 @@ exploreFediverse: "استكشف الفديفرس" popularTags: "الوسوم الرائجة" userList: "القوائم" about: "عن" -aboutMisskey: "عن Misskey" +aboutFirefish: "عن Firefish" administrator: "المدير" token: "الرمز المميز" twoStepAuthentication: "الإستيثاق بعاملَيْن" @@ -856,7 +855,7 @@ _registry: keys: "المفاتيح" domain: "النّطاق" createKey: "أنشئ مفتاحًا" -_aboutMisskey: +_aboutFirefish: about: "ميسكي هو برمجية مفتوحة المصدر يطورها syuilo منذ 2014." contributors: "المساهمون الرئيسيون" allContributors: "كل المساهمين" @@ -1028,7 +1027,7 @@ _time: hour: "سا" day: "ي" _tutorial: - title: "How to use Calckey" + title: "How to use Firefish" step1_1: "Welcome!" step1_2: "Let's get you set up. You'll be up and running in no time!" step2_1: "First, please fill out your profile." @@ -1045,13 +1044,13 @@ _tutorial: step5_6: "The Social {icon} timeline is where you can see posts from friends of your followers." step5_7: "The Global {icon} timeline is where you can see posts from every other connected instance." step6_1: "So, what is this place?" - step6_2: "Well, you didn't just join Calckey. You joined a portal to the Fediverse, an interconnected network of thousands of servers, called \"instances\"." - step6_3: "Each server works in different ways, and not all servers run Calckey. This one does though! It's a bit complicated, but you'll get the hang of it in no time." + step6_2: "Well, you didn't just join Firefish. You joined a portal to the Fediverse, an interconnected network of thousands of servers, called \"instances\"." + step6_3: "Each server works in different ways, and not all servers run Firefish. This one does though! It's a bit complicated, but you'll get the hang of it in no time." step6_4: "Now go, explore, and have fun!" _2fa: alreadyRegistered: "سجلت سلفًا جهازًا للاستيثاق بعاملين." - registerDevice: "سجّل جهازًا جديدًا" - registerKey: "تسجيل مفتاح أمان جديد" + registerTOTP: "سجّل جهازًا جديدًا" + registerSecurityKey: "تسجيل مفتاح أمان جديد" step1: "أولًا ثبّت تطبيق استيثاق على جهازك (مثل {a} و{b})." step2: "امسح رمز الاستجابة السريعة الموجد على الشاشة." step3: "أدخل الرمز الموجود في تطبيقك لإكمال التثبيت." @@ -1180,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 3ed6e53aa6..962f1fccca 100644 --- a/locales/bn-BD.yml +++ b/locales/bn-BD.yml @@ -177,7 +177,6 @@ operations: "ক্রিয়াকলাপ" software: "সফটওয়্যার" version: "সংস্করণ" metadata: "মেটাডাটা" -withNFiles: "{n} টি ফাইল" monitor: "মনিটর" jobQueue: "জব কিউ" cpuAndMemory: "সিপিউ এবং মেমরি" @@ -199,9 +198,9 @@ noUsers: "কোন ব্যাবহারকারী নেই" editProfile: "প্রোফাইল সম্পাদনা করুন" noteDeleteConfirm: "আপনি কি নোট ডিলিট করার ব্যাপারে নিশ্চিত?" pinLimitExceeded: "আপনি আর কোন নোট পিন করতে পারবেন না" -intro: "Misskey এর ইন্সটলেশন সম্পন্ন হয়েছে!দয়া করে অ্যাডমিন ইউজার তৈরি করুন।" +intro: "Firefish এর ইন্সটলেশন সম্পন্ন হয়েছে!দয়া করে অ্যাডমিন ইউজার তৈরি করুন।" done: "সম্পন্ন" -processing: "প্রক্রিয়াধীন..." +processing: "প্রক্রিয়াধীন" preview: "পূর্বরূপ দেখুন" default: "পূর্বনির্ধারিত" noCustomEmojis: "কোন ইমোজি নাই" @@ -377,7 +376,7 @@ exploreFediverse: "Fediverse ঘুরে দেখুন" popularTags: "জনপ্রিয় ট্যাগগুলি" userList: "লিস্ট" about: "আপনার সম্পর্কে" -aboutMisskey: "Misskey সম্পর্কে" +aboutFirefish: "Firefish সম্পর্কে" administrator: "প্রশাসক" token: "টোকেন" twoStepAuthentication: "২-ধাপ প্রমাণীকরণ" @@ -644,7 +643,7 @@ createNew: "নতুন" optional: "প্রয়োজনীয় নয়" createNewClip: "নতুন ক্লিপ তৈরি করুন" public: "সর্বজনীন" -i18nInfo: "Calckey স্বেচ্ছাসেবকদের দ্বারা বিভিন্ন ভাষায় অনুবাদ করা হচ্ছে। আপনি {link} এ গিয়ে অনুবাদে সহযোগিতা করতে পারেন।" +i18nInfo: "Firefish স্বেচ্ছাসেবকদের দ্বারা বিভিন্ন ভাষায় অনুবাদ করা হচ্ছে। আপনি {link} এ গিয়ে অনুবাদে সহযোগিতা করতে পারেন।" manageAccessTokens: "অ্যাক্সেস টোকেন পরিচালনা করুন" accountInfo: "অ্যাকাউন্টের তথ্য" notesCount: "নোটের সংখ্যা" @@ -695,7 +694,7 @@ onlineUsersCount: "{n} জন ব্যাবহারকারী অনলা nUsers: "{n} জন ব্যাবহারকারী" nNotes: "{n} টি নোট" sendErrorReports: "ক্রুটি প্রতিবেদন পাঠান" -sendErrorReportsDescription: "চালু থাকলে, বিস্তারিত ত্রুটির তথ্য Misskey-এর সাথে শেয়ার করা হয়। যা সফ্টওয়্যারটির গুণমান উন্নত করতে সাহায্য করে। ত্রুটির তথ্যের মধ্যে রয়েছে OS সংস্করণ, ব্রাউজারের ধরন, কর্মের ইতিহাস ইত্যাদি।" +sendErrorReportsDescription: "চালু থাকলে, বিস্তারিত ত্রুটির তথ্য Firefish-এর সাথে শেয়ার করা হয়। যা সফ্টওয়্যারটির গুণমান উন্নত করতে সাহায্য করে। ত্রুটির তথ্যের মধ্যে রয়েছে OS সংস্করণ, ব্রাউজারের ধরন, কর্মের ইতিহাস ইত্যাদি।" myTheme: "আমার থিম" backgroundColor: "পটভূমির রং" accentColor: "এক্সেন্টের রং" @@ -786,7 +785,7 @@ hashtags: "হ্যাশট্যাগ" troubleshooting: "ট্রাবলশুটিং" useBlurEffect: "UI তে ব্লার ইফেক্ট ব্যাবহার করুন" learnMore: "আরও জানুন" -misskeyUpdated: "Misskey আপডেট করা হয়েছে!" +misskeyUpdated: "Firefish আপডেট করা হয়েছে!" whatIsNew: "পরিবর্তনগুলি দেখান" translate: "অনুবাদ" translatedFrom: "{x} হতে অনুবাদ করা" @@ -897,13 +896,13 @@ _registry: keys: "কী - সমূহ" domain: "ডোমেন" createKey: "কী বানান" -_aboutMisskey: +_aboutFirefish: about: "Misskey, একটি ওপেন সোর্স সফ্টওয়্যার যা 2014 সাল থেকে syuilo তৈরি করছেন।" contributors: "প্রধান কন্ট্রিবিউটারগণ" allContributors: "সকল কন্ট্রিবিউটারগণ" source: "সোর্স কোড" - translation: "Misskey অনুবাদ করুন" - donate: "Misskey তে দান করুন" + translation: "Firefish অনুবাদ করুন" + donate: "Firefish তে দান করুন" morePatrons: "আরও অনেকে আমাদের সাহায্য করছেন। তাদের সবাইকে ধন্যবাদ 🥰" patrons: "সমর্থনকারী" _nsfw: @@ -912,7 +911,7 @@ _nsfw: force: "সকল মিডিয়া লুকান" _mfm: cheatSheet: "MFM চিটশিট" - intro: "MFM একটি মার্কআপ ভাষা যা Misskey-এর মধ্যে বিভিন্ন জায়গায় ব্যবহার করা যেতে পারে। এখানে আপনি MFM-এর সিনট্যাক্সগুলির একটি তালিকা দেখতে পারবেন।" + intro: "MFM একটি মার্কআপ ভাষা যা Firefish-এর মধ্যে বিভিন্ন জায়গায় ব্যবহার করা যেতে পারে। এখানে আপনি MFM-এর সিনট্যাক্সগুলির একটি তালিকা দেখতে পারবেন।" dummy: "মিসকি ফেডিভার্সের বিশ্বকে প্রসারিত করে" mention: "উল্লেখ" mentionDescription: "@ চিহ্ন + ব্যবহারকারীর নাম একটি নির্দিষ্ট ব্যবহারকারীকে নির্দেশ করতে ব্যবহার করা যায়।" @@ -1109,7 +1108,7 @@ _time: hour: "ঘণ্টা" day: "দিন" _tutorial: - title: "How to use Calckey" + title: "How to use Firefish" step1_1: "Welcome!" step1_2: "Let's get you set up. You'll be up and running in no time!" step2_1: "First, please fill out your profile." @@ -1126,13 +1125,13 @@ _tutorial: step5_6: "The Social {icon} timeline is where you can see posts from friends of your followers." step5_7: "The Global {icon} timeline is where you can see posts from every other connected instance." step6_1: "So, what is this place?" - step6_2: "Well, you didn't just join Calckey. You joined a portal to the Fediverse, an interconnected network of thousands of servers, called \"instances\"." - step6_3: "Each server works in different ways, and not all servers run Calckey. This one does though! It's a bit complicated, but you'll get the hang of it in no time." + step6_2: "Well, you didn't just join Firefish. You joined a portal to the Fediverse, an interconnected network of thousands of servers, called \"instances\"." + step6_3: "Each server works in different ways, and not all servers run Firefish. This one does though! It's a bit complicated, but you'll get the hang of it in no time." step6_4: "Now go, explore, and have fun!" _2fa: alreadyRegistered: "আপনি ইতিমধ্যে একটি 2-ফ্যাক্টর অথেনটিকেশন ডিভাইস নিবন্ধন করেছেন৷" - registerDevice: "নতুন ডিভাইস নিবন্ধন করুন" - registerKey: "সিকিউরিটি কী নিবন্ধন করুন" + registerTOTP: "নতুন ডিভাইস নিবন্ধন করুন" + registerSecurityKey: "সিকিউরিটি কী নিবন্ধন করুন" step1: "প্রথমে, আপনার ডিভাইসে {a} বা {b} এর মতো একটি অথেনটিকেশন অ্যাপ ইনস্টল করুন৷" step2: "এরপরে, অ্যাপের সাহায্যে প্রদর্শিত QR কোডটি স্ক্যান করুন।" step2Url: "ডেস্কটপ অ্যাপে, নিম্নলিখিত URL লিখুন:" @@ -1269,7 +1268,7 @@ _profile: youCanIncludeHashtags: "হ্যাশট্যাগ অন্তর্ভুক্ত করা যেতে পারে।" metadata: "অতিরিক্ত তথ্য" metadataEdit: "অতিরিক্ত তথ্য সম্পাদনা করুন" - metadataDescription: "আপনি আপনার প্রোফাইলে একটি টেবিল হিসাবে চারটি অতিরিক্ত তথ্য দেখাতে পারেন।" + metadataDescription: "আপনি আপনার প্রোফাইলে একটি টেবিল হিসাবে চারটি অতিরিক্ত তথ্য দেখাতে পারেন।. আপনি আপনার প্রোফাইলে লিঙ্কটি যাচাই করতে {rel} এর সাথে একটি {a} ট্যাগ বা {l} ট্যাগ যোগ করতে পারেন!" metadataLabel: "লেবেল" metadataContent: "বিষয়বস্তু" changeAvatar: "অ্যাভাটার পরিবর্তন করুন" diff --git a/locales/bul_BG.yml b/locales/bul_BG.yml new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/locales/bul_BG.yml @@ -0,0 +1 @@ +{} diff --git a/locales/ca-ES.yml b/locales/ca-ES.yml index f329275191..5a943e5045 100644 --- a/locales/ca-ES.yml +++ b/locales/ca-ES.yml @@ -1,197 +1,2170 @@ ---- _lang_: "Català" -headlineMisskey: "Una xarxa connectada per notes" -introMisskey: "Benvingut! Misskey és un servei de microblogging descentralitzat de codi obert.\nCrea \"notes\" per compartir els teus pensaments amb tots els que t'envolten. 📡\nAmb \"reaccions\", també pots expressar ràpidament els teus sentiments sobre les notes de tothom. 👍\nExplorem un món nou! 🚀" +headlineMisskey: "Una xarxa social de codi obert, descentralitzada i gratuïta per + a sempre! 🚀" +introMisskey: "Benvinguts! Firefish és una plataforma social de codi obert, descentralitzada + i gratuïta per a sempre! 🚀" monthAndDay: "{day}/{month}" -search: "Cercar" +search: "Cerca" notifications: "Notificacions" username: "Nom d'usuari" password: "Contrasenya" forgotPassword: "Contrasenya oblidada" fetchingAsApObject: "Cercant en el Fediverse" -ok: "OK" +ok: "D'acord" gotIt: "Ho he entès!" -cancel: "Cancel·lar" +cancel: "Cancel·la" enterUsername: "Introdueix el teu nom d'usuari" -renotedBy: "Resignat per {usuari}" -noNotes: "Cap nota" +renotedBy: "Impulsat per {user}" +noNotes: "Cap publicació" noNotifications: "Cap notificació" -instance: "Instàncies" +instance: "Servidor" settings: "Preferències" basicSettings: "Configuració bàsica" -otherSettings: "Configuració avançada" -openInWindow: "Obrir en una nova finestra" +otherSettings: "Altres opcions" +openInWindow: "Obre en una finestra nova" profile: "Perfil" timeline: "Línia de temps" noAccountDescription: "Aquest usuari encara no ha escrit la seva biografia." -login: "Iniciar sessió" -loggingIn: "Identificant-se" -logout: "Tancar la sessió" -signup: "Registrar-se" -uploading: "Pujant..." -save: "Desar" +login: "Inicia sessió" +loggingIn: "Iniciant sessió" +logout: "Tanca la sessió" +signup: "Registra'm" +uploading: "S'està pujant…" +save: "Desa" users: "Usuaris" -addUser: "Afegir un usuari" -favorite: "Afegir a preferits" -favorites: "Favorits" -unfavorite: "Eliminar dels preferits" -favorited: "Afegit als preferits." -alreadyFavorited: "Ja s'ha afegit als preferits." -cantFavorite: "No s'ha pogut afegir als preferits." -pin: "Fixar al perfil" -unpin: "Para de fixar del perfil" -copyContent: "Copiar el contingut" -copyLink: "Copiar l'enllaç" -delete: "Eliminar" -deleteAndEdit: "Esborrar i editar" -deleteAndEditConfirm: "Estàs segur que vols suprimir aquesta nota i editar-la? Perdràs totes les reaccions, notes i respostes." -addToList: "Afegir a una llista" -sendMessage: "Enviar un missatge" -copyUsername: "Copiar nom d'usuari" -searchUser: "Cercar usuaris" -reply: "Respondre" -loadMore: "Carregar més" -showMore: "Veure més" +addUser: "Afegeix un usuari" +favorite: "Afegeix als marcadors" +favorites: "Marcadors" +unfavorite: "Elimina dels marcadors" +favorited: "S'ha afegit el marcador." +alreadyFavorited: "Ja està afegida als marcadors." +cantFavorite: "No s'ha pogut afegir als marcadors." +pin: "Fixa al perfil" +unpin: "Deixa de fixar al perfil" +copyContent: "Copia el contingut" +copyLink: "Copia l'enllaç" +delete: "Elimina" +deleteAndEdit: "Elimina i edita" +deleteAndEditConfirm: "Segur que vols eliminar la publicació i editar-la? Perdràs + totes les reaccions, impulsos i respostes." +addToList: "Afegeix a la llista" +sendMessage: "Envia un missatge" +copyUsername: "Copia el nom d'usuari" +searchUser: "Cerca un usuari" +reply: "Respon" +loadMore: "Carrega'n més" +showMore: "Mostra'n més" youGotNewFollower: "t'ha seguit" receiveFollowRequest: "Sol·licitud de seguiment rebuda" followRequestAccepted: "Sol·licitud de seguiment acceptada" mention: "Menció" mentions: "Mencions" -directNotes: "Notes directes" -importAndExport: "Importar / Exportar" -import: "Importar" -export: "Exportar" +directNotes: "Missatges directes" +importAndExport: "Importa/exporta dades" +import: "Importa" +export: "Exporta" files: "Fitxers" -download: "Baixar" -driveFileDeleteConfirm: "Estàs segur que vols suprimir el fitxer \"{name}\"? Les notes associades a aquest fitxer adjunt també se suprimiran." -unfollowConfirm: "Estàs segur que vols deixar de seguir {name}?" -exportRequested: "Has sol·licitat una exportació. Això pot trigar una estona. S'afegirà a la teva unitat un cop completat." +download: "Baixa" +driveFileDeleteConfirm: "Segur que vols eliminar el fitxer «{name}»? S'eliminarà de + totes les notes que el continguin com a fitxer adjunt." +unfollowConfirm: "Segur que vols deixar de seguir a {name}?" +exportRequested: "Has sol·licitat una exportació. Això pot trigar una estona. S'afegirà + al teu Disc un cop completada." importRequested: "Has sol·licitat una importació. Això pot trigar una estona." lists: "Llistes" -noLists: "No tens cap llista" -note: "Nota" -notes: "Notes" +noLists: "No teniu cap llista" +note: "Publicació" +notes: "Publicacions" following: "Seguint" followers: "Seguidors" followsYou: "Et segueix" -createList: "Crear llista" -manageLists: "Gestionar les llistes" +createList: "Crea una llista" +manageLists: "Gestiona les llistes" error: "Error" somethingHappened: "S'ha produït un error" retry: "Torna-ho a intentar" -pageLoadError: "S'ha produït un error en carregar la pàgina" -pageLoadErrorDescription: "Això normalment es deu a errors de xarxa o a la memòria cau del navegador. Prova d'esborrar la memòria cau i torna-ho a provar després d'esperar una estona." +pageLoadError: "S'ha produït un error en carregar la pàgina." +pageLoadErrorDescription: "Això normalment es deu a errors de xarxa o a la memòria + cau del navegador. Prova d'esborrar la memòria cau i torna-ho a provar després d'esperar + una estona." serverIsDead: "Aquest servidor no respon. Espera una estona i torna-ho a provar." -youShouldUpgradeClient: "Per veure aquesta pàgina, actualitzeu-la per actualitzar el vostre client." +youShouldUpgradeClient: "Per veure aquesta pàgina, actualitzeu-la per actualitzar + el vostre client." enterListName: "Introdueix un nom per a la llista" privacy: "Privadesa" makeFollowManuallyApprove: "Les sol·licituds de seguiment requereixen aprovació" defaultNoteVisibility: "Visibilitat per defecte" -follow: "Seguint" -followRequest: "Enviar la sol·licitud de seguiment" +follow: "Segueix" +followRequest: "Sol·licitud de Seguiment" followRequests: "Sol·licituds de seguiment" -unfollow: "Deixar de seguir" +unfollow: "Deixa de seguir" followRequestPending: "Sol·licituds de seguiment pendents" -enterEmoji: "Introduir un emoji" -renote: "Renotar" -unrenote: "Anul·lar renota" -renoted: "Renotat." -cantRenote: "Aquesta publicació no pot ser renotada." -cantReRenote: "Impossible renotar una renota." -quote: "Citar" -pinnedNote: "Nota fixada" -pinned: "Fixar al perfil" +enterEmoji: "Introdueix un emoji" +renote: "Impulsa" +unrenote: "Anul·la l'impuls" +renoted: "S'ha impulsat." +cantRenote: "Aquesta publicació no es pot impulsar." +cantReRenote: "No es pot impulsar un impuls." +quote: "Cita" +pinnedNote: "Publicació fixada" +pinned: "Fixa al perfil" you: "Tu" -clickToShow: "Fes clic per mostrar" +clickToShow: "Fes clic per a mostrar" sensitive: "NSFW" -add: "Afegir" +add: "Afegeix" reaction: "Reaccions" reactionSetting: "Reaccions a mostrar al selector de reaccions" -reactionSettingDescription2: "Arrossega per reordenar, fes clic per suprimir, prem \"+\" per afegir." +reactionSettingDescription2: "Arrossega per reordenar, fes clic per suprimir, prem + \"+\" per afegir." rememberNoteVisibility: "Recorda la configuració de visibilitat de les notes" -attachCancel: "Eliminar el fitxer adjunt" -markAsSensitive: "Marcar com a NSFW" -unmarkAsSensitive: "Deixar de marcar com a sensible" -enterFileName: "Defineix nom del fitxer" +attachCancel: "Elimina el fitxer adjunt" +markAsSensitive: "Marca com a NSFW" +unmarkAsSensitive: "Desmarca com a NSFW" +enterFileName: "Introdueix un nom de fitxer" mute: "Silencia" unmute: "Deixa de silenciar" block: "Bloqueja" unblock: "Desbloqueja" suspend: "Suspèn" -unsuspend: "Deixa de suspendre" -instances: "Instàncies" -remove: "Eliminar" +unsuspend: "Treu la suspensió" +instances: "Servidors" +remove: "Elimina" nsfw: "NSFW" -pinnedNotes: "Nota fixada" +pinnedNotes: "Publicacions fixades" userList: "Llistes" smtpUser: "Nom d'usuari" smtpPass: "Contrasenya" -user: "Usuaris" +user: "Usuari" searchByGoogle: "Cercar" -file: "Fitxers" +file: "Fitxer" _email: _follow: - title: "t'ha seguit" + title: "Tens un nou seguidor" + _receiveFollowRequest: + title: Heu rebut una sol·licitud de seguiment _mfm: mention: "Menció" quote: "Citar" search: "Cercar" + dummy: Firefish amplia el món del Fediverse + hashtag: Etiqueta + intro: MFM és un llenguatge de marques utilitzat a Misskey, Firefish, Akkoma i més + que es pot utilitzar en molts llocs. Aquí podeu veure una llista de tota la sintaxi + MFM disponible. + hashtagDescription: Podeu especificar una etiqueta mitjançant un coixinet i un text. + url: URL + urlDescription: Es poden mostrar URLS. + link: Enllaç + linkDescription: Parts específiques del text es poden mostrar com a URL. + bold: Negreta + boldDescription: Ressalta les lletres fent-les més gruixudes. + smallDescription: Mostra contingut petit i prim. + small: Petit + centerDescription: Mostra el contingut centrat. + inlineCode: Codi (en línia) + inlineMathDescription: Mostra fórmules matemàtiques (KaTeX) en línia + blockCode: Codi (Bloc) + blockCodeDescription: Mostra el ressaltat de sintaxi per al codi de diverses línies + (programa) en un bloc. + inlineMath: Matemàtiques (en línia) + jellyDescription: Dóna al contingut una animació semblant a una gelatina. + bounceDescription: Ofereix al contingut una animació de rebot. + jumpDescription: Dóna al contingut una animació de salt. + shake: Animació (Shake) + shakeDescription: Dóna al contingut una animació tremolosa. + bounce: Animació (Bounce) + x3Description: Mostra contingut encara més gran. + x2Description: Mostra contingut més gran. + twitchDescription: Ofereix al contingut una animació fortament convulsa. + spin: Animació (Spin) + spinDescription: Dóna al contingut una animació giratòria. + x2: Gran + x3: Molt gran + x4: Increïblement gran + blur: Desenfocament + x4Description: Mostra contingut fins i tot més gran que gran que gran. + rainbowDescription: Fa que el contingut aparegui en colors de l'arc de Sant Martí. + sparkle: Brillantor + sparkleDescription: Dóna al contingut un efecte de partícula brillant. + rotate: Girar + rotateDescription: Gira el contingut en un angle especificat. + positionDescription: Mou el contingut en una quantitat especificada. + fontDescription: Estableix el tipus de lletra en què voleu mostrar el contingut. + position: Posició + rainbow: Arc de Sant Martí + jelly: Animació (Jelly) + tada: Animació (Tada) + tadaDescription: Dóna al contingut una animació tipus "Tada!". + jump: Animació (Jump) + twitch: Animació (Twitch) + blurDescription: Desenfoca el contingut. Es mostrarà clarament quan passeu el cursor + per sobre. + font: Tipus de lletra + cheatSheet: Full de trucs de MFM + mentionDescription: Podeu especificar un usuari mitjançant un arrova i un nom d'usuari. + center: Centre + inlineCodeDescription: Mostra el ressaltat de sintaxi en línia per al codi (de programa). + blockMath: Matemàtiques (Bloc) + blockMathDescription: Mostra fórmules matemàtiques (KaTeX) en un bloc + quoteDescription: Mostra el contingut com una cita. + emoji: Emoji personalitzat + emojiDescription: Un emoji personalitzat és pot mostrar envoltant el nom amb dos + punts. + searchDescription: Mostra un quadre de cerca amb el text introduït prèviament. + flip: Capgirar + flipDescription: Capgira el contingut horitzontalment o verticalment. + plainDescription: Desactiva els efectes de tots els MFM continguts en aquest efecte + MFM. + scale: Escala + foreground: Color de primer pla + background: Color de fons + backgroundDescription: Canvia el color de fons del text. + scaleDescription: Escala el contingut en una quantitat especificada. + foregroundDescription: Canvia el color de primer pla del text. + plain: Pla + stop: Parar MFM + play: Posar en marxa MFM + warn: MFM pot contenir animacions cridaneres o que es mouen ràpidament + alwaysPlay: Reprodueix automàticament tots els MFM animats + fade: Esvair + fadeDescription: Esvaeix el contingut cap a dintre i cap en fora. + crop: Retallar + advanced: MFM avançat + advancedDescription: Si està desactivat, només permet l'etiquetatge bàsic tret que + es reproduïnt un MFM animat + cropDescription: Retalla el contingut. _theme: keys: mention: "Menció" - renote: "Renotar" + renote: "Impulsar" + fg: Text + navBg: Fons de la barra lateral + navFg: Text de la barra lateral + navHoverFg: Text de la barra lateral (Hover) + hashtag: Etiquetes + mentionMe: Mencions (Jo) + infoBg: Fons de l'informació + infoFg: Text informatiu + toastBg: Fons de notificació + listItemHoverBg: Fons de la llista d'elements (Hover) + driveFolderBg: Fons de la carpeta Disc + wallpaperOverlay: Superposició de fons de pantalla + badge: Distintiu + accentLighten: Accent (Lluminós) + accentDarken: Accent (enfosquit) + fgHighlighted: Text ressaltat + indicator: Indicador + focus: Centrar-se + panel: Panell + navIndicator: Indicador de la barra lateral + accent: Accent + header: Encapçalament + navActive: Text de la barra lateral (Active) + link: Enllaç + modalBg: Fons del modal + divider: Divisor + scrollbarHandle: Mànec de la barra de desplaçament + scrollbarHandleHover: Mànec de la barra de desplaçament (Hover) + dateLabelFg: Text de l'etiqueta de data + infoWarnBg: Fons d'advertència + cwBg: Fons del botó CW + cwFg: Text del botó CW + messageBg: Fons del xat + infoWarnFg: Text d'advertència + bg: Fons + shadow: Ombra + cwHoverBg: Fons del botó CW (Hover) + toastFg: Text de notificació + buttonHoverBg: Fons del botó (Hover) + inputBorder: Vora del camp d'entrada + buttonBg: Fons del botó + description: Descripció + installed: "{name} s'ha instal·lat" + installedThemes: Temes instal·lats + builtinThemes: Temes integrats + alreadyInstalled: Aquest tema ja està instal·lat + invalid: El format d'aquest tema no és vàlid + make: Fes un tema + defaultValue: 'Per defecte: {value}' + color: Color + refProp: Fes referència a una propietat + refConst: Fes referència a una constant + key: Clau + func: Funcions + funcKind: Tipus de funció + argument: Argument + basedProp: Propietat de referència + importInfo: Si introdueixes el codi de tema aquí, podeu importar-lo a l'editor de + temes + inputConstantName: Introdueix un nom per a aquesta constant + addConstant: Afegir una constant + code: Codi del tema + alpha: Opacitat + deleteConstantConfirm: De debò vols esborrar la constant {const}? + manage: Gestionar temes + explore: Explora Temes + darken: Enfosquir + base: Fundament + constant: Constant + lighten: Clar + install: Instal·lar un tema _sfx: - note: "Notes" + note: "Publicació nova" notification: "Notificacions" + antenna: Antenes + channel: Notificacions del canal + noteMy: Publicació propia + chat: Xat + chatBg: Fons del xat _2fa: step2Url: "També pots inserir aquest enllaç i utilitzes una aplicació d'escriptori:" + alreadyRegistered: Ja heu registrat un dispositiu d'autenticació de dos factors. + registerTOTP: Registrar un dispositiu nou + securityKeyInfo: A més de l'autenticació d'empremta digital o PIN, també podeu configurar + l'autenticació mitjançant claus de seguretat de maquinari compatibles amb FIDO2 + per protegir encara més el vostre compte. + step4: A partir d'ara, qualsevol intent d'inici de sessió futur demanarà aquest + token d'inici de sessió. + registerSecurityKey: Registrar una clau de seguretat o d'accés + step1: En primer lloc, instal·la una aplicació d'autenticació (com ara {a} o {b}) + al dispositiu. + step2: A continuació, escaneja el codi QR que es mostra en aquesta pantalla. + step3: Introdueix el token que t'ha proporcionat l'aplicació per finalitzar la configuració. + step3Title: Introduïu un codi d'autenticació + chromePasskeyNotSupported: Les claus de pas de Chrome actualment no s'admeten. + securityKeyName: Introduïu un nom de clau + removeKey: Suprimeix la clau de seguretat + removeKeyConfirm: Vols suprimir la clau {name}? + renewTOTP: Tornar a configurar l'aplicació d'autenticació + renewTOTPOk: Reconfigurar + renewTOTPCancel: Cancel·lar + step2Click: Fer clic en aquest codi QR us permetrà registrar 2FA a la vostra clau + de seguretat o aplicació d'autenticació del telèfon. + securityKeyNotSupported: El vostre navegador no admet claus de seguretat. + registerTOTPBeforeKey: Configureu una aplicació d'autenticació per registrar una + clau de seguretat o de passi. + tapSecurityKey: Si us plau, seguiu el vostre navegador per registrar la clau de + seguretat o d'accés + renewTOTPConfirm: Això farà que els codis de verificació de l'aplicació anterior + deixin de funcionar + whyTOTPOnlyRenew: L’aplicació d’autenticació no es pot eliminar sempre que es hi + hagi una clau de seguretat registrada. + token: Token 2FA _widgets: notifications: "Notificacions" timeline: "Línia de temps" + unixClock: Rellotge d'UNIX + federation: Federació + instanceCloud: Núvol de servidors + trends: Tendència + clock: Rellotge + calendar: Calendari + activity: Activitat + photos: Fotos + rssTicker: Teletip RSS + onlineUsers: Usuaris en línia + memo: Notes adhesives + digitalClock: Rellotge digital + postForm: Formulari per publicar + slideshow: Presentació de diapositives + serverMetric: Estadístiques del servidor + userList: Llista d'usuaris + rss: Lector d'RSS + jobQueue: Cua de treball + _userList: + chooseList: Selecciona una llista + aiscript: Consola AiScript + button: Botó + serverInfo: Informació del servidor + meiliStatus: Estat del servidor + meiliSize: Mida de l'índex + meiliIndexCount: Publicacions indexades _cw: show: "Carregar més" + files: '{count} fitxers' + hide: Amaga + chars: '{count} caràcters' _visibility: followers: "Seguidors" + publicDescription: La teva publicació serà visible per a totes les línies de temps + públiques + localOnly: Només Local + specified: Directe + home: Sense llistar + homeDescription: Publica només a la línea de temps local + followersDescription: Fes visible només per als teus seguidors i usuaris mencionats + specifiedDescription: Fer visible només per a usuaris determinats + public: Públic + localOnlyDescription: No és visible per als usuaris remots _profile: username: "Nom d'usuari" + metadataEdit: Editar informació addicional + youCanIncludeHashtags: També pots incloure etiquetes al teu perfil. + metadata: Informació adicional + description: Perfil + metadataLabel: Etiqueta + metadataContent: Contingut + changeAvatar: Canvia l'avatar + changeBanner: Canvia el banner + 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. Podeu afegir una etiqueta {a} o una etiqueta {l} amb {rel} per + verificar l'enllaç al vostre perfil!" _exportOrImport: - followingList: "Seguint" + followingList: "Usuaris que segueixes" muteList: "Silencia" blockingList: "Bloqueja" userLists: "Llistes" + excludeMutingUsers: Exclou els usuaris silenciats + allNotes: Totes les notes + excludeInactiveUsers: Exclou usuaris inactius _pages: script: categories: list: "Llistes" + flow: Control de flux + random: Aleatori + value: Valors + fn: Funcions + text: Operacions de text + convert: Transformacions + logical: Operació lògica + operation: Càlcul + comparison: Comparació blocks: _join: arg1: "Llistes" + arg2: Separador _randomPick: arg1: "Llistes" _dailyRandomPick: arg1: "Llistes" _seedRandomPick: arg2: "Llistes" + arg1: Llavor _pick: arg1: "Llistes" + arg2: Posició _listLen: arg1: "Llistes" + add: Afegir + _subtract: + arg1: A + arg2: B + subtract: Restar + _round: + arg1: Número + eq: A i B són iguals + _mod: + arg2: B + arg1: A + round: Arrodoniment decimal + _and: + arg1: A + arg2: B + or: A O B + _or: + arg1: A + arg2: B + lt: < A és menor que B + _lt: + arg1: A + arg2: B + gt: '> A és més gran que B' + _gt: + arg1: A + arg2: B + seedRannum: Nombre aleatori (amb llavor) + _seedRannum: + arg1: Llavor + arg2: Valor mínim + arg3: Valor màxim + _eq: + arg1: A + arg2: B + ltEq: <= A és menor o igual que B + _multiply: + arg2: B + arg1: A + divide: Dividir + notEq: A i B són diferents + _notEq: + arg1: A + arg2: B + and: A I B + _ltEq: + arg2: B + arg1: A + gtEq: '>= A és més gran o igual que B' + _gtEq: + arg1: A + arg2: B + if: Branca + _if: + arg1: Si + arg2: Aleshores + arg3: Altrament + not: NO + random: Aleatori + _dailyRandom: + arg1: Probabilitat + dailyRannum: Nombre aleatori (canvia un cop al dia per a cada usuari) + _add: + arg1: A + arg2: B + _divide: + arg1: A + arg2: B + mod: Resta + _not: + arg1: NO + _random: + arg1: Probabilitat + rannum: Nombre aleatori + _rannum: + arg1: Valor mínim + arg2: Valor màxim + randomPick: Tria aleatòriament de la llista + dailyRandom: Aleatori (canvia un cop al dia per a cada usuari) + _dailyRannum: + arg2: Valor màxim + arg1: Valor mínim + dailyRandomPick: Tria aleatòriament d'una llista (Canvis un cop al dia per a + cada usuari) + seedRandom: Aleatori (amb llavor) + _seedRandom: + arg1: Llavor + arg2: Probabilitat + seedRandomPick: Tria aleatòriament de la llista (amb llavor) + multiply: Multiplicar + text: Text + _strPick: + arg1: Text + arg2: Ubicació de la cadena + strPick: Extreure cadena + strReplace: Cadena de substitució + _strReplace: + arg1: Text + arg3: Substitueix per + arg2: Text a substituir + strReverse: Voltejar text + _strReverse: + arg1: Text + join: Concatenació de textos + pick: Selecciona de la llista + listLen: Obtenir la longitud de la llista + stringToNumber: Text a número + number: Número + _stringToNumber: + arg1: Text + splitStrByLine: Dividir el text per salts de línia + _fn: + slots: Ranures + slots-info: Separa cada ranura amb un salt de línia + arg1: Sortida + aiScriptVar: Variable AiScript + fn: Funció + for: Repetir + _numberToString: + arg1: Número + _DRPWPM: + arg1: Llista de text + numberToString: Número a text + _splitStrByLine: + arg1: Text + ref: Variable + DRPWPM: Tria aleatòriament d'una llista ponderada (Canvis un cop al dia per + a cada usuari) + _for: + arg1: Nombre de vegades a repetir + arg2: Acció + strLen: Longitud del text + multiLineText: Text (multilínia) + _strLen: + arg1: Text + textList: Llista de text + _textList: + info: Separa cada ranura amb un salt de línia types: array: "Llistes" + stringArray: Llista de text + boolean: Bandera + string: Text + number: Número + emptySlot: Ranura buida + enviromentVariables: Variables d'entorn + pageVariables: Variables de pàgina + argVariables: Ranures d'entrada + thereIsEmptySlot: L'espai {slot} està buit! + typeError: L'espai {slot} accepta valors del tipus "{expect}", però el valor proporcionat + és del tipus "{actual}"! + newPage: Crea una pàgina nova + editPage: Edita aquesta pàgina + readPage: S'està veient la font d'aquesta pàgina + created: Pàgina creada correctament + updated: Pàgina editada correctament + invalidNameText: Assegurat que el títol de la pàgina no estigui buit + editThisPage: Edita aquesta pàgina + deleted: Pàgina suprimida correctament + pageSetting: Configuració de la pàgina + nameAlreadyExists: L'URL de la pàgina especificat ja existeix + invalidNameTitle: L'URL de la pàgina especificat no és vàlid + viewPage: Consulta la teva pàgina + like: M'agrada + viewSource: Veure la font + summary: Resum de la pàgina + alignCenter: Centrar elements + hideTitleWhenPinned: Amaga el títol de la pàgina quan estigui fixat al perfil + font: Tipus de lletra + fontSerif: Serif + fontSansSerif: Sans Serif + eyeCatchingImageSet: Estableix una miniatura + eyeCatchingImageRemove: Suprimeix la miniatura + chooseBlock: Afegeix un bloc + selectType: Selecciona un tipus + enterVariableName: Introduïu un nom de variable + blocks: + section: Secció + text: Text + textarea: Àrea de text + image: Imatges + if: Si + _if: + variable: Variable + post: Formulari de notes + _post: + text: Contingut + attachCanvasImage: Adjuntar imatge de llenç + canvasId: ID del llenç + _textInput: + name: Nom de la variable + text: Títol + default: Valor per defecte + textInput: Entrada de text + _textareaInput: + name: Nom de la variable + text: Títol + default: Valor per defecte + textareaInput: Entrada de text multilínia + numberInput: Entrada numèrica + _note: + id: ID de la publicació + idDescription: També podeu enganxar l'URL de la publicació aquí. + detailed: Vista detallada + switch: Canviar + canvas: Llenç + _canvas: + id: Identificador de llenç + width: Amplada + height: Alçada + note: Publicació incrustada + _counter: + name: Nom de la variable + text: Títol + inc: Pas + _button: + text: Títol + colored: De colors + action: Comportament quan es prem el botó + _action: + _dialog: + content: Contingut + resetRandom: Restableix la llavor aleatòria + pushEvent: Envia un esdeveniment + _pushEvent: + event: Nom de l'esdeveniment + message: Missatge que s'ha de mostrar quan s'activa + variable: Variable per enviar + no-variable: Cap + dialog: Mostra un diàleg + callAiScript: Invoca AiScript + _callAiScript: + functionName: Nom de la funció + _switch: + default: Valor per defecte + name: Nom de la variable + text: Títol + counter: Comptador + _numberInput: + name: Nom de la variable + text: Títol + default: Valor per defecte + button: Botó + _radioButton: + name: Nom de la variable + title: Títol + values: Llista d'opcions separades per salts de línia + default: Valor per defecte + radioButton: Elecció + variableNameIsAlreadyUsed: Aquest nom de variable ja està en ús + contentBlocks: Contingut + inputBlocks: Entrada + specialBlocks: Especial + variables: Variables + title: Títol + url: URL de la pàgina + unlike: Elimina m'agrada + my: Les meves pàgines + liked: Pàgines que m'han agradat + content: Bloc de pàgines + featured: Popular + inspector: Inspector + contents: Contingut _notification: youWereFollowed: "t'ha seguit" _types: - follow: "Seguint" + follow: "Nous seguidors" mention: "Menció" - renote: "Renotar" + renote: "Impulsos" quote: "Citar" reaction: "Reaccions" + all: Tots + reply: Respostes + pollEnded: S'acaben les enquestes + receiveFollowRequest: S'han rebut peticions de seguiment + followRequestAccepted: Sol·licituds de seguiment acceptades + groupInvited: Invitacions per a grups + app: Notificacions d'aplicacions enllaçades + pollVote: Votacions a les enquestes _actions: reply: "Respondre" - renote: "Renotar" + renote: "Impulsos" + followBack: t'ha tornat el seguiment + youGotQuote: "{name} t'ha citat" + fileUploaded: El fitxer s'ha penjat correctament + youGotMention: "{nom} t'ha esmentat" + youGotReply: "{name} t'ha respost" + youRenoted: Impuls de {name} + youGotPoll: '{name} ha votat a la teva enquesta' + youGotMessagingMessageFromUser: "{name} t'ha enviat un missatge de xat" + youGotMessagingMessageFromGroup: S'ha enviat un missatge de xat al grup {name} + youReceivedFollowRequest: Has rebut una sol·licitud de seguiment + yourFollowRequestAccepted: S'ha acceptat la vostra sol·licitud de seguiment + pollEnded: Es resultat de la enquesta ja està disponible + emptyPushNotificationMessage: Les notificacions push s'han actualitzat + youWereInvitedToGroup: "{userName} t'ha convidat a un grup" + reacted: Ha reaccionat a la teva publicació + renoted: Ha impulsat la teva publicació + voted: Ha votat a la teva enquesta _deck: _columns: notifications: "Notificacions" tl: "Línia de temps" list: "Llistes" mentions: "Mencions" + widgets: Ginys + main: Principal + antenna: Antena + direct: Missatges directes + channel: Canal + alwaysShowMainColumn: Mostra sempre la columna principal + columnAlign: Alinear columnes + introduction: Crea la interfície perfecta per a tu organitzant columnes lliurement! + swapRight: Canvia amb la columna de la dreta + swapUp: Canvia amb la columna de d'alt + swapDown: Canvia amb la columna de sota + stackLeft: Apilar amb la columna de l'esquerra + popRight: Treu a la dreta + profile: Espai de treball + newProfile: Nou espai de treball + deleteProfile: Suprimir l'espai de treball + introduction2: Feu clic al + a la dreta de la pantalla per afegir noves columnes + sempre que vulgueu. + widgetsIntroduction: Selecciona "Editar ginys" al menú de columnes i afegeix un + giny. + addColumn: Afegeix una columna + configureColumn: Configuració de columnes + swapLeft: Canvia amb la columna de l'esquerra + renameProfile: Canvia el nom de l'espai de treball + nameAlreadyExists: Aquest nom d'espai de treball ja existeix. +blockConfirm: Segur que vols bloquejar aquest compte? +unsuspendConfirm: Segur que vols treure la suspensió d'aquest compte? +unblockConfirm: Segur que vols treure el bloqueig d'aquest compte? +suspendConfirm: Segur que vols suspendre aquest compte? +selectList: Selecciona una llista +selectAntenna: Selecciona una antena +selectWidget: Selecciona un giny +editWidgets: Edita els ginys +editWidgetsExit: Fet +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 +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. +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! +flagShowTimelineRepliesDescription: Si s'activa, es mostraran les respostes d'usuaris + a publicacions d'altres usuaris. +general: General +autoAcceptFollowed: Aprova automàticament les peticions de seguiment d'usuaris que + segueixes +accountMoved: "L'usuari s'ha mogut a un compte nou:" +addAccount: Afegeix un compte +loginFailed: No s'ha pogut iniciar sessió +showOnRemote: Obre la pàgina original +wallpaper: Fons de pantalla +setWallpaper: Estableix fons de pantalla +removeWallpaper: Elimina el fons de pantalla +followConfirm: Segur que vols seguir a {name}? +proxyAccount: Compte proxy +proxyAccountDescription: Un compte proxy es un compte que actua com un seguidor remot + per a usuaris sota determinades condicions. Per exemple, quant un usuari afegeix + un usuari remot a la llista, l'activitat de l'usuari remot no serà entregada al + servidor si cap usuari local el segueix, així el compte proxy el seguirà. +host: Amfitrió +selectUser: Selecciona un usuari +latestStatus: Últim estat +storageUsage: Ús del emmagatzematge +metadata: Metadades +monitor: Seguiment +software: Programari +version: Versió +jobQueue: Cua de feina +cpuAndMemory: CPU i memòria +network: Xarxa +disk: Disc +instanceInfo: Informació del servidor +statistics: Estadístiques +clearCachedFiles: Esborra la memòria cau +clearQueueConfirmText: Qualsevol publicació que continuï a la cua sense entregar no + será federada. Normalment aquesta operació no es necessària. +clearCachedFilesConfirm: Segur que vols esborrar els fitxers remots de la memòria + cau? +blockedUsers: Usuaris blocats +noUsers: No hi ha cap usuari +editProfile: Edita el perfil +noteDeleteConfirm: Segur que vols eliminar la publicació? +pinLimitExceeded: No pots fixar més notes +muteAndBlock: Silenciats i blocats +mutedUsers: Usuaris silenciats +done: Fet +preview: Vista prèvia +default: Per defecte +intro: La instal·lació de Firefish ha acabat! Crea un compte d'usuari d'administració. +processing: S'està processant +noCustomEmojis: No hi ha cap emoji +noJobs: No hi ha cap feina +federating: Federant +blocked: Bloquejat +subscribing: Subscrivint +publishing: Publicant +notResponding: Sense resposta +instanceUsers: Usuaris d'aquest servidor +instanceFollowing: Seguint al servidor +instanceFollowers: Seguidors del servidor +security: Seguretat +newPasswordRetype: Torna a entrar la nova contrasenya +more: Més! +featured: Destacat +usernameOrUserId: Nom o ID d'usuari +noSuchUser: No s'ha trobat l'usuari +lookup: Cerca +attachFile: Afegeix un fitxer +currentPassword: Contrasenya actual +newPassword: Nova contrasenya +announcements: Anuncis +imageUrl: URL de la imatge +removed: S'ha eliminat correctament +removeAreYouSure: Segur que vols eliminar «{x}»? +deleteAreYouSure: Segur que vols eliminar «{x}»? +resetAreYouSure: Segur que vols restablir? +fromUrl: Des d'una URL +saved: S'ha desat +messaging: Xat +upload: Puja +keepOriginalUploading: Desa la imatge original +keepOriginalUploadingDescription: Desa la imatge original pujada tal com es. Si es + desactiva, es generarà una versió per mostrar en la web al pujar. +fromDrive: Des del Disc +uploadFromUrl: Puja des d'una adreça URL +uploadFromUrlDescription: Adreça URL del fitxer que vols pujar +uploadFromUrlRequested: Pujada demanada +noMoreHistory: No hi ha més historial +tos: Condicions d'ús +start: Comença +startMessaging: Comença una conversa +manageGroups: Gestiona els grups +nUsersRead: llegit per {n} +agreeTo: Estic d'acord amb {0} +activity: Activitat +home: Inici +remoteUserCaution: La informació dels usuaris remots pot estar incompleta. +themeForDarkMode: Tema a fer servir en mode fosc +light: Clar +registeredDate: Data de registre +dark: Fosc +lightThemes: Temes clars +location: Ubicació +theme: Temes +themeForLightMode: Tema a fer servir en mode clar +drive: Disc +selectFile: Tria un fitxer +selectFiles: Tria fitxers +darkThemes: Temes foscos +syncDeviceDarkMode: Sincronitza el mode fosc amb la configuració del teu dispositiu +fileName: Nom del fitxer +createFolder: Crea una carpeta +renameFolder: Canvia-li el nom a la carpeta +deleteFolder: Elimina la carpeta +selectFolder: Tria una carpeta +selectFolders: Tria carpetes +renameFile: Canvia el nom del fitxer +folderName: Nom de la carpeta +inputNewFolderName: Escriu un nom de carpeta nou +addFile: Afegeix un fitxer +emptyDrive: El teu Disc és buit +emptyFolder: Aquesta carpeta és buida +unableToDelete: No es pot eliminar +inputNewFileName: Escriu un nou nom per al fitxer +inputNewDescription: Escriu una descripció nova +circularReferenceFolder: La carpeta de destí és una subcarpeta de la carpeta que vols + moure. +hasChildFilesOrFolders: Aquesta carpeta no es pot eliminar perquè no és buida. +whenServerDisconnected: Quant es perd la conexió amb el servidor +disconnectedFromServer: S'ha perdut la conexió al servidor +reload: Torna a carregar +avatar: Avatar +banner: Bàner +doNothing: Ignora +reloadConfirm: Vols tornar a carregar la línea temporal? +watch: Veure +maintainerName: Administrador +maintainerEmail: Correu electrònic de l'administrador +instanceName: Nom del servidor +instanceDescription: Descripció del servidor +today: Avui +dayX: '{day}' +tosUrl: URL de les Condicions d'ús +thisYear: Any +thisMonth: Mes +integration: Integracions +driveCapacityPerRemoteAccount: Capacitat del Disc per usuari remot +inMb: En megabytes +iconUrl: Adreça URL de la icona +enableRegistration: Activa el registre d'usuaris nous +invite: Convidar +driveCapacityPerLocalAccount: Capacitat del Disc per usuari local +bannerUrl: Adreça URL del banner +backgroundImageUrl: Adreça URL del fons de pantalla +basicInfo: Informació bàsica +pinnedPages: Pàgines fixades +pinnedUsersDescription: Llista de noms d'usuaris per fixar a la pestanya "Explorar" + Un nom per línea. +pinnedPagesDescription: Introdueix la ruta a les pàgines que vols fixar a la página + principal d'aquest servidor, una ruta per línea. +pinnedUsers: Usuaris fixats +enableHcaptcha: Activa hCaptcha +hcaptchaSiteKey: Clau del lloc +hcaptchaSecretKey: Clau secreta +recaptcha: reCAPTCHA +enableGlobalTimeline: Activa la línia de temps global +disablingTimelinesInfo: Els Administradors i Moderadors sempre tenen accés a totes + les líneas temporals, inclòs si hi són desactivades. +showLess: Tanca +clearQueue: Esborra la cua +uploadFromUrlMayTakeTime: Pot trigar un temps fins que la pujada es completi. +noThankYou: No, gràcies +addInstance: Afegeix un servidor +emoji: Emojis +emojis: Emojis +emojiName: Nom del emoji +emojiUrl: URL de l'emoji +addEmoji: Afegeix +settingGuide: Configuració recomenada +searchWith: 'Cerca: {q}' +youHaveNoLists: No tens cap llista +flagSpeakAsCat: Parla com un gat +selectInstance: Selecciona un servidor +flagSpeakAsCatDescription: Les teves publicacions es transformaran en miols quan estiguis + en mode gat +recipient: Destinatari(s) +annotation: Comentaris +blockedInstances: Servidors bloquejats +blockedInstancesDescription: Llista les adreces dels servidors que vols bloquejar. + Els servidors de la llista no podrán comunicarse amb aquests servidors. +hiddenTags: Etiquetes amagades +hiddenTagsDescription: 'Enumereu les etiquetes (sense el #) que voleu ocultar de tendències + i explorar. Les etiquetes ocultes encara es poden descobrir per altres mitjans.' +noInstances: No hi ha cap servidor +defaultValueIs: 'Per defecte: {value}' +suspended: Suspès +all: Tot +changePassword: Canvia la contrasenya +clearQueueConfirmTitle: Segur que vols esborrar la cua? +retypedNotMatch: Els camps no coincideixen. +normal: Normal +monthX: '{month}' +enableRecaptcha: Activa reCAPTCHA +recaptchaSiteKey: Clau del lloc +recaptchaSecretKey: Clau secreta +avoidMultiCaptchaConfirm: Fent servir diferents sistemes de Captcha pot causar interferències + entre ells. Vols desactivar els altres sistemes que es troben activats? Si vols + deixar-los activats fes clic a cancelar. +antennas: Antenes +enableEmojiReactions: Activa reaccions amb emojis +blockThisInstance: Bloqueja aquest servidor +registration: Registra't +showEmojisInReactionNotifications: Mostra els emojis a les notificacions de les reaccions +renoteMute: Silencia els impulsos +renoteUnmute: Treu el silenci als impulsos +cacheRemoteFiles: Fitxers remots a la memòria cau +federation: Federació +registeredAt: Registrat a +latestRequestSentAt: Última petició enviada +latestRequestReceivedAt: Última petició rebuda +charts: Gràfics +perHour: Per hora +perDay: Per dia +stopActivityDelivery: Para d'enviar activitats +operations: Operacions +explore: Explora +messageRead: Llegit +images: Imatges +birthday: Aniversari +yearsOld: '{age} anys' +copyUrl: Copia l'adreça URL +rename: Renombrar +unwatch: Deixa de veure +accept: Accepta +reject: Rebutja +yearX: '{year}' +pages: Pàgines +disconnectService: Desconnectar +connectService: Connectar +enableLocalTimeline: Activa la línea de temps local +enableRecommendedTimeline: Activa la línea de temps de recomanacions +pinnedClipId: ID del clip que vols fixar +hcaptcha: hCaptcha +manageAntennas: Gestiona les Antenes +name: Nom +notesAndReplies: Notes i respostes +silence: Posa en silenci +withFiles: Amb fitxers +popularUsers: Usuaris populars +exploreUsersCount: Hi han {count} usuaris +exploreFediverse: Explora el Fesiverse +popularTags: Etiquetes populars +about: Sobre +recentlyUpdatedUsers: Usuaris actius fa poc +recentlyRegisteredUsers: Usuaris registrats fa poc +recentlyDiscoveredUsers: Nous suaris descoberts +administrator: Administrador +token: Token +registerSecurityKey: Registreu una clau de seguretat +securityKeyName: Nom clau +lastUsed: Feta servir per última vegada +unregister: Anul·lar el registre +passwordLessLogin: Identificació sense contrasenya +share: Comparteix +notFound: No s'ha trobat +newPasswordIs: La nova contrasenya és "{password}" +notFoundDescription: No es pot trobar cap pàgina que correspongui a aquesta adreça + URL. +uploadFolder: Carpeta per defecte per pujar arxius +cacheClear: Netejar la memòria cau +markAsReadAllNotifications: Marca totes les notificacions com llegides +markAsReadAllUnreadNotes: Marca totes les notes com a llegides +markAsReadAllTalkMessages: Marca tots els missatges com llegits +help: Ajuda +inputMessageHere: Escriu aquí el missatge +close: Tancar +group: Grup +groups: Grups +createGroup: Crea un grup +ownedGroups: Grups que et pertanyen +joinedGroups: Grups als que t'has unit +groupName: Nom del grup +members: Membres +transfer: Transferir +messagingWithUser: Conversa privada +title: Títol +text: Text +enable: Activar +next: Següent +retype: Torna a entrar +noteOf: Publicació de {user} +inviteToGroup: Invitar a un grup +quoteAttached: Cita +quoteQuestion: Adjuntar com a cita? +noMessagesYet: Encara no hi han missatges +signinRequired: Si us plau registrat o inicia sessió per continuar +invitations: Invitacions +invitationCode: Codi d'invitació +checking: Comprovant... +usernameInvalidFormat: Pots fer servir lletres en majúscules o minúscules, nombres + i guions baixos. +tooShort: Massa curt +tooLong: Massa llarg +weakPassword: Contrasenya amb seguretat feble +strongPassword: Contrasenya amb seguretat forta +passwordMatched: Coincidències +signinWith: Inicieu sessió com {x} +signinFailed: No es pot iniciar sessió. El nom d'usuari o la contrasenya són incorrectes. +or: O +language: Idioma +uiLanguage: Idioma de la interfície d'usuari +groupInvited: T'han invitat a un grup +aboutX: Sobre {x} +youHaveNoGroups: No tens grups +disableDrawer: No facis servir els menús amb estil de calaix +noHistory: No hi ha historial disponible +signinHistory: Historial d'inicis de sessió +disableAnimatedMfm: Desactiva les animacions amb MFM +doing: Processant... +category: Categoría +existingAccount: El compte ja existeix +regenerate: Regenerar +docSource: Font d'aquest document +createAccount: Crear compte +fontSize: Mida del text +noFollowRequests: No tens cap sol·licitud de seguiment per aprovar +openImageInNewTab: Obre les imatges en una pestanya nova +dashboard: Panell +local: Local +remote: Remot +total: Total +weekOverWeekChanges: Canvis d'ençà la passada setmana +dayOverDayChanges: Canvis d'ençà ahir +appearance: Aparença +clientSettings: Configuració del client +accountSettings: Configuració del compte +promotion: Promogut +promote: Promoure +numberOfDays: Nombre de dies +objectStorageBaseUrl: Adreça URL base +hideThisNote: Amaga aquesta publicació +showFeaturedNotesInTimeline: Mostra les notes destacades a les líneas de temps +objectStorage: Emmagatzematge d'objectes +useObjectStorage: Fes servir l'emmagatzema d'objectes +expandTweet: Amplia el tuit +themeEditor: Editor de temes +description: Descripció +leaveConfirm: Hi han canvis que no s'han desat. Els vols descartar? +manage: Administració +plugins: Afegits +preferencesBackups: Preferències de còpies de seguretat +undeck: Treure el Taulell +useBlurEffectForModal: Fes servir efectes de difuminació en les finestres modals +useFullReactionPicker: Fes servir el selector de reaccions a tamany complert +deck: Taulell +width: Amplada +generateAccessToken: Genera un token d'accés +medium: Mitja +small: Petit +permission: Permisos +enableAll: Activa tots +tokenRequested: Garantir accés al compte +pluginTokenRequestedDescription: Aquest afegit podrà fer servir els permisos configurats + aquí. +emailServer: Servidor de correu electrònic +notificationType: Tipus de notificació +edit: Editar +emailAddress: Adreça de Correu electrònic +smtpConfig: Configuració del servidor SMTP +smtpHost: Host +enableEmail: Activa la distribució de correu electrònic +smtpPort: Port +emailConfigInfo: Fet servir per confirmar les adreçats de correu electrònic al registrar-se + o si s'oblida la contrasenya +email: Correu electrònic +smtpSecure: Fes servir SSL/TLS implícit per connectar-se per SMTP +emptyToDisableSmtpAuth: Deixa el nom d'usuari i la contrasenya sense emplenar per + desactivar la verificació SMTP +smtpSecureInfo: Desactiva això quant facis servir STARTTLS +testEmail: Envia un correu electrònic de verificació +wordMute: Silenciar paraules +regexpError: Error a la Expressió Regular +regexpErrorDescription: 'Hi ha un error a la expressió regular a la línea {line} de + la teva {tab} de paraules silenciades:' +userSaysSomething: '{name} va dir alguna cosa' +instanceMute: Silenciar servidor +logs: Registres +copy: Copiar +delayed: Retardat +metrics: Mètriques +overview: Vista general +database: Base de dades +regenerateLoginToken: Regenera el token d'inici de sessió +reduceUiAnimation: Redueix les animacions de la UI +messagingWithGroup: Conversa en grup +invites: Invitacions +unavailable: No disponible +newMessageExists: Tens nous missatges +onlyOneFileCanBeAttached: Només pots adjuntar un fitxer per missatge +normalPassword: Contrasenya amb seguretat mitjana +passwordNotMatched: No hi han coincidències +useOsNativeEmojis: Fes servir els emojis per defecte del Sistema Operatiu +joinOrCreateGroup: Fes que et convidin a un grup o crea el teu propi. +objectStorageBaseUrlDesc: "Es l'adreça URL que serveix com a referència. Específica + la adreça URL del CDN o Proxy si fas servir.\nPer fer servir S3 'https://.s3.amazonaws.com' + i per GCS o serveis semblants 'https://storage.googleapis.com/', etc." +height: Alçada +large: Gran +notificationSetting: Preferències de notificacions +makeActive: Activar +notificationSettingDesc: Tria el tipus de notificació que es veure. +notifyAntenna: Notificar publicacions noves +withFileAntenna: Només notes amb fitxers +enableServiceworker: Activa les notificacions push per al teu navegador +antennaUsersDescription: Escriu un nom d'usuari per línea +antennaInstancesDescription: Escriu la adreça d'un servidor per línea +tags: Etiquetes +antennaSource: Font de la antena +antennaKeywords: Paraules claus a escoltar +antennaExcludeKeywords: Paraules clau a excluir +antennaKeywordsDescription: Separades amb espais per fer una condició AND i amb una + línea nova per fer una condició OR. +caseSensitive: Sensible a majúscules i minúscules +withReplies: Inclou respostes +connectedTo: Aquest(s) compte(s) estan connectats +silenceConfirm: Segur que vols posa en silenci aquest usuari? +unsilence: Desfés posar en silenci +unsilenceConfirm: Segur que vols treure el silenci a aquest usuari? +aboutFirefish: Sobre Firefish +twoStepAuthentication: Autentificació de dos factors +moderator: Moderador +moderation: Moderació +available: Disponible +tapSecurityKey: Escriu la teva clau de seguretat +nUsersMentioned: Esmentat per {n} usuari(s) +securityKey: Clau de seguretat +resetPassword: Restablir contrasenya +describeFile: Afegeix una descripció +enterFileDescription: Entra una descripció +author: Autor +disableAll: Desactiva tots +userSaysSomethingReason: '{name} va dir {reason}' +display: Visualització +channel: Canals +create: Crear +useGlobalSetting: Fes servir els ajusts globals +useGlobalSettingDesc: Si s'activa, es faran servir els ajusts de notificacions del + teu compte. Si es desactiva , es poden fer configuracions individuals. +other: Altres +menu: Menú +addItem: Afegeix un element +divider: Divisor +relays: Relés +addRelay: Afegeix un Relé +inboxUrl: Adreça de la safata d'entrada +addedRelays: Relés afegits +serviceworkerInfo: Ha de estar activat per les notificacions push. +poll: Enquesta +deletedNote: Publicació esborrada +disablePlayer: Tancar el reproductor de vídeo +fileIdOrUrl: ID o adreça URL del fitxer +behavior: Comportament +regenerateLoginTokenDescription: Regenera el token que es fa servir de manera interna + durant l'inici de sessió. Normalment això no és necessari. Si es torna a genera + el token, es tancarà la sessió a tots els dispositius. +setMultipleBySeparatingWithSpace: Separa diferents entrades amb espais. +reportAbuseOf: Informa d'un abús de {name} +sample: Exemple +abuseReports: Informes +reportAbuse: Informe +reporter: Informador +reporterOrigin: Origen informador +forwardReport: Envia l'informe a un servidor remot +abuseReported: El teu informe ha sigut enviat. Moltes gràcies. +reporteeOrigin: Origen de l'informe +send: Enviar +abuseMarkAsResolved: Marcar l'informe com a resolt +visibility: Visibilitat +useCw: Amaga el contingut +enablePlayer: Obre el reproductor de vídeo +yourAccountSuspendedDescription: Aquest compte ha sigut suspès per no seguir els termes + de servei d'aquest servidor o quelcom similar. Contacte amb l'administrador si vols + conèixer la raó amb més detall. Si us plau no facis un compte nou. +invisibleNote: Publicació oculta +enableInfiniteScroll: Carregar més de forma automàtica +fillAbuseReportDescription: Si us plau omple els detalls sobre aquest informe. Si + es sobre una publicació en concret, si us plau, inclou l'adreça URL. +forwardReportIsAnonymous: Com a informador el servidor remot no veure el teu compte, + si no un compte anònim. +openInNewTab: Obrir en una pestanya nova +openInSideView: Obrir a la vista lateral +defaultNavigationBehaviour: Navegació per defecte +editTheseSettingsMayBreakAccount: Si edites aquestes configuracions pots fer mal bé + el teu compte. +userSilenced: Aquest usuari ha sigut silenciat. +instanceTicker: Informació de notes del servidor +waitingFor: Esperant a {x} +random: Aleatori +system: Sistema +switchUi: Interfície d'usuari +createNewClip: Crear un clip nou +unclip: Treure clip +public: Públic +renotesCount: Nombre d'impulsos fets +sentReactionsCount: Nombre de reaccions fetes +receivedReactionsCount: Nombre de reaccions rebudes +pollVotesCount: Nombre de vots fets en enquestes +pollVotedCount: Nombre de vots rebuts en enquestes +yes: Sí +no: No +noCrawle: Rebutjar la indexació dels restrejadors +driveUsage: Espai fet servir al Disk +noCrawleDescription: No permetre que els buscadors guardin la informació de les pàgines + de perfil, notes, Pàgines, etc. +alwaysMarkSensitive: Marcar per defecte com a NSFW +lockedAccountInfo: Si has configurat la visibilitat del compte per "Només seguidors" + les teves notes no seren visibles per a ningú més, inclús si has d'aprovar els teus + seguidors manualment. +disableShowingAnimatedImages: No reproduir les imatges animades +verificationEmailSent: S'ha enviat correu electrònic de verificació. Si us plau segueix + les instruccions per completar la verificació. +notSet: Sense especificar +emailVerified: El correu electrònic s'ha verificat +loadRawImages: Carregar les imatges originals en comptes de mostrar les miniatures +noteFavoritesCount: Nombre de notes afegides a favorits +useSystemFont: Fes servir la font per defecte del sistema +contact: Contacte +clips: Retalls +experimentalFeatures: Característiques experimentals +developer: Desenvolupador +makeExplorableDescription: Si desactives aquesta funció el teu compte no sortirà a + la secció "Explora". +showGapBetweenNotesInTimeline: Mostra un espai entre notes a la línea de temps +makeExplorable: Fes el compte visible a "Explora" +duplicate: Duplicar +left: Esquerra +wide: Ample +narrow: Estret +reloadToApplySetting: Aquesta configuració només sortirà efecte després de recarregar + la pàgina. Vols fer-ho ara? +needReloadToApply: Es requereix recarregar la pàgina perquè això surti efecte. +showTitlebar: Mostrar la barra de títol +onlineUsersCount: Hi han {n} usuaris connectats +nUsers: '{n} Usuaris' +nNotes: '{n} Notes' +sendErrorReports: Enviar informe d'error +clearCache: Netejar memòria cau +switchAccount: Canvia de compte +enabled: Activat +configure: Configurar +noBotProtectionWarning: La protecció contra bots no està configurada. +ads: Publicitat +ratio: Ràtio +global: Global +sent: Enviat +received: Rebut +whatIsNew: Mostra els canvis +usernameInfo: Un nom que identifica el vostre compte d'altres en aquest servidor. + Podeu utilitzar l'alfabet (a~z, A~Z), els dígits (0~9) o el guió baix (_). Els noms + d'usuari no es poden canviar més tard. +breakFollow: Suprimeix el seguidor +makeReactionsPublicDescription: Això farà que la llista de totes les vostres reaccions + passades sigui visible públicament. +hide: Amagar +leaveGroupConfirm: Estàs segur que vols deixar "{name}"? +voteConfirm: Vols confirmar el teu vot per a "{choice}"? +leaveGroup: Sortir del grup +rateLimitExceeded: S'ha excedit el límit proporcionat +cropImage: Retalla la imatge +cropImageAsk: Vols retallar aquesta imatge? +failedToFetchAccountInformation: No s'ha pogut obtenir la informació del compte +driveCapOverrideCaption: Restableix la capacitat per defecte introduint un valor de + 0 o inferior. +type: Tipus +label: Etiqueta +beta: Beta +navbar: Barra de navegació +adminCustomCssWarn: Aquesta configuració només s'ha d'utilitzar si sabeu què fa. La + introducció de valors inadequats pot fer que els clients de TOTS deixin de funcionar + amb normalitat. Assegureu-vos que el vostre CSS funcioni correctament provant-lo + a la configuració de l'usuari. +showUpdates: Mostra una finestra emergent quan Firefish s'actualitzi +recommendedInstances: Servidors recomanats +recommendedInstancesDescription: Servidors recomanats separats per salts de línia + que apareixen a la línia de temps recomanada. +caption: Descripció Automàtica +splash: Pantalla de Benvinguda +swipeOnDesktop: Permet lliscar a l'estil del mòbil a l'escriptori +updateAvailable: Pot ser que hi hagi una actualització disponible! +logoImageUrl: URL de la imatge del logotip +showAdminUpdates: Indica que hi ha disponible una versió nova de Firefish (només per + a administradors) +replayTutorial: Repetició del tutorial +migration: Migració +moveAccountDescription: Aquest procés és irreversible. Assegureu-vos que hàgiu configurat + un àlies per a aquest compte al vostre compte nou abans de moure's. Introduïu l'etiqueta + del compte amb el format @persona@servidor.com +moveToLabel: 'Compte al qual us moveu:' +moveAccount: Mou el compte! +moveFromDescription: Això establirà un àlies del vostre compte antic perquè pugueu + passar d'aquest compte a aquest actual. Feu això ABANS de moure's del vostre compte + anterior. Introduïu l'etiqueta del compte amb el format @persona@servidor.com +_sensitiveMediaDetection: + description: Redueix l'esforç de moderació del servidor mitjançant el reconeixement + automàtic dels mitjans NSFW mitjançant l'aprenentatge automàtic. Això augmentarà + lleugerament la càrrega al servidor. + setSensitiveFlagAutomaticallyDescription: Els resultats de la detecció interna es + conservaran encara que aquesta opció estigui desactivada. + analyzeVideos: Activa l'anàlisi de vídeos + analyzeVideosDescription: Analitza vídeos a més d'imatges. Això augmentarà lleugerament + la càrrega al servidor. + setSensitiveFlagAutomatically: Marca com a NSFW + sensitivity: Sensibilitat de detecció + sensitivityDescription: La reducció de la sensibilitat comportarà menys deteccions + errònies (falsos positius), mentre que augmentar-la comportarà menys deteccions + falses (falsos negatius). +_emailUnavailable: + used: Aquesta adreça de correu electrònic ja s'està utilitzant + format: El format d'aquesta adreça de correu electrònic no és vàlid + disposable: Les adreces de correu electrònic d'un sol ús no es poden utilitzar + mx: Aquest servidor de correu electrònic no és vàlid + smtp: Aquest servidor de correu electrònic no respon +_ffVisibility: + public: Públic + followers: Visible només per als seguidors + private: Privat +_signup: + emailAddressInfo: Introduïu la vostra adreça de correu electrònic. No es farà públic. + almostThere: Gairebé està + emailSent: S'ha enviat un correu electrònic de confirmació a la vostra adreça electrònica + ({email}). Feu clic a l'enllaç inclòs per completar la creació del compte. +_accountDelete: + started: S'ha iniciat la supressió. + accountDelete: Suprimeix el compte + mayTakeTime: Com que la supressió del compte és un procés que requereix molts recursos, + pot ser que trigui algun temps a completar-se en funció de la quantitat de contingut + que hàgiu creat i de quants fitxers hàgiu penjat. + sendEmail: Un cop s'hagi completat la supressió del compte, s'enviarà un correu + electrònic a l'adreça de correu electrònic registrada en aquest compte. + inProgress: La supressió del compte està en curs + requestAccountDelete: Sol·licitar la supressió del compte +_ad: + back: Enrera + reduceFrequencyOfThisAd: Mostrar aquest anunci menys +_gallery: + my: La meva Galeria + liked: Notes que m'han agradat + unlike: Elimina m'agrada + like: M'agrada +_forgotPassword: + contactAdmin: Aquest servidor no admet l'ús d'adreces de correu electrònic; poseu-vos + en contacte amb l'administrador del servidor per restablir la contrasenya. + ifNoEmail: Si no heu utilitzat cap correu electrònic durant el registre, poseu-vos + en contacte amb l'administrador del servidor. + enterEmail: Introduïu l'adreça de correu electrònic que heu utilitzat per registrar-vos. + A continuació, se li enviarà un enllaç amb el qual podeu restablir la vostra contrasenya. +_plugin: + install: Instal·leu connectors + installWarn: Si us plau, no instal·leu connectors que no siguin fiables. + manage: Gestionar els connectors +_preferencesBackups: + saveNew: Desa una còpia de seguretat nova + apply: Aplicar a aquest dispositiu + loadFile: Carrega des del fitxer + save: Desa els canvis + nameAlreadyExists: Ja existeix una còpia de seguretat anomenada "{name}". Introduïu + un nom diferent. + renameConfirm: Canviar el nom d'aquesta còpia de seguretat de "{old}" a "{new}"? + noBackups: No existeixen còpies de seguretat. Podeu fer una còpia de seguretat de + la configuració del vostre client en aquest servidor utilitzant "Crea una còpia + de seguretat nova". + deleteConfirm: Vols suprimir la còpia de seguretat anomanada {name}? + updatedAt: 'Actualitzat el: {time} {date}' + createdAt: 'Creat el: {time} {date}' + cannotLoad: No s'ha pogut carregar + inputName: Introduïu un nom per a aquesta còpia de seguretat + saveConfirm: Deseu la còpia de seguretat com a {name}? + invalidFile: Format de fitxer no vàlid + applyConfirm: Realment voleu aplicar la còpia de seguretat "{name}" a aquest dispositiu? + La configuració existent d'aquest dispositiu es sobreescriurà. + list: Còpies de seguretat creades + cannotSave: S'ha produït un error en desar +_registry: + domain: Domini + createKey: Crea la clau + scope: Àmbit + key: Clau + keys: Claus +silenced: Silenciat +objectStorageUseSSL: Fes servir SSL +yourAccountSuspendedTitle: Aquest compte està suspès +i18nInfo: Firefish està sent traduït a diversos idiomes per voluntaris. Pots ajudar + {link}. +manageAccessTokens: Administrar tokens d'accés +accountInfo: Informació del compte +pageLikedCount: Nombre de m'agrada rebuts a Pàgines +center: Centre +registry: Registre +closeAccount: Tancar el compte +currentVersion: Versió actual +latestVersion: Versió més nova +newVersionOfClientAvailable: Aquesta és la versió del client més nova disponible. +usageAmount: Ús +capacity: Capacitat +editCode: Editar codi +apply: Aplicar +repliesCount: Nombre de contestacions fetes +repliedCount: Nombre de respostes rebudes +renotedCount: Nombre d'impulsos rebuts +followingCount: Nombre de comptes seguits +followersCount: Nombre de seguidors +goBack: Enrera +quitFullView: Sortí de la vista complerta +addDescription: Afegeix una descripció +notSpecifiedMentionWarning: Aquesta publicació conté mencions a usuaris no inclosos + com a destinataris +info: Sobre +hideOnlineStatus: Amagar l'estat de conexió +onlineStatus: Estat de conexió +online: En línea +offline: Desconectat +notRecommended: No recomanat +botProtection: Protecció contra Bots +instanceBlocking: Gestió de la federació +selectAccount: Seleccionar un compte +disabled: Desactivat +quickAction: Accions ràpides +administration: Administració +switch: Canviar +gallery: Galeria +popularPosts: Pàgines populars +shareWithNote: Comparteix amb una publicació +expiration: Data límit +memo: Recordatori +priority: Prioritat +high: Alta +middle: Mitjana +low: Baixa +emailNotConfiguredWarning: L'adreça de correu electrònic no està definida. +instanceSecurity: Seguretat del servidor +privateMode: Mode Privat +allowedInstances: Servidors a la llista blanca +allowedInstancesDescription: Llista blanca de Hosts amb qui federar, cadascún separat + per una línia nova (només s'aplica en mode privat). +previewNoteText: Mostra la vista prèvia +customCss: CSS personalitzat +recommended: Recomanat +seperateRenoteQuote: Botons d'impuls i de citació separats +searchResult: Resultats de la cerca +hashtags: Etiquetes +troubleshooting: Resolució de problemes +learnMore: Més informació +misskeyUpdated: Firefish s'ha actualitzat! +translate: Tradueix +translatedFrom: Traduït per {x} +aiChanMode: Ai-chan a la interfície d'usuari clàssica +keepCw: Mantenir els avisos de contingut +pubSub: Comptes Pub/Sub +lastCommunication: Última comunicació +breakFollowConfirm: Confirmes que vols eliminar el seguidor? +itsOn: Activat +itsOff: Desactivat +emailRequiredForSignup: Requereix una adreça de correu electrònic per registrar-te +unread: Sense llegir +controlPanel: Tauler de control +manageAccounts: Gestionar comptes +makeReactionsPublic: Estableix l'historial de reaccions com a públic +classic: Centrat +muteThread: Silenciar el fil +ffVisibility: Visibilitat dels Seguiments/Seguidors +incorrectPassword: Contrasenya incorrecta. +clickToFinishEmailVerification: Feu clic a [{ok}] per completar la verificació del + correu electrònic. +overridedDeviceKind: Tipus de dispositiu +smartphone: Telèfon intel·ligent +tablet: Tauleta +auto: Automàtic +recentNHours: Últimes {n} hores +recentNDays: Últims {n} dies +noEmailServerWarning: El servidor de correu electrònic no està configurat. +check: Comprovar +fast: Ràpida +sensitiveMediaDetection: Detecció de mitjans NSFW +remoteOnly: Només remotes +failedToUpload: S'ha produït un error en la càrrega +cannotUploadBecauseInappropriate: Aquest fitxer no s'ha pogut carregar perquè s'han + detectat parts d'aquest com a potencialment NSFW. +cannotUploadBecauseNoFreeSpace: La pujada ha fallat a causa de la manca d'espai al + Disc. +enableAutoSensitive: Marcatge automàtic NSFW +moveTo: Mou el compte actual al compte nou +customKaTeXMacro: Macros KaTeX personalitzats +_aboutFirefish: + contributors: Col·laboradors principals + allContributors: Tots els col·laboradors + donate: Fes una donació a Firefish + source: Codi font + translation: Tradueix Firefish + about: Firefish és una bifurcació de Misskey feta per ThatOneCalculator, que està + en desenvolupament des del 2022. + morePatrons: També agraïm el suport de molts altres ajudants que no figuren aquí. + Gràcies! 🥰 + patrons: Mecenes de Firefish + patronsList: Llistats cronològicament, no per la quantitat donada. Fes una donació + amb l'enllaç de dalt per veure el teu nom aquí! + donateTitle: T'agrada Firefish? + pleaseDonateToFirefish: Penseu en fer una donació a Firefish per donar suport al seu + desenvolupament. + pleaseDonateToHost: Penseu també en fer una donació a la vostre instància, {host}, + per ajudar-lo a suportar els costos de funcionament. + donateHost: Fes una donació a {host} + sponsors: Patrocinadors de Calckey +unknown: Desconegut +pageLikesCount: Nombre de pàgines amb M'agrada +youAreRunningUpToDateClient: Estás fent servir la versió del client més nova. +unlikeConfirm: Vols treure el teu m'agrada? +fullView: Vista complerta +desktop: Escritori +notesCount: Nombre de notes +confirmToUnclipAlreadyClippedNote: Aquesta publicació ja és al clip "{name}". La vols + treure d'aquest clip? +driveFilesCount: Nombre de fitxers al Disk +silencedInstances: Servidors silenciats +silenceThisInstance: Silencia el servidor +silencedInstancesDescription: Llista amb els noms dels servidors que vols silenciar. + Els comptes als servidors silenciats seran tractades com "Silenciades", només poden + fer sol·licituds de seguiments, i no poden mencionar comptes locals si no les segueixen. + Això no afectarà els servidors bloquejats. +objectStorageEndpointDesc: Deixa això buit si fas servir AWS, S3, d'una altre manera + específica un "endpoint" com a '' o ':', depend del proveïdor + que facis servir. +objectStorageRegionDesc: Especifica una regió com a 'xx-east-1'. Si el teu proveïdor + no distingeix entre regions, deixa això en buit o pots escriure 'us-east-1'. +userPagePinTip: Pots mostrar publicacions aquí escollint "Fixar al perfil" dintre + del menú de cada publicació. +userInfo: Informació d'usuari +hideOnlineStatusDescription: Amagant el teu estat en línea redueix la comoditat d'ús + d'algunes característiques com ara la recerca. +active: Actiu +accounts: Comptes +postToGallery: Crea una publicació nova a la galeria +secureMode: Mode segur (Recuperació Autoritzada) +customCssWarn: Aquesta configuració només s'ha d'utilitzar si sabeu què fa. La introducció + de valors indeguts pot provocar que el client deixi de funcionar amb normalitat. +squareAvatars: Mostra avatars quadrats +secureModeInfo: Quan es faci una solicitut d'altres servidors no contestar sense una + prova. +privateModeInfo: Quan està activat, només els servidors a la llista blanca es poden + federar amb el vostre servidor. Totes les publicacions s'amagaran al públic. +useBlurEffect: Utilitzeu efectes de desenfocament a la interfície d'usuari +accountDeletionInProgress: La supressió del compte està en curs +unmuteThread: Desfés el silenci al fil +deleteAccountConfirm: Això suprimirà el vostre compte de manera irreversible. Procedir? +requireAdminForView: Heu d'iniciar sessió amb un compte d'administrador per veure-ho. +enableAutoSensitiveDescription: Permet la detecció i el marcatge automàtics dels mitjans + NSFW mitjançant Machine Learning sempre que sigui possible. Fins i tot si aquesta + opció està desactivada, és possible que estigui habilitada a tot el servidor. +localOnly: Només local +customKaTeXMacroDescription: "Configura macros per escriure expressions matemàtiques + fàcilment! La notació s'ajusta a les definicions de l'ordre LaTeX i s'escriu com + a \\newcommand{\\ name}{content} o \\newcommand{\\name}[nombre d'arguments]{content}. + Per exemple, \\newcommand{\\add}[2]{#1 + #2} ampliarà \\add{3}{foo} a 3 + foo. Els + claudàtors que envolten el nom de la macro es poden canviar per claudàtors rodons + o quadrats. Això afecta els claudàtors utilitzats per als arguments. Es pot definir + una (i només una) macro per línia, i no podeu trencar la línia al mig de la definició. + Les línies no vàlides simplement s'ignoren. Només s'admeten funcions de substitució + de cadenes senzilles; La sintaxi avançada, com ara la ramificació condicional, no + es pot utilitzar aquí." +objectStorageRegion: Regió +objectStoragePrefix: Prefix +objectStoragePrefixDesc: Els fitxers es guardaran dins de carpetes amb aquest prefix. +objectStorageEndpoint: Extrem +newNoteRecived: Hi han notes noves +sounds: Sons +listen: Escoltar +none: Res +showInPage: Mostrar a la página +popout: Apareixa +volume: Volum +objectStorageUseSSLDesc: Desactiva això si no fas servir HTTPS per les connexions + API +objectStorageUseProxy: Connectar-se mitjançant un Proxy +objectStorageUseProxyDesc: Desactiva això si no faràs servir un servidor Proxy per + conexions amb l'API +objectStorageSetPublicRead: Fixar com a "public-read" al pujar +serverLogs: Registres del servidor +deleteAll: Esborrar tot +showFixedPostForm: Mostrar el formulari de notes al principi de la línia de temps +unableToProcess: Aquesta operació no es pot acabar +recentUsed: Fet servir fa poc +install: Instal·lar +masterVolume: Volum principal +uninstall: Desinstal·lar +installedApps: Aplicacions autoritzades +nothing: No hi a res per veure +installedDate: Data d'autorització +details: Detalls +chooseEmoji: Selecciona un emoji +removeAllFollowingDescription: Fent això deixes de seguir tots els comptes de {host}. + Si us plau fes servir això sí, per exemple, el servidor deixa d'existir. +userSuspended: Aquest usuari ha sigut suspès. +lastUsedDate: Data d'últim ús +state: Estat +sort: Ordenar +ascendingOrder: Ascendent +descendingOrder: Descendent +scratchpad: Bloc de notes +scratchpadDescription: El bloc de notes proporciona un entorn per experiments amb + AiScript. Pots escriure, executar i comprovar els resultats interactuant amb Firefish. +output: Sortida +script: Script +disablePagesScript: Desactivar AiScript a les pàgines +updateRemoteUser: Actualitzar la informació de l'usuari remot +deleteAllFiles: Esborrar tots els fitxers +deleteAllFilesConfirm: Segur que vols esborrar tots els fitxers? +removeAllFollowing: Deixar de seguir a tots els usuaris que segueixes +accentColor: Color principal +textColor: Color del text +value: Valor +sendErrorReportsDescription: "Quan està activat, quan es produeixi un problema la + informació detallada d'errors es compartirà amb Firefish, ajudant a millorar la qualitat + de Firefish.\nAixò inclourà informació com la versió del vostre sistema operatiu, + quin navegador utilitzeu, la vostra activitat a Firefish, etc." +myTheme: El meu tema +backgroundColor: Color de fons +saveAs: Desa com... +advanced: Avançat +invalidValue: Valor invàlid. +createdAt: Data de creació +updatedAt: Data d'actualització +saveConfirm: Desa canvis? +deleteConfirm: De veritat ho vols esborrar? +receiveAnnouncementFromInstance: Rep notificacions d'aquest servidor +emailNotification: Notificacions per correu electrònic +publish: Publicar +inChannelSearch: Buscar al canal +useReactionPickerForContextMenu: Obrir el selector de reaccions al fer click esquerra +typingUsers: L'{users} està escrivint +oneDay: Un dia +instanceDefaultLightTheme: Tema de llum predeterminat per a tot el servidor +instanceDefaultDarkTheme: Tema fosc predeterminat per tot el servidor +instanceDefaultThemeDescription: Introduïu el codi del tema en format d'objecte. +mutePeriod: Durada del silenci +indefinitely: Permanentment +tenMinutes: 10 minuts +oneHour: Una hora +oneWeek: Una setmana +reflectMayTakeTime: Pot trigar una mica a reflectir-se. +thereIsUnresolvedAbuseReportWarning: Hi ha informes sense resoldre. +driveCapOverrideLabel: Canvieu la capacitat del disc per a aquest usuari +isSystemAccount: Aquest compte és creat i operat automàticament pel sistema. Si us + plau, no modereu, editeu, suprimiu o modifiqueu aquest compte de cap forma, o podria + trencar el vostre servidor. +typeToConfirm: Introduïu {x} per confirmar +deleteAccount: Suprimeix el compte +document: Documentació +sendPushNotificationReadMessage: Suprimeix les notificacions push un cop s'hagin llegit + les notificacions o missatges rellevants +sendPushNotificationReadMessageCaption: Es mostrarà una notificació amb el text "{emptyPushNotificationMessage}" + durant un breu temps. Això pot augmentar l'ús de la bateria del vostre dispositiu, + si escau. +showAds: Mostrar publicitat +enterSendsMessage: Pren retorn al formulari del missatge per enviar (quant no s'activa + es Ctrl + Return) +customMOTD: MOTD personalitzat (missatges de la pantalla de benvinguda) +customMOTDDescription: Missatges personalitzats per al MOTD (pantalla de benvinguda) + separats per salts de línia, es mostraran aleatòriament cada vegada que un usuari + carrega/recarrega la pàgina. +customSplashIcons: Icones personalitzades de la pantalla de benvinguda (urls) +customSplashIconsDescription: Les URLS de les icones personalitzades a la pantalla + de benvinguda separades per salts de línia. Es mostraran aleatòriament cada vegada + que un usuari carrega/recarrega la pàgina. Si us plau, assegureu-vos que les imatges + estiguin en una URL estàtica, preferiblement amb imatges amb la de 192 x 192. +moveFrom: Mou a aquest compte des d'un compte anterior +moveFromLabel: 'Compte des del qual us moveu:' +migrationConfirm: "Esteu absolutament segur que voleu migrar el vostre compte a {account}? + Un cop ho feu, no podreu revertir-ho i no podreu tornar a utilitzar el vostre compte + amb normalitat.\nA més, assegureu-vos d'haver configurat aquest compte actual com + el compte del qual us moveu." +defaultReaction: Reacció d'emoji predeterminada per a notes sortints i entrants +enableCustomKaTeXMacro: Activa les macros KaTeX personalitzades +noteId: ID de la publicació +_nsfw: + respect: Amaga els mitjans NSFW + ignore: No amagueu els mitjans NSFW + force: Amaga tots els mitjans +inUse: Utilitzat +ffVisibilityDescription: Et permet configurar qui pot veure a qui segueixes i qui + et segueix. +continueThread: Continuar el fil +reverse: Revés +objectStorageBucket: Cubell +objectStorageBucketDesc: Si us plau específica el nom del cubell que faràs servir + al teu proveïdor. +clip: Retall +createNew: Crear una nova +optional: Opcional +jumpToSpecifiedDate: Vés a una data concreta +showingPastTimeline: Ara es mostra un línea de temps antiga +clear: Netejar +markAllAsRead: Marcar tot com a llegit +recentPosts: Pàgines recents +noMaintainerInformationWarning: La informació de l'administrador no està configurada. +resolved: Resolt +unresolved: Sense resoldre +filter: Filtre +slow: Lenta +useDrawerReactionPickerForMobile: Mostra el selector de reaccions com a calaix al + mòbil +welcomeBackWithName: Benvingut de nou, {name} +showLocalPosts: 'Mostra les notes locals a:' +homeTimeline: Línea de temps Inicial +socialTimeline: Línea de temps Social +themeColor: Color del Teletip del servidor +size: Mida +numberOfColumn: Nombre de columnes +numberOfPageCache: Nombre de pàgines emmagatzemades a la memòria cau +numberOfPageCacheDescription: L'augment d'aquest nombre millorarà la comoditat dels + usuaris, però provocarà més càrrega del servidor i utilitzarà més memòria. +logoutConfirm: Vols tancar la sessió? +lastActiveDate: Data d'últim ús +statusbar: Barra d'estat +pleaseSelect: Selecciona una opció +colored: Color +refreshInterval: "Interval d'actualització " +speed: Velocitat +cannotUploadBecauseExceedsFileSizeLimit: Aquest fitxer no s'ha pogut carregar perquè + supera la mida màxima permesa. +activeEmailValidationDescription: Permet una validació més estricta de les adreces + de correu electrònic, que inclou la comprovació d'adreces d'un sol ús i si realment + es pot comunicar amb elles. Quan no està marcat, només es valida el format del correu + electrònic. +shuffle: Barrejar +account: Compte +move: Moure +pushNotification: Notificacions push +subscribePushNotification: Activar les notificacions push +unsubscribePushNotification: Desactivar les notificacions push +pushNotificationAlreadySubscribed: Les notificacions push ja estan activades +pushNotificationNotSupported: El vostre navegador o servidor no admet notificacions + push +license: Llicència +indexPosts: Índex de notes +indexFrom: Índex a partir de l'ID de Publicacions +indexFromDescription: Deixeu en blanc per indexar cada publicació +indexNotice: Ara indexant. Això probablement trigarà una estona, si us plau, no reinicieu + el servidor durant almenys una hora. +_instanceTicker: + none: No mostrar mai + remote: Mostra per a usuaris remots + always: Mostra sempre +_serverDisconnectedBehavior: + nothing: No fer res + quiet: Mostra un avís discret + reload: Torna a carregar automàticament + dialog: Mostra el diàleg d'avís +_channel: + create: Crea un canal + edit: Edita el canal + setBanner: Establir bàner + removeBanner: Suprimeix el bàner + featured: Tendència + owned: Propietari + usersCount: '{n} Participants' + following: Seguit per + notesCount: '{n} Notes' + nameAndDescription: Nom i descripció + nameOnly: Només nom +_instanceMute: + instanceMuteDescription: Això silenciara les publicacions o els impulsos dels servidors + indicats, incloses les dels usuaris que responguin a un usuari des d'un servidor + silenciat. + title: Amaga les publicacions dels servidors a la llista. + instanceMuteDescription2: Separar amb noves línies + heading: Llista de servidors que cal silenciar +_ago: + future: Futur + justNow: Ara mateix + minutesAgo: Fa {n}m + hoursAgo: Fa {n}h + daysAgo: Fa {n}d + secondsAgo: Fa {n}s + weeksAgo: Fa {n}set + monthsAgo: Fa {n}me + yearsAgo: Fa {n}a +_time: + second: Segon(s) + minute: Minut(s) + hour: Hora(s) + day: Dia(s) +_tutorial: + step5_4: La línea de temps Local {icon} és on pots veure les publicacions de tots + els altres usuaris d'aquest servidor. + step5_2: El teu servidor té activades {timelines} diferents. + step5_3: La línea de temps d'inici {icon} es on pots veure les publicacions dels + comptes que segueixes. + step5_6: La línia de temps de Recomanats {icon} és on pots veure les publicacions + dels servidors que recomanen els administradors. + step5_7: La línia de temps Global {icon} és on pots veure les publicacions de tots + els servidors connectats. + step6_1: Aleshores, què és aquest lloc? + step6_4: Ara ves, explora i diverteix-te! + step1_2: Anem a fer la configuració. Estaràs en funcionament en un tres i no res! + title: Com utilitzar Firefish + step1_1: Benvingut! + step2_1: En primer lloc, empleneu el vostre perfil. + step4_1: Anem a treure't allà fora. + step5_5: La línea de temps Social {icon} és una combinació de les línies de temps + d'Inici i Local. + step6_3: Cada servidor funciona de diferents maneres, i no tots els servidors executen + Firefish. Aquest sí que sí! És una mica complicat, però ho aconseguiràs en poc + temps. + step2_2: Proporcionar informació sobre qui sou facilitarà que altres puguin saber + si volen veure les vostres notes o seguir-vos. + step3_1: Ara toca seguir a algunes persones! + step3_2: "Les teves líneas de temps d'inici i social es basen en qui seguiu, així + que proveu de seguir un parell de comptes per començar.\nFeu clic al cercle més + situat a la part superior dreta d'un perfil per seguir-los." + step4_2: A algunes persones els agrada fer una publicació de {introduction} o un + senzill "Hola món!" + step5_1: Línies de temps, línies de temps a tot arreu! + step6_2: Bé, no només t'has unit a Firefish. T'has unit a un portal al Fediverse, + una xarxa interconnectada de milers de servidors. +_permissions: + "read:account": Consulta la informació del teu compte + "read:blocks": Consulta la teva llista d'usuaris bloquejats + "write:account": Editar la informació del compte + "read:drive": Accedir als fitxers i carpetes del Disc + "read:messaging": Consulta els teus xats + "write:following": Segueix o deixa de seguir altres comptes + "write:mutes": Editar la teva llista d'usuaris silenciats + "read:notifications": Consulta les teves notificacions + "write:notifications": Gestiona les teves notificacions + "write:user-groups": Editar o suprimir grups d'usuaris + "write:blocks": Editar la llista d'usuaris bloquejats + "write:notes": Redactar o suprimir notes + "write:channels": Editar els teus canals + "read:gallery-likes": Consulta la llista de notes que t'agraden de la galeria + "write:drive": Editar o suprimir fitxers i carpetes del Disc + "read:favorites": Consulta la teva llista d'adreces d'interès + "write:favorites": Editeu la teva llista d'adreces d'interès + "write:messaging": Escriu o suprimeix missatges de xat + "read:mutes": Consulta la teva llista d'usuaris silenciats + "write:reactions": Edita les teves reaccions + "write:votes": Vota en una enquesta + "write:pages": Edita o suprimeix la teva pàgina + "write:page-likes": Editar les pàgines que t'agraden + "read:user-groups": Consulta els teus grups d'usuaris + "read:channels": Consulta els teus canals + "read:gallery": Consulta la teva galeria + "write:gallery": Edita la teva galeria + "write:gallery-likes": Edita la llista de notes que t'agraden de la galeria + "read:following": Consulta la informació sobre a qui segueixes + "read:reactions": Consulta les teves reaccions + "read:pages": Consulta la teva pàgina + "read:page-likes": Veure les pàgines que t'agraden +_poll: + noOnlyOneChoice: Calen almenys dues opcions + canMultipleVote: Permet seleccionar diverses opcions + expiration: Finalitzar l'enquesta + after: Acaba després... + duration: Durada + votesCount: '{n} vots' + totalVotes: '{n} vots en total' + showResult: Veure resultats + choiceN: Opció {n} + noMore: No es poden afegir més opcions + infinite: Mai + at: Acaba el... + deadlineDate: Data de finalització + deadlineTime: Temps + remainingHours: Queden {h} hora(s) {m} minut(s) + remainingDays: Queden {d} dia(s) {h} hores + remainingMinutes: Queden {m} minut(s) {s} segons + voted: Votat + closed: S'ha acabat + remainingSeconds: Queden {s} segons + vote: Vota +_postForm: + _placeholders: + d: Què vols dir? + e: Comença a escriure... + f: Esperant que escriguis... + b: Què passa al teu voltant? + c: En què penses? + a: Què et portes entre mans? + quotePlaceholder: Cita aquesta publicació... + replyPlaceholder: Respon a aquesta publicació... + channelPlaceholder: Publica en un canal... +_charts: + federation: Federació + usersIncDec: Diferència en el nombre d'usuaris + apRequest: Sol·licituds + usersTotal: Nombre total d'usuaris + activeUsers: Usuaris actius + notesIncDec: Diferència en el nombre de notes + localNotesIncDec: Diferència en el nombre de notes locals + remoteNotesIncDec: Diferència en el nombre de notes remotes + notesTotal: Nombre total de notes + filesIncDec: Diferència en el nombre de fitxers + filesTotal: Nombre total de fitxers + storageUsageTotal: Ús total d'emmagatzematge + storageUsageIncDec: Diferència en l'ús d'emmagatzematge +_instanceCharts: + requests: Sol·licituds + users: Diferència en el nombre d'usuaris + usersTotal: Nombre acumulat d'usuaris + notes: Diferència en el nombre de notes + ffTotal: Nombre acumulat d'usuaris que segueixes/et segueixen + cacheSize: Diferència en la mida de la memòria cau + cacheSizeTotal: Mida total acumulada de la memòria cau + files: Diferència en el nombre de fitxers + filesTotal: Nombre acumulat de fitxers + notesTotal: Nombre acumulat de notes + ff: "Diferència en el nombre d'usuaris que segueixes/que et segueixen " +_timelines: + home: Inici + local: Local + recommended: Recomanat + social: Social + global: Global +_menuDisplay: + hide: Amagar + top: Superior + sideFull: Costat + sideIcon: Costat (Icones) +_wordMute: + muteWords: Paraules silenciades + muteWordsDescription: Separeu amb espais per a una condició AND o amb salts de línia + per a una condició OR. + soft: Suau + hard: Dur + muteWordsDescription2: Envolta les paraules clau amb barres inclinades per utilitzar + expressions regulars. + softDescription: Amaga les notes que compleixen les condicions establertes de la + línia de temps. + hardDescription: Evita que les notes que compleixin les condicions establertes s'afegeixin + a la línia de temps. A més, aquestes notes no s'afegiran a la línia de temps encara + que es modifiquin les condicions. + mutedNotes: Notes silenciades +_auth: + shareAccessAsk: Estàs segur que vols autoritzar aquesta aplicació per accedir al + teu compte? + shareAccess: Vols autoritzar "{name}" per accedir a aquest compte? + permissionAsk: 'Aquesta aplicació sol·licita els següents permisos:' + callback: Tornant a l'aplicació + denied: Accés denegat + pleaseGoBack: Si us plau, torneu a l'aplicació + copyAsk: "Posa el següent codi d'autorització a l'aplicació:" + allPermissions: Accés complet al compte +_weekday: + wednesday: Dimecres + saturday: Dissabte + monday: Dilluns + tuesday: Dimarts + friday: Divendres + sunday: Diumenge + thursday: Dijous +_messaging: + groups: Grups + dms: Privat +_antennaSources: + all: Totes les notes + homeTimeline: Publicacions dels usuaris que segueixes + users: Notes d'usuaris concrets + userGroup: Notes d'usuaris d'un grup determinat + userList: Notes d'una llista determinada d'usuaris + instances: Publicacions de tots els usuaris d'un servidor +_relayStatus: + requesting: Pendent + accepted: Acceptat + rejected: Rebutjat +deleted: Eliminat +editNote: Edita la nota +edited: 'Editat a {date} {time}' +findOtherInstance: Cercar un altre servidor +signupsDisabled: Actualment, les inscripcions en aquest servidor estan desactivades, + però sempre podeu registrar-vos en un altre servidor. Si teniu un codi d'invitació + per a aquest servidor, introduïu-lo a continuació. +userSaysSomethingReasonQuote: '{name} ha citat una publicació que conté {reason}' +userSaysSomethingReasonReply: '{name} ha respost a una publicació que conté {reason}' +userSaysSomethingReasonRenote: '{name} ha impulsat una publicació que conté {reason}' +highlightCw: Ressalta el contingut de les publicacions advertides +apps: Aplicacions +sendModMail: Envia avís de moderació +preventAiLearning: Evita l'indexació dels bots +preventAiLearningDescription: Sol·liciteu que els models de llenguatge d'IA de tercers + no estudiïn el contingut que pengeu, com ara publicacions i imatges. +pwa: Instal·lar PWA +_experiments: + alpha: Alfa + beta: Beta + release: Publicà + title: Experiments + enablePostImports: Activar l'importació de publicacions + postImportsCaption: Permet els usuaris importar publicacions desde comptes a Firefish, + Misskey, Mastodon, Akkoma i Pleroma. Pot fer que el servidor vagi més lent durant + la càrrega si tens un coll d'ampolla a la cua. +noGraze: Si us plau, desactiva l'extensió del navegador "Graze for Mastodon", ja que + interfereix amb Firefish. +accessibility: Accessibilitat +jumpToReply: Vés a la resposta +newer: Més nou +older: Més antic +silencedWarning: S'està mostrant aquesta pàgina per què aquest usuari és d'un servidor + que l'administrador a silenciat, així que pot ser spam. +jumpToPrevious: Vés a l'anterior +cw: Avís de contingut +antennasDesc: "Les antenes mostren publicacions noves que coincideixen amb els criteris + establerts!\nS'hi pot accedir des de la pàgina de línies de temps." +expandOnNoteClick: Obre la publicació amb un clic +expandOnNoteClickDesc: Si està desactivat, encara pots obrir les publicacions al menú + del botó dret o fent clic a la marca de temps. +channelFederationWarn: Els canals encara no es federen amb altres servidors +searchPlaceholder: Cerca a Firefish +listsDesc: Les llistes et permeten crear línies de temps amb usuaris específics. Es + pot accedir des de la pàgina de línies de temps. +clipsDesc: Els clips són com marcadors categoritzats que es poden compartir. Podeu + crear clips des del menú de publicacions individuals. +selectChannel: Selecciona un canal +isLocked: Aquest compte té les següents aprovacions +isPatron: Mecenes de Calkey +isBot: Aquest compte és un bot +isModerator: Moderador +isAdmin: Administrador +_filters: + fromDomain: Des del domini + notesBefore: Publicacions anteriors + notesAfter: Publicacions posteriors + followingOnly: Només seguint + followersOnly: Només seguidors + withFile: Amb arxiu + fromUser: De l'usuari +image: Imatge +video: Vídeo +audio: Àudio +_dialog: + charactersExceeded: "S'han superat el màxim de caràcters! Actual: {current}/Límit: + {max}" + charactersBelow: 'No hi ha caràcters suficients! Corrent: {current}/Limit: {min}' +removeReaction: Elimina la teva reacció +reactionPickerSkinTone: To de pell d'emoji preferit +alt: ALT +_skinTones: + light: Clar + mediumLight: Clar Mitx + medium: Mitx + mediumDark: Fosc Mitx + dark: Fosc + yellow: Groc +swipeOnMobile: Permet lliscar entre pàgines +enableIdenticonGeneration: Habilitar la generació d'Identicon +enableServerMachineStats: Habilitar les estadístiques del maquinari del servidor +showPopup: Notificar els usuaris amb una finestra emergent +showWithSparkles: Mostra amb espurnes +youHaveUnreadAnnouncements: Tens anuncis sense llegir +xl: XL +donationLink: Enllaç a la pàgina de donacions +neverShow: No tornis a mostrar +remindMeLater: Potser després +removeMember: Elimina el membre +removeQuote: Elimina la cita +removeRecipient: Elimina el destinatari +verifiedLink: Enllaç verificat +_feeds: + rss: RSS + atom: Atom + jsonFeed: Feed JSON + copyFeed: Copiar feed diff --git a/locales/cs-CZ.yml b/locales/cs-CZ.yml index cb2f0a1df6..869066f2dd 100644 --- a/locales/cs-CZ.yml +++ b/locales/cs-CZ.yml @@ -1,7 +1,9 @@ ---- _lang_: "Čeština" headlineMisskey: "Síť propojená poznámkami" -introMisskey: "Vítejte! Misskey je otevřený a decentralizovaný microblogový servis.\n\"Poznámkami\" můžete sdílet co se zrovna děje se všemi ve Vašem okolí. 📡\nPomocí \"reakcí\" můžete sdílet své názory a pocity na ostatní poznámky. 👍\nPojďte objevovat nový svět! 🚀" +introMisskey: "Vítejte! Firefish je otevřený a decentralizovaný microblogový servis.\n\ + \"Poznámkami\" můžete sdílet co se zrovna děje se všemi ve Vašem okolí. \U0001F4E1\ + \nPomocí \"reakcí\" můžete sdílet své názory a pocity na ostatní poznámky. \U0001F44D\ + \nPojďte objevovat nový svět! \U0001F680" monthAndDay: "{day}. {month}." search: "Vyhledávání" notifications: "Oznámení" @@ -44,7 +46,8 @@ copyContent: "Zkopírovat obsah" copyLink: "Kopírovat odkaz" delete: "Smazat" deleteAndEdit: "Smazat a upravit" -deleteAndEditConfirm: "Jste si jistí že chcete smazat tuto poznámku a editovat ji? Ztratíte tím všechny reakce, sdílení a odpovědi na ni." +deleteAndEditConfirm: "Jste si jistí že chcete smazat tuto poznámku a editovat ji?\ + \ Ztratíte tím všechny reakce, sdílení a odpovědi na ni." addToList: "Přidat do seznamu" sendMessage: "Odeslat zprávu" copyUsername: "Kopírovat uživatelské jméno" @@ -63,9 +66,11 @@ import: "Importovat" export: "Exportovat" files: "Soubor(ů)" download: "Stáhnout" -driveFileDeleteConfirm: "Opravdu chcete smazat soubor \"{name}\"? Poznámky, ke kterým je tento soubor připojen, budou také smazány." +driveFileDeleteConfirm: "Opravdu chcete smazat soubor \"{name}\"? Soubor bude odstraněn\ + \ ze všech příspěvků, které ji obsahují jako přílohu." unfollowConfirm: "Jste si jisti že už nechcete sledovat {name}?" -exportRequested: "Požádali jste o export. To může chvíli trvat. Přidáme ho na váš Disk až bude dokončen." +exportRequested: "Požádali jste o export. To může chvíli trvat. Přidáme ho na váš\ + \ Disk až bude dokončen." importRequested: "Požádali jste o export. To může chvilku trvat." lists: "Seznamy" noLists: "Nemáte žádné seznamy" @@ -81,7 +86,8 @@ somethingHappened: "Jejda. Něco se nepovedlo." retry: "Opakovat" pageLoadError: "Nepodařilo se načíst stránku" serverIsDead: "Server neodpovídá. Počkejte chvíli a zkuste to znovu." -youShouldUpgradeClient: "Pro zobrazení této stránky obnovte stránku pro aktualizaci klienta." +youShouldUpgradeClient: "Pro zobrazení této stránky obnovte stránku pro aktualizaci\ + \ klienta." enterListName: "Jméno seznamu" privacy: "Soukromí" makeFollowManuallyApprove: "Žádosti o sledování vyžadují potvrzení" @@ -105,7 +111,8 @@ clickToShow: "Klikněte pro zobrazení" sensitive: "NSFW" add: "Přidat" reaction: "Reakce" -reactionSettingDescription2: "Přetažením změníte pořadí, kliknutím smažete, zmáčkněte \"+\" k přidání" +reactionSettingDescription2: "Přetažením změníte pořadí, kliknutím smažete, zmáčkněte\ + \ \"+\" k přidání" rememberNoteVisibility: "Zapamatovat nastavení zobrazení poznámky" attachCancel: "Odstranit přílohu" markAsSensitive: "Označit jako NSFW" @@ -134,13 +141,18 @@ emojiUrl: "URL obrázku" addEmoji: "Přidat emoji" settingGuide: "Doporučené nastavení" cacheRemoteFiles: "Ukládání vzdálených souborů do mezipaměti" -cacheRemoteFilesDescription: "Zakázání tohoto nastavení způsobí, že vzdálené soubory budou odkazovány přímo, místo aby byly ukládány do mezipaměti. Tím se ušetří úložiště na serveru, ale zvýší se provoz, protože se negenerují miniatury." +cacheRemoteFilesDescription: "Zakázání tohoto nastavení způsobí, že vzdálené soubory\ + \ budou odkazovány přímo, místo aby byly ukládány do mezipaměti. Tím se ušetří úložiště\ + \ na serveru, ale zvýší se provoz, protože se negenerují miniatury." flagAsBot: "Tento účet je bot" -flagAsBotDescription: "Pokud je tento účet kontrolován programem zaškrtněte tuto možnost. To označí tento účet jako bot pro ostatní vývojáře a zabrání tak nekonečným interakcím s ostatními boty a upraví Misskey systém aby se choval k tomuhle účtu jako bot." +flagAsBotDescription: "Pokud je tento účet kontrolován programem zaškrtněte tuto možnost.\ + \ To označí tento účet jako bot pro ostatní vývojáře a zabrání tak nekonečným interakcím\ + \ s ostatními boty a upraví Firefish systém aby se choval k tomuhle účtu jako bot." flagAsCat: "Tenhle účet je kočka" flagAsCatDescription: "Vyberte tuto možnost aby tento účet byl označen jako kočka." flagShowTimelineReplies: "Zobrazovat odpovědi na časové ose" -flagShowTimelineRepliesDescription: "Je-li zapnuto, zobrazí odpovědi uživatelů na poznámky jiných uživatelů na vaší časové ose." +flagShowTimelineRepliesDescription: "Je-li zapnuto, zobrazí odpovědi uživatelů na\ + \ poznámky jiných uživatelů na vaší časové ose." autoAcceptFollowed: "Automaticky akceptovat následování od účtů které sledujete" addAccount: "Přidat účet" loginFailed: "Přihlášení se nezdařilo." @@ -153,7 +165,10 @@ searchWith: "Hledat: {q}" youHaveNoLists: "Nemáte žádné seznamy" followConfirm: "Jste si jisti, že chcete sledovat {name}?" proxyAccount: "Proxy účet" -proxyAccountDescription: "Proxy účet je účet, který za určitých podmínek sleduje uživatele na dálku vaším jménem. Například když uživatel zařadí vzdáleného uživatele do seznamu, pokud nikdo nesleduje uživatele na seznamu, aktivita nebude doručena instanci, takže místo toho bude uživatele sledovat účet proxy." +proxyAccountDescription: "Proxy účet je účet, který za určitých podmínek sleduje uživatele\ + \ na dálku vaším jménem. Například když uživatel zařadí vzdáleného uživatele do\ + \ seznamu, pokud nikdo nesleduje uživatele na seznamu, aktivita nebude doručena\ + \ instanci, takže místo toho bude uživatele sledovat účet proxy." host: "Hostitel" selectUser: "Vyberte uživatele" recipient: "Pro" @@ -174,7 +189,6 @@ operations: "Operace" software: "Software" version: "Verze" metadata: "Metadata" -withNFiles: "{n} soubor(ů)" monitor: "Monitorovat" jobQueue: "Fronta úloh" cpuAndMemory: "CPU a paměť" @@ -189,7 +203,7 @@ blockedInstances: "Blokované instance" noUsers: "Žádní uživatelé" editProfile: "Upravit můj profil" pinLimitExceeded: "Nemůžete připnout další poznámky." -intro: "Instalace Misskey byla dokončena! Prosím vytvořte admina." +intro: "Instalace Firefish byla dokončena! Prosím vytvořte admina." done: "Hotovo" processing: "Zpracovávám" preview: "Náhled" @@ -239,7 +253,8 @@ agreeTo: "Souhlasím s {0}" tos: "Podmínky užívání" start: "Začít" home: "Domů" -remoteUserCaution: "Tyto informace nemusí být aktuální jelikož uživatel je ze vzdálené instance." +remoteUserCaution: "Tyto informace nemusí být aktuální jelikož uživatel je ze vzdálené\ + \ instance." activity: "Aktivita" images: "Obrázky" birthday: "Datum narození" @@ -332,7 +347,7 @@ recentlyUpdatedUsers: "Nedávno aktívni uživatelé" popularTags: "Populární tagy" userList: "Seznamy" about: "Informace" -aboutMisskey: "O Misskey" +aboutFirefish: "O Firefish" administrator: "Administrátor" token: "Token" twoStepAuthentication: "Dvoufaktorová autentikace" @@ -548,7 +563,8 @@ info: "Informace" unknown: "Neznámý" onlineStatus: "Online status" hideOnlineStatus: "Skrýt Váš online status" -hideOnlineStatusDescription: "Skrytí vašeho online stavu může snížit funkcionalitu některých funkcí, například vyhledávání." +hideOnlineStatusDescription: "Skrytí vašeho online stavu může snížit funkcionalitu\ + \ některých funkcí, například vyhledávání." online: "Online" active: "Aktivní" offline: "Offline" @@ -630,7 +646,7 @@ _registry: keys: "Klíče" domain: "Doména" createKey: "Vytvořit klíč" -_aboutMisskey: +_aboutFirefish: allContributors: "Všichni přispěvatelé" source: "Zdrojový kód" _mfm: @@ -682,8 +698,8 @@ _time: minute: "Minut" hour: "Hodin" _2fa: - registerDevice: "Přidat zařízení" - registerKey: "Přidat bezpečnostní klíč" + registerTOTP: "Přidat zařízení" + registerSecurityKey: "Přidat bezpečnostní klíč" _weekday: sunday: "Neděle" monday: "Pondělí" @@ -928,3 +944,66 @@ _deck: antenna: "Antény" list: "Seznamy" mentions: "Zmínění" +noteDeleteConfirm: Chcete opravdu smazat tento příspěvek? +defaultValueIs: 'Výchozí: {value}' +lookup: Hledat +keepOriginalUploading: Ponechat originální obrázek +uploadFromUrlRequested: Vyžádáno nahrání souboru +manageGroups: Spravovat skupiny +reloadConfirm: Znovu načíst časovou osu? +driveCapacityPerRemoteAccount: Místo na disku pro vzdálené uživatele +silenceThisInstance: Ztlumit tuto instance +silencedInstances: Ztlumené instance +blockedInstancesDescription: Zadejte seznam domén instancí, jež chcete blokovat. Uvedené + instance nebudou moci s touto instancí komunikovat. +hiddenTags: Skryté hashtagy +noInstances: Nejsou zde žádné instance +silenced: Ztlumené +disablingTimelinesInfo: Administrátoři a moderátoři budou vždy mít přístup ke všem + časovým osám, i pokud jsou vypnuté. +deleted: Vymazáno +editNote: Upravit poznámku +edited: 'Upraveno dne {date} {time}' +silencedInstancesDescription: Vypište hostnames instancí, které chcete ztlumit. Účty + v uvedených instancích jsou považovány za "ztlumené", mohou pouze zadávat požadavky + na sledování a nemohou zmiňovat místní účty, pokud nejsou sledovány. Na blokované + instance toto nebude mít vliv. +hiddenTagsDescription: 'Vypište hashtagy (bez #), které chcete skrýt před trendy a + prozkoumat. Skryté hashtagy jsou stále zjistitelné jinými způsoby. Blokované případy + nejsou ovlivněny, i když jsou zde uvedeny.' +circularReferenceFolder: Cílová složka je podsložka přesouvané složky. +whenServerDisconnected: Při ztrátě spojení se serverem +pinnedUsersDescription: Uveďte uživatelská jména uživatelů připnutých na stránce "Procházet", + jedno na řádek. +pinnedPagesDescription: Zadejte cesty ke stránkám, které chcete připnout na horní + stránku této instance, oddělené zlomy řádků. +pageLoadErrorDescription: Toto je obvykle způsobeno chybami sítě nebo mezipaměti prohlížeče. + Zkuste vymazat mezipaměť a po chvíli čekání to zkuste znovu. +emptyDrive: Váš disk je prázdný +inputNewDescription: Zadejte nový popisek +hasChildFilesOrFolders: Složka nemůže být smazána, protože není prázdná. +noThankYou: Ne, děkuji +addInstance: Přidat instance +selectInstance: Vybrat si instance +blockedUsers: Zablokovaní uživatelé +muteAndBlock: Ztlumení a blokace +noJobs: Žádné úlohy +federating: Federace +clearQueueConfirmText: Nedoručené příspěvky, které zůstanou ve frontě, nebudou federovány. + Obvykle tato operace není potřeba. +clearCachedFilesConfirm: Chcete opravdu vymazat mezipaměť všech vzdálených souborů? +accountMoved: 'Uživatel/ka se přesunul/a na nový účet:' +keepOriginalUploadingDescription: Ponechá originálně nahraný obrázek tak, jak je. + Pokud vypnuto, verze pro zobrazení na webu bude vygenerována při nahrání. +mutedUsers: Ztlumení uživatelé +enableRecommendedTimeline: Povolit doporučenou časovou osu +driveCapacityPerLocalAccount: Místo na disku pro místní uživatele +pinnedPages: Připnuté Stránky +directNotes: Přímé zprávy +enableEmojiReactions: Povolit reakce pomocí emoji +showEmojisInReactionNotifications: Zobrazit emotikony v oznámeních o reakcích +reactionSetting: Reakce, které se mají zobrazit v seznamu reakcí +renoteMute: Ztlumit přeposílání +renoteUnmute: Zrušit ztlumení přeposílání +flagSpeakAsCat: Mluvit jako kočka +flagSpeakAsCatDescription: Vaše příspěvky budou v kočičím režimu nyanifikovány. diff --git a/locales/da-DK.yml b/locales/da-DK.yml index 08c15ed092..3a17f60993 100644 --- a/locales/da-DK.yml +++ b/locales/da-DK.yml @@ -1,2 +1,236 @@ ---- _lang_: "Dansk" +monthAndDay: '{month}/{day}' +search: Søge +notifications: Notifikationer +username: Brugernavn +password: Adgangskode +forgotPassword: Glemt adgangskode +fetchingAsApObject: Henter fra Fediverset +ok: OK +gotIt: Forstået! +cancel: Annullere +enterUsername: Indtast brugernavn +instance: Instans +renotedBy: Forstærket fra {user} +noNotes: Ingen opslag +otherSettings: Andre Indstillinger +profile: Profil +timeline: Tidslinje +signup: Registrere +logout: Log Ud +login: Log ind +uploading: Uploader... +save: Gem +users: Brugere +favorited: Tilsat til bogmærker. +unfavorite: Fjerne fra bogmærker +alreadyFavorited: Allerede inden i bogmærker. +pin: Fastgøre til profil +unpin: Løse fra profil +delete: Slet +addToList: Tilsæt til liste +deleteAndEdit: Slet og ændre +reply: Svar +loadMore: Indlæs mere +receiveFollowRequest: Følgeanmodning er blevet sendt +import: Importere +export: Eksportere +driveFileDeleteConfirm: Er du sikker på at du vil slette filen "{name}"? Denne vil + blive slettet fra alle tilknyttede opslage. +unfollowConfirm: Er du sikker på at du vil ikke følge {name} længere? +privacy: Privatlivs +enterListName: Indtast navnen for denne list +makeFollowManuallyApprove: Følgeanmodninger kræver godkendelse +unrenote: Fratag forstærkelse +renote: Forstærk +add: Tilsæt +reactionSetting: Reaktioner til at vise i reaktion-vælgeren +reactionSettingDescription2: Bevæg til at flytte om på, tryk til at slette og indtast + "+" til at tilsætte. +rememberNoteVisibility: Husk opslagsynlidhedsindstillinger +emojis: Emoji +flagShowTimelineReplies: Vis svare i tidslinjen +flagAsCatDescription: Du kommer til at få katøre og tale som en kat! +showOnRemote: Vis på fjerninstans +general: Generelt +accountMoved: 'Bruger har flyttet til et nyt konto:' +settings: Indstillinger +basicSettings: Primær Indstillinger +openInWindow: Åben i vindue +noAccountDescription: Denne bruger har ikke skrevet deres bio endnu. +loggingIn: Logger ind +cantFavorite: Kunne ikke tilsætte til bogmærker. +copyUsername: Kopi brugernavn +copyContent: Kopi indholdet +copyLink: Kopi link +searchUser: Søg for en bruger +files: Filer +noLists: Du har ingen liste +lists: Lister +reaction: Reaktioner +sensitive: NSFW +emoji: Emoji +cacheRemoteFilesDescription: Når denne indstilling er deaktiveret, fremmed filer bliver + indlæset direkte fra denne fjerneinstans. Hvis du deaktivere dette så vil det formindske + brugte opbevaringsplads men det vil også få netværktraffic til at stige fordi miniaturebilleder + vil ikke blive skabt. +flagAsBot: Markere denne konto som en robot +flagShowTimelineRepliesDescription: Vis svare af brugere til opslage af andre brugere + i tidslinjen hvis den bliver tændt. +loginFailed: Kunne ikke logge ind +silenceThisInstance: Nedtone denne instans +deleteAndEditConfirm: Er du sikker på at du vil slet denne opslag og ændre det? Du + vil tabe alle reaktioner, forstærkninger og svarer indenfor denne opslag. +editNote: Ændre note +deleted: Slettet +edited: 'Ændret den {date} {time}' +sendMessage: Send en besked +youShouldUpgradeClient: Til at vise denne side, vær sød at refresh til at opdatere + din brugerenhed. +defaultNoteVisibility: Standard synlighed +follow: Følge +followRequest: Følge +followRequests: Følgeanmodninger +unfollow: Følge ikke længere +followRequestPending: Følgeanmodning ventes på +enterEmoji: Indtast en emoji +renoted: Forstærket. +cantRenote: Denne opslag kunne ikke forstærkes. +cantReRenote: En forstærkelse kan ikke forstærkes. +quote: Citere +pinnedNote: Fastgjort opslag +pinned: Fastgøre til profil +you: Dig +clickToShow: Tryk til at vise +unblock: Blokere ikke længere +suspend: Suspendere +unsuspend: Suspendere ikke længere +blockConfirm: Er du sikker på at du vil blokere denne konto? +unblockConfirm: Er du sikker på at du vil ikke blokere denne konto endnu længere? +suspendConfirm: Er du sikker på at du vil suspendere denne konto? +selectAntenna: Vælg en antenne +selectWidget: Vælg en widget +editWidgets: Ændre widgettere +customEmojis: Brugerdefineret emoji +emojiName: Emoji navn +operations: Operationer +software: Software +metadata: Metadata +version: Version +monitor: Vagt +jobQueue: Jobkø +statistics: Statistik +cpuAndMemory: CPU og hukommelse +network: Netværk +disk: Disk +instanceInfo: Instans information +noThankYou: Nej tak +noNotifications: Intet notifikationer +addUser: Indsæt en bruger +addInstance: Indsæt en instans +favorite: Indsæt til bogmærker +favorites: Bogmærker +showMore: Vis mere +showLess: Luk +youGotNewFollower: følgte dig +followRequestAccepted: Følgeanmodning accepteret +mention: Nævne +mentions: Nævnene +directNotes: Direkt beskeder +importAndExport: Importere/Eksporter data +download: Download +exportRequested: Du har bedt om en eksport. Det vil tage noget tid. Den vil blive + tilsæt til din Drev når den er færdig. +importRequested: Du har bedt om en eksport. Det vil tage noget tid. +note: Opslag +notes: Opslage +following: Følger +followers: Følgere +followsYou: Følger dig +createList: Skab en list +manageLists: Administrere lister +error: Fejl +somethingHappened: En fejl har opstået +retry: Gentage +pageLoadError: En fejl har opstået ved indlæsning af siden. +pageLoadErrorDescription: Dette er normalt på grund af netværksproblemer eller din + browser's cache. Prøv at ryd cachen og så gentage efter et styk tid. +serverIsDead: Serveren svarer ikke. Vær sød at vente et styk tid og prøv igen. +editWidgetsExit: Færdig +headlineMisskey: En åben-kildekode, decentraliseret social-media platform som er frit + forevigt! 🚀 +introMisskey: Velkommen! Firefish er en åbent-kildekode, decentraliseret social-media + platform som er frit forevigt!🚀 +enableEmojiReactions: Aktivere emoji reaktioner +unsuspendConfirm: Er du sikker på at du vil ikke suspendere denne konto endnu længere? +selectList: Vælg en list +showEmojisInReactionNotifications: Vis emoji i reaktion notifikationer +attachCancel: Fjern tilknyttelse +markAsSensitive: Markere som NSFW +unmarkAsSensitive: Markere ikke som NSFW længere +enterFileName: Indtast filnavn +mute: Nedtone +unmute: Nedtone ikke længere +renoteMute: Nedtone forstærkninger +renoteUnmute: Nedtone forstærkninger ikke længere +block: Blokere +cacheRemoteFiles: Cachere fremmed filer +flagAsBotDescription: Aktivere denne valgmulighed hvis denne konto er kontrolleret + af en komputerprogram. Hvis den et tændt så vil det signalere til andre udviklere + som arbejder på komputer-kontrolleret social-media kontoer og det vil også adjustere + Firefish's indresystemer til at behandle denne konto som en robot. +flagAsCat: Er du en kat? 😺 +flagSpeakAsCat: Tale som en kat +emojiUrl: Emoji URL +addEmoji: Tilsæt +settingGuide: Anbefalet indstillinger +flagSpeakAsCatDescription: Din opslage vil blive nyaniferet når du er i kat-mode +autoAcceptFollowed: Automatisk godkende følgeanmodninger fra brugere som du selv følger +addAccount: Tilsæt konto +wallpaper: Baggrund +setWallpaper: Sæt baggrund +removeWallpaper: Fjern baggrund +host: Host +selectUser: Vælg en bruger +searchWith: 'Søge: {q}' +youHaveNoLists: Du har ingen liste +followConfirm: Er du sikker på at du vil gerne følge {name}? +proxyAccount: Proxykonto +proxyAccountDescription: En proxykonto er en konto som virker som en fremmed følger + for bruger under særlige konditioner. For eksempel, når en bruger tilsætter en fjernbruger + til denne list, vil denne fjernbruger's aktivitet ikke blive leveret til den instans + hvis ingen lokalebruger følger fjernbrugeren, så denne proxykonto vil følge den + istedetfor. +instances: Instanser +registeredAt: Registreret på +latestRequestSentAt: Sidste anmodning sendt +latestRequestReceivedAt: Sidste anmodning modtaget +selectInstance: Vælg en instans +recipient: Recipient(er) +annotation: Kommentarer +federation: Føderation +latestStatus: Senest status +storageUsage: Opbevaringspladsbrug +charts: Grafer +perHour: Hver time +perDay: Hver dag +stopActivityDelivery: Stop med at sende aktiviteter +blockThisInstance: Blokere denne instans +muteAndBlock: Mutes og blokeringer +mutedUsers: Mutede brugere +newer: nyere +older: ældre +silencedInstances: Nedtonede servere +clearQueue: Ryd kø +clearQueueConfirmTitle: Er du sikker på, at du ønsker at rydde køen? +clearCachedFiles: Ryd cache +clearCachedFilesConfirm: Er du sikker på, at du ønsker at slette alle cachede eksterne + filer? +blockedInstances: Blokerede servere +blockedInstancesDescription: Listen af navne på servere, du ønsker at blokere. Servere + på listen vil ikke længere kunne kommunikere med denne server. +hiddenTags: Skjulte hashtags +clearQueueConfirmText: De indlæg i denne kø, der ikke allerede er leveret, vil ikke + blive federeret. Denne operation er almindeligvis ikke påkrævet. +jumpToPrevious: Spring til tidligere +cw: Advarsel om indhold diff --git a/locales/de-DE.yml b/locales/de-DE.yml index a51bc0c486..da7d423bd4 100644 --- a/locales/de-DE.yml +++ b/locales/de-DE.yml @@ -1,278 +1,307 @@ ---- _lang_: "Deutsch" -headlineMisskey: "Ein durch Posts verbundenes Netzwerk" -introMisskey: "Willkommen! Calckey ist eine dezentralisierte Open-Source Microblogging-Platform.\nVerfasse „Posts“ um mitzuteilen, was gerade passiert oder um Ereignisse mit anderen zu teilen. 📡\nMit „Reaktionen“ kannst du außerdem schnell deine Gefühle über Posts anderer Benutzer zum Ausdruck bringen. 👍\nEine neue Welt wartet auf dich! 🚀" -monthAndDay: "{day}.{month}." +headlineMisskey: "Eine dezentralisierte Open-Source Social Media Plattform, die für + immer gratis bleibt! 🚀" +introMisskey: "Willkommen! Firefish ist eine dezentralisierte Open-Source Social Media + Plattform, die für immer gratis bleibt!🚀" +monthAndDay: "{month}/{day}" search: "Suchen" notifications: "Benachrichtigungen" -username: "Benutzername" +username: "Nutzername" password: "Passwort" forgotPassword: "Passwort vergessen" fetchingAsApObject: "Wird aus dem Fediverse angefragt" ok: "OK" gotIt: "Verstanden!" cancel: "Abbrechen" -enterUsername: "Benutzername eingeben" -renotedBy: "Renote von {user}" -noNotes: "Keine Notizen gefunden" -noNotifications: "Keine Benachrichtigungen gefunden" -instance: "Instanz" +enterUsername: "Nutzername eingeben" +renotedBy: "Geteilt von {user}" +noNotes: "Keine Beiträge" +noNotifications: "Keine Benachrichtigungen" +instance: "Server" settings: "Einstellungen" -basicSettings: "Allgemeine Einstellungen" +basicSettings: "Grundeinstellungen" otherSettings: "Weitere Einstellungen" openInWindow: "In einem Fenster öffnen" profile: "Profil" -timeline: "Chronik" -noAccountDescription: "Dieser Nutzer hat seine Profilbeschreibung noch nicht ausgefüllt" -login: "Anmelden" -loggingIn: "Du wirst angemeldet …" -logout: "Abmelden" +timeline: "Timelines" +noAccountDescription: "Dieser Nutzer hat seine Profilbeschreibung noch nicht ausgefüllt." +login: "Login" +loggingIn: "Du wirst angemeldet" +logout: "Logout" signup: "Registrieren" uploading: "Wird hochgeladen …" save: "Speichern" -users: "Benutzer" -addUser: "Benutzer hinzufügen" -favorite: "Zu Favoriten hinzufügen" -favorites: "Favoriten" -unfavorite: "Aus Favoriten entfernen" -favorited: "Zu Favoriten hinzugefügt." -alreadyFavorited: "Bereits zu den Favoriten hinzugefügt." -cantFavorite: "Hinzufügen zu Favoriten fehlgeschlagen." +users: "Nutzer" +addUser: "Nutzer hinzufügen" +favorite: "Zu den Lesezeichen hinzufügen" +favorites: "Lesezeichen" +unfavorite: "Aus den Lesezeichen entfernen" +favorited: "Zu den Lesezeichen hinzugefügt." +alreadyFavorited: "Bereits zu den Lesezeichen hinzugefügt." +cantFavorite: "Hinzufügen zu den Lesezeichen fehlgeschlagen." pin: "An dein Profil anheften" unpin: "Von deinem Profil lösen" copyContent: "Inhalt kopieren" copyLink: "Link kopieren" delete: "Löschen" deleteAndEdit: "Löschen und Bearbeiten" -deleteAndEditConfirm: "Möchtest du diese Notiz wirklich löschen und bearbeiten? Alle Reaktionen, Renotes und Antworten dieser Notiz werden verloren gehen." +deleteAndEditConfirm: "Möchtest du diesen Beitrag wirklich löschen und bearbeiten? + Alle Rückmeldungen, Renotes und Antworten dieses Beitrages werden verloren gehen." addToList: "Zu Liste hinzufügen" -sendMessage: "Nachricht senden" -copyUsername: "Benutzernamen kopieren" -searchUser: "Nach einem Benutzer suchen" +sendMessage: "Eine Mitteilung senden" +copyUsername: "Nutzernamen kopieren" +searchUser: "Nach einem Nutzer suchen" reply: "Antworten" loadMore: "Mehr laden" showMore: "Mehr anzeigen" showLess: "Schließen" -youGotNewFollower: "ist dir gefolgt" +youGotNewFollower: "folgt dir" receiveFollowRequest: "Follow-Anfrage erhalten" followRequestAccepted: "Follow-Anfrage akzeptiert" mention: "Erwähnung" mentions: "Erwähnungen" -directNotes: "Direktnachrichten" -importAndExport: "Import und Export" +directNotes: "Direktmitteilungen" +importAndExport: "Daten Im- und Export" import: "Import" export: "Export" files: "Dateien" download: "Herunterladen" -driveFileDeleteConfirm: "Möchtest du die Datei „{name}“ wirklich löschen? Notizen mit dieser Datei werden ebenso verschwinden." -unfollowConfirm: "Möchtest du {name} nicht mehr folgen?" -exportRequested: "Du hast einen Export angefragt. Dies kann etwas Zeit in Anspruch nehmen. Sobald der Export abgeschlossen ist, wird er deiner Drive hinzugefügt." -importRequested: "Du hast einen Import angefragt. Dies kann etwas Zeit in Anspruch nehmen." +driveFileDeleteConfirm: "Möchtest du die Datei \"{name}\" wirklich löschen? Es wird + aus allen Beiträgen entfernt, die die Datei als Anhang enthalten." +unfollowConfirm: "Bist du dir sicher, daß du {name} nicht mehr folgen möchtest?" +exportRequested: "Du hast einen Export angefragt. Dies kann etwas Zeit in Anspruch + nehmen. Sobald der Export abgeschlossen ist, wird er deinem Laufwerk hinzugefügt." +importRequested: "Du hast einen Import angefragt. Dies kann etwas Zeit in Anspruch + nehmen." lists: "Listen" -noLists: "Keine Listen gefunden" -note: "Notiz" -notes: "Notizen" -following: "Folgt" -followers: "Gefolgt von" +noLists: "Du hast keine Listen angelegt" +note: "Beitrag" +notes: "Beiträge" +following: "Folge ich" +followers: "Folgen mir" followsYou: "Folgt dir" createList: "Liste erstellen" manageLists: "Listen verwalten" error: "Fehler" somethingHappened: "Ein Fehler ist aufgetreten" retry: "Wiederholen" -pageLoadError: "Die Seite konnte nicht geladen werden." -pageLoadErrorDescription: "Dieser Fehler wird meist durch Netzwerkfehler oder den Browser-Cache verursacht. Bitte leere den Cache oder versuche es nach einiger Zeit erneut." -serverIsDead: "Dieser Server antwortet nicht. Bitte warte einen Moment und versuche es dann erneut." -youShouldUpgradeClient: "Bitte aktualisiere diese Seite, um eine neuere Version deines Clients zu verwenden." -enterListName: "Listennamen eingeben" +pageLoadError: "Beim Laden der Seite ist ein Fehler aufgetreten." +pageLoadErrorDescription: "Dies wird in der Regel durch Netzwerkfehler oder den Cache + des Browsers verursacht. Versuchen Sie, den Cache zu leeren, und versuchen Sie es + dann erneut, nachdem Sie eine Weile gewartet haben." +serverIsDead: "Der Server antwortet nicht. Bitte warte einen Moment und versuche es + dann erneut." +youShouldUpgradeClient: "Bitte aktualisiere diese Seite, um eine neuere Version deines + Clients zu verwenden." +enterListName: "Gib einen Namen für die Liste ein" privacy: "Privatsphäre" -makeFollowManuallyApprove: "Follow-Anfragen benötigen Bestätigung" -defaultNoteVisibility: "Standardsichtbarkeit" -follow: "Folgen" +makeFollowManuallyApprove: "Folgeanfragen bedürfen der Genehmigung" +defaultNoteVisibility: "Standard-Sichtbarkeit" +follow: "Folge ich" followRequest: "Follow anfragen" followRequests: "Follow-Anfragen" unfollow: "Nicht mehr folgen" -followRequestPending: "Follow-Anfrage ausstehend" -enterEmoji: "Gib ein Emoji ein" -renote: "Renote" -unrenote: "Renote zurücknehmen" -renoted: "Renote getätigt." -cantRenote: "Renote dieses Beitrags nicht möglich." -cantReRenote: "Renote einer Renote nicht möglich." +followRequestPending: "Follow-up-Anfrage ausstehend" +enterEmoji: "Ein Emoji eingeben" +renote: "Boost" +unrenote: "Boost zurücknehmen" +renoted: "Geboostet." +cantRenote: "Dieser Beitrag kann nicht geboostet werden." +cantReRenote: "Ein Boost kann nicht geboostet werden." quote: "Zitieren" -pinnedNote: "Angeheftete Notiz" -pinned: "Angeheftet" +pinnedNote: "Angepinnter Beitrag" +pinned: "An das Profil anheften" you: "Du" clickToShow: "Zum Anzeigen anklicken" sensitive: "NSFW" add: "Hinzufügen" reaction: "Reaktionen" -reactionSetting: "In der Reaktionsauswahl anzuzeigende Reaktionen" -reactionSettingDescription2: "Ziehe um Anzuordnen, klicke um zu löschen, drücke „+“ um hinzuzufügen" -rememberNoteVisibility: "Notizsichtbarkeit merken" +reactionSetting: "Reaktionen, die in der Reaktionsauswahl angezeigt werden sollen" +reactionSettingDescription2: "Ziehen Sie, um neu zu ordnen,\nklicken Sie, um zu löschen,\n + drücken Sie \"+\", um hinzuzufügen." +rememberNoteVisibility: "Einstellungen für die Sichtbarkeit von Beiträgen speichern" attachCancel: "Anhang entfernen" -markAsSensitive: "Als NSFW markieren" -accountMoved: "Benutzer hat zu einem anderen Account gewechselt." -unmarkAsSensitive: "Als nicht NSFW markieren" -enterFileName: "Dateinamen eingeben" +markAsSensitive: "Als NSFW kennzeichnen" +accountMoved: "Der Nutzer ist zu einem neuen Konto umgezogen:" +unmarkAsSensitive: "NSFW Kennzeichnung aufheben" +enterFileName: "Dateiname eingeben" mute: "Stummschalten" unmute: "Stummschaltung aufheben" block: "Blockieren" unblock: "Blockierung aufheben" -suspend: "Sperren" -unsuspend: "Sperrung aufheben" -blockConfirm: "Möchtest du diesen Benutzer wirklich blockieren?" -unblockConfirm: "Möchtest du diese Blockierung wirklich aufheben?" -suspendConfirm: "Möchtest du diesen Benutzer wirklich sperren?" -unsuspendConfirm: "Möchtest du diesen Benutzer wirklich entsperren?" -selectList: "Liste auswählen" -selectAntenna: "Antenne auswählen" -selectWidget: "Widget auswählen" +suspend: "Suspendieren" +unsuspend: "Suspendierung aufheben" +blockConfirm: "Sind Sie sicher, dass Sie dieses Konto sperren wollen?" +unblockConfirm: "Sind Sie sicher, dass Sie die Sperrung dieses Kontos aufheben wollen?" +suspendConfirm: "Sind Sie sicher, dass Sie dieses Konto sperren wollen?" +unsuspendConfirm: "Sind Sie sicher, dass Sie dieses Konto entsperren wollen?" +selectList: "Wählen Sie eine Liste aus" +selectAntenna: "News-Picker auswählen" +selectWidget: "Ein Widget auswählen" editWidgets: "Widgets bearbeiten" -editWidgetsExit: "Fertig" -customEmojis: "Benutzerdefinierte Emojis" +editWidgetsExit: "Erledigt" +customEmojis: "Benutzerdefinierte Emoji" emoji: "Emoji" -emojis: "Emojis" +emojis: "Emoji" emojiName: "Emoji-Name" emojiUrl: "Emoji-URL" addEmoji: "Emoji hinzufügen" -settingGuide: "Empfohlene Einstellung" -cacheRemoteFiles: "Dateien von fremden Instanzen im Cache speichern" -cacheRemoteFilesDescription: "Ist diese Einstellung deaktiviert, so werden Dateien fremder Instanzen direkt von dort geladen. Hierdurch wird Speicherplatz auf diesem Server gespart, aber durch fehlende Generierung von Vorschaubildern mehr Bandbreite verwendet." -flagAsBot: "Als Bot markieren" -flagAsBotDescription: "Aktiviere diese Option, falls dieses Benutzerkonto durch ein Programm gesteuert wird. Falls aktiviert, agiert es als Flag für andere Entwickler zur Verhinderung von endlosen Kettenreaktionen mit anderen Bots und lässt Misskeys interne Systeme dieses Benutzerkonto als Bot behandeln." -flagAsCat: "Als Katze markieren" -flagAsCatDescription: "Aktiviere diese Option, um dieses Benutzerkonto als Katze zu markieren." -flagShowTimelineReplies: "Antworten in der Chronik anzeigen" -flagShowTimelineRepliesDescription: "Ist diese Option aktiviert, so werden Antworten von Benutzern auf die Notizen anderer Benutzer in der Chronik angezeigt." -autoAcceptFollowed: "Follow-Anfragen von Benutzern, denen du folgst, automatisch akzeptieren" -addAccount: "Benutzerkonto hinzufügen" +settingGuide: "Empfohlene Einstellungen" +cacheRemoteFiles: "Cache für entfernte Dateien" +cacheRemoteFilesDescription: "Ist diese Einstellung deaktiviert, so werden Dateien + von anderen Servern direkt von dort geladen. Hierdurch wird Speicherplatz auf diesem + Server eingespart, aber durch die fehlende Generierung von Vorschaubildern wird + mehr Bandbreite benötigt." +flagAsBot: "Dieses Nutzerkonto als Bot kennzeichnen" +flagAsBotDescription: "Aktiviere diese Option, falls dieses Nutzerkonto durch ein + Programm gesteuert wird. Falls aktiviert, agiert es als Flag für andere Entwickler + zur Verhinderung von endlosen Kettenreaktionen mit anderen Bots und lässt Firefishs + interne Systeme dieses Nutzerkonto als Bot behandeln." +flagAsCat: "Bist du eine Katze? 😺" +flagAsCatDescription: "Du bekommst Katzenohren und sprichst wie eine Katze!" +flagShowTimelineReplies: "Antworten in der Timeline anzeigen" +flagShowTimelineRepliesDescription: "Zeigt Antworten von Nutzern auf Beiträge anderer + Nutzer in der Timeline an, wenn diese Funktion aktiviert ist." +autoAcceptFollowed: "Automatisches Genehmigen von Folgeanfragen von Benutzern, denen + Sie folgen" +addAccount: "Nutzerkonto hinzufügen" loginFailed: "Anmeldung fehlgeschlagen" -showOnRemote: "Auf Ursprungsinstanz ansehen" +showOnRemote: "Zur Ansicht auf dem Herkunftsserver" general: "Allgemein" -wallpaper: "Hintergrund" -setWallpaper: "Hintergrund festlegen" -removeWallpaper: "Hintergrund entfernen" +wallpaper: "Hintergrundbild" +setWallpaper: "Hintergrundbild festlegen" +removeWallpaper: "Hintergrundbild entfernen" searchWith: "Suchen: {q}" -youHaveNoLists: "Du hast keine Listen" -followConfirm: "Möchtest du {name} wirklich folgen?" -proxyAccount: "Proxy-Benutzerkonto" -proxyAccountDescription: "Ein Proxy-Benutzerkonto ist ein Benutzerkonto, das sich für Nutzer unter bestimmten Konditionen wie ein Follower aus einer fremden Instanz verhält. Zum Beispiel wird die Aktivität eines Nutzers aus einer fremden Instanz nicht an diese Instanz übermittelt, falls es keinen Benutzer dieser Instanz gibt, der diesem Nutzer aus fremder Instanz folgt. In diesem Fall folgt stattdessen das Proxy-Benutzerkonto." -host: "Hostname" -selectUser: "Benutzer auswählen" +youHaveNoLists: "Sie haben keine Listen" +followConfirm: "Sind Sie sicher, dass Sie {name} folgen möchten?" +proxyAccount: "Proxy-Konto" +proxyAccountDescription: "Ein Proxy-Konto ist ein Nutzerkonto, das sich für Nutzer + unter bestimmten Konditionen wie ein Follower von einem anderen Server verhält. + Zum Beispiel wird die Aktivität eines Nutzers von einem anderen Server nicht an + diesen Server übermittelt, falls es keinen Nutzer von diesem Server gibt, der diesem + Nutzer von einem anderen Server folgt. In diesem Fall folgt stattdessen das Proxy-Nutzerkonto." +host: "Host" +selectUser: "Wählen Sie einen Nutzer" recipient: "Empfänger" -annotation: "Anmerkung" +annotation: "Anmerkungen" federation: "Föderation" -instances: "Instanzen" -registeredAt: "Registriert am" +instances: "Server" +registeredAt: "Registriert unter" latestRequestSentAt: "Letzte Anfrage gesendet" -latestRequestReceivedAt: "Letzte Anfrage erhalten" -latestStatus: "Neuster Status" -storageUsage: "Verbrauchter Speicherplatz" +latestRequestReceivedAt: "Letzte erhaltene Anfrage" +latestStatus: "Aktueller Stand" +storageUsage: "Nutzung des Speichers" charts: "Diagramme" perHour: "Pro Stunde" perDay: "Pro Tag" -stopActivityDelivery: "Senden von Aktivitäten einstellen" -blockThisInstance: "Diese Instanz blockieren" -operations: "Aktionen" +stopActivityDelivery: "Sendeaktivitäten einstellen" +blockThisInstance: "Diesen Server blockieren" +operations: "Tätigkeiten" software: "Software" version: "Version" metadata: "Metadaten" -withNFiles: "{n} Datei(en)" -monitor: "Beobachten" -jobQueue: "Job-Warteschlange" -cpuAndMemory: "CPU und Arbeitsspeicher" +monitor: "Überwachung" +jobQueue: "Auftragswarteschlange" +cpuAndMemory: "CPU und Speicher" network: "Netzwerk" disk: "Festplatte" -instanceInfo: "Instanzinformationen" +instanceInfo: "Serverinformationen" statistics: "Statistiken" -clearQueue: "Warteschlange leeren" -clearQueueConfirmTitle: "Möchtest du die Warteschlange wirklich leeren?" -clearQueueConfirmText: "Hierdurch werden jegliche noch nicht gesendete Notizen nicht förderiert. Normalerweise wird dies nicht benötigt." +clearQueue: "Warteschlange löschen" +clearQueueConfirmTitle: "Sind Sie sicher, dass Sie die Warteschlange löschen wollen?" +clearQueueConfirmText: "Nicht zugestellte Beiträge, die in der Warteschlange verbleiben, + werden nicht föderiert. Normalerweise ist dieser Vorgang nicht erforderlich." clearCachedFiles: "Cache leeren" -clearCachedFilesConfirm: "Sollen alle im Cache gespeicherten Dateien von anderen Instanzen wirklich gelöscht werden?" -blockedInstances: "Blockierte Instanzen" -blockedInstancesDescription: "Gib die Hostnamen der Instanzen, welche blockiert werden sollen, durch Zeilenumbrüche getrennt an. Blockierte Instanzen können mit dieser instanz nicht mehr kommunizieren." +clearCachedFilesConfirm: "Sind Sie sicher, dass Sie alle im Cache zwischengespeicherten + Dateien löschen wollen?" +blockedInstances: "Blockierte Server" +blockedInstancesDescription: "Geben Sie die Hostnamen der Server, getrennt durch einen + Zeilenumbruch, an, die Sie blockieren möchten. Aufgelistete (blockierte) Server + können nicht mehr mit diesem Server kommunizieren." muteAndBlock: "Stummschaltungen und Blockierungen" -mutedUsers: "Stummgeschaltete Benutzer" -blockedUsers: "Blockierte Benutzer" -noUsers: "Keine Benutzer gefunden" +mutedUsers: "Stummgeschaltete Nutzer" +blockedUsers: "Blockierte Nutzer" +noUsers: "Es sind keine Nutzer vorhanden" editProfile: "Profil bearbeiten" -noteDeleteConfirm: "Möchtest du diese Notiz wirklich löschen?" -pinLimitExceeded: "Du kannst nicht noch mehr Notizen anheften." -intro: "Misskey ist installiert! Lass uns nun ein Administratorkonto einrichten." -done: "Fertig" -processing: "In Bearbeitung …" +noteDeleteConfirm: "Sind Sie sicher, dass Sie diesen Beitrag löschen wollen?" +pinLimitExceeded: "Sie können keine weiteren Beiträge anpinnen" +intro: "Die Installation von Firefish ist abgeschlossen! Bitte erstellen Sie einen + Admin-Benutzer." +done: "Erledigt" +processing: "In Bearbeitung" preview: "Vorschau" default: "Standard" -defaultValueIs: "Standardwert: {value}" -noCustomEmojis: "Keine benutzerdefinierten Emojis gefunden" +defaultValueIs: "Der Standardwert ist: {value}" +noCustomEmojis: "Es gibt keine benutzerdefinierten Emoji" noJobs: "Keine Jobs vorhanden" -federating: "Wird föderiert" +federating: "Eine Verbindung zum Server wird hergestellt" blocked: "Blockiert" -suspended: "Gesperrt" +suspended: "suspendiert" all: "Alles" -subscribing: "Wird abonniert" -publishing: "Wird veröffentlicht" +subscribing: "Registrieren" +publishing: "Veröffentlichen" notResponding: "Antwortet nicht" -instanceFollowing: "Gefolgt auf der Instanz" -instanceFollowers: "Follower der Instanz" -instanceUsers: "Benutzer der Instanz" +instanceFollowing: "Folgen auf dem Server" +instanceFollowers: "Follower des Servers" +instanceUsers: "Nutzer dieses Servers" changePassword: "Passwort ändern" security: "Sicherheit" retypedNotMatch: "Die Eingaben stimmen nicht überein." currentPassword: "Aktuelles Passwort" newPassword: "Neues Passwort" newPasswordRetype: "Neues Passwort bestätigen" -attachFile: "Datei anhängen" +attachFile: "Dateien anhängen" more: "Mehr!" -featured: "Beliebt" -usernameOrUserId: "Benutzername oder Benutzer-ID" -noSuchUser: "Benutzer nicht gefunden" -lookup: "Anfragen" -announcements: "Ankündigungen" +featured: "Besonderheiten" +usernameOrUserId: "Nutzername oder Nutzer-ID" +noSuchUser: "Nutzer nicht gefunden" +lookup: "Suche nach" +announcements: "Bekanntmachungen" imageUrl: "Bild-URL" remove: "Löschen" removed: "Erfolgreich gelöscht" -removeAreYouSure: "Möchtest du „{x}“ wirklich entfernen?" -deleteAreYouSure: "Möchtest du „{x}“ wirklich löschen?" +removeAreYouSure: "Sind Sie sicher, dass Sie \"{x}\" entfernen wollen?" +deleteAreYouSure: "Sind Sie sicher, dass Sie \"{x}\" löschen wollen?" resetAreYouSure: "Wirklich zurücksetzen?" -saved: "Erfolgreich gespeichert" +saved: "Gespeichert" messaging: "Chat" upload: "Hochladen" -keepOriginalUploading: "Originalbild speichern" -keepOriginalUploadingDescription: "Speichert das Originalbild so, wie es ist. Ist dies deaktiviert, wird eine Version zum Anzeigen im Internet generiert." -fromDrive: "Aus Drive" +keepOriginalUploading: "Originalbild behalten" +keepOriginalUploadingDescription: "Speichert das ursprünglich hochgeladene Bild so, + wie es ist. Wenn diese Option deaktiviert ist, wird beim Hochladen eine Version + für die Anzeige im Web erstellt." +fromDrive: "Vom Laufwerk" fromUrl: "Von einer URL" uploadFromUrl: "Von einer URL hochladen" -uploadFromUrlDescription: "URL der hochzuladenden Datei" +uploadFromUrlDescription: "URL der Datei, die Sie hochladen wollen" uploadFromUrlRequested: "Upload angefordert" -uploadFromUrlMayTakeTime: "Es kann eine Weile dauern, bis das Hochladen abgeschlossen ist." +uploadFromUrlMayTakeTime: "Es kann einige Zeit dauern, bis das Hochladen abgeschlossen + ist." explore: "Erkunden" messageRead: "Gelesen" -noMoreHistory: "Kein weiterer Verlauf vorhanden" -startMessaging: "Neuen Chat erstellen" -nUsersRead: "Von {n} Benutzern gelesen" +noMoreHistory: "Es gibt keine weitere Historie" +startMessaging: "Einen neuen Chat beginnen" +nUsersRead: "Gelesen von {n}" agreeTo: "Ich stimme {0} zu" tos: "Nutzungsbedingungen" -start: "Anfangen" -home: "Startseite" -remoteUserCaution: "Informationen von fremden Instanzen sind möglicherweise unvollständig." +start: "Beginnen Sie" +home: "Home" +remoteUserCaution: "Informationen von Nutzern anderer Server sind möglicherweise unvollständig." activity: "Aktivität" images: "Bilder" birthday: "Geburtstag" yearsOld: "{age} Jahre alt" -registeredDate: "Registrationsdatum" +registeredDate: "Registriert am" location: "Ort" -theme: "Farbschema" -themeForLightMode: "Helles Farbschema" -themeForDarkMode: "Dunkles Farbschema" +theme: "Farbverwaltung" +themeForLightMode: "Farbkombination zur Verwendung im hellen Modus" +themeForDarkMode: "Farbkombination zur Verwendung im dunklen Modus" light: "Hell" dark: "Dunkel" -lightThemes: "Helle Farbschemata" -darkThemes: "Dunkle Farbschemata" +lightThemes: "Helle Farbkombinationen" +darkThemes: "Dunkle Farbkombinationen" syncDeviceDarkMode: "Einstellung deines Geräts übernehmen" -drive: "Drive" +drive: "Cloud-Drive" fileName: "Dateiname" selectFile: "Datei auswählen" selectFiles: "Dateien auswählen" @@ -284,14 +313,16 @@ createFolder: "Ordner erstellen" renameFolder: "Ordner umbenennen" deleteFolder: "Ordner löschen" addFile: "Datei hinzufügen" -emptyDrive: "Deine Drive ist leer" +emptyDrive: "Deine Cloud-Drive ist leer" emptyFolder: "Dieser Ordner ist leer" unableToDelete: "Nicht löschbar" inputNewFileName: "Gib einen neuen Dateinamen ein" inputNewDescription: "Gib eine neue Beschreibung ein" inputNewFolderName: "Gib einen neuen Ordnernamen ein" -circularReferenceFolder: "Der Zielordner ist ein Unterorder des Ordners, den du verschieben möchtest." -hasChildFilesOrFolders: "Dieser Ordner kann nicht gelöscht werden, da er nicht leer ist." +circularReferenceFolder: "Der Zielordner ist ein Unterorder des Ordners, den du verschieben + möchtest." +hasChildFilesOrFolders: "Dieser Ordner kann nicht gelöscht werden, da er nicht leer + ist." copyUrl: "URL kopieren" rename: "Umbenennen" avatar: "Profilbild" @@ -307,8 +338,8 @@ unwatch: "Nicht mehr beobachten" accept: "Akzeptieren" reject: "Ablehnen" normal: "Normal" -instanceName: "Name der Instanz" -instanceDescription: "Beschreibung der Instanz" +instanceName: "Server-Name" +instanceDescription: "Server-Beschreibung" maintainerName: "Betreiber" maintainerEmail: "Betreiber-Email" tosUrl: "URL der Nutzungsbedingungen" @@ -318,29 +349,33 @@ today: "Heute" dayX: "{day}" monthX: "{month}" yearX: "{year}" -pages: "Seiten" +pages: "Nutzer-Seiten" integration: "Integration" connectService: "Verbinden" disconnectService: "Trennen" -enableLocalTimeline: "Lokale Chronik aktivieren" -enableGlobalTimeline: "Globale Chronik aktivieren" -disablingTimelinesInfo: "Administratoren und Moderatoren haben immer Zugriff auf alle Chroniken, auch wenn diese deaktiviert sind." +enableLocalTimeline: "Local-Timeline aktivieren" +enableGlobalTimeline: "Global-Timeline aktivieren" +disablingTimelinesInfo: "Administratoren und Moderatoren haben immer Zugriff auf alle + Timelines, auch wenn diese deaktiviert sind." registration: "Registrieren" -enableRegistration: "Registration neuer Benutzer erlauben" +enableRegistration: "Registration neuer Nutzer erlauben" invite: "Einladen" -driveCapacityPerLocalAccount: "Drive-Kapazität pro lokalem Benutzerkonto" -driveCapacityPerRemoteAccount: "Drive-Kapazität pro Benutzer fremder Instanzen" +driveCapacityPerLocalAccount: "Cloud-Drive-Kapazität pro lokalem Nutzerkonto" +driveCapacityPerRemoteAccount: "Laufwerkskapazität pro Remote-Nutzer" inMb: "In Megabytes" iconUrl: "Icon-URL (favicon etc)" bannerUrl: "Banner-URL" backgroundImageUrl: "Hintergrundbild-URL" basicInfo: "Grundlegende Informationen" -pinnedUsers: "Angeheftete Benutzer" -pinnedUsersDescription: "Gib durch Leerzeichen getrennte Benutzer an, die an die \"Erkunden\"-Seite angeheftet werden sollen." -pinnedPages: "Angeheftete Seiten" -pinnedPagesDescription: "Gib durch Leerzeilen getrennte Pfäde zu Seiten an, die an die Startseite dieser Instanz angeheftet werden sollen.\n" +pinnedUsers: "Angeheftete Nutzer" +pinnedUsersDescription: "Gib durch Leerzeichen getrennte Nutzer an, die an die \"\ + Erkunden\"-Seite angeheftet werden sollen." +pinnedPages: "Angeheftete Nutzer-Seiten" +pinnedPagesDescription: "Geben Sie die Dateipfade, getrennt durch Zeilenumbrüche, + derjenigen Seiten ein, die Sie an die obere Seitenbegrenzung des Servers anpinnen + möchten." pinnedClipId: "ID des anzuheftenden Clips" -pinnedNotes: "Angeheftete Notizen" +pinnedNotes: "Angeheftete Beiträge" hcaptcha: "hCaptcha" enableHcaptcha: "hCaptcha aktivieren" hcaptchaSiteKey: "Site key" @@ -349,43 +384,48 @@ recaptcha: "reCAPTCHA" enableRecaptcha: "reCAPTCHA aktivieren" recaptchaSiteKey: "Site key" recaptchaSecretKey: "Secret key" -avoidMultiCaptchaConfirm: "Das Verwenden von mehreren Captcha-Systemen kann zu Störungen führen. Sollen die anderen Systeme deaktiviert werden? Durch Abbrechen können mehrere Systeme aktiviert bleiben." -antennas: "Antennen" -manageAntennas: "Antennen verwalten" +avoidMultiCaptchaConfirm: "Das Verwenden von mehreren Captcha-Systemen kann zu Störungen + führen. Sollen die anderen Systeme deaktiviert werden? Durch Abbrechen können mehrere + Systeme aktiviert bleiben." +antennas: "News-Picker" +manageAntennas: "News-Picker verwalten" name: "Name" -antennaSource: "Antennenquelle" +antennaSource: "Quellen der News-Picker" antennaKeywords: "Zu beobachtende Schlüsselwörter" antennaExcludeKeywords: "Zu ignorierende Schlüsselwörter" -antennaKeywordsDescription: "Zum Nutzen einer \"UND\"-Verknüpfung Einträge mit Leerzeichen trennen, zum Nutzen einer \"ODER\"-Verknüpfung Einträge mit einem Zeilenumbruch trennen" -notifyAntenna: "Über neue Notizen benachrichtigen" -withFileAntenna: "Nur Notizen mit Dateien" +antennaKeywordsDescription: "Zum Nutzen einer \"UND\"-Verknüpfung Einträge mit Leerzeichen + trennen, zum Nutzen einer \"ODER\"-Verknüpfung Einträge mit einem Zeilenumbruch + trennen." +notifyAntenna: "Über neue Beiträge benachrichtigen" +withFileAntenna: "Nur Beiträge mit Dateien" enableServiceworker: "Push-Benachrichtigungen im Browser aktivieren" -antennaUsersDescription: "Benutzernamen getrennt durch Zeilenumbrüche angeben" +antennaUsersDescription: "Nutzernamen getrennt durch Zeilenumbrüche angeben" caseSensitive: "Groß-/Kleinschreibung unterscheiden" withReplies: "Antworten beinhalten" -connectedTo: "Mit folgenden Benutzerkonten verknüpft" -notesAndReplies: "Notizen und Antworten" -withFiles: "Notizen mit Dateien" -silence: "Instanzweit stummschalten" -silenceConfirm: "Möchtest du diesen Benutzer wirklich instanzweit stummschalten?" -unsilence: "Instanzweite Stummschaltung aufheben" -unsilenceConfirm: "Möchtest du die instanzweite Stummschaltung dieses Benutzers wirklich aufheben?" -popularUsers: "Beliebte Benutzer" -recentlyUpdatedUsers: "Vor kurzem aktive Benutzer" -recentlyRegisteredUsers: "Vor kurzem registrierte Benutzer" -recentlyDiscoveredUsers: "Vor kurzem gefundene Benutzer" -exploreUsersCount: "Es gibt {count} Benutzer" +connectedTo: "Mit folgenden Nutzerkonten verknüpft" +notesAndReplies: "Beiträge und Antworten" +withFiles: "Beiträge mit Dateien" +silence: "stummschalten" +silenceConfirm: "Sind Sie sicher, dass Sie diesen Benutzer Stummschalten möchten?" +unsilence: "Stummschaltung aufheben" +unsilenceConfirm: "Sind Sie sicher, dass Sie die Stummschaltung dieses Benutzers rückgängig + machen wollen?" +popularUsers: "Beliebte Nutzer" +recentlyUpdatedUsers: "Vor kurzem aktive Nutzer" +recentlyRegisteredUsers: "Vor kurzem registrierte Nutzer" +recentlyDiscoveredUsers: "Vor kurzem gefundene Nutzer" +exploreUsersCount: "Es gibt {count} Nutzer" exploreFediverse: "Das Fediverse erkunden" popularTags: "Beliebte Schlagwörter" userList: "Liste" about: "Über" -aboutMisskey: "Über Misskey" +aboutFirefish: "Über Firefish" administrator: "Administrator" token: "Token" twoStepAuthentication: "Zwei-Faktor-Authentifizierung" moderator: "Moderator" moderation: "Moderation" -nUsersMentioned: "Von {n} Benutzern erwähnt" +nUsersMentioned: "Von {n} Nutzern erwähnt" securityKey: "Sicherheitsschlüssel" securityKeyName: "Schlüsselname" registerSecurityKey: "Sicherheitsschlüssel registrieren" @@ -401,10 +441,10 @@ notFoundDescription: "Es konnte keine Seite unter dieser URL gefunden werden." uploadFolder: "Standardordner für Uploads" cacheClear: "Cache leeren" markAsReadAllNotifications: "Alle Benachrichtigungen als gelesen markieren" -markAsReadAllUnreadNotes: "Alle Notizen als gelesen markieren" +markAsReadAllUnreadNotes: "Alle Beiträge als gelesen markieren" markAsReadAllTalkMessages: "Alle Chats als gelesen markieren" help: "Hilfe" -inputMessageHere: "Hier Nachricht eingeben" +inputMessageHere: "Hier Beitrag eingeben" close: "Schließen" group: "Gruppe" groups: "Gruppen" @@ -422,20 +462,21 @@ text: "Text" enable: "Aktivieren" next: "Weiter" retype: "Erneut eingeben" -noteOf: "Notiz von {user}" +noteOf: "Beitrag von {user}" inviteToGroup: "Zu Gruppe einladen" quoteAttached: "Zitat" quoteQuestion: "Als Zitat anhängen?" -noMessagesYet: "Noch keine Nachrichten vorhanden" +noMessagesYet: "Noch keine Beiträge vorhanden" newMessageExists: "Du hast eine neue Nachricht" -onlyOneFileCanBeAttached: "Es kann pro Nachricht nur eine Datei angehängt werden" +onlyOneFileCanBeAttached: "Es kann pro Beitrag nur eine Datei angehängt werden" signinRequired: "Bitte registriere oder melde dich an, um fortzufahren" invitations: "Einladungen" invitationCode: "Einladungscode" checking: "Wird überprüft …" available: "Verfügbar" unavailable: "Unverfügbar" -usernameInvalidFormat: "Du kannst Klein- und Großbuchstaben, Zahlen sowie Unterstriche verwenden" +usernameInvalidFormat: "Du kannst Klein- und Großbuchstaben, Zahlen sowie Unterstriche + verwenden." tooShort: "Zu kurz" tooLong: "Zu lang" weakPassword: "Schwaches Passwort" @@ -444,7 +485,7 @@ strongPassword: "Starkes Passwort" passwordMatched: "Stimmt überein" passwordNotMatched: "Stimmt nicht überein" signinWith: "Mit {x} anmelden" -signinFailed: "Anmeldung fehlgeschlagen. Überprüfe Benutzername und Passswort." +signinFailed: "Anmeldung fehlgeschlagen. Überprüfe Nutzername und Passswort." tapSecurityKey: "Tippe deinen Sicherheitsschlüssel an" or: "Oder" language: "Sprache" @@ -462,8 +503,8 @@ doing: "In Bearbeitung …" category: "Kategorie" tags: "Schlagwörter" docSource: "Quellcode dieses Dokuments" -createAccount: "Benutzerkonto erstellen" -existingAccount: "Bestehendes Benutzerkonto" +createAccount: "Nutzerkonto erstellen" +existingAccount: "Bestehendes Nutzerkonto" regenerate: "Regenerieren" fontSize: "Schriftgröße" noFollowRequests: "Keine ausstehenden Follow-Anfragen vorhanden" @@ -476,33 +517,41 @@ weekOverWeekChanges: "Veränderung zu letzter Woche" dayOverDayChanges: "Veränderung zu Gestern" appearance: "Aussehen" clientSettings: "Client-Einstellungen" -accountSettings: "Benutzerkonto-Einstellungen" -promotion: "Werbung" -promote: "Werbung schalten" +accountSettings: "Nutzerkonto-Einstellungen" +promotion: "geworben" +promote: "Werben" numberOfDays: "Anzahl der Tage" -hideThisNote: "Diese Notiz verstecken" -showFeaturedNotesInTimeline: "Beliebte Notizen in der Chronik anzeigen" -objectStorage: "Object Storage" +hideThisNote: "Diesen Beitrag verstecken" +showFeaturedNotesInTimeline: "Beliebte Beiträge in der Timeline anzeigen" +objectStorage: "Objektspeicher" useObjectStorage: "Object Storage verwenden" objectStorageBaseUrl: "Basis-URL" -objectStorageBaseUrlDesc: "Die als Referenz verwendete URL. Verwendest du einen CDN oder Proxy, gib dessen URL an. Für S3 verwende 'https://.s3.amazonaws.com'. Für GCS o.ä. verwende 'https://storage.googleapis.com/'." -objectStorageBucket: "Bucket" -objectStorageBucketDesc: "Bitte gib den Namen des Buckets an, der bei deinem Anbieter verwendet wird." +objectStorageBaseUrlDesc: "Die als Referenz verwendete URL. Verwendest du einen CDN + oder Proxy, gib dessen URL an. \nFür S3 verwende 'https://.s3.amazonaws.com'. + Für GCS o.ä. verwende 'https://storage.googleapis.com/'." +objectStorageBucket: "Eimer" +objectStorageBucketDesc: "Bitte gib den Namen des Buckets an, der bei deinem Anbieter + verwendet wird." objectStoragePrefix: "Prefix" objectStoragePrefixDesc: "Dateien werden in Ordnern unter diesem Prefix gespeichert." -objectStorageEndpoint: "Endpoint" -objectStorageEndpointDesc: "Im Falle von S3 leerlassen, für andere Anbieter den relevanten Endpoint im Format „“ oder „:“ angeben." +objectStorageEndpoint: "Limit" +objectStorageEndpointDesc: "Im Falle von S3 leerlassen, für andere Anbieter den relevanten + Endpoint im Format „“ oder „:“ angeben." objectStorageRegion: "Region" -objectStorageRegionDesc: "Gib eine Region wie z.B. „xx-east-1“ an. Falls dein Anbieter nicht zwischen Regionen unterscheidet, lass dieses Feld leer oder gib „us-east-1“ an." +objectStorageRegionDesc: "Gib eine Region wie z.B. „xx-east-1“ an. Falls dein Anbieter + nicht zwischen Regionen unterscheidet, lass dieses Feld leer oder gib „us-east-1“ + an." objectStorageUseSSL: "SSL verwenden" -objectStorageUseSSLDesc: "Deaktiviere dies, falls du für API-Verbindungen kein HTTPS verwenden wirst" +objectStorageUseSSLDesc: "Deaktiviere dies, falls du für API-Verbindungen kein HTTPS + verwenden wirst" objectStorageUseProxy: "Über Proxy verbinden" -objectStorageUseProxyDesc: "Deaktiviere dies, falls du keinen Proxy für den Objektspeicher verwenden wirst" +objectStorageUseProxyDesc: "Deaktiviere dies, falls du keinen Proxy für den Objektspeicher + verwenden wirst" objectStorageSetPublicRead: "Bei Upload auf \"public-read\" stellen" serverLogs: "Serverprotokolle" deleteAll: "Alle löschen" -showFixedPostForm: "Bereich zum Schreiben neuer Notizen am Anfang der Chronik anzeigen" -newNoteRecived: "Es gibt neue Notizen" +showFixedPostForm: "Bereich zum Schreiben neuer Beiträge am Anfang der Timeline anzeigen" +newNoteRecived: "Es gibt neue Beiträge" sounds: "Töne" listen: "Anhören" none: "Nichts" @@ -525,19 +574,24 @@ sort: "Sortieren" ascendingOrder: "Aufsteigende Reihenfolge" descendingOrder: "Absteigende Reihenfolge" scratchpad: "Testumgebung" -scratchpadDescription: "Die Testumgebung bietet einen Bereich für AiScript-Experimente. Dort kannst du AiScript schreiben, ausführen sowie dessen Auswirkungen auf Misskey überprüfen." +scratchpadDescription: "Die Testumgebung bietet einen Bereich für AiScript-Experimente. + Dort kannst du AiScript schreiben, ausführen sowie dessen Auswirkungen auf Firefish + überprüfen." output: "Ausgabe" script: "Skript" disablePagesScript: "AiScript auf Seiten deaktivieren" -updateRemoteUser: "Benutzerinformationen aktualisieren" +updateRemoteUser: "Nutzerinformationen aktualisieren" deleteAllFiles: "Alle Dateien löschen" deleteAllFilesConfirm: "Möchtest du wirklich alle Dateien löschen?" -removeAllFollowing: "Allen gefolgten Benutzern entfolgen" -removeAllFollowingDescription: "Dies entfolgt allen Benutzerkonten von {host}. Bitte führe dies durch, falls diese Instanz z.B. nicht mehr existiert." -userSuspended: "Dieser Benutzer wurde gesperrt." -userSilenced: "Dieser Benutzer wurde instanzweit stummgeschaltet." -yourAccountSuspendedTitle: "Dieses Benutzerkonto ist gesperrt" -yourAccountSuspendedDescription: "Dieses Benutzerkonto wurde gesperrt, da es gegen die Nutzungsbedingungen dieses Servers verstoßen hat. Trete mit dem Betreiber in Kontakt, falls du weitere Details erfahren möchtest. Bitte erstelle kein neues Benutzerkonto." +removeAllFollowing: "Allen gefolgten Nutzern entfolgen" +removeAllFollowingDescription: "Wenn Sie dies ausführen, werden alle Konten von {host} + entfolgt. Bitte führen Sie dies aus, wenn der Server beispielsweise nicht mehr existiert." +userSuspended: "Dieser Nutzer wurde gesperrt." +userSilenced: "Dieser Nutzer wurde instanzweit stummgeschaltet." +yourAccountSuspendedTitle: "Dieses Nutzerkonto ist gesperrt" +yourAccountSuspendedDescription: "Dieses Nutzerkonto wurde gesperrt, da es gegen die + Nutzungsbedingungen dieses Servers verstoßen hat. Trete mit dem Betreiber in Kontakt, + falls du weitere Details erfahren möchtest. Bitte erstelle kein neues Nutzerkonto." menu: "Menü" divider: "Trenner" addItem: "Element hinzufügen" @@ -546,8 +600,8 @@ addRelay: "Relay hinzufügen" inboxUrl: "inbox-URL" addedRelays: "Hinzugefügte Relays" serviceworkerInfo: "Muss für Push-Benachrichtigungen aktiviert sein." -deletedNote: "Gelöschte Notiz" -invisibleNote: "Private Notiz" +deletedNote: "Gelöschter Beitrag" +invisibleNote: "Privater Beitrag" enableInfiniteScroll: "Automatisch mehr laden" visibility: "Sichtbarkeit" poll: "Umfrage" @@ -555,7 +609,7 @@ useCw: "Inhaltswarnung verwenden" enablePlayer: "Video-Player öffnen" disablePlayer: "Video-Player schließen" expandTweet: "Tweet ausklappen" -themeEditor: "Farbschema-Editor" +themeEditor: "Farbkombinations-Editor" description: "Beschreibung" describeFile: "Beschreibung hinzufügen" enterFileDescription: "Beschreibung eingeben" @@ -577,88 +631,102 @@ generateAccessToken: "Zugriffstoken generieren" permission: "Berechtigungen" enableAll: "Alle aktivieren" disableAll: "Alle deaktivieren" -tokenRequested: "Zugriff zum Benutzerkonto gewähren" -pluginTokenRequestedDescription: "Dieses Plugin wird die hier konfigurierten Berechtigungen verwenden können." +tokenRequested: "Zugriff zum Nutzerkonto gewähren" +pluginTokenRequestedDescription: "Dieses Plugin wird die hier konfigurierten Berechtigungen + verwenden können." notificationType: "Art der Benachrichtigung" edit: "Bearbeiten" emailServer: "Email-Server" enableEmail: "Email-Versand aktivieren" -emailConfigInfo: "Zur Email-Bestätigung bei Registrierung oder zum Zurücksetzen des Passworts verwendet" +emailConfigInfo: "Zur Email-Bestätigung bei Registrierung oder zum Zurücksetzen des + Passworts verwendet" email: "Email" emailAddress: "Email-Adresse" smtpConfig: "SMTP-Server Konfiguration" smtpHost: "Host" smtpPort: "Port" -smtpUser: "Benutzername" +smtpUser: "Nutzername" smtpPass: "Passwort" -emptyToDisableSmtpAuth: "Benutzername und Passwort leer lassen, um SMTP-Verifizierung zu deaktivieren" +emptyToDisableSmtpAuth: "Nutzername und Passwort leer lassen, um SMTP-Verifizierung + zu deaktivieren" smtpSecure: "Für SMTP-Verbindungen implizit SSL/TLS verwenden" -smtpSecureInfo: "Schalte dies aus, falls du STARTTLS verwendest." +smtpSecureInfo: "Schalte dies aus, falls du STARTTLS verwendest" testEmail: "Emailversand testen" -wordMute: "Wortstummschaltung" +wordMute: "Wortfilter" regexpError: "Fehler in einem regulären Ausdruck" -regexpErrorDescription: "Im regulären Ausdruck deiner {tab}en Wortstummschaltungen ist ein Fehler aufgetreten:" -instanceMute: "Instanzstummschaltungen" +regexpErrorDescription: "Im regulären Ausdruck deines {tab}en Wortfilters ist ein + Fehler aufgetreten:" +instanceMute: "Server-Stummschaltungen" userSaysSomething: "{name} hat etwas gesagt" makeActive: "Aktivieren" display: "Anzeigeart" copy: "Kopieren" metrics: "Metriken" overview: "Übersicht" -logs: "Logs" +logs: "Protokolle" delayed: "Verzögert" database: "Datenbank" -channel: "Kanäle" +channel: "Channels" create: "Erstellen" notificationSetting: "Benachrichtigungseinstellungen" notificationSettingDesc: "Wähle die Art der anzuzeigenden Benachrichtigungen." useGlobalSetting: "Globale Einstellung verwenden" -useGlobalSettingDesc: "Ist diese Option aktiviert, werden die Benachrichtigungseinstellungen deines Benutzerkontos verwendet. Durch ausschalten dieser Option können individuelle Einstellungen vorgenommen werden." +useGlobalSettingDesc: "Ist diese Option aktiviert, werden die Benachrichtigungseinstellungen + deines Nutzerkontos verwendet. Durch ausschalten dieser Option können individuelle + Einstellungen vorgenommen werden." other: "Anderes" regenerateLoginToken: "Anmeldetoken regenerieren" -regenerateLoginTokenDescription: "Den zur Anmeldung intern verwendeten Token regenerieren. Normalerweise wird dies nicht benötigt. Bei Regeneration werden alle Geräte ausgeloggt." -setMultipleBySeparatingWithSpace: "Trenne Elemente durch ein Leerzeichen um mehrere Einstellungen zu kofigurieren." +regenerateLoginTokenDescription: "Den zur Anmeldung intern verwendeten Token regenerieren. + Normalerweise wird dies nicht benötigt. Bei Regeneration werden alle Geräte ausgeloggt." +setMultipleBySeparatingWithSpace: "Trenne Elemente durch ein Leerzeichen um mehrere + Einstellungen zu kofigurieren." fileIdOrUrl: "Datei-ID oder URL" behavior: "Verhalten" sample: "Beispiel" abuseReports: "Meldungen" reportAbuse: "Melden" reportAbuseOf: "{name} melden" -fillAbuseReportDescription: "Bitte gib zusätzliche Informationen zu dieser Meldung an. Falls es sich um eine spezielle Notiz handelt, bitte gib dessen URL an." +fillAbuseReportDescription: "Bitte gib zusätzliche Informationen zu dieser Meldung + an. Falls es sich um einen ungewöhnlichen Beitrag handelt, gib bitte dessen URL + an." abuseReported: "Deine Meldung wurde versendet. Vielen Dank." reporter: "Melder" reporteeOrigin: "Herkunft des Gemeldeten" reporterOrigin: "Herkunft des Meldenden" -forwardReport: "Meldung an fremde Instanz weiterleiten" -forwardReportIsAnonymous: "Anstatt deines Benutzerkontos wird bei der fremden Instanz ein anonymes Systemkonto als Melder angezeigt." +forwardReport: "Meldung auch an den mit-beteiligten Server weiterleiten" +forwardReportIsAnonymous: "Anstelle deines Nutzerkontos wird ein anonymes Systemkonto + als Hinweisgeber auf dem mit-beteiligten Server angezeigt." send: "Senden" abuseMarkAsResolved: "Meldung als gelöst markieren" openInNewTab: "In neuem Tab öffnen" openInSideView: "In Seitenansicht öffnen" defaultNavigationBehaviour: "Standardnavigationsverhalten" -editTheseSettingsMayBreakAccount: "Bei Bearbeitung dieser Einstellungen besteht die Gefahr, dein Benutzerkonto zu beschädigen." -instanceTicker: "Instanz-Informationen von Notizen" -waitingFor: "Warte auf {x} …" +editTheseSettingsMayBreakAccount: "Bei Bearbeitung dieser Einstellungen besteht die + Gefahr, dein Nutzerkonto zu beschädigen." +instanceTicker: "Zeige zu einem Beitrag den Herkunfts-Server an" +waitingFor: "Warte auf {x}" random: "Zufällig" system: "System" -switchUi: "UI wechseln" +switchUi: "Layout" desktop: "Desktop" clip: "Clip erstellen" createNew: "Neu erstellen" -optional: "Optional" +optional: "optional" createNewClip: "Neuen Clip erstellen" unclip: "Aus Clip entfernen" -confirmToUnclipAlreadyClippedNote: "Diese Notiz ist bereits im \"{name}\" Clip enthalten. Möchtest du sie aus diesem Clip entfernen?" +confirmToUnclipAlreadyClippedNote: "Dieser Beitrag ist bereits im \"{name}\" Clip + enthalten. Möchtest du ihn aus diesem Clip entfernen?" public: "Öffentlich" -i18nInfo: "Calckey wird durch freiwillige Helfer in viele verschiedene Sprachen übersetzt. Auf {link} kannst du mithelfen." +i18nInfo: "Firefish wird durch freiwillige Helfer in viele verschiedene Sprachen übersetzt. + Auf {link} kannst du mithelfen." manageAccessTokens: "Zugriffstokens verwalten" -accountInfo: "Benutzerkonto-Informationen" -notesCount: "Anzahl der Notizen" +accountInfo: "Nutzerkonto-Informationen" +notesCount: "Anzahl der Beiträge" repliesCount: "Anzahl gesendeter Antworten" renotesCount: "Anzahl getätigter Renotes" repliedCount: "Anzahl erhaltener Antworten" renotedCount: "Anzahl erhaltener Renotes" -followingCount: "Anzahl gefolgter Benutzer" +followingCount: "Anzahl gefolgter Nutzer" followersCount: "Anzahl an Followern" sentReactionsCount: "Anzahl gesendeter Reaktionen" receivedReactionsCount: "Anzahl erhaltener Reaktionen" @@ -666,43 +734,54 @@ pollVotesCount: "Anzahl gesendeter Antworten auf Umfragen" pollVotedCount: "Anzahl erhaltener Antworten auf Umfragen" yes: "Ja" no: "Nein" -driveFilesCount: "Anzahl der Dateien in Drive" -driveUsage: "Drive-Auslastung" +driveFilesCount: "Anzahl der Dateien in Cloud-Drive" +driveUsage: "Cloud-Drive-Auslastung" noCrawle: "Crawler-Indexierung ablehnen" -noCrawleDescription: "Suchmaschinen bitten, die eigene Profilseite, Notizen, Seiten usw. nicht zu indexieren." -lockedAccountInfo: "Auch wenn du Follow-Anfragen auf manuelle Bestätigung setzt, wird jede deiner Notizen öffentlich sichtbar sein, sofern du ihre Notizsichtbarkeit nicht auf \"Nur Follower\" setzt." +noCrawleDescription: "Suchmaschinen bitten, die eigene Profilseite, Beiträge, Nutzer-Seiten + usw. nicht zu indexieren." +lockedAccountInfo: "Auch wenn du Follow-Anfragen auf manuelle Bestätigung setzt, wird + jeder deiner Posts öffentlich sichtbar sein, sofern du ihre Sichtbarkeit nicht auf + \"Nur Follower\" setzt." alwaysMarkSensitive: "Medien standardmäßig als NSFW markieren" loadRawImages: "Anstatt Vorschaubilder immer Originalbilder anzeigen" disableShowingAnimatedImages: "Animierte Bilder nicht abspielen" -verificationEmailSent: "Eine Bestätigungsmail wurde an deine Email-Adresse versendet. Besuche den dort enthaltenen Link, um die Verifizierung abzuschließen." +verificationEmailSent: "Eine Bestätigungsmail wurde an deine Email-Adresse versendet. + Besuche den dort enthaltenen Link, um die Verifizierung abzuschließen." notSet: "Nicht konfiguriert" emailVerified: "Email-Adresse bestätigt" -noteFavoritesCount: "Anzahl an als Favorit markierter Notizen" -pageLikesCount: "Anzahl an als \"Gefällt mir\" markierter Seiten" -pageLikedCount: "Anzahl erhaltener \"Gefällt mir\" auf Seiten" +noteFavoritesCount: "Anzahl der favorisierten Beiträge" +pageLikesCount: "Anzahl an als \"Gefällt mir\" markierter Nutzer-Seiten" +pageLikedCount: "Anzahl erhaltener \"Gefällt mir\" auf Nutzer-Seiten" contact: "Kontakt" useSystemFont: "Standardschriftart des Systems verwenden" clips: "Clips" experimentalFeatures: "Experimentelle Funktionalitäten" developer: "Entwickler" -makeExplorable: "Benutzerkonto in „Erkunden“ sichtbar machen" -makeExplorableDescription: "Wenn diese Option deaktiviert ist, ist dein Benutzerkonto nicht im „Erkunden“-Bereich sichtbar." -showGapBetweenNotesInTimeline: "Abstände zwischen Notizen auf der Chronik anzeigen" +makeExplorable: "Nutzerkonto in „Erkunden“ sichtbar machen" +makeExplorableDescription: "Wenn diese Option deaktiviert ist, ist dein Nutzerkonto + nicht im „Erkunden“-Bereich sichtbar." +showGapBetweenNotesInTimeline: "Abstände zwischen Beiträgen in der Timeline anzeigen" duplicate: "Duplizieren" left: "Links" center: "Mittig" wide: "Breit" narrow: "Schmal" -reloadToApplySetting: "Diese Einstellung tritt nach einer Aktualisierung der Seite in Kraft. Jetzt aktualisieren?" -needReloadToApply: "Diese Einstellung tritt nach einer Aktualisierung der Seite in Kraft." +reloadToApplySetting: "Diese Einstellung tritt nach einer Aktualisierung der Seite + in Kraft. Jetzt aktualisieren?" +needReloadToApply: "Diese Einstellung tritt nach einer Aktualisierung der Seite in + Kraft." showTitlebar: "Titelleiste anzeigen" clearCache: "Cache leeren" -onlineUsersCount: "{n} Benutzer sind online" -nUsers: "{n} Benutzer" -nNotes: "{n} Notizen" +onlineUsersCount: "{n} Nutzer sind online" +nUsers: "{n} Nutzer" +nNotes: "{n} Beiträge" sendErrorReports: "Fehlerberichte senden" -sendErrorReportsDescription: "Ist diese Option aktiviert, so werden beim Auftreten von Fehlern detaillierte Fehlerinformationen an Misskey weitergegeben, was zur Verbesserung der Qualität von Misskey beiträgt.\nEnthalten in diesen Informationen sind u.a. die Version deines Betriebssystems, welchen Browser du verwendest und ein Verlauf deiner Aktivitäten innerhalb Misskey." -myTheme: "Mein Farbschema" +sendErrorReportsDescription: "Ist diese Option aktiviert, so werden beim Auftreten + von Fehlern detaillierte Fehlerinformationen an Firefish weitergegeben, was zur Verbesserung + der Qualität von Firefish beiträgt.\nEnthalten in diesen Informationen sind u.a. + die Version deines Betriebssystems, welchen Browser du verwendest und ein Verlauf + deiner Aktivitäten innerhalb Firefish." +myTheme: "Meine Farbkombination" backgroundColor: "Hintergrundfarbe" accentColor: "Akzentfarbe" textColor: "Textfarbe" @@ -715,7 +794,7 @@ saveConfirm: "Änderungen speichern?" deleteConfirm: "Wirklich löschen?" invalidValue: "Dieser Wert ist ungültig." registry: "Registry" -closeAccount: "Benutzerkonto schließen" +closeAccount: "Nutzerkonto schließen" currentVersion: "Momentane Version" latestVersion: "Neuste Version" youAreRunningUpToDateClient: "Du verwendest die neuste Version deines Clients." @@ -725,53 +804,56 @@ capacity: "Kapazität" inUse: "Verwendet" editCode: "Code bearbeiten" apply: "Anwenden" -receiveAnnouncementFromInstance: "Benachrichtigungen von dieser Instanz empfangen" +receiveAnnouncementFromInstance: "Benachrichtigungen von diesem Server empfangen" emailNotification: "Email-Benachrichtigungen" publish: "Veröffentlichen" inChannelSearch: "In Kanal suchen" useReactionPickerForContextMenu: "Reaktionsauswahl durch Rechtsklick öffnen" -typingUsers: "{users} ist/sind am schreiben …" +typingUsers: "{users} ist/sind am schreiben" jumpToSpecifiedDate: "Zu bestimmtem Datum springen" -showingPastTimeline: "Es wird eine alte Chronik angezeigt" -clear: "Zurückkehren" +showingPastTimeline: "Es wird eine alte Timeline angezeigt" +clear: "Leeren" markAllAsRead: "Alle als gelesen markieren" goBack: "Zurück" unlikeConfirm: "\"Gefällt mir\" wirklich entfernen?" fullView: "Vollansicht" quitFullView: "Vollansicht verlassen" addDescription: "Beschreibung hinzufügen" -userPagePinTip: "Um Notizen hier erscheinen zu lassen, drücke \"An dein Profil anheften\" im Menü individueller Notizen." -notSpecifiedMentionWarning: "Diese Notiz enthält Erwähnungen von Nutzern, die nicht als Empfänger ausgewählt sind" +userPagePinTip: "Um Beiträge hier erscheinen zu lassen, drücke \"An dein Profil anheften\"\ + \ im Menü individueller Beiträge." +notSpecifiedMentionWarning: "Dieser Beitrag enthält Erwähnungen von Nutzern, die nicht + als Empfänger ausgewählt sind" info: "Über" -userInfo: "Benutzerinformation" +userInfo: "Nutzerinformation" unknown: "Unbekannt" onlineStatus: "Onlinestatus" hideOnlineStatus: "Onlinestatus verbergen" -hideOnlineStatusDescription: "Das Verbergen deines Onlinestatuses reduziert die Nützlichkeit von Funktionen wie der Suche." +hideOnlineStatusDescription: "Das Verbergen deines Onlinestatuses reduziert die Nützlichkeit + von Funktionen wie der Suche." online: "Online" active: "Aktiv" offline: "Offline" notRecommended: "Nicht empfohlen" botProtection: "Schutz vor Bots" -instanceBlocking: "Blockierte Instanzen" -selectAccount: "Benutzerkonto auswählen" +instanceBlocking: "Verbundene Server verwalten" +selectAccount: "Nutzerkonto auswählen" switchAccount: "Konto wechseln" enabled: "Aktiviert" disabled: "Deaktiviert" quickAction: "Schnellaktionen" -user: "Benutzer" +user: "Nutzer" administration: "Verwaltung" -accounts: "Benutzerkonten" +accounts: "Nutzerkonten" switch: "Wechseln" noMaintainerInformationWarning: "Betreiberinformationen sind nicht konfiguriert." noBotProtectionWarning: "Schutz vor Bots ist nicht konfiguriert." configure: "Konfigurieren" -postToGallery: "Neuen Galeriebeitrag erstellen" -gallery: "Galerie" +postToGallery: "Erstelle einen neuen Beitrag zur Bilder-Galerie" +gallery: "Bilder-Galerie" recentPosts: "Neue Beiträge" popularPosts: "Beliebte Beiträge" -shareWithNote: "Mit Notiz teilen" -ads: "Werbung" +shareWithNote: "Mit Beitrag teilen" +ads: "Werbeanzeigen" expiration: "Frist" memo: "Merkzettel" priority: "Priorität" @@ -782,7 +864,8 @@ emailNotConfiguredWarning: "Keine Email-Adresse hinterlegt." ratio: "Verhältnis" previewNoteText: "Vorschau anzeigen" customCss: "Benutzerdefiniertes CSS" -customCssWarn: "Verwende diese Einstellung nur, wenn du weißt, was sie tut. Ungültige Eingaben können dazu führen, dass der Client nicht mehr normal funktioniert." +customCssWarn: "Verwende diese Einstellung nur, wenn du weißt, was sie tut. Ungültige + Eingaben können dazu führen, dass der Client nicht mehr normal funktioniert." global: "Global" squareAvatars: "Profilbilder quadratisch anzeigen" sent: "Gesendet" @@ -792,15 +875,17 @@ hashtags: "Hashtags" troubleshooting: "Problembehandlung" useBlurEffect: "Weichzeichnungseffekt in der Benutzeroberfläche verwenden" learnMore: "Mehr erfahren" -misskeyUpdated: "Misskey wurde aktualisiert!" +misskeyUpdated: "Firefish wurde aktualisiert!" whatIsNew: "Änderungen anzeigen" translate: "Übersetzen" translatedFrom: "Aus {x} übersetzt" -accountDeletionInProgress: "Die Löschung deines Benutzerkontos ist momentan in Bearbeitung." -usernameInfo: "Ein Name, durch den dein Benutzerkonto auf diesem Server identifiziert werden kann. Du kannst das Alphabet (a~z, A~Z), Ziffern (0~9) oder Unterstriche (_) verwenden. Benutzernamen können später nicht geändert werden." +accountDeletionInProgress: "Die Löschung deines Nutzerkontos ist momentan in Bearbeitung" +usernameInfo: "Ein Name, durch den dein Nutzerkonto auf diesem Server identifiziert + werden kann. Du kannst das Alphabet (a~z, A~Z), Ziffern (0~9) oder Unterstriche + (_) verwenden. Nutzernamen können später nicht geändert werden." aiChanMode: "Ai-Modus" keepCw: "Inhaltswarnungen beibehalten" -pubSub: "Pub/Sub Benutzerkonten" +pubSub: "Pub/Sub Nutzerkonten" lastCommunication: "Letzte Kommunikation" resolved: "Gelöst" unresolved: "Ungelöst" @@ -811,69 +896,78 @@ emailRequiredForSignup: "Angabe einer Email-Adresse als benötigt markieren" unread: "Ungelesen" filter: "Filter" controlPanel: "Systemsteuerung" -manageAccounts: "Benutzerkonten verwalten" +manageAccounts: "Nutzerkonten verwalten" makeReactionsPublic: "Reaktionsverlauf veröffentlichen" -makeReactionsPublicDescription: "Jeder wird die Liste deiner gesendeten Reaktionen einsehen können." -classic: "Classic" +makeReactionsPublicDescription: "Jeder wird die Liste deiner gesendeten Reaktionen + einsehen können." +classic: "Mittig/zentriert" muteThread: "Thread stummschalten" unmuteThread: "Threadstummschaltung aufheben" ffVisibility: "Sichtbarkeit von Gefolgten/Followern" -ffVisibilityDescription: "Konfiguriere wer sehen kann, wem du folgst sowie wer dir folgt." -continueThread: "Weiteren Threadverlauf anzeigen" -deleteAccountConfirm: "Dein Benutzerkonto wird unwiderruflich gelöscht. Trotzdem fortfahren?" +ffVisibilityDescription: "Konfiguriere wer sehen kann, wem du folgst sowie wer dir + folgt." +continueThread: "Beitrag fortsetzen" +deleteAccountConfirm: "Dein Nutzerkonto wird unwiderruflich gelöscht. Trotzdem fortfahren?" incorrectPassword: "Falsches Passwort." voteConfirm: "Wirklich für „{choice}“ abstimmen?" hide: "Inhalt verbergen" leaveGroup: "Gruppe verlassen" leaveGroupConfirm: "Möchtest du „{name}“ wirklich verlassen?" -useDrawerReactionPickerForMobile: "Auf mobilen Geräten ausfahrbare Reaktionsauswahl anzeigen" +useDrawerReactionPickerForMobile: "Auf mobilen Geräten ausfahrbare Reaktionsauswahl + anzeigen" welcomeBackWithName: "Willkommen zurück, {name}" -clickToFinishEmailVerification: "Drücke bitte auf [{ok}], um die Email-Bestätigung abzuschließen." +clickToFinishEmailVerification: "Drücke bitte auf [{ok}], um die Email-Bestätigung + abzuschließen." overridedDeviceKind: "Gerätetyp" smartphone: "Smartphone" tablet: "Tablet" auto: "Automatisch" -themeColor: "Farbe der Instanz-Information" +themeColor: "Farbe der Ticker-Laufschrift" size: "Größe" numberOfColumn: "Spaltenanzahl" searchByGoogle: "Suchen" -instanceDefaultLightTheme: "Instanzweites Standardfarbschema (Hell)" -instanceDefaultDarkTheme: "Instanzweites Standardfarbschema (Dunkel)" +instanceDefaultLightTheme: "Standard-Farbkombination auf diesem Server: \"Hell\"" +instanceDefaultDarkTheme: "Standard-Farbkombination auf diesem Server: \"Dunkel\"" instanceDefaultThemeDescription: "Gib den Farbschemencode im Objektformat ein." -mutePeriod: "Stummschaltungsdauer" +mutePeriod: "Dauer der Stummschaltung" indefinitely: "Dauerhaft" tenMinutes: "10 Minuten" oneHour: "Eine Stunde" oneDay: "Einen Tag" oneWeek: "Eine Woche" reflectMayTakeTime: "Es kann etwas dauern, bis sich dies widerspiegelt." -failedToFetchAccountInformation: "Benutzerkontoinformationen konnten nicht abgefragt werden" -rateLimitExceeded: "Versuchsanzahl überschritten" +failedToFetchAccountInformation: "Nutzerkontoinformationen konnten nicht abgefragt + werden" +rateLimitExceeded: "Anzahl der Versuche überschritten" cropImage: "Bild zuschneiden" cropImageAsk: "Möchtest du das Bild zuschneiden?" file: "Datei" -recentNHours: "Letzten {n} Stunden" -recentNDays: "Letzten {n} Tage" +recentNHours: "Die letzten {n} Stunden" +recentNDays: "Die letzten {n} Tage" noEmailServerWarning: "Es ist kein Email-Server konfiguriert." thereIsUnresolvedAbuseReportWarning: "Es liegen ungelöste Meldungen vor." -recommended: "Empfehlung" -check: "Check" -driveCapOverrideLabel: "Die Drive-Kapazität dieses Nutzers verändern" -driveCapOverrideCaption: "Gib einen Wert von 0 oder weniger ein, um die Kapazität auf den Standard zurückzusetzen." +recommended: "Favoriten" +check: "Kontrolle" +driveCapOverrideLabel: "Die Cloud-Drive-Kapazität dieses Nutzers verändern" +driveCapOverrideCaption: "Gib einen Wert von 0 oder weniger ein, um die Kapazität + auf den Standard zurückzusetzen." requireAdminForView: "Melde dich mit einem Administratorkonto an, um dies einzusehen." -isSystemAccount: "Ein Benutzerkonto, dass durch das System erstellt und automatisch kontrolliert wird." +isSystemAccount: "Dieses Konto wird vom System erstellt und automatisch verwaltet. + Bitte moderieren, bearbeiten, löschen oder manipulieren Sie dieses Konto nicht, + da es sonst zu einem Server-Absturz kommen könnte." typeToConfirm: "Bitte gib zur Bestätigung {x} ein" -deleteAccount: "Benutzerkonto löschen" +deleteAccount: "Nutzerkonto löschen" document: "Dokumentation" -numberOfPageCache: "Seitencachegröße" -numberOfPageCacheDescription: "Das Erhöhen dieses Caches führt zu einer angenehmerern Benutzererfahrung, erhöht aber Serverlast und Arbeitsspeicherauslastung." +numberOfPageCache: "Anzahl der zwischengespeicherten Seiten" +numberOfPageCacheDescription: "Das Erhöhen dieses Caches führt zu einer angenehmerern + Nutzererfahrung, erhöht aber Serverlast und Arbeitsspeicherauslastung." logoutConfirm: "Wirklich abmelden?" lastActiveDate: "Zuletzt verwendet am" statusbar: "Statusleiste" pleaseSelect: "Wähle eine Option" reverse: "Umkehren" colored: "Farbig" -refreshInterval: "Aktualisierungsrate" +refreshInterval: "Aktualisierungsintervall " label: "Beschriftung" type: "Art" speed: "Geschwindigkeit" @@ -881,26 +975,38 @@ slow: "Langsam" fast: "Schnell" sensitiveMediaDetection: "Erkennung von NSFW-Medien" localOnly: "Nur Lokal" -remoteOnly: "Nur für fremde Instanzen" +remoteOnly: "Nur für andere/fremde Server" failedToUpload: "Hochladen fehlgeschlagen" -cannotUploadBecauseInappropriate: "Diese Datei kann nicht hochgeladen werden, da Anteile der Datei als möglicherweise NSFW festgestellt wurden." -cannotUploadBecauseNoFreeSpace: "Die Datei konnte nicht hochgeladen werden, da dein Drive-Speicherplatz aufgebraucht ist." +cannotUploadBecauseInappropriate: "Diese Datei kann nicht hochgeladen werden, da Anteile + der Datei als möglicherweise NSFW festgestellt wurden." +cannotUploadBecauseNoFreeSpace: "Die Datei konnte nicht hochgeladen werden, da dein + Cloud-Drive-Speicherplatz aufgebraucht ist." beta: "Beta" -enableAutoSensitive: "NSFW-Automarkierung" -enableAutoSensitiveDescription: "Setzt soweit möglich durch Verwendung von Machine Learning automatisch NSFW-Markierungen für Medien, die NSFW-Anteile beinhalten. Auch wenn du diese Option deaktiviert hast, ist sie möglicherweise auf Instanzebene aktiviert." -activeEmailValidationDescription: "Aktivert strengere Überprüfung von E-Mail-Adressen, d.h. Testen auf Wegwerfadressen und darauf, ob mit der Adresse tatsächlich kommuniziert werden kann. Ist dies deaktiviert, so wird nur das Format der E-Mail überprüft." +enableAutoSensitive: "Selbstständige NSFW-Kennzeichnung" +enableAutoSensitiveDescription: "Erlaubt, wo möglich, die automatische Erkennung und + Kennzeichnung von NSFW-Medien durch maschinelles Lernen. Auch wenn diese Option + deaktiviert ist, kann sie über den Server aktiviert sein." +activeEmailValidationDescription: "Aktivert strengere Überprüfung von E-Mail-Adressen, + d.h. Testen auf Wegwerfadressen und darauf, ob mit der Adresse tatsächlich kommuniziert + werden kann. Ist dies deaktiviert, so wird nur das Format der E-Mail überprüft." navbar: "Navigationsleiste" shuffle: "Mischen" -account: "Benutzerkonto" +account: "Nutzerkonto" move: "Verschieben" _sensitiveMediaDetection: - description: "Ermöglicht eine Erleichterung der Servermoderation durch die automatische Erkennungen von NSFW-Medien unter Verwendung von Machine Learning. Hierdurch wird die Serverlast etwas erhöht." + description: "Ermöglicht eine Erleichterung der Servermoderation durch die automatische + Erkennungen von NSFW-Medien unter Verwendung von Machine Learning. Hierdurch wird + die Serverlast etwas erhöht." sensitivity: "Erkennungssensitivität" - sensitivityDescription: "Durch das Senken der Sensitivität kann die Anzahl an Fehlerkennungen (sog. false positives) reduziert werden. Durch ein Erhöhen dieser kann die Anzahl an verpassten Erkennungen (sog. false negatives) reduziert werden." - setSensitiveFlagAutomatically: "Als NSFW markieren" - setSensitiveFlagAutomaticallyDescription: "Die Resultate der internen Erkennung werden beibehalten, auch wenn diese Option deaktiviert ist." + sensitivityDescription: "Durch das Senken der Sensitivität kann die Anzahl an Fehlerkennungen + (sog. false positives) reduziert werden. Durch ein Erhöhen dieser kann die Anzahl + an verpassten Erkennungen (sog. false negatives) reduziert werden." + setSensitiveFlagAutomatically: "Als NSFW kennzeichnen" + setSensitiveFlagAutomaticallyDescription: "Die Resultate der internen Erkennung + werden beibehalten, auch wenn diese Option deaktiviert ist." analyzeVideos: "Videoanalyse aktivieren" - analyzeVideosDescription: "Analysiert zusätzlich zu Bildern auch Videos. Die Last des Servers wird hierdurch etwas erhöht." + analyzeVideosDescription: "Analysiert zusätzlich zu Bildern auch Videos. Die Last + des Servers wird hierdurch etwas erhöht." _emailUnavailable: used: "Diese Email-Adresse wird bereits verwendet" format: "Das Format dieser Email-Adresse ist ungültig" @@ -913,24 +1019,33 @@ _ffVisibility: private: "Privat" _signup: almostThere: "Fast geschafft" - emailAddressInfo: "Bitte gib deine Email-Adresse ein. Sie wird nicht öffentlich einsehbar sein." - emailSent: "An deine Email-Adresse ({email}) wurde soeben eine Bestätigungsmail geschickt. Bitte klicke auf den enthaltenen Link, um die Erstellung deines Benutzerkontos abzuschließen." + emailAddressInfo: "Bitte gib deine Email-Adresse ein. Sie wird nicht öffentlich + einsehbar sein." + emailSent: "An deine Email-Adresse ({email}) wurde soeben eine Bestätigungsmail + geschickt. Bitte klicke auf den enthaltenen Link, um die Erstellung deines Nutzerkontos + abzuschließen." _accountDelete: - accountDelete: "Benutzerkonto löschen" - mayTakeTime: "Da die Löschung eines Benutzerkontos ein aufwendiger Prozess ist, kann dessen Dauer davon abhängen, wie viel Inhalt von diesem erstellt wurde oder wie viele Dateien von diesem hochgeladen wurden." - sendEmail: "Sobald die Löschung abgeschlossen ist, wird an die mit ihm verknüpfte Email-Adresse eine Benachrichtigung versendet." - requestAccountDelete: "Löschung deines Benutzerkontos anfordern" + accountDelete: "Nutzerkonto löschen" + mayTakeTime: "Da die Löschung eines Nutzerkontos ein aufwendiger Prozess ist, kann + dessen Dauer davon abhängen, wie viel Inhalt von diesem erstellt wurde oder wie + viele Dateien von diesem hochgeladen wurden." + sendEmail: "Sobald die Löschung abgeschlossen ist, wird an die mit ihm verknüpfte + Email-Adresse eine Benachrichtigung versendet." + requestAccountDelete: "Löschung deines Nutzerkontos anfordern" started: "Die Löschung wurde eingeleitet." inProgress: "Löschung in Bearbeitung" _ad: back: "Zurück" - reduceFrequencyOfThisAd: "Diese Werbung weniger anzeigen" + reduceFrequencyOfThisAd: "Diese Werbeanzeige weniger anzeigen" _forgotPassword: - enterEmail: "Gib die Email-Adresse ein, mit der du dich registriert hast. An diese wird ein Link gesendet, mit dem du dein Passwort zurücksetzen kannst." - ifNoEmail: "Solltest du bei der Registrierung keine Email-Adresse angegeben haben, wende dich bitte an den Administrator." - contactAdmin: "Diese Instanz unterstützt die Verwendung von Email-Adressen nicht. Wende dich an den Administrator, um dein Passwort zurückzusetzen." + enterEmail: "Gib die Email-Adresse ein, mit der du dich registriert hast. An diese + wird ein Link gesendet, mit dem du dein Passwort zurücksetzen kannst." + ifNoEmail: "Solltest du bei der Registrierung keine Email-Adresse angegeben haben, + wende dich bitte an den Server-Administrator." + contactAdmin: "Dieser Server unterstützt keine Verwendung von Email-Adressen. Kontaktiere + bitte den Server-Administrator, um dein Passwort zurücksetzen zu lassen." _gallery: - my: "Meine Galerie" + my: "Meine Bilder-Galerie" liked: "Mit \"Gefällt mir\" markierte Beiträge" like: "Gefällt mir" unlike: "\"Gefällt mir\" entfernen" @@ -941,7 +1056,7 @@ _email: title: "Du hast eine Follow-Anfrage erhalten" _plugin: install: "Plugins installieren" - installWarn: "Installiere bitte nur vertrauenswürdige Plugins." + installWarn: "Bitte nur vertrauenswürdige Plugins installieren." manage: "Plugins verwalten" _preferencesBackups: list: "Erstellte Backups" @@ -951,41 +1066,49 @@ _preferencesBackups: save: "Speichern" inputName: "Gib einen Namen für dieses Backup ein" cannotSave: "Speichern fehlgeschlagen" - nameAlreadyExists: "Es existiert bereits ein Backup unter dem Namen \"{name}\". Bitte gib einen anderen Namen ein." - applyConfirm: "Wirklich das Backup \"{name}\" auf dieses Gerät anwenden? Bestehende Einstellungen darauf werden überschrieben." + nameAlreadyExists: "Es existiert bereits ein Backup unter dem Namen \"{name}\". + Bitte gib einen anderen Namen ein." + applyConfirm: "Wirklich das Backup \"{name}\" auf dieses Gerät anwenden? Bestehende + Einstellungen darauf werden überschrieben." saveConfirm: "Als {name} speichern?" deleteConfirm: "Das Backup {name} löschen?" renameConfirm: "Soll dieses Backup von \"{old}\" zu \"{new}\" umbenannt werden?" - noBackups: "Keine Backups existieren. Backups können über \"Neu erstellen\" erstelllt werden." + noBackups: "Keine Backups existieren. Backups können über \"Neu erstellen\" erstelllt + werden." createdAt: "Erstellt am: {date} {time}" updatedAt: "Aktualisiert am: {date} {time}" cannotLoad: "Laden fehlgeschlagen" - invalidFile: "Ungültiges Dateiformat." + invalidFile: "Ungültiges Dateiformat" _registry: scope: "Scope" key: "Schlüssel" keys: "Schlüssel" domain: "Domain" createKey: "Schlüssel erstellen" -_aboutMisskey: - about: "Misskey ist Open-Source-Software, welche von syuilo seit 2014 entwickelt wird." +_aboutFirefish: + about: "Firefish ist ein Fork von Misskey, der seit 2022 von ThatOneCalculator entwickelt + wird." contributors: "Hauptmitwirkende" allContributors: "Alle Mitwirkenden" source: "Quellcode" - translation: "Misskey übersetzen" - donate: "An Misskey spenden" - morePatrons: "Wir schätzen ebenso die Unterstützung vieler anderer hier nicht gelisteter Personen sehr. Danke! 🥰" + translation: "Firefish übersetzen" + donate: "An Firefish spenden" + morePatrons: "Wir schätzen ebenso die Unterstützung vieler anderer hier nicht gelisteter + Personen sehr. Danke! 🥰" patrons: "UnterstützerInnen" _nsfw: - respect: "Als NSFW markierte Bilder verbergen" - ignore: "Als NSFW markierte Bilder nicht verbergen" + respect: "Mit NSFW gekennzeichnete Bilder verbergen" + ignore: "Mit NSFW gekennzeichnete Bilder nicht verbergen" force: "Alle Medien verbergen" _mfm: cheatSheet: "MFM Spickzettel" - intro: "MFM ist eine Misskey-exklusive Markup-Sprache, die in Misskey an vielen Stellen verwendet werden kann. Hier kannst du eine Liste von verfügbarer MFM-Syntax einsehen." - dummy: "Misskey erweitert die Welt des Fediverse" + intro: "MFM ist eine Markup-Sprache, die in Misskey, Firefish, Akkoma und anderen + Programmen verwendet wird und an vielen Stellen eingesetzt werden kann. Hier können + Sie eine Liste aller verfügbaren MFM-Syntaxe einsehen." + dummy: "Firefish erweitert die Welt des Fediverse" mention: "Erwähnung" - mentionDescription: "Mit At-Zeichen und Benutzername kann ein individueller Nutzer angegeben werden." + mentionDescription: "Mit At-Zeichen und Nutzername kann ein individueller Nutzer + angegeben werden." hashtag: "Hashtag" hashtagDescription: "Mit einer Raute und Text kann ein Hashtag angegeben werden." url: "URL" @@ -1001,15 +1124,17 @@ _mfm: inlineCode: "Code (Eingebettet)" inlineCodeDescription: "Syntax-Hervorhebung für (Programm-)Code eingebettet anzeigen." blockCode: "Code (Block)" - blockCodeDescription: "Syntax-Hervorhebung für mehrzeiligen (Programm-)Code als Block anzeigen." + blockCodeDescription: "Syntax-Hervorhebung für mehrzeiligen (Programm-)Code als + Block anzeigen." inlineMath: "Mathe (Eingebettet)" - inlineMathDescription: "Mathematische Formeln (KaTeX) eingebettet anzeigen." + inlineMathDescription: "Mathematische Formeln (KaTeX) eingebettet anzeigen" blockMath: "Mathe (Block)" - blockMathDescription: "Mehrzeilige mathematische Formeln (KaTeX) als Block einbetten." + blockMathDescription: "Mathematische Formeln (KaTeX) als Block einbetten" quote: "Zitationen" quoteDescription: "Inhalt als Zitat anzeigen." emoji: "Benutzerdefinierte Emojis" - emojiDescription: "Durch das Umschließen von Emoji-Namen durch Doppelpunkte können benutzerdefinierte Emojis angezeigt werden." + emojiDescription: "Durch das Umschließen von Emoji-Namen durch Doppelpunkte können + benutzerdefinierte Emojis angezeigt werden." search: "Suche" searchDescription: "Eine vorgefertige Suchanfragebox anzeigen." flip: "Spiegelung" @@ -1017,7 +1142,7 @@ _mfm: jelly: "Animation (Dehnen)" jellyDescription: "Verleiht Inhalt eine sich dehnende Animation." tada: "Animation (Tada)" - tadaDescription: "Verleiht Inhalt eine Animation mit \"Tada!\"-Gefühl" + tadaDescription: "Verleiht Inhalt eine Animation mit \"Tada!\"-Gefühl." jump: "Animation (Sprung)" jumpDescription: "Verleiht Inhalt eine springende Animation." bounce: "Animation (Federn)" @@ -1035,7 +1160,8 @@ _mfm: x4: "Unglaublich groß" x4Description: "Lässt Inhalte noch größer als größer als groß angezeigt werden." blur: "Weichzeichnen" - blurDescription: "Inhalte durch Weihzeichnung verschwimmen lassen. Durch das Bewegen des Mauszeigers über den Inhalt wird er klar angezeigt." + blurDescription: "Inhalte durch Weihzeichnung verschwimmen lassen. Durch das Bewegen + des Mauszeigers über den Inhalt wird er klar angezeigt." font: "Schriftart" fontDescription: "Setzt die Schriftart des Inhaltes fest." rainbow: "Regenbogen" @@ -1044,16 +1170,36 @@ _mfm: sparkleDescription: "Verleiht Inhalt einen glitzernden Partikeleffekt." rotate: "Drehen" rotateDescription: "Dreht den Inhalt um einen angegebenen Winkel." + fade: "Ein-/Ausblenden" + fadeDescription: "Blended Inhalt ein and aus." plain: "Schlicht" - plainDescription: "Deaktiviert jegliche MFM-Syntax, die sich innerhalb dieses MFM-Effekts befindet." + plainDescription: "Deaktiviert jegliche MFM-Syntax, die sich innerhalb dieses MFM-Effekts + befindet." + foreground: Vordergrundfarbe + background: Hintergrundfarbe + positionDescription: Inhalt um einen bestimmten Betrag verschieben. + position: Position + cropDescription: Inhalt zuschneiden. + crop: Zuschneiden + scale: Maßstab + scaleDescription: Skaliere den Inhalt um einen bestimmten Betrag. + foregroundDescription: Ändern der Vordergrundfarbe von Text. + backgroundDescription: Ändern der Hintergrundfarbe von Text + play: MFM abspielen + stop: MFM anhalten + warn: MFM können schnell bewegte oder anderweitig auffallende Animationen enthalten + alwaysPlay: Alle animierten MFM immer automatisch abspielen + advancedDescription: Wenn diese Funktion deaktiviert ist, können nur einfache Formatierungen + vorgenommen werden, es sei denn, animiertes MFM ist aktiviert _instanceTicker: none: "Nie anzeigen" - remote: "Für Benutzer fremder Instanzen anzeigen" + remote: "Für Nutzer eines anderen Servers anzeigen" always: "Immer anzeigen" _serverDisconnectedBehavior: reload: "Automatisch aktualisieren" dialog: "Warnungsfenster zeigen" quiet: "Unaufdringlich warnen" + nothing: Nichts ändern _channel: create: "Kanal erstellen" edit: "Kanal bearbeiten" @@ -1063,7 +1209,9 @@ _channel: owned: "In Besitz" following: "Gefolgt" usersCount: "{n} Teilnehmer" - notesCount: "{n} Notizen" + notesCount: "{n} Beiträge" + nameAndDescription: Name und Beschreibung + nameOnly: Nur den Namen _menuDisplay: sideFull: "Seitlich" sideIcon: "Seitlich (Icons)" @@ -1071,30 +1219,38 @@ _menuDisplay: hide: "Ausblenden" _wordMute: muteWords: "Stummgeschaltete Wörter" - muteWordsDescription: "Zum Nutzen einer \"UND\"-Verknüpfung Einträge mit Leerzeichen trennen, zum Nutzen einer \"ODER\"-Verknüpfung Einträge mit einem Zeilenumbruch trennen." - muteWordsDescription2: "Umgib Schlüsselworter mit Schrägstrichen, um Reguläre Ausdrücke zu verwenden." - softDescription: "Notizen, die die angegebenen Konditionen erfüllen, in der Chronik ausblenden." - hardDescription: "Verhindern, dass Notizen, die die angegebenen Konditionen erfüllen, der Chronik hinzugefügt werden. Zudem werden diese Notizen auch nicht der Chronik hinzugefügt, falls die Konditionen geändert werden." + muteWordsDescription: "Zum Nutzen einer \"UND\"-Verknüpfung Einträge mit Leerzeichen + trennen, zum Nutzen einer \"ODER\"-Verknüpfung Einträge mit einem Zeilenumbruch + trennen." + muteWordsDescription2: "Umgib Schlüsselworter mit Schrägstrichen, um Reguläre Ausdrücke + zu verwenden." + softDescription: "Beiträge, die die angegebenen Konditionen erfüllen, in der Timeline + ausblenden." + hardDescription: "Verhindern, dass Beiträge, die die angegebenen Konditionen erfüllen, + der Timeline hinzugefügt werden. Zudem werden diese Beiträge auch nicht der Timeline + hinzugefügt, falls die Konditionen geändert werden." soft: "Leicht" hard: "Schwer" - mutedNotes: "Stummgeschaltete Notizen" + mutedNotes: "Stummgeschaltete Beiträge" _instanceMute: - instanceMuteDescription: "Schaltet alle Notizen/Renotes stumm, die von den gelisteten Instanzen stammen, inklusive Antworten von Benutzern an einen Benutzer einer stummgeschalteten Instanz." - instanceMuteDescription2: "Instanzen getrennt durch Zeilenumbrüchen angeben" - title: "Blendet Notizen von stummgeschalteten Instanzen aus." - heading: "Liste der stummzuschaltenden Instanzen" + instanceMuteDescription: "Schaltet alle Beiträge/Boosts stumm, die von den gelisteten + Servern stammen, inklusive Antworten von Nutzern an einen Nutzer eines stummgeschalteten + Servers." + instanceMuteDescription2: "Mit Zeilenumbrüchen trennen" + title: "Blendet Beiträge von aufgelisteten Servern aus." + heading: "Liste der Server die stummgeschaltet werden sollen" _theme: - explore: "Farbschemata erforschen" - install: "Farbschemata installieren" - manage: "Farbschemaverwaltung" + explore: "Farbkombinationen finden" + install: "Eine Farbkombination installieren" + manage: "Farbkombinationen verwalten" code: "Farbschemencode" description: "Beschreibung" installed: "{name} wurde installiert" - installedThemes: "Installierte Farbschemata" - builtinThemes: "Eingebaute Farbschemata" - alreadyInstalled: "Dieses Farbschema ist bereits installiert" - invalid: "Der Code dieses Farbschemas ist ungültig" - make: "Farbschema erstellen" + installedThemes: "Installierte Farbkombinationen" + builtinThemes: "Vorinstallierte Farbkombinationen" + alreadyInstalled: "Diese Farbkombination ist bereits installiert" + invalid: "Diese Farbkombination ist nicht möglich" + make: "Erstelle eine Farbkombination" base: "Vorlage" addConstant: "Konstante hinzufügen" constant: "Konstante" @@ -1150,7 +1306,7 @@ _theme: buttonHoverBg: "Hintergrund von Schaltflächen (Mouseover)" inputBorder: "Rahmen von Eingabefeldern" listItemHoverBg: "Hintergrund von Listeneinträgen (Mouseover)" - driveFolderBg: "Hintergrund von Drive-Ordnern" + driveFolderBg: "Hintergrund von Cloud-Drive-Ordnern" wallpaperOverlay: "Hintergrundbild-Overlay" badge: "Wappen" messageBg: "Hintergrund von Chats" @@ -1158,105 +1314,148 @@ _theme: accentLighten: "Akzent (Erhellt)" fgHighlighted: "Hervorgehobener Text" _sfx: - note: "Notizen" - noteMy: "Meine Notizen" + note: "Beiträge" + noteMy: "Meine Beiträge" notification: "Benachrichtigungen" chat: "Chat" chatBg: "Chat (Hintergrund)" - antenna: "Antennen" + antenna: "News-Picker" channel: "Kanalbenachrichtigung" _ago: future: "Zukunft" justNow: "Gerade eben" - secondsAgo: "vor {n} Sekunde(n)" - minutesAgo: "vor {n} Minute(n)" - hoursAgo: "vor {n} Stunde(n)" - daysAgo: "vor {n} Tag(en)" - weeksAgo: "vor {n} Woche(n)" - monthsAgo: "vor {n} Monat(en)" - yearsAgo: "vor {n} Jahr(en)" + secondsAgo: "vor {n} s" + minutesAgo: "vor {n} min" + hoursAgo: "vor {n} h" + daysAgo: "vor {n} T" + weeksAgo: "vor {n} W" + monthsAgo: "vor {n} M" + yearsAgo: "vor {n} J" _time: second: "Sekunde(n)" minute: "Minute(n)" hour: "Stunde(n)" day: "Tag(en)" _tutorial: - title: "Wie man Calckey benutzt" + title: "Wie man Firefish benutzt" step1_1: "Willkommen!" step1_2: "Wir werden Sie einrichten. Sie werden im Handumdrehen einsatzbereit sein!" step2_1: "Bitte füllen Sie zuerst Ihr Profil aus." - step2_2: "Wenn du ein paar Angaben zu deiner Person machst, können andere leichter erkennen, ob sie deine Notizen sehen oder dir folgen wollen." - step3_1: "Jetzt ist es Zeit, einigen Leuten zu folgen!" - step3_2: "Deine Home- und Social-Timeline basiert darauf, wem du folgst, also folge für den Anfang ein paar Accounts." + step2_2: "Wenn du ein paar Angaben zu deiner Person machst, können andere leichter + erkennen, ob sie deine Beiträge sehen oder dir folgen wollen." + step3_1: "Jetzt ist es an der Zeit, einigen Leuten zu folgen!" + step3_2: "Deine Home- und Social-Timeline basiert darauf, wem du folgst, also folge + für den Anfang ein paar Nutzerkonten.\nKlicke das Plus Symbol oben links in einem + Profil um ihm zu folgen." step4_1: "Wir bringen dich nach draußen." - step4_2: "Für deinen ersten Beitrag machen manche Leute gerne einen {introduction} Beitrag oder ein einfaches \"Hallo Welt!\"" + step4_2: "Für Ihren ersten Beitrag machen einige Leute gerne einen {introduction}-Beitrag + oder ein einfaches \"Hallo Welt!\"" step5_1: "Timelines, Timelines überall!" - step5_2: "Deine Instanz hat {Zeitleisten} verschiedene Zeitleisten aktiviert." - step5_3: "Die Zeitleiste Home {icon} ist die Zeitleiste, in der du die Beiträge deiner Follower sehen kannst." - step5_4: "In der lokalen {Icon} Zeitleiste kannst du die Beiträge aller anderen Mitglieder dieser Instanz sehen." - step5_5: "In der Zeitleiste Empfohlen {icon} kannst du Beiträge von Instanzen sehen, die von den Administratoren empfohlen werden." - step5_6: "In der sozialen {icon} Zeitleiste kannst du Beiträge von Freunden deiner Follower sehen." - step5_7: "In der globalen {icon} Zeitleiste kannst du Beiträge von allen anderen verbundenen Instanzen sehen." + step5_2: "Dein Server hat {timelines} verschiedene Timelines aktiviert." + step5_3: "Die {icon} Home-Timeline ist die Timeline, in der du die Beiträge der + Nutzerkonten sehen kannst, denen du folgst." + step5_4: "In der {Icon} Local-Timeline kannst du die Beiträge von jedem/jeder sehen + der/die auf diesem Server registriert ist." + step5_5: "Die Social-Timeline {icon} ist eine Kombination aus der Home-Timeline + und der Local-Timeline." + step5_6: "In der Empfohlen-Timeline {icon} kannst du Posts sehen, die von den Admins + vorgeschlagen wurden." + step5_7: "In der {icon} Global-Timeline können Sie Beiträge von allen verknüpften + Servern aus dem Fediverse sehen." step6_1: "Also, was ist das hier?" - step6_2: "Nun, du bist nicht nur Calckey beigetreten. Du bist einem Portal zum Fediversum beigetreten, einem zusammenhängenden Netzwerk von Tausenden von Servern, genannt \"Instanzen\"." - step6_3: "Jeder Server funktioniert auf unterschiedliche Weise, und nicht auf allen Servern läuft Calckey. Dieser hier aber schon! Es ist ein bisschen kompliziert, aber du wirst den Dreh schnell raus haben." - step6_4: "Jetzt geh, erkunde und hab Spaß!" + step6_2: "Mit Deiner Anmeldung zu Firefish bist Du gleichzeitig einem Portal zum + Fediverse beigetreten, einem Netzwerk mit Tausenden von, miteinander verbundenen, + Servern." + step6_3: "Jeder der Server funktioniert auf unterschiedliche Weise, und nicht alle + Server führen Firefish aus. Dieser jedoch schon! Es ist zu Beginn vielleicht ein + wenig kompliziert, aber Sie werden in kürzester Zeit den Dreh raus haben." + step6_4: "Jetzt bist Du startbereit, entdecke die Möglichkeiten und hab Spaß dabei!" _2fa: - alreadyRegistered: "Du hast bereits ein Gerät für Zwei-Faktor-Authentifizierung registriert." - registerDevice: "Neues Gerät registrieren" - registerKey: "Neuen Sicherheitsschlüssel registrieren" - step1: "Installiere zuerst eine Authentifizierungsapp (z.B. {a} oder {b}) auf deinem Gerät." + alreadyRegistered: "Du hast bereits ein Gerät für Zwei-Faktor-Authentifizierung + registriert." + registerTOTP: "Neues Gerät registrieren" + registerSecurityKey: "Neuen Sicherheitsschlüssel registrieren" + step1: "Installiere zuerst eine Authentifizierungsapp (z.B. {a} oder {b}) auf deinem + Gerät." step2: "Dann, scanne den angezeigten QR-Code mit deinem Gerät." step2Url: "Nutzt du ein Desktopprogramm kannst du alternativ diese URL eingeben:" step3: "Gib zum Abschluss den Token ein, der von deiner App angezeigt wird." - step4: "Alle folgenden Anmeldungsversuche werden ab sofort die Eingabe eines solchen Tokens benötigen." - securityKeyInfo: "Du kannst neben Fingerabdruck- oder PIN-Authentifizierung auf deinem Gerät auch Anmeldung mit Hilfe eines FIDO2-kompatiblen Hardware-Sicherheitsschlüssels einrichten." + step4: "Alle folgenden Anmeldungsversuche werden ab sofort die Eingabe eines solchen + Tokens benötigen." + securityKeyInfo: "Du kannst neben Fingerabdruck- oder PIN-Authentifizierung auf + deinem Gerät auch Anmeldung mit Hilfe eines FIDO2-kompatiblen Hardware-Sicherheitsschlüssels + einrichten." + step3Title: Gib deinen Authentifizierungscode ein + renewTOTPOk: Neu konfigurieren + securityKeyNotSupported: Dein Browser unterstützt Hardware-Security-Keys nicht. + chromePasskeyNotSupported: Chrome Passkeys werden momentan nicht unterstützt. + renewTOTP: Konfiguriere deine Authenticator App neu + renewTOTPCancel: Abbrechen + tapSecurityKey: Bitte folge den Anweisungen deines Browsers, um einen Hardware-Security-Key + oder einen Passkey zu registrieren + removeKey: Entferne deinen Hardware-Security-Key + removeKeyConfirm: Möchtest du wirklich deinen Key mit der Bezeichnung {name} löschen? + renewTOTPConfirm: Das wird dazu führen, dass du Verifizierungscodes deiner vorherigen + Authenticator App nicht mehr nutzen kannst + whyTOTPOnlyRenew: Die Authentificator App kann nicht entfernt werden, solange ein + Hardware-Security-Key registriert ist. + step2Click: Ein Klick auf diesen QR-Code erlaubt es dir eine 2FA-Methode zu deinem + Security Key oder deiner Authenticator App hinzuzufügen. + registerTOTPBeforeKey: Bitte registriere eine Authentificator App, um einen Hardware-Security-Key + oder einen Passkey zu nutzen. + securityKeyName: Gib einen Namen für den Key ein _permissions: - "read:account": "Deine Benutzerkontoinformationen lesen" - "write:account": "Deine Benutzerkontoinformationen bearbeiten" - "read:blocks": "Die Liste deiner blockierten Benutzer lesen" - "write:blocks": "Die Liste deiner blockierten Benutzer bearbeiten" - "read:drive": "Deine Drive-Dateien und Ordner lesen" - "write:drive": "Deine Drive-Dateien und Ordner bearbeiten oder löschen" - "read:favorites": "Deine Favoriten-Liste lesen" - "write:favorites": "Deine Favoriten-Liste bearbeiten" - "read:following": "Die Liste der Benutzer, denen du folgst, lesen" - "write:following": "Anderen Benutzern folgen oder entfolgen" + "read:account": "Deine Nutzerkontoinformationen lesen" + "write:account": "Deine Nutzerkontoinformationen bearbeiten" + "read:blocks": "Die Liste deiner blockierten Nutzer lesen" + "write:blocks": "Die Liste deiner blockierten Nutzer bearbeiten" + "read:drive": "Deine Cloud-Drive-Dateien und Ordner lesen" + "write:drive": "Deine Cloud-Drive-Dateien und Ordner bearbeiten oder löschen" + "read:favorites": "Deine Lesezeichen-Liste lesen" + "write:favorites": "Deine Lesezeichen-Liste bearbeiten" + "read:following": "Die Liste der Nutzer, denen du folgst, lesen" + "write:following": "Anderen Nutzern folgen oder entfolgen" "read:messaging": "Chats lesen" "write:messaging": "Chats bedienen" "read:mutes": "Stummschaltungen lesen" "write:mutes": "Stummschaltungen bearbeiten" - "write:notes": "Notizen schreiben oder löschen" + "write:notes": "Beiträge schreiben oder löschen" "read:notifications": "Benachrichtigungen lesen" "write:notifications": "Benachrichtigungen bedienen" "read:reactions": "Reaktionen lesen" "write:reactions": "Reaktionen bedienen" "write:votes": "Umfragen bedienen" - "read:pages": "Deine Seiten lesen" - "write:pages": "Deine Seiten bearbeiten oder löschen" - "read:page-likes": "Liste der Seiten, die mir gefallen, lesen" - "write:page-likes": "Liste der Seiten, die mir gefallen, bearbeiten" - "read:user-groups": "Benutzergruppen lesen" - "write:user-groups": "Benutzergruppen bearbeiten oder löschen" - "read:channels": "Kanäle lesen" - "write:channels": "Kanäle bedienen" - "read:gallery": "Beiträge deiner Galerie lesen" - "write:gallery": "Deine Galerie bearbeiten" - "read:gallery-likes": "Liste deiner mit \"Gefällt mir\" markierten Galerie-Beiträge lesen" - "write:gallery-likes": "Liste deiner mit \"Gefällt mir\" markierten Galerie-Beiträge bearbeiten" + "read:pages": "Deine Nutzer-Seiten lesen" + "write:pages": "Deine Nutzer-Seiten bearbeiten oder löschen" + "read:page-likes": "Liste der Nutzer-Seiten, die mir gefallen, lesen" + "write:page-likes": "Liste der Nutzer-Seiten, die mir gefallen, bearbeiten" + "read:user-groups": "Nutzergruppen lesen" + "write:user-groups": "Nutzergruppen bearbeiten oder löschen" + "read:channels": "Channels lesen" + "write:channels": "Channels bedienen" + "read:gallery": "Beiträge deiner Bilder-Galerie lesen" + "write:gallery": "Deine Bilder-Galerie bearbeiten" + "read:gallery-likes": "Liste deiner mit \"Gefällt mir\" markierten Bilder-Galerie-Beiträge + lesen" + "write:gallery-likes": "Liste deiner mit \"Gefällt mir\" markierten Bilder-Galerie-Beiträge + bearbeiten" _auth: - shareAccess: "Möchtest du „{name}“ authorisieren, auf dieses Benutzerkonto zugreifen zu können?" - shareAccessAsk: "Bist du dir sicher, dass du diese Anwendung authorisieren möchtest, auf dein Benutzerkonto zugreifen zu können?" + shareAccess: "Möchtest du „{name}“ authorisieren, auf dieses Nutzerkonto zugreifen + zu können?" + shareAccessAsk: "Bist du dir sicher, dass du diese Anwendung authorisieren möchtest, + auf dein Nutzerkonto zugreifen zu können?" permissionAsk: "Diese Anwendung fordert folgende Berechtigungen" pleaseGoBack: "Bitte kehre zur Anwendung zurück" callback: "Es wird zur Anwendung zurückgekehrt" denied: "Zugriff verweigert" + copyAsk: Bitte fügen Sie den folgenden Autorisierungscode in die Anwendung ein _antennaSources: - all: "Alle Notizen" - homeTimeline: "Notizen von Benutzern, denen gefolgt wird" - users: "Notizen von einem oder mehreren angegebenen Benutzern" - userList: "Notizen von allen Benutzern einer Liste" - userGroup: "Notizen von allen Benutzern einer Gruppe" + all: "Alle Beiträge" + homeTimeline: "Beiträge von Nutzern, denen gefolgt wird" + users: "Beiträge von einem oder mehreren angegebenen Nutzern" + userList: "Beiträge von allen Nutzern einer Liste" + userGroup: "Beiträge von allen Nutzern einer Gruppe" + instances: Beiträge von allen Nutzern auf einem Server _weekday: sunday: "Sonntag" monday: "Montag" @@ -1268,28 +1467,35 @@ _weekday: _widgets: memo: "Merkzettel" notifications: "Benachrichtigungen" - timeline: "Chronik" + timeline: "Timeline" calendar: "Kalender" trends: "Trends" clock: "Uhr" rss: "RSS-Reader" - rssTicker: "RSS-Ticker" + rssTicker: "RSS Ticker" activity: "Aktivität" photos: "Fotos" digitalClock: "Digitaluhr" unixClock: "UNIX-Uhr" federation: "Föderation" - instanceCloud: "Instanzwolke" - postForm: "Notizfenster" + instanceCloud: "Server-Cloud" + postForm: "Beitragsfeld" slideshow: "Diashow" button: "Knopf" - onlineUsers: "Benutzer Online" + onlineUsers: "Nutzer Online" jobQueue: "Job-Warteschlange" serverMetric: "Servermetriken" aiscript: "AiScript-Konsole" aichan: "Ai" + _userList: + chooseList: Wählen Sie eine Liste aus + userList: Benutzerliste + serverInfo: Server-Infos + meiliStatus: Server-Status + meiliSize: Indexgröße + meiliIndexCount: Indexierte Beiträge _cw: - hide: "Inhalt verbergen" + hide: "Verbergen" show: "Inhalt anzeigen" chars: "{count} Zeichen" files: "{count} Datei(en)" @@ -1317,19 +1523,19 @@ _poll: remainingSeconds: "{s} Sekunde(n) verbleibend" _visibility: public: "Öffentlich" - publicDescription: "Deine Notiz wird global für alle Benutzer sichtbar sein" - home: "Startseite" - homeDescription: "Notiz nur in die Startseiten-Chronik schicken" + publicDescription: "Dein Beitrag wird global für alle Nutzer sichtbar sein" + home: "nicht aufgelistet" + homeDescription: "Beitrag nur auf der Home-Timeline anzeigen" followers: "Follower" followersDescription: "Nur für Follower sichtbar" specified: "Direkt" - specifiedDescription: "Nur für bestimmte Benutzer sichtbar" + specifiedDescription: "Nur für bestimmte Nutzer sichtbar" localOnly: "Nur Lokal" - localOnlyDescription: "Unsichtbar für Benutzer anderer Instanzen" + localOnlyDescription: "Unsichtbar für Nutzer anderer Server" _postForm: - replyPlaceholder: "Dieser Notiz antworten …" - quotePlaceholder: "Diese Notiz zitieren …" - channelPlaceholder: "In einen Kanal senden" + replyPlaceholder: "Diesem Beitrag antworten …" + quotePlaceholder: "Diesen Beitrag zitieren …" + channelPlaceholder: "In einen Kanal senden..." _placeholders: a: "Was machst du momentan?" b: "Was ist um dich herum los?" @@ -1344,52 +1550,56 @@ _profile: youCanIncludeHashtags: "Du kannst auch Hashtags in deiner Profilbeschreibung verwenden." metadata: "Zusätzliche Informationen" metadataEdit: "Zusätzliche Informationen bearbeiten" - metadataDescription: "Hierdurch kannst du auf deinem Profil zusätzliche Informationsblöcke anzeigen lassen." + metadataDescription: "Hierdurch kannst du auf deinem Profil zusätzliche Informationsblöcke + 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" changeBanner: "Banner ändern" + locationDescription: Wenn Sie Ihren Ort zuerst eingeben, wird für andere Benutzer + die Ortszeit angezeigt. _exportOrImport: - allNotes: "Alle Notizen" - followingList: "Gefolgte Benutzer" + allNotes: "Alle Beiträge" + followingList: "Gefolgte Nutzer" muteList: "Stummschaltungen" blockingList: "Blockierungen" userLists: "Listen" - excludeMutingUsers: "Stummgeschaltete Benutzer aussortieren" - excludeInactiveUsers: "Inaktive Benutzer aussortieren" + excludeMutingUsers: "Stummgeschaltete Nutzer aussortieren" + excludeInactiveUsers: "Inaktive Nutzer aussortieren" _charts: federation: "Föderation" apRequest: "Anfragen" - usersIncDec: "Unterschied in der Anzahl von Benutzern" - usersTotal: "Anzahl aller Benutzer" - activeUsers: "Aktive Benutzer" - notesIncDec: "Unterschied in der Anzahl an Notizen" - localNotesIncDec: "Unterschied in der Anzahl an lokalen Notizen" - remoteNotesIncDec: "Unterschied in der Anzahl an Notizen von fremden Instanzen" - notesTotal: "Anzahl aller Notizen" + usersIncDec: "Unterschied in der Anzahl von Nutzern" + usersTotal: "Anzahl aller Nutzer" + activeUsers: "Aktive Nutzer" + notesIncDec: "Unterschied bei der Anzahl an Beiträgen" + localNotesIncDec: "Unterschied bei der Anzahl an lokalen Beiträgen" + remoteNotesIncDec: "Differenz zur Anzahl von Beiträgen von anderen Servern." + notesTotal: "Anzahl aller Beiträge" filesIncDec: "Unterschied in der Anzahl an Dateien" filesTotal: "Anzahl aller Dateien" storageUsageIncDec: "Unterschied in der Höhe der Speichernutzung" storageUsageTotal: "Gesamte Speichernutzung" _instanceCharts: requests: "Anfragen" - users: "Unterschied in der Anzahl an Benutzern" - usersTotal: "Gesamtanzahl an Benutzern" - notes: "Unterschied in der Anzahl an Notizen" - notesTotal: "Gesamtanzahl an Notizen" - ff: "Unterschied in der Anzahl an gefolgten Benutzern und Followern" - ffTotal: "Gesamtanzahl an gefolgten Benutzern und Followern" + users: "Unterschied in der Anzahl an Nutzern" + usersTotal: "Gesamtanzahl an Nutzern" + notes: "Unterschied in der Anzahl an Beiträgen" + notesTotal: "Gesamtanzahl der Beiträge" + ff: "Unterschied in der Anzahl an gefolgten Nutzern und Followern " + ffTotal: "Gesamtanzahl an gefolgten Nutzern und Followern" cacheSize: "Unterschied in der Größe des Caches" cacheSizeTotal: "Gesamtgröße des Caches" files: "Unterschied in der Anzahl an Dateien" filesTotal: "Gesamtanzahl an Dateien" _timelines: - home: "Startseite" - local: "Lokal" - social: "Sozial" - global: "Global" + home: "Home-TL" + local: "Local-TL" + social: "Social-TL" + global: "Global-TL" + recommended: Admin-Favoriten _pages: - newPage: "Seite erstellen" + newPage: "Neue Seite erstellen" editPage: "Seite bearbeiten" readPage: "Quelltextansicht" created: "Seite erfolgreich erstellt" @@ -1404,27 +1614,28 @@ _pages: viewPage: "Seite anschauen" like: "Gefällt mir" unlike: "\"Gefällt mir\" entfernen" - my: "Meine Seiten" - liked: "Seiten, die mir gefallen" + my: "Meine Nutzer-Seiten" + liked: "Nutzer-Seiten, die mir gefallen" featured: "Beliebt" inspector: "Inspektor" contents: "Inhalte" content: "Seitenblock" variables: "Variablen" title: "Titel" - url: "Seiten-URL" + url: "Nutzer-Seiten-URL" summary: "Zusammenfassung" alignCenter: "Zentrieren" - hideTitleWhenPinned: "Seitentitel wenn angeheftet ausblenden" + hideTitleWhenPinned: "Nutzer-Seitentitel wenn angeheftet ausblenden" font: "Schriftart" fontSerif: "Serif" - fontSansSerif: "Sans Serif" + fontSansSerif: "sans-serif" eyeCatchingImageSet: "Vorschaubild festlegen" eyeCatchingImageRemove: "Vorschaubild entfernen" chooseBlock: "Block hinzufügen" selectType: "Typ auswählen" enterVariableName: "Gib einen Variablennamen ein" - variableNameIsAlreadyUsed: "Dieser Name wird bereits von einer anderen Variable verwendet" + variableNameIsAlreadyUsed: "Dieser Name wird bereits von einer anderen Variable + verwendet" contentBlocks: "Inhalt" inputBlocks: "Eingabe" specialBlocks: "Spezial" @@ -1437,7 +1648,7 @@ _pages: if: "Falls" _if: variable: "Variable" - post: "Notizfenster" + post: "Beitragsfeld" _post: text: "Inhalt" attachCanvasImage: "Leinwandbild anfügen" @@ -1462,10 +1673,10 @@ _pages: id: "Leinwand-ID" width: "Breite" height: "Höhe" - note: "Eingebettete Notiz" + note: "Eingebetteter Beitrag" _note: - id: "Notiz-ID" - idDescription: "Du kannst alternativ auch die Notiz-URL angeben." + id: "Beitrags-ID" + idDescription: "Du kannst alternativ auch die Beitrags-URL angeben." detailed: "Detailierte Ansicht" switch: "Fallunterscheidung" _switch: @@ -1489,7 +1700,7 @@ _pages: pushEvent: "Ein Event senden" _pushEvent: event: "Eventname" - message: "Nachricht, die bei Auslösung des Events angezeigt werden soll" + message: "Meldung, die bei Aktivierung angezeigt werden soll" variable: "Variable, die gesendet werden soll" no-variable: "Keine" callAiScript: "AiScript ausführen" @@ -1665,7 +1876,8 @@ _pages: _for: arg1: "Anzahl der Schleifendurchläufe" arg2: "Aktion" - typeError: "Slot {slot} akzeptiert Werte vom Typ „{expect}“, aber es wurde ein „{actual}“ Wert angegeben!" + typeError: "Slot {slot} akzeptiert Werte vom Typ „{expect}“, aber es wurde ein + „{actual}“ Wert angegeben!" thereIsEmptySlot: "Slot {slot} ist leer!" types: string: "Text" @@ -1686,11 +1898,12 @@ _notification: youGotMention: "{name} hat dich erwähnt" youGotReply: "{name} hat dir geantwortet" youGotQuote: "{name} hat dich zitiert" - youRenoted: "Renote deiner Notiz von {name}" + youRenoted: "Renote deines Beitrages von {name}" youGotPoll: "{name} hat in deiner Umfrage abgestimmt" youGotMessagingMessageFromUser: "{name} hat dir eine Chatnachricht gesendet" - youGotMessagingMessageFromGroup: "In die Gruppe {name} wurde eine Chatnachricht gesendet" - youWereFollowed: "ist dir gefolgt" + youGotMessagingMessageFromGroup: "In die Gruppe {name} wurde eine Chatnachricht + gesendet" + youWereFollowed: "folgt dir nun" youReceivedFollowRequest: "Du hast eine Follow-Anfrage erhalten" yourFollowRequestAccepted: "Deine Follow-Anfrage wurde akzeptiert" youWereInvitedToGroup: "{userName} hat dich in eine Gruppe eingeladen" @@ -1714,6 +1927,9 @@ _notification: followBack: "folgt dir nun auch" reply: "Antworten" renote: "Renote" + voted: haben bei deiner Umfrage abgestimmt + reacted: hat auf deinen Beitrag reagiert + renoted: hat Ihren Beitrag geteilt _deck: alwaysShowMainColumn: "Hauptspalte immer zeigen" columnAlign: "Spaltenausrichtung" @@ -1725,18 +1941,212 @@ _deck: swapDown: "Mit unterer Spalte tauschen" stackLeft: "Auf linke Spalte stapeln" popRight: "Nach rechts vom Stapel nehmen" - profile: "Profil" - newProfile: "Neues Profil" - deleteProfile: "Profil löschen" - introduction: "Erstelle eine auf dich zugeschneiderte Benutzeroberfläche durch das Aneinanderreihen von Spalten!" + profile: "Arbeitsbereich" + newProfile: "Neuer Arbeitsbereich" + deleteProfile: "Arbeitsbereich löschen" + introduction: "Erstelle eine auf dich zugeschneiderte Benutzeroberfläche durch das + Aneinanderreihen von Spalten!" introduction2: "Klicke auf das + rechts um wann immer du möchtest neue Spalten hinzuzufügen." - widgetsIntroduction: "Drücke bitte \"Widgets bearbeiten\" im Spaltenmenü und füge ein Widget hinzu." + widgetsIntroduction: "Drücke bitte \"Widgets bearbeiten\" im Spaltenmenü und füge + ein Widget hinzu." _columns: main: "Hauptspalte" widgets: "Widgets" notifications: "Benachrichtigungen" - tl: "Chronik" - antenna: "Antennen" + tl: "Timeline" + antenna: "Antenne" list: "Listen" mentions: "Erwähnungen" direct: "Direktnachrichten" + channel: Kanal + renameProfile: Arbeitsbereich umbenennen + nameAlreadyExists: Der Name für den Arbeitsbereich ist bereits vorhanden. +enableRecommendedTimeline: '"Favoriten"-Timeline einschalten' +secureMode: Sicherer Modus (Autorisierter Abruf) +instanceSecurity: Server-Sicherheit +manageGroups: Gruppen verwalten +noThankYou: Nein, danke +privateMode: Privater Modus +enableEmojiReactions: Emoji-Reaktionen aktivieren +flagSpeakAsCat: Wie eine Katze sprechen +showEmojisInReactionNotifications: Emojis in Reaktionsbenachrichtigungen anzeigen +userSaysSomethingReason: '{name} sagte {reason}' +hiddenTagsDescription: 'Geben sie hier die Schlagworte (ohne #hashtag) an, die vom + "Trending and Explore" ausgeschlossen werden sollen. Versteckte Schlagworte sind + immer noch über andere Wege auffindbar.' +addInstance: Server hinzufügen +flagSpeakAsCatDescription: Deine Beiträge werden im Katzenmodus nyanisiert +hiddenTags: Versteckte Hashtags +antennaInstancesDescription: Geben sie einen Server-Namen pro Zeile ein +secureModeInfo: Bei Anfragen an andere Server nicht ohne Nachweis zurücksenden. +renoteMute: Boosts stummschalten +renoteUnmute: Stummschaltung von Boosts aufheben +noInstances: Keine Server gefunden +privateModeInfo: Wenn diese Option aktiviert ist, können nur als vertrauenswürdig + eingestufte Server mit diesem Server kommunizieren. Alle Beiträge werden für die + Öffentlichkeit verborgen. +allowedInstances: Vertrauenswürdige Server +selectInstance: Wähle einen Server aus +silencedInstancesDescription: Liste die Hostnamen der Server auf, die du stummschalten + möchtest. Nutzerkonten in den aufgelisteten Servern werden als "Stumm" behandelt, + können nur Follow-Anfragen stellen und können keine lokalen Nutzerkonten erwähnen, + wenn sie nicht gefolgt werden. Dies wirkt sich nicht auf die blockierten Server + aus. +editNote: Beitrag bearbeiten +edited: 'Bearbeitet um {date} {time}' +silenceThisInstance: Diesen Server stummschalten +silencedInstances: Stummgeschaltete Server +silenced: Stummgeschaltet +deleted: Gelöscht +breakFollowConfirm: Sind sie sicher, dass sie eine(n) Follower entfernen möchten? +unsubscribePushNotification: Push-Benachrichtigungen deaktivieren +pushNotificationAlreadySubscribed: Push-Benachrichtigungen sind bereits aktiviert +pushNotificationNotSupported: Ihr Browser oder der Server unterstützt keine Push-Benachrichtigungen +pushNotification: Push-Benachrichtigungen +subscribePushNotification: Push-Benachrichtigungen aktivieren +showLocalPosts: 'Zeige lokale Beiträge in:' +homeTimeline: Home-Timeline +cannotUploadBecauseExceedsFileSizeLimit: Die Datei konnte nicht hochgeladen werden, + da sie die maximal zulässige Größe überschreitet. +moveFromLabel: 'Nutzerkonto von dem Sie umziehen:' +moveAccount: Nutzerkonto umziehen! +defaultReaction: Standard-Emoji-Reaktion für ausgehende und eingehende Beiträge +moveTo: Umzug des Nutzerkontos zu einem neuen Nutzerkonto +moveToLabel: 'Nutzerkonto zu dem sie umziehen:' +moveAccountDescription: 'Dieser Vorgang kann nicht rückgängig gemacht werden! Stellen + sie vor dem Umzug dieses Nutzerkontos sicher, dass Sie einen Namen für Ihr neues + Nutzerkonto eingerichtet haben. Bitte geben sie die Bezeichnung des neuen Nutzerkontos + wie folgt ein: @name@server.xyz' +findOtherInstance: Einen anderen Server finden +sendPushNotificationReadMessage: Löschung der Push-Benachrichtigungen sobald die entsprechenden + Benachrichtigungen oder Beiträge gelesen wurden. +signupsDisabled: Derzeit sind keine Anmeldungen auf diesem Server möglich! Anmeldungen + auf anderen Servern sind jedoch möglich! Wenn Sie einen Einladungscode für diesen + Server haben, geben Sie ihn bitte unten ein. +swipeOnDesktop: Am Desktop PC das Wischen wie bei mobilen Geräten zulassen +enterSendsMessage: Drücken sie zum Senden des Beitrages die Eingabetaste (Strg-Taste + ausgeschaltet) +showUpdates: Zeigt ein Popup-Fenster an, wenn Firefish aktualisiert wird. +socialTimeline: Social-Timeline +moveFrom: Bisheriges Nutzerkonto zu diesem Nutzerkonto umziehen +_messaging: + groups: Gruppen + dms: Privat +recommendedInstances: Empfohlene Server +logoImageUrl: URL des Logo-Bildes +userSaysSomethingReasonReply: '{name} hat auf einen Beitrag geantwortet der {reason} + beinhaltet' +userSaysSomethingReasonRenote: '{name} hat einen Beitrag geteilt der {reason} beinhaltet' +userSaysSomethingReasonQuote: '{name} hat einen Beitrag zitiert der {reason} beinhaltet' +seperateRenoteQuote: Getrennte Boost- und Zitat-Schaltflächen +showAds: Anzeigen anzeigen +splash: Begrüßungsbildschirm +customSplashIconsDescription: URLs für benutzerdefinierte Splash-Screen-Symbole, die + durch Zeilenumbrüche getrennt sind und nach dem Zufallsprinzip jedes Mal angezeigt + werden, wenn ein Benutzer die Seite lädt/neu lädt. Bitte stelle sicher, dass die + Bilder unter einer statischen URL stehen, vorzugsweise alle in der Größe 192x192. +sendPushNotificationReadMessageCaption: Eine Benachrichtigung mit dem Text "{emptyPushNotificationMessage}" + wird für kurze Zeit angezeigt. Dies kann ggf. den Akkuverbrauch Ihres Geräts erhöhen. +customSplashIcons: Benutzerdefinierte Begrüßungsbildschirmsymbole (URLs) +adminCustomCssWarn: Diese Einstellung sollte nur verwendet werden, wenn Sie wissen, + was sie tut. Die Eingabe falscher Werte kann dazu führen, dass ALLE Clients nicht + mehr normal funktionieren. Bitte stellen Sie sicher, dass Ihr CSS ordnungsgemäß + funktioniert, indem Sie es in Ihren Benutzereinstellungen testen. +customMOTD: Benutzerdefinierte Meldung des Tages (Begrüßungsbildschirmmeldungen) +allowedInstancesDescription: Hosts von Servern, die zur Verbindung auf die Liste vertrauenswürdiger + Server gesetzt werden sollen, werden jeweils durch eine neue Zeile getrennt eingegeben + (gilt nur im privaten Modus). +migration: Migration +updateAvailable: Es könnte eine Aktualisierung verfügbar sein! +showAdminUpdates: Anzeigen, dass eine neue Firefish-Version verfügbar ist (nur Administrator) +customMOTDDescription: Benutzerdefinierte Meldungen für die Meldung des Tages (Begrüßungsbildschirm), + die durch Zeilenumbrüche getrennt sind und nach dem Zufallsprinzip jedes Mal angezeigt + werden, wenn ein Benutzer die Seite (neu) lädt. +recommendedInstancesDescription: Empfohlene Server, die durch Zeilenumbrüche getrennt + sind, werden in der "Favoriten"-Timeline angezeigt. Fügen Sie NICHT "https://" hinzu, + sondern NUR die Domain. +sendModMail: Moderationshinweis senden +moveFromDescription: 'Dadurch wird ein Alias Ihres alten Nutzerkontos festgelegt, + sodass Sie von ihrem bisherigen Konto zu diesem Nutzerkonto wechseln können. Tun + Sie dies, BEVOR Sie von Ihrem bisherigen Nutzerkonto hierhin wechseln. Bitte geben + Sie den Namen des Nutzerkontos wie folgt ein: @person@server.xyz' +preventAiLearning: KI gestütztes bot-scraping unterdrücken +preventAiLearningDescription: Fordern Sie KI-Sprachmodelle von Drittanbietern auf, + die von Ihnen hochgeladenen Inhalte, wie z. B. Beiträge und Bilder, nicht zu untersuchen. +license: Genehmigung +indexPosts: Gelistete Beiträge +migrationConfirm: "Sind Sie absolut sicher, dass Sie Ihr Nutzerkonto zu diesem {account} + umziehen möchten? Sobald Sie dies bestätigt haben, kann dies nicht mehr rückgängig + gemacht werden und Ihr Nutzerkonto kann nicht mehr von ihnen genutzt werden.\nStellen + Sie außerdem sicher, dass Sie dieses Nutzerkonto als das Konto festgelegt haben, + von dem Sie umziehen." +noteId: Beitrags-ID +customKaTeXMacro: Individuelle KaTeX Makros +enableCustomKaTeXMacro: Individuelle KaTeX-Makros aktivieren +replayTutorial: Wiederhole die Benutzeranleitung +apps: Apps +caption: Automatische Untertitelung +pwa: PWA installieren +cw: Inhaltswarnung +older: älter +newer: neuer +accessibility: Erreichbarkeit +jumpToPrevious: Zum Vorherigen springen +silencedWarning: Diese Meldung wird angezeigt, weil diese Nutzer von Servern stammen, + die Ihr Administrator abgeschaltet hat, so dass es sich möglicherweise um Spam handelt. +_experiments: + title: Funktionstests + enablePostImports: Beitragsimporte aktivieren + postImportsCaption: Erlaubt es Nutzer:innen ihre Posts von alten Firefish, Misskey, + Mastodon, Akkoma und Pleroma Accounts zu importieren. Bei Engpässen in der Warteschlange + kann es zu Verlangsamungen beim Laden während des Imports kommen. +noGraze: Bitte deaktivieren Sie die Browsererweiterung "Graze for Mastodon", da sie + die Funktion von Firefish stört. +indexFrom: Indexieren ab Beitragskennung aufwärts +indexNotice: Wird jetzt indexiert. Dies wird wahrscheinlich eine Weile dauern, bitte + starten Sie Ihren Server für mindestens eine Stunde nicht neu. +customKaTeXMacroDescription: "Richten Sie Makros ein, um mathematische Ausdrücke einfach + zu schreiben! Die Notation entspricht den LaTeX-Befehlsdefinitionen und wird als\n + \\newcommand{\\name}{content} or \\newcommand{\\name}[number of arguments]{content}\n + geschrieben.\nZum Beispiel wird\n\\newcommand{\\add}[2]{#1 + #2} \\add{3}{foo} um + 3 + foo erweitert.\nDie geschweiften Klammern, die den Makronamen umgeben, können + in runde oder eckige Klammern geändert werden. Dies hat Auswirkungen auf die Klammern, + die für die Argumente verwendet werden. Pro Zeile kann ein (und nur ein) Makro definiert + werden, und Sie können die Zeile nicht mitten in der Definition umbrechen. Ungültige + Zeilen werden einfach ignoriert. Es werden nur einfache Funktionen zur Substitution + von Zeichenketten unterstützt; erweiterte Syntax, wie z. B. bedingte Verzweigungen, + können hier nicht verwendet werden." +expandOnNoteClickDesc: Wenn deaktiviert, können Sie Beiträge trotzdem über das Rechtsklickmenü + oder durch Anklicken des Zeitstempels öffnen. +selectChannel: Wählen Sie einen Kanal aus +expandOnNoteClick: Beitrag bei Klick öffnen +image: Bild +video: Video +audio: Audio +indexFromDescription: Leer lassen, um jeden Beitrag zu indexieren +_filters: + fromUser: Von Benutzer + notesAfter: Beiträge nach + withFile: Mit Datei + fromDomain: Von Domain + notesBefore: Beiträge vor + followingOnly: Nur Folgende +isBot: Dieses Konto ist ein Bot +isModerator: Moderator +isAdmin: Administrator +_dialog: + charactersExceeded: 'Maximale Anzahl an Zeichen aufgebraucht! Limit: {current} / + {max}' + charactersBelow: Nicht genug Zeichen! Du hast aktuell {current} von {min} Zeichen +searchPlaceholder: Firefish durchsuchen +antennasDesc: "Antennen zeigen neue Posts an, die deinen definierten Kriterien entsprechen!\n + Sie können von der Timeline-Seite aufgerufen werden." +isPatron: Firefish Patron +removeReaction: Entferne deine Reaktion +listsDesc: Listen lassen dich Timelines mit bestimmten Nutzer:innen erstellen. Sie + können von der Timeline-Seite erreicht werden. +clipsDesc: Clips sind wie teilbare, kategorisierte Lesezeichen. Du kannst Clips vom + Menü individueller Posts aus erstellen. +channelFederationWarn: Kanäle föderieren noch nicht zu anderen Servern +reactionPickerSkinTone: Bevorzugte Emoji-Hautfarbe +swipeOnMobile: Wischen zwischen den Seiten erlauben diff --git a/locales/el-GR.yml b/locales/el-GR.yml index d32dd9c728..b42a0a7076 100644 --- a/locales/el-GR.yml +++ b/locales/el-GR.yml @@ -1,22 +1,21 @@ ---- _lang_: "Ελληνικά" -monthAndDay: "{μήνας}/{ημέρα}" +monthAndDay: "{day}/{month}" search: "Αναζήτηση" notifications: "Ειδοποιήσεις" username: "Όνομα μέλους" password: "Κωδικός πρόσβασης" forgotPassword: "Ξέχασα τον κωδικό πρόσβασης" -fetchingAsApObject: "Μαζεύοντας από το Fediverse..." +fetchingAsApObject: "Άντληση από το Fediverse" ok: "Εντάξει" gotIt: "Τό'πιασα!" cancel: "Ακύρωση" -enterUsername: "Εισάγετε το όνομα μέλους" -renotedBy: "Κοινοποιήθηκε από {user}" -noNotes: "Δεν υπάρχουν σημειώματα" +enterUsername: "Εισαγωγή ονόματος μέλους" +renotedBy: "Προωθήθηκε από {user}" +noNotes: "Δεν υπάρχουν δημοσιεύσεις" noNotifications: "Δεν υπάρχουν ειδοποιήσεις" settings: "Ρυθμίσεις" -basicSettings: "Βασικές ρυθμίσεις" -otherSettings: "Άλλες ρυθμίσεις" +basicSettings: "Βασικές Ρυθμίσεις" +otherSettings: "Άλλες Ρυθμίσεις" openInWindow: "Άνοιγμα σε παράθυρο" profile: "Προφίλ" timeline: "Χρονολόγιο" @@ -24,24 +23,25 @@ noAccountDescription: "Αυτό το μέλος δεν έχει γράψει β login: "Σύνδεση" loggingIn: "Συνδέεστε" logout: "Αποσύνδεση" -signup: "Δημιουργία λογαριασμού" +signup: "Εγγραφή" uploading: "Ανέβασμα..." save: "Αποθήκευση" users: "Μέλη" addUser: "Προσθήκη μέλους" -favorite: "Προσθήκη στα αγαπημένα" -favorites: "Αγαπημένα" -unfavorite: "Αφαίρεση από αγαπημένα" -favorited: "Προστέθηκε στα αγαπημένα." -alreadyFavorited: "Έχει ήδη προστεθεί στα αγαπημένα." -cantFavorite: "Αδυναμία προσθήκης στα αγαπημένα." +favorite: "Προσθήκη στους σελιδοδείκτες" +favorites: "Σελιδοδείκτες" +unfavorite: "Αφαίρεση από τους σελιδοδείκτες" +favorited: "Προστέθηκε στους σελιδοδείκτες." +alreadyFavorited: "Έχει ήδη προστεθεί στους σελιδοδείκτες." +cantFavorite: "Αδυναμία προσθήκης στους σελιδοδείκτες." pin: "Καρφίτσωμα στο προφίλ" unpin: "Ξεκαρφίτσωμα από το προφίλ" copyContent: "Αντιγραφή περιεχομένων" copyLink: "Αντιγραφή συνδέσμου" delete: "Διαγραφή" deleteAndEdit: "Διαγραφή και επεξεργασία" -deleteAndEditConfirm: "Σίγουρα θέλετε να διαγράψετε αυτό το σημείωμα και να το επεξεργαστείτε; Θα χάσετε όλες τις αντιδράσεις, κοινοποιήσεις και απαντήσεις σε αυτό." +deleteAndEditConfirm: "Σίγουρα θέλετε να διαγράψετε αυτή τη δημοσίευση και να την\ + \ επεξεργαστείτε; Θα χάσετε όλες τις αντιδράσεις, προωθήσεις και απαντήσεις σε αυτήν." addToList: "Προσθήκη στη λίστα" sendMessage: "Αποστολή μηνύματος" copyUsername: "Αντιγραφή ονόματος μέλους" @@ -55,20 +55,22 @@ receiveFollowRequest: "Λάβατε αίτημα ακολούθησης" followRequestAccepted: "Το αίτημα ακολούθησης έγινε δεκτό" mention: "Επισήμανση" mentions: "Επισημάνσεις" -directNotes: "Απευθείας σημειώματα" -importAndExport: "Εισαγωγή / Εξαγωγή" +directNotes: "Απευθείας μηνύματα" +importAndExport: "Εισαγωγή/Εξαγωγή Δεδομένων" import: "Εισαγωγή" export: "Εξαγωγή" files: "Αρχεία" -download: "Λήψη" -driveFileDeleteConfirm: "Θέλετε σίγουρα να διαγράψετε το αρχείο \"{name}\"; Τα σημειώματα με αυτό το συνημμένο αρχείο επίσης θα διαγραφούν." +download: "Κατέβασμα" +driveFileDeleteConfirm: "Θέλετε σίγουρα να διαγράψετε το αρχείο \"{name}\"; Οι δημοσιεύσεις\ + \ με αυτό το συνημμένο αρχείο επίσης θα διαγραφούν." unfollowConfirm: "Θέλετε σίγουρα να σταματήσετε να ακολουθείτε το μέλος {name};" -exportRequested: "Ζητήσατε μία εξαγωγή. Αυτό μπορεί να πάρει κάποιον χρόνο. Επίσης θα προστεθεί στον Δίσκο σας μόλις ολοκληρωθεί." -importRequested: "Ζητήσατε μία εισαγωγή. Αυτό μπορεί να πάρει κάποιον χρόνο." +exportRequested: "Ζητήσατε μία εξαγωγή. Αυτό μπορεί να πάρει κάποιον χρόνο. Θα προστεθεί\ + \ στον Αποθηκευτικό Χώρο σας μόλις ολοκληρωθεί." +importRequested: "Ζητήσατε μια εισαγωγή. Αυτό μπορεί να πάρει κάποιον χρόνο." lists: "Λίστες" noLists: "Δεν έχετε λίστες" -note: "Σημείωμα" -notes: "Σημειώματα" +note: "Δημοσίευση" +notes: "Δημοσιεύσεις" following: "Ακολουθεί" followers: "Ακολουθούν" followsYou: "Σε ακολουθεί" @@ -78,69 +80,74 @@ error: "Σφάλμα" somethingHappened: "Προέκυψε ένα σφάλμα" retry: "Προσπάθεια ξανά" pageLoadError: "Ένα σφάλμα προέκυψε φορτώνοντας τη σελίδα." -pageLoadErrorDescription: "Αυτό κανονικά προκαλείται από σφάλματα δικτύου ή από την προσωρινή μνήμη του προγράμματος περιήγησης. Δοκιμάστε να σβήσετε την προσωρινή μνήμη (cache) και ξαναδοκιμάστε μετά από λίγο." -serverIsDead: "Αυτός ο server δεν αποκρίνεται. Παρακαλώ περιμέντε λίγο και δοκιμάστε ξανά." -youShouldUpgradeClient: "Για να δείτε αυτή τη σελίδα, παρακαλώ επαναφορτώστε για να ενημερωθεί το πρόγραμμα." +pageLoadErrorDescription: "Αυτό κανονικά προκαλείται από σφάλματα δικτύου ή από την\ + \ προσωρινή μνήμη του προγράμματος περιήγησης. Δοκιμάστε να σβήσετε την προσωρινή\ + \ μνήμη (cache) και να δοκιμάσετε ξανά μετά από λίγο." +serverIsDead: "Αυτός ο διακομιστής (server) δεν αποκρίνεται. Παρακαλώ περιμένετε λίγο\ + \ και δοκιμάστε ξανά." +youShouldUpgradeClient: "Για να δείτε αυτή τη σελίδα, παρακαλώ επαναφορτώστε για να\ + \ γίνει ενημέρωση." enterListName: "Πληκτρολογήστε ένα όνομα για τη λίστα" privacy: "Ιδιωτικότητα" makeFollowManuallyApprove: "Τα αιτήματα ακολούθησης χρειάζονται έγκριση" defaultNoteVisibility: "Προεπιλεγμένη ορατότητα" follow: "Ακολουθήστε" -followRequest: "Στείλτε αίτημα ακολούθησης" +followRequest: "Ακολουθήστε" followRequests: "Αιτήματα ακολούθησης" unfollow: "Να μην ακολουθώ" followRequestPending: "Το αίτημα ακολούθησης εκκρεμεί" enterEmoji: "Εισάγετε ένα emoji" -renote: "Κοινοποίηση σημειώματος" -unrenote: "Ακύρωση κοινοποίησης" -renoted: "Κοινοποιήθηκε." -cantRenote: "Αυτή η δημοσίευση δεν μπορεί να κοινοποιηθεί." -cantReRenote: "Μία κοινοποίηση δεν μπορεί να κοινοποιηθεί." +renote: "Προώθηση" +unrenote: "Αναίρεση προώθησης" +renoted: "Προωθήθηκε." +cantRenote: "Αυτή η δημοσίευση δεν μπορεί να προωθηθεί." +cantReRenote: "Μία προώθηση δεν μπορεί να προωθηθεί." quote: "Παράθεση" -pinnedNote: "Καρφιτσωμένο σημείωμα" +pinnedNote: "Καρφιτσωμένη δημοσίευση" pinned: "Καρφίτσωμα στο προφίλ" you: "Εσύ" clickToShow: "Κάντε κλικ για εμφάνιση" -add: "Προσθέστε" +add: "Προσθήκη" reaction: "Αντιδράσεις" -reactionSetting: "Αντιδράσεις για εμφάνιση στην επιλογή αντίδρασης" -reactionSettingDescription2: "Σύρετε για να αλλάξετε τη σειρά, κάντε κλικ για να διαγράψετε, πατήστε \"+\" για να προσθέσετε." -rememberNoteVisibility: "Θυμήσου τις ρυθμίσεις ορατότητας σημειώματος" -attachCancel: "Διαγραφή αρχείου" +reactionSetting: "Αντιδράσεις που θα εμφανίζονται στον επιλογέα" +reactionSettingDescription2: "Σύρετε για να αλλάξετε τη σειρά, κάντε κλικ για να διαγράψετε,\ + \ πατήστε \"+\" για να προσθέσετε." +rememberNoteVisibility: "Θυμήσου τις ρυθμίσεις ορατότητας για τις δημοσιεύσεις" +attachCancel: "Αφαίρεση επισυναπτόμενου" enterFileName: "Πληκτρολογήστε όνομα αρχείου" mute: "Σίγαση" -unmute: "Άρση σίγασης" +unmute: "Διακοπή σίγασης" block: "Μπλοκάρισμα" -unblock: "Άρση μπλοκαρίσματος" +unblock: "Διακοπή μπλοκαρίσματος" suspend: "Αποβολή" -unsuspend: "Άρση αποβολής" +unsuspend: "Διακοπή αποβολής" blockConfirm: "Θέλετε σίγουρα να μπλοκάρετε αυτόν τον λογαριασμό;" unblockConfirm: "Θέλετε σίγουρα να ξεμπλοκάρετε αυτόν τον λογαριασμό;" -suspendConfirm: "Θέλετε σίγουρα να αποβάλλετε αυτόν τον λογαριασμό;" +suspendConfirm: "Θέλετε σίγουρα να αποβάλετε αυτόν τον λογαριασμό;" unsuspendConfirm: "Θέλετε σίγουρα να άρετε την αποβολή αυτού του λογαριασμού;" -selectList: "Επιλέξτε μία λίστα" -selectAntenna: "Επιλέξτε μία αντένα" -selectWidget: "Επιλέξτε ένα μαραφέτι" -editWidgets: "Επεξεργασία μαραφετίων" +selectList: "Επιλέξτε μια λίστα" +selectAntenna: "Επιλέξτε μια αντένα" +selectWidget: "Επιλέξτε ένα πρόσθετο" +editWidgets: "Επεξεργασία πρόσθετων" editWidgetsExit: "Ολοκληρώθηκε" -customEmojis: "Επιπλέον emoji" +customEmojis: "Προσαρμοσμένα Emoji" emojiName: "Όνομα emoji" -addEmoji: "Προσθήκη emoji" -settingGuide: "Συνιστώμενες ρυθμίσεις" -flagAsBot: "Αυτός ο λογαριασμός είναι bot" -flagAsCat: "Αυτός ο λογαριασμός είναι γάτα" +addEmoji: "Προσθήκη" +settingGuide: "Προτεινόμενες ρυθμίσεις" +flagAsBot: "Δήλωση αυτού του λογαριασμού ως bot" +flagAsCat: "Είσαι γατί; \U0001F63A" flagShowTimelineReplies: "Εμφάνιση απαντήσεων στο χρονολόγιο" addAccount: "Προσθήκη λογαριασμού" general: "Γενικές" wallpaper: "Ταπετσαρία" setWallpaper: "Ορισμός ταπετσαρίας" -removeWallpaper: "Διαγραφή ταπετσαρίας" +removeWallpaper: "Αφαίρεση ταπετσαρίας" searchWith: "Αναζήτηση: {q}" youHaveNoLists: "Δεν έχετε λίστες" followConfirm: "Θέλετε σίγουρα να ακολουθήσετε τον λογαριασμό {name};" -host: "Φιλοξενεί" +host: "Φιλοξενεί (Host)" selectUser: "Επιλέξτε ένα μέλος" -recipient: "Αποδέκτης-τρια" +recipient: "Αποδέκτης-τρια(-ες)" annotation: "Σχόλια" federation: "Ομοσπονδία" storageUsage: "Χρήση χώρου" @@ -148,11 +155,11 @@ version: "Έκδοση" metadata: "Μεταδεδομένα" network: "Δίκτυο" disk: "Δίσκος" -instanceInfo: "Πληροφορίες του instance" +instanceInfo: "Πληροφορίες Instance" statistics: "Στατιστικά" clearQueue: "Εκκαθάριση ουράς" clearQueueConfirmTitle: "Θέλετε να διαγράψετε την ουρά;" -clearCachedFiles: "Εκκαθάριση προσωρινής μνήμης" +clearCachedFiles: "Εκκαθάριση προσωρινής μνήμης (cache)" done: "Ολοκληρώθηκε" attachFile: "Επισύναψη αρχείων" more: "Περισσότερα!" @@ -166,12 +173,12 @@ messaging: "Συνομιλία" upload: "Ανεβάστε" fromDrive: "Από τον Αποθηκευτικό Χώρο" fromUrl: "Από URL" -uploadFromUrl: "Ανεβάστε από URL" -explore: "Εξερευνήστε" +uploadFromUrl: "Ανέβασμα από URL" +explore: "Εξερεύνηση" messageRead: "Διαβάστηκε" -startMessaging: "Ξεκινήστε μία συνομιλία" +startMessaging: "Ξεκινήστε μια νέα συνομιλία" nUsersRead: "διαβάστηκε από {n}" -tos: "Όροι χρήσης" +tos: "Όροι Χρήσης" start: "Ας αρχίσουμε" home: "Κεντρικό" activity: "Δραστηριότητα" @@ -180,8 +187,8 @@ birthday: "Γενέθλια" registeredDate: "Έγινε μέλος στις" location: "Τοποθεσία" theme: "Θέματα" -light: "Ανοιχτόχρωμο" -dark: "Σκούρο" +light: "Φωτεινό" +dark: "Σκοτεινό" drive: "Αποθηκευτικός Χώρος" fileName: "Όνομα αρχείου" selectFile: "Επιλέξτε ένα αρχείο" @@ -189,16 +196,16 @@ selectFiles: "Επιλέξτε αρχεία" selectFolder: "Επιλέξτε φάκελο" selectFolders: "Επιλέξτε φακέλους" renameFile: "Μετονομασία αρχείου" -addFile: "Προσθήκη αρχείου" +addFile: "Προσθέστε ένα αρχείο" emptyDrive: "Ο Αποθηκευτικός Χώρος σας είναι άδειος" -copyUrl: "Αντιγραφή URL" -rename: "Αλλαγή ονόματος" -avatar: "Εικονίδιο" -banner: "Πανό" +copyUrl: "Αντιγραφή διεύθυνσης URL" +rename: "Μετονομασία" +avatar: "Άβαταρ" +banner: "Εξώφυλλο" reload: "Ανανέωση" doNothing: "Αγνόηση" watch: "Παρακολούθηση" -unwatch: "Τέλος παρακολούθησης" +unwatch: "Διακοπή παρακολούθησης" accept: "Αποδοχή" reject: "Απόρριψη" normal: "Κανονικό" @@ -212,23 +219,23 @@ connectService: "Σύνδεση" disconnectService: "Αποσύνδεση" registration: "Εγγραφή" pinnedPages: "Καρφιτσωμένες Σελίδες" -pinnedNotes: "Καρφιτσωμένα σημειώματα" +pinnedNotes: "Καρφιτσωμένες δημοσιεύσεις" antennas: "Αντένες" -manageAntennas: "Διαχείριση αντενών" +manageAntennas: "Διαχείριση Αντενών" name: "Όνομα" -antennaSource: "Πηγή αντένας" +antennaSource: "Πηγή Αντένας" antennaKeywords: "Λέξεις-κλειδιά για παρακολούθηση" -antennaExcludeKeywords: "Λέξεις-κλειδιά για αποκλεισμό" -notifyAntenna: "Ειδοποίηση για νέα σημειώματα" -withFileAntenna: "Μόνο σημειώματα με αρχεία" +antennaExcludeKeywords: "Λέξεις-κλειδιά για εξαίρεση" +notifyAntenna: "Ειδοποίηση για νέες δημοσιεύσεις" +withFileAntenna: "Μόνο δημοσιεύσεις με αρχεία" caseSensitive: "Διάκριση Πεζών-Κεφαλαίων" popularTags: "Δημοφιλείς ετικέτες" userList: "Λίστες" -about: "Πληροφορίες" -moderator: "Συντονιστής" +about: "Σχετικά με" +moderator: "Συντονιστής/στρια" moderation: "Συντονισμός" -cacheClear: "Εκκαθάριση προσωρινής μνήμης" -markAsReadAllNotifications: "Όλες οι ειδοποιήσεις διαβάστηκαν" +cacheClear: "Εκκαθάριση προσωρινής μνήμης (cache)" +markAsReadAllNotifications: "Σημειώστε όλες τις ειδοποιήσεις ως διαβασμένες" group: "Ομάδα" groups: "Ομάδες" createGroup: "Δημιουργία ομάδας" @@ -236,13 +243,13 @@ ownedGroups: "Οι ομάδες σας" groupName: "Όνομα ομάδας" members: "Μέλη" transfer: "Μεταφορά" -messagingWithUser: "Ιδιωτική συνομιλία" +messagingWithUser: "Προσωπική συνομιλία" messagingWithGroup: "Ομαδική συνομιλία" title: "Τίτλος" text: "Κείμενο" enable: "Ενεργοποίηση" next: "Επόμενο" -noteOf: "Σημείωμα από {user}" +noteOf: "Δημοσίευση από {user}" inviteToGroup: "Πρόσκληση στην ομάδα" quoteAttached: "Παράθεση" signinRequired: "Παρακαλούμε δημιουργήστε λογαριασμό ή συνδεθείτε πριν συνεχίσετε" @@ -250,26 +257,26 @@ category: "Κατηγορία" tags: "Ετικέτες" createAccount: "Δημιουργία λογαριασμού" local: "Τοπικό" -remote: "Απομακρυσμένo" +remote: "Απομακρυσμένο" total: "Σύνολο" appearance: "Εμφάνιση" -accountSettings: "Ρυθμίσεις λογαριασμού" +accountSettings: "Ρυθμίσεις Λογαριασμού" sounds: "Ήχοι" sound: "Ήχοι" listen: "Ακρόαση" showInPage: "Εμφάνιση στη σελίδα" volume: "Ένταση" -masterVolume: "Κύρια ένταση" +masterVolume: "Κεντρική ένταση" details: "Λεπτομέρειες" -install: "Εγκατάσταση" -uninstall: "Κατάργηση εγκατάστασης" +install: "Εγκαταστήστε" +uninstall: "Απεγκατάσταση" manage: "Διαχείριση" -smtpHost: "Φιλοξενεί" +smtpHost: "Φιλοξενεί (Host)" smtpUser: "Όνομα μέλους" -smtpPass: "Κωδικός πρόσβασης" +smtpPass: "Κωδικός" notificationSetting: "Ρυθμίσεις ειδοποιήσεων" -notificationSettingDesc: "Επιλέξτε τους τύπους ειδοποιήσεων που εμφανίζονται" -switchUi: "Αλλαγή UI" +notificationSettingDesc: "Επιλέξτε τους τύπους ειδοποιήσεων για προβολή." +switchUi: "Διάταξη" clip: "Κλιπ" driveFilesCount: "Αριθμός αρχείων Αποθηκευτικού Χώρου" driveUsage: "Χρήση Αποθηκευτικού Χώρου" @@ -293,7 +300,8 @@ manageAccounts: "Διαχείριση Λογαριασμών" searchByGoogle: "Αναζήτηση" file: "Αρχεία" recommended: "Προτεινόμενα" -cannotUploadBecauseNoFreeSpace: "Το ανέβασμα απέτυχε λόγω ανεπαρκούς Αποθηκευτικού Χώρου" +cannotUploadBecauseNoFreeSpace: "Το ανέβασμα απέτυχε λόγω ανεπαρκούς Αποθηκευτικού\ + \ Χώρου." _email: _follow: title: "Έχετε ένα νέο ακόλουθο" @@ -327,15 +335,20 @@ _ago: monthsAgo: "{n} μήνα(ες) πριν" yearsAgo: "{n} έτος(η) πριν" _permissions: - "write:drive": "Επεξεργαστείτε ή διαγράψτε τα αρχεία και τους φακέλους του Αποθηκευτικού Χώρου σας" - "read:favorites": "Δείτε τη λίστα των αγαπημένων σας" - "write:favorites": "Επεξεργαστείτε τη λίστα των αγαπημένων σας" + "write:drive": "Επεξεργαστείτε ή διαγράψτε τα αρχεία και τους φακέλους του Αποθηκευτικού\ + \ Χώρου σας" + "read:favorites": "Δείτε τη λίστα με τους σελιδοδείκτες σας" + "write:favorites": "Επεξεργαστείτε τη λίστα με τους σελιδοδείκτες σας" "read:messaging": "Δείτε τις συνομιλίες σας" "write:messaging": "Γράψτε ή διαγράψτε μηνύματα συνομιλίας" "read:notifications": "Δείτε τις ειδοποιήσεις σας" "write:notifications": "Διαχειριστείτε τις ειδοποιήσεις σας" "read:pages": "Δείτε τις Σελίδες σας" "write:pages": "Επεξεργαστείτε ή διαγράψτε τις σελίδες σας" + "write:gallery-likes": Επεξεργασία της λίστας των αγαπημένων σας δημοσιεύσεων γκαλερί + "read:gallery": Δείτε την γκαλερί σας + "write:gallery": Επεξεργασία της γκαλερί σας + "read:gallery-likes": Δείτε τη λίστα των αγαπημένων σας δημοσιεύσεων γκαλερί _antennaSources: all: "Όλα τα σημειώματα" homeTimeline: "Σημειώματα από μέλη που ακολουθείτε" @@ -368,6 +381,7 @@ _visibility: _profile: name: "Όνομα" username: "Όνομα μέλους" + changeAvatar: Αλλαγή άβαταρ _exportOrImport: allNotes: "Όλα τα σημειώματα" followingList: "Ακολουθεί" @@ -398,11 +412,408 @@ _notification: reply: "Απάντηση" renote: "Κοινοποίηση σημειώματος" _deck: - widgetsIntroduction: "Παρακαλούμε επιλέξτε \"Επεξεργασία μαραφετίων\" στο μενού και προσθέστε μαραφέτι." + widgetsIntroduction: "Παρακαλούμε επιλέξτε \"Επεξεργασία πρόσθετων\" στο μενού και\ + \ προσθέστε μαραφέτι." _columns: - widgets: "Μαραφέτια" + widgets: "Πρόσθετα" notifications: "Ειδοποιήσεις" tl: "Χρονολόγιο" antenna: "Αντένες" list: "Λίστα" mentions: "Επισημάνσεις" +sensitive: Ευαίσθητο περιεχόμενο (NSFW) +createFolder: Δημιουργία φακέλου +uploadFromUrlDescription: Το URL του αρχείου που θέλετε να ανεβάσετε +emptyFolder: Αυτός ο φάκελος είναι άδειος +unableToDelete: Αδυναμία διαγραφής +recentlyUpdatedUsers: Πρόσφατα ενεργά μέλη +recentlyRegisteredUsers: Νέα μέλη +exploreUsersCount: Υπάρχουν {count} μέλη +help: Βοήθεια +inputNewFileName: Πληκτρολογήστε ένα νέο όνομα αρχείου +nothing: Δεν υπάρχει τίποτα να δείτε εδώ +newNoteRecived: Υπάρχουν νέες δημοσιεύσεις +passwordMatched: Ταιριάζει +unmarkAsSensitive: Αναίρεση επισήμανσης ως Ευαίσθητο Περιεχόμενο (NSFW) +blockedUsers: Μπλοκαρισμένα μέλη +noteDeleteConfirm: Θέλετε σίγουρα να διαγράψετε αυτή τη δημοσίευση; +preview: Προεπισκόπηση +noCustomEmojis: Δεν υπάρχουν emoji +tosUrl: URL Όρων Χρήσης +monthX: '{month}' +markAsReadAllTalkMessages: Σημειώστε όλα τα μηνύματα ως διαβασμένα +inputMessageHere: Γράψτε εδώ το μήνυμά σας +close: Κλείσιμο +newMessageExists: Υπάρχουν νέα μηνύματα +usernameInvalidFormat: Μπορείτε να χρησιμοποιήσετε κεφαλαία και μικρά γράμματα, αριθμούς, + και κάτω παύλες. +tooShort: Πολύ σύντομο +passwordNotMatched: Δεν ταιριάζει +existingAccount: Υπάρχων λογαριασμός +deleteAll: Διαγραφή όλων +chooseEmoji: Επιλέξτε ένα emoji +sort: Ταξινόμηση +descendingOrder: Φθίνουσα +deleteAllFiles: Διαγραφή όλων των αρχείων +userSuspended: Αυτό το μέλος έχει αποβληθεί. +menu: Μενού +divider: Χώρισμα +deletedNote: Διαγραμμένη δημοσίευση +useCw: Απόκρυψη περιεχομένου +description: Περιγραφή +width: Πλάτος +disableAll: Απενεργοποίηση όλων +notificationType: Τύπος ειδοποίησης +wordMute: Σίγαση λέξεων +userSaysSomething: '{name} είπε κάτι' +metrics: Μετρήσεις +overview: Γενική εικόνα +database: Βάση δεδομένων +channel: Κανάλια +other: Άλλα +abuseReports: Αναφορές +reportAbuse: Αναφορά +unclip: Ακύρωση κλιπ +public: Δημόσιο +renotedCount: Αριθμός προωθήσεων που ελήφθησαν +alwaysMarkSensitive: Επισήμανση ως ευαίσθητο περιεχόμενο (NSFW) ως προεπιλογή +markAllAsRead: Σημειώστε τα όλα ως διαβασμένα +_gallery: + like: Μου αρέσει + liked: Αγαπημένες δημοσιεύσεις + my: Η Γκαλερί μου + unlike: Δεν μου αρέσει +showOnRemote: Δείτε στο απομακρυσμένο instance +perDay: Ανά Ημέρα +software: Λογισμικό +cpuAndMemory: CPU και Μνήμη +noUsers: Δεν υπάρχουν μέλη +processing: Επεξεργασία +changePassword: Αλλαγή κωδικού +security: Ασφάλεια +featured: Προτεινόμενα +keepOriginalUploading: Διατήρηση πρωτότυπης εικόνας +manageGroups: Διαχείριση ομάδων +deleteFolder: Διαγραφή φακέλου +nsfw: Ευαίσθητο περιεχόμενο (NSFW) +nUsersMentioned: Έχει αναφερθεί από {n} μέλη +notFound: Δεν βρέθηκε +markAsReadAllUnreadNotes: Σημειώστε όλες τις δημοσιεύσεις ως διαβασμένες +invites: Προσκλήσεις +quoteQuestion: Να προστεθεί ως Παράθεση; +noMessagesYet: Δεν υπάρχουν μηνύματα ακόμη +onlyOneFileCanBeAttached: Μπορείτε να επισυνάψετε μόνο ένα αρχείο σε ένα μήνυμα +tooLong: Υπερβολικά μακροσκελές +or: Ή +language: Γλώσσα +groupInvited: Προσκληθήκατε σε μία ομάδα +ascendingOrder: Αύξουσα +visibility: Ορατότητα +invisibleNote: Αόρατη δημοσίευση +enableInfiniteScroll: Αυτόματη φόρτωση περισσοτέρων +poll: Ψηφοφορία +enablePlayer: Άνοιγμα προβολής βίντεο +large: Μεγάλο +medium: Μεσαίο +small: Μικρό +postToGallery: Δημιουργία νέας δημοσίευσης γκαλερί +reloadConfirm: Θα θέλατε να ανανεώσετε το χρονολόγιο; +enableAll: Ενεργοποίηση όλων +permission: Εξουσιοδοτήσεις +sample: Δείγμα +copy: Αντιγραφή +display: Προβολή +send: Αποστολή +behavior: Συμπεριφορά +useGlobalSetting: Χρήση παγκόσμιων ρυθμίσεων +abuseMarkAsResolved: Επισήμανση της αναφοράς ως επιλυμένης +openInNewTab: Άνοιγμα σε νέα καρτέλα +_sensitiveMediaDetection: + setSensitiveFlagAutomatically: Επισήμανση ως ευαίσθητο περιεχόμενο (NSFW) +defaultNavigationBehaviour: Προεπιλεγμένη συμπεριφορά περιήγησης +system: Σύστημα +createNew: Δημιουργία νέου +createNewClip: Δημιουργία νέου κλιπ +repliesCount: Αριθμός απεσταλμένων απαντήσεων +optional: Προαιρετικό +renotesCount: Αριθμός προωθήσεων σε δημοσιεύσεις άλλων +addItem: Προσθήκη αντικειμένου +disablePlayer: Κλείσιμο προβολής βίντεο +describeFile: Προσθήκη περιγραφής +enterFileDescription: Πληκτρολογήστε περιγραφή +author: Συντάκτης/τρια +setMultipleBySeparatingWithSpace: Διαχωρίστε πολλαπλές καταχωρήσεις με κενά. +random: Τυχαίο +accountInfo: Πληροφορίες Λογαριασμού +notesCount: Αριθμός δημοσιεύσεων +repliedCount: Αριθμός απαντήσεων που ελήφθησαν +flagAsCatDescription: Θα έχεις γατοαυτιά και θα μιλάς σαν γατί! +muteAndBlock: Σιγάσεις και Μπλοκαρίσματα +mutedUsers: Σιγασμένα μέλη +editProfile: Επεξεργασία προφίλ +pinLimitExceeded: Δεν μπορείτε να καρφιτσώσετε άλλες δημοσιεύσεις +currentPassword: Τρέχων κωδικός +newPassword: Νέος κωδικός +newPasswordRetype: Ξαναπληκτρολογήστε τον νέο κωδικό +notesAndReplies: Δημοσιεύσεις και απαντήσεις +popularUsers: Δημοφιλή μέλη +share: Κοινοποίηση +retype: Πληκτρολογήστε ξανά +invitations: Προσκλήσεις +available: Διαθέσιμο +unavailable: Μη διαθέσιμο +youHaveNoGroups: Δεν έχετε ομάδες +doing: Επεξεργασία... +yourAccountSuspendedTitle: Αυτός ο λογαριασμός έχει αποβληθεί +leaveConfirm: Υπάρχουν αλλαγές που δεν έχουν σωθεί. Θέλετε να τις απορρίψετε; +height: Ύψος +edit: Επεξεργασία +headlineMisskey: Μία ανοιχτού λογισμικού, αποκεντρωμένη πλατφόρμα κοινωνικής δικτύωσης + που θα είναι για πάντα ελεύθερη! 🚀 +introMisskey: Καλώς ήρθατε! Το Firefish είναι μία ανοιχτού λογισμικού, αποκεντρωμένη + πλατφόρμα κοινωνικής δικτύωσης που θα είναι για πάντα ελεύθερη! 🚀 +markAsSensitive: Επισήμανση ως Ευαίσθητο Περιεχόμενο (NSFW) +autoAcceptFollowed: Αυτόματη έγκριση αιτημάτων ακολούθησης από λογαριασμούς που ακολουθείτε +loginFailed: Αποτυχία σύνδεσης +accountMoved: 'Έχει μεταφερθεί σε νέο λογαριασμό:' +perHour: Ανά Ώρα +remoteUserCaution: Οι πληροφορίες από απομακρυσμένους λογαριασμούς μπορεί να είναι + ατελείς. +folderName: Όνομα φακέλου +renameFolder: Μετονομασία φακέλου +recentUsed: Χρησιμοποιήθηκαν πρόσφατα +deleteAllFilesConfirm: Σίγουρα θέλετε να διαγράψετε όλα τα αρχεία; +removeAllFollowing: Διακοπή ακολούθησης όλων των ακολουθούμενων μελών +userSilenced: Αυτό το μέλος είναι υπό σιώπηση. +makeActive: Ενεργοποίηση +create: Δημιουργία +reportAbuseOf: Αναφορά {name} +cacheRemoteFilesDescription: Όταν αυτή η ρύθμιση είναι απενεργοποιημένη, τα απομακρυσμένα + αρχεία φορτώνονται απευθείας από το απομακρυσμένο instance. Η απενεργοποίηση θα + μειώσει τη χρήση του δίσκου σας, αλλά θα αυξήσει την κίνηση δεδομένων, καθώς δεν + θα δημιουργούνται σμικρύνσεις αρχείων (thumbnails). +registeredAt: Εγγράφηκε στις +latestStatus: Τελευταία κατάσταση +charts: Πίνακες +stopActivityDelivery: Σταμάτα να στέλνεις δραστηριότητες +operations: Λειτουργίες +monitor: Παρακολούθηση +jobQueue: Ουρά εργασιών +blockedInstances: Μπλοκαρισμένα Instances +blockedInstancesDescription: Παραθέστε τις διευθύνσεις (hostnames) των instances που + θέλετε να μπλοκάρετε. Τα παρακάτω instances δεν θα μπορούν πλέον να επικοινωνούν + με αυτό το instance. +intro: Η εγκατάσταση του Firefish τελείωσε! Παρακαλώ δημιουργήστε ένα μέλος διαχειριστή/στρια. +noThankYou: Όχι, ευχαριστώ +addInstance: Προσθήκη instance +renoteMute: Σίγαση προωθήσεων +emojiUrl: Διεύθυνση emoji (URL) +cacheRemoteFiles: Προσωρινή αποθήκευση απομακρυσμένων αρχείων +flagSpeakAsCat: Να μιλάς σαν γατί +flagSpeakAsCatDescription: Οι δημοσιεύσεις σου θα nyaοποιούνται όταν είσαι γατί +selectInstance: Επιλέξτε ένα instance +latestRequestSentAt: Τελευταίο αίτημα στάλθηκε +hiddenTags: Κρυμμένες Ετικέτες (Hashtags) +noInstances: Δεν υπάρχουν instances +renoteUnmute: Διακοπή σίγασης προωθήσεων +flagAsBotDescription: Ενεργοποιήστε αυτή την επιλογή αν αυτός ο λογαριασμός ελέγχεται + από ένα πρόγραμμα. Αν ενεργοποιηθεί, θα λειτουργεί σαν σημάδι για τους προγραμματιστές, + ώστε να αποφύγουν ατέρμονη αλληλεπίδραση με άλλα bots και για να ρυθμίσει τα εσωτερικά + συστήματα του Firefish ώστε να αντιμετωπίζουν αυτόν τον λογαριασμό ως bot. +flagShowTimelineRepliesDescription: Εμφάνιση απαντήσεων μελών σε δημοσιεύσεις άλλων + μελών στο χρονολόγιο. +latestRequestReceivedAt: Τελευταίο αίτημα ελήφθη +blockThisInstance: Μπλοκάρισμα αυτού του instance +clearQueueConfirmText: Τυχόν δημοσιεύσεις στην ουρά που δεν έχουν αποσταλεί δεν θα + ομοσπονδοποιηθούν. Συνήθως αυτή η λειτουργία δεν χρειάζεται. +clearCachedFilesConfirm: Σίγουρα θέλετε να διαγράψετε όλα τα προσωρινά αποθηκευμένα + απομακρυσμένα αρχεία; +default: Προεπιλεγμένο +defaultValueIs: 'Προεπιλεγμένο: {value}' +noJobs: Δεν υπάρχουν εργασίες (jobs) +federating: Ομοσπονδοποιείται +blocked: Μπλοκαρισμένο +suspended: Σε αποβολή +instanceFollowing: Ακολουθεί στο instance +instanceFollowers: Ακόλουθοι του instance +instanceUsers: Μέλη αυτού του instance +retypedNotMatch: Οι καταχωρήσεις δεν ταιριάζουν. +usernameOrUserId: Όνομα μέλους ή ταυτότητα μέλους (id) +removeAreYouSure: Θέλετε σίγουρα να αφαιρέσετε το "{x}"; +deleteAreYouSure: Θέλετε σίγουρα να διαγράψετε το "{x}"; +resetAreYouSure: Σίγουρα επανεκκίνηση; +uploadFromUrlMayTakeTime: Ίσως πάρει λίγο χρόνο μέχρι το ανέβασμα να ολοκληρωθεί. +noMoreHistory: Δεν υπάρχει περαιτέρω ιστορικό +agreeTo: Συμφωνώ στο {0} +yearsOld: '{age} ετών' +themeForDarkMode: Θέμα για τη Σκοτεινή Λειτουργία +syncDeviceDarkMode: Συγχρονισμός της Σκοτεινής Λειτουργίας με τις ρυθμίσεις της συσκευής + σας +inputNewDescription: Προσθέστε νέα περιγραφή +whenServerDisconnected: Όταν χάνεται η σύνδεση στον σέρβερ +disconnectedFromServer: Η σύνδεση στον σέρβερ έχει χαθεί +instanceDescription: Περιγραφή instance +maintainerEmail: Διεύθυνση email προγραμματιστή/στριας +yearX: '{year}' +enableGlobalTimeline: Ενεργοποίηση παγκόσμιου χρονολογίου +enableLocalTimeline: Ενεργοποίηση τοπικού χρονολογίου +enableRegistration: Ενεργοποίηση εγγραφής νέων μελών +invite: Πρόσκληση +disablingTimelinesInfo: Οι Διαχειρίστριες-ες και οι Συντονιστές-στριες θα έχουν πάντα + πρόσβαση σε όλα τα χρονολόγια, ακόμα κι αν δεν είναι ενεργοποιημένα. +inMb: Σε megabytes +iconUrl: Διεύθυνση URL εικονιδίου +bannerUrl: Διεύθυνση URL εικόνας Εξώφυλλου +pinnedUsers: Καρφιτσωμένα μέλη +hcaptchaSiteKey: Κλειδί του site +recaptcha: Προστασία reCAPTCHA +enableServiceworker: Ενεργοποίηση Ειδοποιήσεων Push για τον browser σας +recentlyDiscoveredUsers: Μέλη που ανακαλύφθηκαν πρόσφατα +twoStepAuthentication: Επαλήθευση δύο παραγόντων +securityKey: Κλειδί ασφάλειας +registerSecurityKey: Καταχωρήστε ένα κλειδί ασφάλειας +resetPassword: Επαναφορά κωδικού +newPasswordIs: Ο νέος κωδικός είναι "{password}" +uploadFolder: Προεπιλεγμένος φάκελος για ανέβασμα αρχείων +joinedGroups: Οι ομάδες που είστε μέλος +checking: Έλεγχος... +invitationCode: Κωδικός πρόσκλησης +normalPassword: Μέτριος κωδικός +weakPassword: Αδύναμος κωδικός +strongPassword: Δυνατός κωδικός +signinWith: Συνδεθείτε με {x} +tapSecurityKey: Βάλτε το κλειδί ασφάλειας +signinFailed: Αδυναμία σύνδεσης. Το όνομα μέλους ή ο κωδικός είναι λάθος. +aboutX: Σχετικά με {x} +useOsNativeEmojis: Χρήση των Emoji του λειτουργικού συστήματος +uiLanguage: Γλώσσα διεπαφής +disableDrawer: Να μη χρησιμοποιούνται μενού σε στιλ συρταριού +noHistory: Δεν υπάρχει διαθέσιμο ιστορικό +joinOrCreateGroup: Λάβετε πρόσκληση για μία ομάδα ή δημιουργήστε τη δική σας. +docSource: Πηγή αυτού του εγγράφου +regenerate: Επαναδημιουργία +fontSize: Μέγεθος γραμματοσειράς +noFollowRequests: Δεν έχετε αιτήματα ακολούθησης σε αναμονή +dashboard: Ταμπλό +clientSettings: Ρυθμίσεις διεπαφής +numberOfDays: Αριθμός ημερών +hideThisNote: Απόκρυψη αυτής της δημοσίευσης +showFeaturedNotesInTimeline: Εμφάνιση προτεινόμενων δημοσιεύσεων στα χρονολόγια +objectStorage: Αποθήκευση Object Storage +useObjectStorage: Χρήση object storage +objectStorageBucket: '' +showFixedPostForm: Εμφάνιση της φόρμας δημοσίευσης στο πάνω μέρος των χρονολογίων +none: Κανένα +unableToProcess: Η επιχείρηση ήταν αδύνατο να ολοκληρωθεί +installedApps: Εφαρμογές με εξουσιοδότηση +state: Κατάσταση +installedDate: Εξουσιοδοτήθηκε στις +lastUsedDate: Χρησιμοποιήθηκε τελευταία φορά στις +scratchpadDescription: Το σημειωματάριο παρέχει ένα περιβάλλον για πειραματισμό με + AiScript. Σε αυτό μπορείτε να γράψετε, να εκτελέσετε, και να δοκιμάσετε τα αποτελέσματα + της αλληλεπίδρασης του AiScript με το Firefish. +scratchpad: Σημειωματάριο +output: Αποτέλεσμα +updateRemoteUser: Ανανέωση πληροφοριών απομακρυσμένου μέλους +disablePagesScript: Απενεργοποίηση του AiScript στις Σελίδες +removeAllFollowingDescription: Η εκτέλεση θα διακόψη την ακολούθηση όλων των μελών + από {host}. Παρακαλούμε εκτελέστε το αν το instance π.χ. δεν υπάρχει πια. +caption: Αυτόματη Περιγραφή +all: Όλα +subscribing: Εγγραφή σε συνδρομή +publishing: Δημοσιεύεται +notResponding: Δεν αποκρίνεται +keepOriginalUploadingDescription: Αποθηκεύει το πρωτότυπο αρχείο όπως είναι. Αν απενεργοποιηθεί, + μία έκδοση για προβολή στο ίντερνετ θα δημιουργηθεί κατά το ανέβασμα. +lookup: Αναζήτηση +lightThemes: Φωτεινά θέματα +darkThemes: Σκοτεινά θέματα +inputNewFolderName: Πληκτρολογήστε ένα νέο όνομα φακέλου +hasChildFilesOrFolders: Εφόσον αυτός ο φάκελος δεν είναι άδειος, δεν μπορεί να διαγραφεί. +integration: Ενσωματώσεις +enableRecommendedTimeline: Ενεργοποίηση χρονολογίου προτεινόμενων +driveCapacityPerLocalAccount: Μέγεθος Αποθηκευτικού Χώρου ανά τοπικό μέλος +driveCapacityPerRemoteAccount: Μέγεθος Αποθηκευτικού Χώρου ανά απομακρυσμένο μέλος +basicInfo: Βασικές πληροφορίες +pinnedClipId: Ταυτότητα (id) του κλιπ για καρφίτσωμα +hcaptcha: Προστασία hCaptcha +enableHcaptcha: Ενεργοποίηση hCaptcha +hcaptchaSecretKey: Μυστικό κλειδί +enableRecaptcha: Ενεργοποίηση reCAPTCHA +recaptchaSiteKey: Κλειδί του site +recaptchaSecretKey: Μυστικό κλειδί +antennaKeywordsDescription: Διαχωρίστε με κενά για συνθήκη ΚΑΙ ή με αλλαγή γραμμής + για συνθήκη Ή. +antennaUsersDescription: Παραθέστε ένα όνομα μέλους ανά γραμμή +antennaInstancesDescription: Παραθέστε ένα instance host ανά γραμμή +withReplies: Να περιλαμβάνονται οι απαντήσεις +withFiles: Να περιλαμβάνουν αρχεία +silence: Σιώπηση +silenceConfirm: Θέλετε σίγουρα να σιωπήσετε αυτό το μέλος; +unsilenceConfirm: Σίγουρα θέλετε να αναιρέσετε τη σιώπηση αυτού του μέλους; +securityKeyName: Όνομα κλειδιού +lastUsed: Τελευταία χρήση +unregister: Απεγγραφή +notFoundDescription: Δεν ήταν δυνατό να βρεθεί σελίδα που να ανταποκρίνεται σε αυτή + τη διεύθυνση URL. +signinHistory: Ιστορικό συνδέσεων +disableAnimatedMfm: Απενεργοποίηση του MFM με κίνηση +dayOverDayChanges: Αλλαγές την τελευταία ημέρα +promotion: Προμοταρισμένα +promote: Προμοτάρισμα +squareAvatars: Εμφάνιση τετραγωνισμένων άβαταρ +aboutFirefish: Σχετικά με το Firefish +maintainerName: Προγραμματιστής/στρια +uploadFromUrlRequested: Το ανέβασμα ζητήθηκε +themeForLightMode: Θέμα για τη Φωτεινή Λειτουργία +circularReferenceFolder: Ο φάκελος του προορισμού είναι υποφάκελος του φακέλου που + θέλετε να μετακινήσετε. +backgroundImageUrl: Διεύθυνση URL εικόνας φόντου +pinnedUsersDescription: Παραθέστε τα ονόματα μελών που θα είναι καρφιτσωμένα στην + καρτέλα "Εξερεύνηση" χωρίζοντάς τα με αλλαγή γραμμής. +openImageInNewTab: Άνοιγμα εικόνων σε νέα καρτέλα +weekOverWeekChanges: Αλλαγές την τελευταία εβδομάδα +exploreFediverse: Εξερευνήστε το Fediverse +unsilence: Αναίρεση σιώπησης +administrator: Διαχειριστής/στρια +passwordLessLogin: Σύνδεση χωρίς κωδικό +reduceUiAnimation: Ελάττωση των κινούμενων εικόνων +serviceworkerInfo: Πρέπει να είναι ενεργοποιημένο για ειδοποιήσεις push. +expandTweet: Διεύρυνση τουιτ +themeEditor: Επεξεργασία θεμάτων +deck: Ντεκ +undeck: Έξοδος από το Ντεκ +useFullReactionPicker: Χρήση επιλογέα αντιδράσεων πλήρους μεγέθους +tokenRequested: Παροχή πρόσβασης στον λογαριασμό +emailServer: Σέρβερ email +enableEmail: Ενεργοποίηση του email distribution +emailAddress: Διεύθυνση email +emailConfigInfo: Χρησιμοποιείται για επιβεβαίωση του email σας κατά την εγγραφή ή + αν ξεχάσετε τον κωδικό σας +regenerateLoginToken: Επαναδημιουργία token σύνδεσης +fileIdOrUrl: Ταυτότητα αρχείου (ID) ή διεύθυνση URL +typingUsers: '{users} πληκτρολογεί' +yourAccountSuspendedDescription: Αυτός ο λογαριασμός έχει αποβληθεί λόγω μη συμμόρφωσης + με τους κανόνες του σέρβερ ή κάτι παρόμοιο. Επικοινωνήστε με τον διαχειριστή/στρια + αν θα θέλατε έναν πιο λεπτομερή λόγο. Παρακαλούμε μη δημιουργήσετε νέο λογαριασμό. +inboxUrl: Διεύθυνση URL των Εισερχομένων +generateAccessToken: Δημιουργία token πρόσβασης +emptyToDisableSmtpAuth: Αφήστε το όνομα μέλους και τον κωδικό άδεια για να απενεργοποιήσετε + την επαλήθευση SMTP +instanceMute: Σιγάσεις instance +userSaysSomethingReason: '{name} είπε {reason}' +logs: Αρχεία καταγραφής +abuseReported: Η αναφορά σας στάλθηκε. Ευχαριστούμε πολύ. +reporter: Έκανε την αναφορά +reporteeOrigin: Καταγωγή αναφερόμενου λογαριασμού +reporterOrigin: Καταγωγή λογαριασμού που έκανε την αναφορά +forwardReport: Προώθηση της αναφοράς στο απομακρυσμένο instance +openInSideView: Άνοιγμα σε προβολή παράθεσης +delayed: Με καθυστέρηση +useGlobalSettingDesc: Αν ενεργοποιηθεί, οι ρυθμίσεις ειδοποιήσεων του λογαριασμού + σας θα χρησιμοποιηθούν. Αν απενεργοποιηθεί, μπορούν να γίνουν ανεξάρτητες ρυθμίσεις. +fillAbuseReportDescription: Παρακαλούμε συμπληρώστε λεπτομέρειες σχετικά με αυτή την + αναφορά. Αν πρόκειται για συγκεκριμένη δημοσίευση, παρακαλούμε συμπεριλάβετε τη + διεύθυνση URL της δημοσίευσης. +forwardReportIsAnonymous: Αντί για τον λογαριασμό σας, μία ανώνυμη αναφορά από λογαριασμό + του συστήματος θα εμφανιστεί στο απομακρυσμένο instance. diff --git a/locales/en-US.yml b/locales/en-US.yml index 7a2bb70fe1..af85419471 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -1,9 +1,11 @@ ---- _lang_: "English" -headlineMisskey: "An open source, decentralized social media platform that's free forever! 🚀" -introMisskey: "Welcome! Calckey is an open source, decentralized social media platform that's free forever! 🚀" +headlineMisskey: "An open source, decentralized social media platform that's free + forever! 🚀" +introMisskey: "Welcome! Firefish is an open source, decentralized social media platform + that's free forever! 🚀" monthAndDay: "{month}/{day}" search: "Search" +searchPlaceholder: "Search Firefish" notifications: "Notifications" username: "Username" password: "Password" @@ -12,11 +14,12 @@ fetchingAsApObject: "Fetching from the Fediverse" ok: "OK" gotIt: "Got it!" cancel: "Cancel" +noThankYou: "No thank you" enterUsername: "Enter username" renotedBy: "Boosted by {user}" noNotes: "No posts" noNotifications: "No notifications" -instance: "Instance" +instance: "Server" settings: "Settings" basicSettings: "Basic Settings" otherSettings: "Other Settings" @@ -32,6 +35,7 @@ uploading: "Uploading..." save: "Save" users: "Users" addUser: "Add a user" +addInstance: "Add a server" favorite: "Add to bookmarks" favorites: "Bookmarks" unfavorite: "Remove from bookmarks" @@ -43,15 +47,22 @@ unpin: "Unpin from profile" copyContent: "Copy contents" copyLink: "Copy link" delete: "Delete" +deleted: "Deleted" deleteAndEdit: "Delete and edit" -deleteAndEditConfirm: "Are you sure you want to delete this post and edit it? You will lose all reactions, boosts and replies to it." +deleteAndEditConfirm: "Are you sure you want to delete this post and edit it? You + will lose all reactions, boosts and replies to it." +editNote: "Edit note" +edited: "Edited at {date} {time}" addToList: "Add to list" sendMessage: "Send a message" copyUsername: "Copy username" searchUser: "Search for a user" reply: "Reply" +jumpToPrevious: "Jump to previous" loadMore: "Load more" showMore: "Show more" +newer: "newer" +older: "older" showLess: "Close" youGotNewFollower: "followed you" receiveFollowRequest: "Follow request received" @@ -59,16 +70,20 @@ followRequestAccepted: "Follow request accepted" mention: "Mention" mentions: "Mentions" directNotes: "Direct messages" +cw: "Content warning" importAndExport: "Import/Export Data" import: "Import" export: "Export" files: "Files" download: "Download" -driveFileDeleteConfirm: "Are you sure you want to delete the file \"{name}\"? Posts with this file attached will also be deleted." +driveFileDeleteConfirm: "Are you sure you want to delete the file \"{name}\"? It will + be removed from all posts that contain it as an attachment." unfollowConfirm: "Are you sure that you want to unfollow {name}?" -exportRequested: "You've requested an export. This may take a while. It will be added to your Drive once completed." +exportRequested: "You've requested an export. This may take a while. It will be added + to your Drive once completed." importRequested: "You've requested an import. This may take a while." lists: "Lists" +listsDesc: "Lists let you create timelines with specified users. They can be accessed from the timelines page." noLists: "You don't have any lists" note: "Post" notes: "Posts" @@ -81,7 +96,8 @@ error: "Error" somethingHappened: "An error has occurred" retry: "Retry" pageLoadError: "An error occurred loading the page." -pageLoadErrorDescription: "This is normally caused by network errors or the browser's cache. Try clearing the cache and then try again after waiting a little while." +pageLoadErrorDescription: "This is normally caused by network errors or the browser's + cache. Try clearing the cache and then try again after waiting a little while." serverIsDead: "This server is not responding. Please wait for a while and try again." youShouldUpgradeClient: "To view this page, please refresh to update your client." enterListName: "Enter a name for the list" @@ -89,7 +105,7 @@ privacy: "Privacy" makeFollowManuallyApprove: "Follow requests require approval" defaultNoteVisibility: "Default visibility" follow: "Follow" -followRequest: "Follow" +followRequest: "Follow Request" followRequests: "Follow requests" unfollow: "Unfollow" followRequestPending: "Follow request pending" @@ -107,6 +123,9 @@ clickToShow: "Click to show" sensitive: "NSFW" add: "Add" reaction: "Reactions" +removeReaction: "Remove your reaction" +enableEmojiReactions: "Enable emoji reactions" +showEmojisInReactionNotifications: "Show emojis in reaction notifications" reactionSetting: "Reactions to show in the reaction picker" reactionSettingDescription2: "Drag to reorder, click to delete, press \"+\" to add." rememberNoteVisibility: "Remember post visibility settings" @@ -116,6 +135,8 @@ unmarkAsSensitive: "Unmark as NSFW" enterFileName: "Enter filename" mute: "Mute" unmute: "Unmute" +renoteMute: "Mute boosts" +renoteUnmute: "Unmute boosts" block: "Block" unblock: "Unblock" suspend: "Suspend" @@ -127,6 +148,7 @@ unsuspendConfirm: "Are you sure that you want to unsuspend this account?" selectList: "Select a list" selectAntenna: "Select an antenna" selectWidget: "Select a widget" +selectChannel: "Select a channel" editWidgets: "Edit widgets" editWidgetsExit: "Done" customEmojis: "Custom Emoji" @@ -137,17 +159,25 @@ emojiUrl: "Emoji URL" addEmoji: "Add" settingGuide: "Recommended settings" cacheRemoteFiles: "Cache remote files" -cacheRemoteFilesDescription: "When this setting is disabled, remote files are loaded directly from the remote instance. Disabling this will decrease storage usage, but increase traffic, as thumbnails will not be generated." +cacheRemoteFilesDescription: "When this setting is disabled, remote files are loaded + directly from the remote server. Disabling this will decrease storage usage, but + increase traffic, as thumbnails will not be generated." flagAsBot: "Mark this account as a bot" -flagAsBotDescription: "Enable this option if this account is controlled by a program. If enabled, it will act as a flag for other developers to prevent endless interaction chains with other bots and adjust Calckey's internal systems to treat this account as a bot." +flagAsBotDescription: "Enable this option if this account is controlled by a program. + If enabled, it will act as a flag for other developers to prevent endless interaction + chains with other bots and adjust Firefish's internal systems to treat this account + as a bot." flagAsCat: "Are you a cat? 😺" flagAsCatDescription: "You'll get cat ears and speak like a cat!" +flagSpeakAsCat: "Speak as a cat" +flagSpeakAsCatDescription: "Your posts will get nyanified when in cat mode" flagShowTimelineReplies: "Show replies in timeline" -flagShowTimelineRepliesDescription: "Shows replies of users to posts of other users in the timeline if turned on." +flagShowTimelineRepliesDescription: "Shows replies of users to posts of other users + in the timeline if turned on." autoAcceptFollowed: "Automatically approve follow requests from users you're following" addAccount: "Add account" loginFailed: "Failed to sign in" -showOnRemote: "View on remote instance" +showOnRemote: "Open original page" general: "General" accountMoved: "User has moved to a new account:" wallpaper: "Wallpaper" @@ -156,14 +186,18 @@ removeWallpaper: "Remove wallpaper" searchWith: "Search: {q}" youHaveNoLists: "You don't have any lists" followConfirm: "Are you sure that you want to follow {name}?" -proxyAccount: "Proxy account" -proxyAccountDescription: "A proxy account is an account that acts as a remote follower for users under certain conditions. For example, when a user adds a remote user to the list, the remote user's activity will not be delivered to the instance if no local user is following that user, so the proxy account will follow instead." +proxyAccount: "Proxy Account" +proxyAccountDescription: "A proxy account is an account that acts as a remote follower + for users under certain conditions. For example, when a user adds a remote user + to the list, the remote user's activity will not be delivered to the server if no + local user is following that user, so the proxy account will follow instead." host: "Host" selectUser: "Select a user" +selectInstance: "Select an server" recipient: "Recipient(s)" annotation: "Comments" federation: "Federation" -instances: "Instances" +instances: "Servers" registeredAt: "Registered at" latestRequestSentAt: "Last request sent" latestRequestReceivedAt: "Last request received" @@ -173,36 +207,48 @@ charts: "Charts" perHour: "Per Hour" perDay: "Per Day" stopActivityDelivery: "Stop sending activities" -blockThisInstance: "Block this instance" +blockThisInstance: "Block this server" +silenceThisInstance: "Silence this server" operations: "Operations" software: "Software" version: "Version" metadata: "Metadata" -withNFiles: "{n} file(s)" monitor: "Monitor" jobQueue: "Job Queue" cpuAndMemory: "CPU and Memory" network: "Network" disk: "Disk" -instanceInfo: "Instance Information" +instanceInfo: "Server Information" statistics: "Statistics" clearQueue: "Clear queue" clearQueueConfirmTitle: "Are you sure that you want to clear the queue?" -clearQueueConfirmText: "Any undelivered posts remaining in the queue will not be federated. Usually this operation is not needed." +clearQueueConfirmText: "Any undelivered posts remaining in the queue will not be federated. + Usually this operation is not needed." clearCachedFiles: "Clear cache" clearCachedFilesConfirm: "Are you sure that you want to delete all cached remote files?" -blockedInstances: "Blocked Instances" -blockedInstancesDescription: "List the hostnames of the instances that you want to block. Listed instances will no longer be able to communicate with this instance." +blockedInstances: "Blocked Servers" +blockedInstancesDescription: "List the hostnames of the servers that you want to block. + Listed servers will no longer be able to communicate with this servers." +silencedInstances: "Silenced Servers" +silencedInstancesDescription: "List the hostnames of the servers that you want to + silence. Accounts in the listed servers are treated as \"Silenced\", can only make + follow requests, and cannot mention local accounts if not followed. This will not + affect the blocked servers." +hiddenTags: "Hidden Hashtags" +hiddenTagsDescription: "List the hashtags (without the #) of the hashtags you wish + to hide from trending and explore. Hidden hashtags are still discoverable via other + means." muteAndBlock: "Mutes and Blocks" mutedUsers: "Muted users" blockedUsers: "Blocked users" noUsers: "There are no users" +noInstances: "There are no servers" editProfile: "Edit profile" noteDeleteConfirm: "Are you sure you want to delete this post?" pinLimitExceeded: "You cannot pin any more posts" -intro: "Installation of Calckey has been finished! Please create an admin user." +intro: "Installation of Firefish has been finished! Please create an admin user." done: "Done" -processing: "Processing..." +processing: "Processing" preview: "Preview" default: "Default" defaultValueIs: "Default: {value}" @@ -210,14 +256,15 @@ noCustomEmojis: "There are no emoji" noJobs: "There are no jobs" federating: "Federating" blocked: "Blocked" +silenced: "Silenced" suspended: "Suspended" all: "All" subscribing: "Subscribing" publishing: "Publishing" notResponding: "Not responding" -instanceFollowing: "Following on instance" -instanceFollowers: "Followers of instance" -instanceUsers: "Users of this instance" +instanceFollowing: "Following on server" +instanceFollowers: "Followers of server" +instanceUsers: "Users of this server" changePassword: "Change password" security: "Security" retypedNotMatch: "The inputs do not match." @@ -241,7 +288,8 @@ saved: "Saved" messaging: "Chat" upload: "Upload" keepOriginalUploading: "Keep original image" -keepOriginalUploadingDescription: "Saves the originally uploaded image as-is. If turned off, a version to display on the web will be generated on upload." +keepOriginalUploadingDescription: "Saves the originally uploaded image as-is. If turned + off, a version to display on the web will be generated on upload." fromDrive: "From Drive" fromUrl: "From URL" uploadFromUrl: "Upload from a URL" @@ -291,7 +339,8 @@ unableToDelete: "Unable to delete" inputNewFileName: "Enter a new filename" inputNewDescription: "Enter new caption" inputNewFolderName: "Enter a new folder name" -circularReferenceFolder: "The destination folder is a subfolder of the folder you wish to move." +circularReferenceFolder: "The destination folder is a subfolder of the folder you + wish to move." hasChildFilesOrFolders: "Since this folder is not empty, it can not be deleted." copyUrl: "Copy URL" rename: "Rename" @@ -308,8 +357,8 @@ unwatch: "Stop watching" accept: "Accept" reject: "Reject" normal: "Normal" -instanceName: "Instance name" -instanceDescription: "Instance description" +instanceName: "Server name" +instanceDescription: "Server description" maintainerName: "Maintainer" maintainerEmail: "Maintainer email" tosUrl: "Terms of Service URL" @@ -320,13 +369,14 @@ dayX: "{day}" monthX: "{month}" yearX: "{year}" pages: "Pages" -integration: "Integration" +integration: "Integrations" connectService: "Connect" disconnectService: "Disconnect" enableLocalTimeline: "Enable local timeline" enableGlobalTimeline: "Enable global timeline" enableRecommendedTimeline: "Enable recommended timeline" -disablingTimelinesInfo: "Adminstrators and Moderators will always have access to all timelines, even if they are not enabled." +disablingTimelinesInfo: "Adminstrators and Moderators will always have access to all + timelines, even if they are not enabled." registration: "Register" enableRegistration: "Enable new user registration" invite: "Invite" @@ -338,9 +388,11 @@ bannerUrl: "Banner image URL" backgroundImageUrl: "Background image URL" basicInfo: "Basic info" pinnedUsers: "Pinned users" -pinnedUsersDescription: "List usernames separated by line breaks to be pinned in the \"Explore\" tab." +pinnedUsersDescription: "List usernames separated by line breaks to be pinned in the + \"Explore\" tab." pinnedPages: "Pinned Pages" -pinnedPagesDescription: "Enter the paths of the Pages you want to pin to the top page of this instance, separated by line breaks." +pinnedPagesDescription: "Enter the paths of the Pages you want to pin to the top page + of this server, separated by line breaks." pinnedClipId: "ID of the clip to pin" pinnedNotes: "Pinned posts" hcaptcha: "hCaptcha" @@ -351,18 +403,23 @@ recaptcha: "reCAPTCHA" enableRecaptcha: "Enable reCAPTCHA" recaptchaSiteKey: "Site key" recaptchaSecretKey: "Secret key" -avoidMultiCaptchaConfirm: "Using multiple Captcha systems may cause interference between them. Would you like to disable the other Captcha systems currently active? If you would like them to stay enabled, press cancel." +avoidMultiCaptchaConfirm: "Using multiple Captcha systems may cause interference between + them. Would you like to disable the other Captcha systems currently active? If you + would like them to stay enabled, press cancel." antennas: "Antennas" +antennasDesc: "Antennas display new posts matching the criteria you set!\n They can be accessed from the timelines page." manageAntennas: "Manage Antennas" name: "Name" antennaSource: "Antenna source" antennaKeywords: "Keywords to listen to" antennaExcludeKeywords: "Keywords to exclude" -antennaKeywordsDescription: "Separate with spaces for an AND condition or with line breaks for an OR condition." +antennaKeywordsDescription: "Separate with spaces for an AND condition or with line + breaks for an OR condition." notifyAntenna: "Notify about new posts" withFileAntenna: "Only posts with files" enableServiceworker: "Enable Push-Notifications for your Browser" antennaUsersDescription: "List one username per line" +antennaInstancesDescription: "List one server host per line" caseSensitive: "Case sensitive" withReplies: "Include replies" connectedTo: "Following account(s) are connected" @@ -381,7 +438,7 @@ exploreFediverse: "Explore the Fediverse" popularTags: "Popular tags" userList: "Lists" about: "About" -aboutMisskey: "About Calckey" +aboutFirefish: "About Firefish" administrator: "Administrator" token: "Token" twoStepAuthentication: "Two-factor authentication" @@ -477,6 +534,7 @@ total: "Total" weekOverWeekChanges: "Changes to last week" dayOverDayChanges: "Changes to yesterday" appearance: "Appearance" +accessibility: "Accessibility" clientSettings: "Client Settings" accountSettings: "Account Settings" promotion: "Promoted" @@ -487,19 +545,26 @@ showFeaturedNotesInTimeline: "Show featured posts in timelines" objectStorage: "Object Storage" useObjectStorage: "Use object storage" objectStorageBaseUrl: "Base URL" -objectStorageBaseUrlDesc: "The URL used as reference. Specify the URL of your CDN or Proxy if you are using either.\nFor S3 use 'https://.s3.amazonaws.com' and for GCS or equivalent services use 'https://storage.googleapis.com/', etc." +objectStorageBaseUrlDesc: "The URL used as reference. Specify the URL of your CDN + or Proxy if you are using either.\nFor S3 use 'https://.s3.amazonaws.com' + and for GCS or equivalent services use 'https://storage.googleapis.com/', + etc." objectStorageBucket: "Bucket" objectStorageBucketDesc: "Please specify the bucket name used at your provider." objectStoragePrefix: "Prefix" objectStoragePrefixDesc: "Files will be stored under directories with this prefix." objectStorageEndpoint: "Endpoint" -objectStorageEndpointDesc: "Leave this empty if you are using AWS S3, otherwise specify the endpoint as '' or ':', depending on the service you are using." +objectStorageEndpointDesc: "Leave this empty if you are using AWS S3, otherwise specify + the endpoint as '' or ':', depending on the service you are using." objectStorageRegion: "Region" -objectStorageRegionDesc: "Specify a region like 'xx-east-1'. If your service does not distinguish between regions, leave this blank or enter 'us-east-1'." +objectStorageRegionDesc: "Specify a region like 'xx-east-1'. If your service does + not distinguish between regions, leave this blank or enter 'us-east-1'." objectStorageUseSSL: "Use SSL" -objectStorageUseSSLDesc: "Turn this off if you are not going to use HTTPS for API connections" +objectStorageUseSSLDesc: "Turn this off if you are not going to use HTTPS for API + connections" objectStorageUseProxy: "Connect over Proxy" -objectStorageUseProxyDesc: "Turn this off if you are not going to use a Proxy for API connections" +objectStorageUseProxyDesc: "Turn this off if you are not going to use a Proxy for + API connections" objectStorageSetPublicRead: "Set \"public-read\" on upload" serverLogs: "Server logs" deleteAll: "Delete all" @@ -527,19 +592,26 @@ sort: "Sort" ascendingOrder: "Ascending" descendingOrder: "Descending" scratchpad: "Scratchpad" -scratchpadDescription: "The scratchpad provides an environment for AiScript experiments. You can write, execute, and check the results of it interacting with Calckey in it." +scratchpadDescription: "The scratchpad provides an environment for AiScript experiments. + You can write, execute, and check the results of it interacting with Firefish in + it." output: "Output" script: "Script" disablePagesScript: "Disable AiScript on Pages" +expandOnNoteClick: "Open post on click" +expandOnNoteClickDesc: "If disabled, you can still open posts in the right-click menu or by clicking the timestamp." updateRemoteUser: "Update remote user information" deleteAllFiles: "Delete all files" deleteAllFilesConfirm: "Are you sure that you want to delete all files?" removeAllFollowing: "Unfollow all followed users" -removeAllFollowingDescription: "Executing this unfollows all accounts from {host}. Please run this if the instance e.g. no longer exists." +removeAllFollowingDescription: "Executing this unfollows all accounts from {host}. + Please run this if the server e.g. no longer exists." userSuspended: "This user has been suspended." userSilenced: "This user is being silenced." yourAccountSuspendedTitle: "This account is suspended" -yourAccountSuspendedDescription: "This account has been suspended due to breaking the server's terms of services or similar. Contact the administrator if you would like to know a more detailed reason. Please do not create a new account." +yourAccountSuspendedDescription: "This account has been suspended due to breaking + the server's terms of services or similar. Contact the administrator if you would + like to know a more detailed reason. Please do not create a new account." menu: "Menu" divider: "Divider" addItem: "Add Item" @@ -572,6 +644,7 @@ useBlurEffectForModal: "Use blur effect for modals" useFullReactionPicker: "Use full-size reaction picker" width: "Width" height: "Height" +xl: "XL" large: "Big" medium: "Medium" small: "Small" @@ -580,12 +653,14 @@ permission: "Permissions" enableAll: "Enable all" disableAll: "Disable all" tokenRequested: "Grant access to account" -pluginTokenRequestedDescription: "This plugin will be able to use the permissions set here." +pluginTokenRequestedDescription: "This plugin will be able to use the permissions + set here." notificationType: "Notification type" edit: "Edit" emailServer: "Email server" enableEmail: "Enable email distribution" -emailConfigInfo: "Used to confirm your email during sign-up or if you forget your password" +emailConfigInfo: "Used to confirm your email during sign-up or if you forget your + password" email: "Email" emailAddress: "Email address" smtpConfig: "SMTP Server Configuration" @@ -599,9 +674,14 @@ smtpSecureInfo: "Turn this off when using STARTTLS" testEmail: "Test email delivery" wordMute: "Word mute" regexpError: "Regular Expression error" -regexpErrorDescription: "An error occurred in the regular expression on line {line} of your {tab} word mutes:" -instanceMute: "Instance Mutes" +regexpErrorDescription: "An error occurred in the regular expression on line {line} + of your {tab} word mutes:" +instanceMute: "Server Mutes" userSaysSomething: "{name} said something" +userSaysSomethingReason: "{name} said {reason}" +userSaysSomethingReasonReply: "{name} replied to a post containing {reason}" +userSaysSomethingReasonRenote: "{name} boosted a post containing {reason}" +userSaysSomethingReasonQuote: "{name} quoted a post containing {reason}" makeActive: "Activate" display: "Display" copy: "Copy" @@ -611,14 +691,18 @@ logs: "Logs" delayed: "Delayed" database: "Database" channel: "Channels" +channelFederationWarn: "Channels do not yet federate to other servers" create: "Create" notificationSetting: "Notification settings" notificationSettingDesc: "Select the types of notification to display." useGlobalSetting: "Use global settings" -useGlobalSettingDesc: "If turned on, your account's notification settings will be used. If turned off, individual configurations can be made." +useGlobalSettingDesc: "If turned on, your account's notification settings will be + used. If turned off, individual configurations can be made." other: "Other" regenerateLoginToken: "Regenerate login token" -regenerateLoginTokenDescription: "Regenerates the token used internally during login. Normally this action is not necessary. If regenerated, all devices will be logged out." +regenerateLoginTokenDescription: "Regenerates the token used internally during login. + Normally this action is not necessary. If regenerated, all devices will be logged + out." setMultipleBySeparatingWithSpace: "Separate multiple entries with spaces." fileIdOrUrl: "File ID or URL" behavior: "Behavior" @@ -626,20 +710,22 @@ sample: "Sample" abuseReports: "Reports" reportAbuse: "Report" reportAbuseOf: "Report {name}" -fillAbuseReportDescription: "Please fill in details regarding this report. If it is about a specific post, please include its URL." +fillAbuseReportDescription: "Please fill in details regarding this report. If it is + about a specific post, please include its URL." abuseReported: "Your report has been sent. Thank you very much." reporter: "Reporter" reporteeOrigin: "Reportee Origin" reporterOrigin: "Reporter Origin" -forwardReport: "Forward report to remote instance" -forwardReportIsAnonymous: "Instead of your account, an anonymous system account will be displayed as reporter at the remote instance." +forwardReport: "Forward report to remote server" +forwardReportIsAnonymous: "Instead of your account, an anonymous system account will + be displayed as reporter at the remote server." send: "Send" abuseMarkAsResolved: "Mark report as resolved" openInNewTab: "Open in new tab" openInSideView: "Open in side view" defaultNavigationBehaviour: "Default navigation behavior" editTheseSettingsMayBreakAccount: "Editing these settings may damage your account." -instanceTicker: "Instance information of posts" +instanceTicker: "Server information of posts" waitingFor: "Waiting for {x}" random: "Random" system: "System" @@ -650,9 +736,11 @@ createNew: "Create new" optional: "Optional" createNewClip: "Create new clip" unclip: "Unclip" -confirmToUnclipAlreadyClippedNote: "This post is already part of the \"{name}\" clip. Do you want to remove it from this clip instead?" +confirmToUnclipAlreadyClippedNote: "This post is already part of the \"{name}\" clip. + Do you want to remove it from this clip instead?" public: "Public" -i18nInfo: "Calckey is being translated into various languages by volunteers. You can help at {link}." +i18nInfo: "Firefish is being translated into various languages by volunteers. You can + help at {link}." manageAccessTokens: "Manage access tokens" accountInfo: "Account Info" notesCount: "Number of posts" @@ -671,12 +759,15 @@ no: "No" driveFilesCount: "Number of Drive files" driveUsage: "Drive space usage" noCrawle: "Reject crawler indexing" -noCrawleDescription: "Ask search engines to not index your profile page, posts, Pages, etc." -lockedAccountInfo: "Unless you set your post visiblity to \"Followers only\", your posts will be visible to anyone, even if you require followers to be manually approved." +noCrawleDescription: "Ask search engines to not index your profile page, posts, Pages, + etc." +lockedAccountInfo: "Unless you set your post visiblity to \"Followers only\", your + posts will be visible to anyone, even if you require followers to be manually approved." alwaysMarkSensitive: "Mark as NSFW by default" loadRawImages: "Load original images instead of showing thumbnails" disableShowingAnimatedImages: "Don't play animated images" -verificationEmailSent: "A verification email has been sent. Please follow the included link to complete verification." +verificationEmailSent: "A verification email has been sent. Please follow the included + link to complete verification." notSet: "Not set" emailVerified: "Email has been verified" noteFavoritesCount: "Number of bookmarked posts" @@ -685,10 +776,12 @@ pageLikedCount: "Number of received Page likes" contact: "Contact" useSystemFont: "Use the system's default font" clips: "Clips" +clipsDesc: "Clips are like share-able categorized bookmarks. You can create clips from the menu of individual posts." experimentalFeatures: "Experimental features" developer: "Developer" makeExplorable: "Make account visible in \"Explore\"" -makeExplorableDescription: "If you turn this off, your account will not show up in the \"Explore\" section." +makeExplorableDescription: "If you turn this off, your account will not show up in + the \"Explore\" section." showGapBetweenNotesInTimeline: "Show a gap between posts on the timeline" duplicate: "Duplicate" left: "Left" @@ -703,7 +796,10 @@ onlineUsersCount: "{n} users are online" nUsers: "{n} Users" nNotes: "{n} Posts" sendErrorReports: "Send error reports" -sendErrorReportsDescription: "When turned on, detailed error information will be shared with Calckey when a problem occurs, helping to improve the quality of Misskey.\nThis will include information such the version of your OS, what browser you're using, your activity in Calckey, etc." +sendErrorReportsDescription: "When turned on, detailed error information will be shared + with Firefish when a problem occurs, helping to improve the quality of Firefish.\n + This will include information such the version of your OS, what browser you're using, + your activity in Firefish, etc." myTheme: "My theme" backgroundColor: "Background color" accentColor: "Accent color" @@ -727,7 +823,7 @@ capacity: "Capacity" inUse: "Used" editCode: "Edit code" apply: "Apply" -receiveAnnouncementFromInstance: "Receive notifications from this instance" +receiveAnnouncementFromInstance: "Receive notifications from this server" emailNotification: "Email notifications" publish: "Publish" inChannelSearch: "Search in channel" @@ -735,27 +831,30 @@ useReactionPickerForContextMenu: "Open reaction picker on right-click" typingUsers: "{users} is typing" jumpToSpecifiedDate: "Jump to specific date" showingPastTimeline: "Currently displaying an old timeline" -clear: "Return" +clear: "Clear" markAllAsRead: "Mark all as read" goBack: "Back" unlikeConfirm: "Really remove your like?" fullView: "Full view" quitFullView: "Exit full view" addDescription: "Add description" -userPagePinTip: "You can display posts here by selecting \"Pin to profile\" from the menu of individual posts." -notSpecifiedMentionWarning: "This post contains mentions of users not included as recipients" +userPagePinTip: "You can display posts here by selecting \"Pin to profile\" from the + menu of individual posts." +notSpecifiedMentionWarning: "This post contains mentions of users not included as + recipients" info: "About" userInfo: "User information" unknown: "Unknown" onlineStatus: "Online status" hideOnlineStatus: "Hide online status" -hideOnlineStatusDescription: "Hiding your online status reduces the convenience of some features such as the search." +hideOnlineStatusDescription: "Hiding your online status reduces the convenience of + some features such as the search." online: "Online" active: "Active" offline: "Offline" notRecommended: "Not recommended" botProtection: "Bot Protection" -instanceBlocking: "Blocked Instances" +instanceBlocking: "Federation Management" selectAccount: "Select account" switchAccount: "Switch account" enabled: "Enabled" @@ -783,19 +882,22 @@ low: "Low" emailNotConfiguredWarning: "Email address not set." ratio: "Ratio" secureMode: "Secure Mode (Authorized Fetch)" -instanceSecurity: "Instance Security" -secureModeInfo: "When requesting from other instances, do not send back without proof." +instanceSecurity: "Server Security" +secureModeInfo: "When requesting from other servers, do not send back without proof." privateMode: "Private Mode" -privateModeInfo: "When enabled, only whitelisted instances can federate with your instances. All posts will be hidden from the public." -allowedInstances: "Whitelisted Instances" -allowedInstancesDescription: "Hosts of instances to be whitelisted for federation, each seperated by a new line (only applies in private mode)." +privateModeInfo: "When enabled, only whitelisted servers can federate with your server. + All posts will be hidden from the public." +allowedInstances: "Whitelisted Servers" +allowedInstancesDescription: "Hosts of servers to be whitelisted for federation, each + separated by a new line (only applies in private mode)." previewNoteText: "Show preview" customCss: "Custom CSS" -customCssWarn: "This setting should only be used if you know what it does. Entering improper values may cause the client to stop functioning normally." +customCssWarn: "This setting should only be used if you know what it does. Entering + improper values may cause the client to stop functioning normally." global: "Global" recommended: "Recommended" squareAvatars: "Display squared avatars" -seperateRenoteQuote: "Seperate boost and quote buttons" +seperateRenoteQuote: "Separate boost and quote buttons" sent: "Sent" received: "Received" searchResult: "Search results" @@ -803,12 +905,14 @@ hashtags: "Hashtags" troubleshooting: "Troubleshooting" useBlurEffect: "Use blur effects in the UI" learnMore: "Learn more" -misskeyUpdated: "Calckey has been updated!" +misskeyUpdated: "Firefish has been updated!" whatIsNew: "Show changes" translate: "Translate" translatedFrom: "Translated from {x}" accountDeletionInProgress: "Account deletion is currently in progress" -usernameInfo: "A name that identifies your account from others on this server. You can use the alphabet (a~z, A~Z), digits (0~9) or underscores (_). Usernames cannot be changed later." +usernameInfo: "A name that identifies your account from others on this server. You + can use the alphabet (a~z, A~Z), digits (0~9) or underscores (_). Usernames cannot + be changed later." aiChanMode: "Ai-chan in Classic UI" keepCw: "Keep content warnings" pubSub: "Pub/Sub Accounts" @@ -825,17 +929,20 @@ filter: "Filter" controlPanel: "Control Panel" manageAccounts: "Manage Accounts" makeReactionsPublic: "Set reaction history to public" -makeReactionsPublicDescription: "This will make the list of all your past reactions publicly visible." -classic: "Classic" +makeReactionsPublicDescription: "This will make the list of all your past reactions + publicly visible." +classic: "Centered" muteThread: "Mute thread" unmuteThread: "Unmute thread" ffVisibility: "Follows/Followers Visibility" -ffVisibilityDescription: "Allows you to configure who can see who you follow and who follows you." -continueThread: "View thread continuation" +ffVisibilityDescription: "Allows you to configure who can see who you follow and who + follows you." +continueThread: "Continue thread" deleteAccountConfirm: "This will irreversibly delete your account. Proceed?" incorrectPassword: "Incorrect password." voteConfirm: "Confirm your vote for \"{choice}\"?" hide: "Hide" +alt: "ALT" leaveGroup: "Leave group" leaveGroupConfirm: "Are you sure you want to leave \"{name}\"?" useDrawerReactionPickerForMobile: "Display reaction picker as drawer on mobile" @@ -845,12 +952,12 @@ overridedDeviceKind: "Device type" smartphone: "Smartphone" tablet: "Tablet" auto: "Auto" -themeColor: "Instance Ticker Color" +themeColor: "Server Ticker Color" size: "Size" numberOfColumn: "Number of columns" searchByGoogle: "Search" -instanceDefaultLightTheme: "Instance-wide default light theme" -instanceDefaultDarkTheme: "Instance-wide default dark theme" +instanceDefaultLightTheme: "Server-wide default light theme" +instanceDefaultDarkTheme: "Server-wide default dark theme" instanceDefaultThemeDescription: "Enter the theme code in object format." mutePeriod: "Mute duration" indefinitely: "Permanently" @@ -864,20 +971,27 @@ rateLimitExceeded: "Rate limit exceeded" cropImage: "Crop image" cropImageAsk: "Do you want to crop this image?" file: "File" +image: "Image" +video: "Video" +audio: "Audio" recentNHours: "Last {n} hours" recentNDays: "Last {n} days" noEmailServerWarning: "Email server not configured." thereIsUnresolvedAbuseReportWarning: "There are unsolved reports." check: "Check" driveCapOverrideLabel: "Change the drive capacity for this user" -driveCapOverrideCaption: "Reset the capacity to default by inputting a value of 0 or lower." +driveCapOverrideCaption: "Reset the capacity to default by inputting a value of 0 + or lower." requireAdminForView: "You must log in with an administrator account to view this." -isSystemAccount: "An account created and automatically operated by the system." +isSystemAccount: "This account is created and automatically operated by the system. + Please do not moderate, edit, delete, or otherwise tamper with this account, or + it may break your server." typeToConfirm: "Please enter {x} to confirm" deleteAccount: "Delete account" document: "Documentation" numberOfPageCache: "Number of cached pages" -numberOfPageCacheDescription: "Increasing this number will improve convenience for users but cause more server load as well as more memory to be used." +numberOfPageCacheDescription: "Increasing this number will improve convenience for + users but cause more server load as well as more memory to be used." logoutConfirm: "Really log out?" lastActiveDate: "Last used at" statusbar: "Status bar" @@ -894,52 +1008,137 @@ sensitiveMediaDetection: "Detection of NSFW media" localOnly: "Local only" remoteOnly: "Remote only" failedToUpload: "Upload failed" -cannotUploadBecauseInappropriate: "This file could not be uploaded because parts of it have been detected as potentially NSFW." +cannotUploadBecauseInappropriate: "This file could not be uploaded because parts of + it have been detected as potentially NSFW." cannotUploadBecauseNoFreeSpace: "Upload failed due to lack of Drive capacity." +cannotUploadBecauseExceedsFileSizeLimit: "This file could not be uploaded because + it exceeds the maximum allowed size." beta: "Beta" enableAutoSensitive: "Automatic NSFW-Marking" -enableAutoSensitiveDescription: "Allows automatic detection and marking of NSFW media through Machine Learning where possible. Even if this option is disabled, it may be enabled instance-wide." -activeEmailValidationDescription: "Enables stricter validation of email addresses, which includes checking for disposable addresses and by whether it can actually be communicated with. When unchecked, only the format of the email is validated." +enableAutoSensitiveDescription: "Allows automatic detection and marking of NSFW media + through Machine Learning where possible. Even if this option is disabled, it may + be enabled server-wide." +activeEmailValidationDescription: "Enables stricter validation of email addresses, + which includes checking for disposable addresses and by whether it can actually + be communicated with. When unchecked, only the format of the email is validated." navbar: "Navigation bar" shuffle: "Shuffle" account: "Account" move: "Move" +pushNotification: "Push notifications" +subscribePushNotification: "Enable push notifications" +unsubscribePushNotification: "Disable push notifications" +pushNotificationAlreadySubscribed: "Push notifications are already enabled" +pushNotificationNotSupported: "Your browser or server does not support push notifications" +sendPushNotificationReadMessage: "Delete push notifications once the relevant notifications + or messages have been read" +sendPushNotificationReadMessageCaption: "A notification containing the text \"{emptyPushNotificationMessage}\"\ + \ will be displayed for a short time. This may increase the battery usage of your + device, if applicable." showAds: "Show ads" enterSendsMessage: "Press Return in Messaging to send message (off is Ctrl + Return)" -adminCustomCssWarn: "This setting should only be used if you know what it does. Entering improper values may cause EVERYONE'S clients to stop functioning normally. Please ensure your CSS works properly by testing it in your user settings." +adminCustomCssWarn: "This setting should only be used if you know what it does. Entering + improper values may cause EVERYONE'S clients to stop functioning normally. Please + ensure your CSS works properly by testing it in your user settings." customMOTD: "Custom MOTD (splash screen messages)" -customMOTDDescription: "Custom messages for the MOTD (splash screen) separated by line breaks to be shown randomly every time a user loads/reloads the page." +customMOTDDescription: "Custom messages for the MOTD (splash screen) separated by + line breaks to be shown randomly every time a user loads/reloads the page." customSplashIcons: "Custom splash screen icons (urls)" -customSplashIconsDescription: "URLs for custom splash screen icons separated by line breaks to be shown randomly every time a user loads/reloads the page. Please make sure the images are on a static URL, preferably all resized to 192x192." -showUpdates: "Show a popup when Calckey updates" -recommendedInstances: "Recommended instances" -recommendedInstancesDescription: "Recommended instances seperated by line breaks to appear in the recommended timeline. Do NOT add `https://`, ONLY the domain." +customSplashIconsDescription: "URLs for custom splash screen icons separated by line + breaks to be shown randomly every time a user loads/reloads the page. Please make + sure the images are on a static URL, preferably all resized to 192x192." +showUpdates: "Show a popup when Firefish updates" +recommendedInstances: "Recommended servers" +recommendedInstancesDescription: "Recommended servers separated by line breaks to + appear in the recommended timeline." caption: "Auto Caption" splash: "Splash Screen" updateAvailable: "There might be an update available!" +swipeOnMobile: "Allow swiping between pages" swipeOnDesktop: "Allow mobile-style swiping on desktop" logoImageUrl: "Logo image URL" -showAdminUpdates: "Indicate a new Calckey version is avaliable (admin only)" +showAdminUpdates: "Indicate a new Firefish version is avaliable (admin only)" replayTutorial: "Replay tutorial" migration: "Migration" moveTo: "Move current account to new account" moveToLabel: "Account you're moving to:" moveAccount: "Move account!" -moveAccountDescription: "This process is irreversible. Make sure you've set up an alias for this account on your new account before moving. Please enter the tag of the account formatted like @person@instance.com" +moveAccountDescription: "This process is irreversible. Make sure you've set up an + alias for this account on your new account before moving. Please enter the tag of + the account formatted like @person@server.com" moveFrom: "Move to this account from an older account" moveFromLabel: "Account you're moving from:" -moveFromDescription: "This will set an alias of your old account so that you can move from that account to this current one. Do this BEFORE moving from your older account. Please enter the tag of the account formatted like @person@instance.com" -migrationConfirm: "Are you absolutely sure you want to migrate your acccount to {account}? Once you do this, you won't be able to reverse it, and you won't be able to use your account normally again.\nAlso, please ensure that you've set this current account as the account you're moving from." +moveFromDescription: "This will set an alias of your old account so that you can move + from that account to this current one. Do this BEFORE moving from your older account. + Please enter the tag of the account formatted like @person@server.com" +migrationConfirm: "Are you absolutely sure you want to migrate your account to {account}? + Once you do this, you won't be able to reverse it, and you won't be able to use + your account normally again.\nAlso, please ensure that you've set this current account + as the account you're moving from." defaultReaction: "Default emoji reaction for outgoing and incoming posts" +license: "License" +indexPosts: "Index Posts" +indexFrom: "Index from Post ID onwards" +indexFromDescription: "Leave blank to index every post" +indexNotice: "Now indexing. This will probably take a while, please don't restart\ + \ your server for at least an hour." +customKaTeXMacro: "Custom KaTeX macros" +customKaTeXMacroDescription: "Set up macros to write mathematical expressions easily! + The notation conforms to the LaTeX command definitions and is written as \\newcommand{\\ + name}{content} or \\newcommand{\\name}[number of arguments]{content}. For example, + \\newcommand{\\add}[2]{#1 + #2} will expand \\add{3}{foo} to 3 + foo. The curly + brackets surrounding the macro name can be changed to round or square brackets. + This affects the brackets used for arguments. One (and only one) macro can be defined + per line, and you can't break the line in the middle of the definition. Invalid + lines are simply ignored. Only simple string substitution functions are supported; + advanced syntax, such as conditional branching, cannot be used here." +enableCustomKaTeXMacro: "Enable custom KaTeX macros" +noteId: "Post ID" +signupsDisabled: "Signups on this server are currently disabled, but you can always + sign up at another server! If you have an invitation code for this server, please + enter it below." +findOtherInstance: "Find another server" +apps: "Apps" +sendModMail: "Send Moderation Notice" +preventAiLearning: "Prevent AI bot scraping" +preventAiLearningDescription: "Request third-party AI language models not to study + content you upload, such as posts and images." +noGraze: "Please disable the \"Graze for Mastodon\" browser extension, as it interferes + with Firefish." +silencedWarning: "This page is showing because these users are from servers your admin + silenced, so they may potentially be spam." +isBot: "This account is a bot" +isLocked: "This account has follow approvals" +isModerator: "Moderator" +isAdmin: "Administrator" +isPatron: "Firefish Patron" +reactionPickerSkinTone: "Preferred emoji skin tone" +enableServerMachineStats: "Enable server hardware statistics" +enableIdenticonGeneration: "Enable Identicon generation" +showPopup: "Notify users with popup" +showWithSparkles: "Show with sparkles" +youHaveUnreadAnnouncements: "You have unread announcements" +donationLink: "Link to donation page" +neverShow: "Don't show again" +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 NSFW media via Machine Learning. This will slightly increase the load on the server." + description: "Reduces the effort of server moderation through automatically recognizing + NSFW media via Machine Learning. This will slightly increase the load on the server." sensitivity: "Detection sensitivity" - sensitivityDescription: "Reducing the sensitivity will lead to fewer misdetections (false positives) whereas increasing it will lead to fewer missed detections (false negatives)." + sensitivityDescription: "Reducing the sensitivity will lead to fewer misdetections + (false positives) whereas increasing it will lead to fewer missed detections (false + negatives)." setSensitiveFlagAutomatically: "Mark as NSFW" - setSensitiveFlagAutomaticallyDescription: "The results of the internal detection will be retained even if this option is turned off." + setSensitiveFlagAutomaticallyDescription: "The results of the internal detection + will be retained even if this option is turned off." analyzeVideos: "Enable analysis of videos" - analyzeVideosDescription: "Analyzes videos in addition to images. This will slightly increase the load on the server." + analyzeVideosDescription: "Analyzes videos in addition to images. This will slightly + increase the load on the server." _emailUnavailable: used: "This email address is already being used" format: "The format of this email address is invalid" @@ -953,11 +1152,15 @@ _ffVisibility: _signup: almostThere: "Almost there" emailAddressInfo: "Please enter your email address. It will not be made public." - emailSent: "A confirmation email has been sent to your email address ({email}). Please click the included link to complete account creation." + emailSent: "A confirmation email has been sent to your email address ({email}). + Please click the included link to complete account creation." _accountDelete: accountDelete: "Delete account" - mayTakeTime: "As account deletion is a resource-heavy process, it may take some time to complete depending on how much content you have created and how many files you have uploaded." - sendEmail: "Once account deletion has been completed, an email will be sent to the email address registered to this account." + mayTakeTime: "As account deletion is a resource-heavy process, it may take some + time to complete depending on how much content you have created and how many files + you have uploaded." + sendEmail: "Once account deletion has been completed, an email will be sent to the + email address registered to this account." requestAccountDelete: "Request account deletion" started: "Deletion has been started." inProgress: "Deletion is currently in progress" @@ -965,9 +1168,12 @@ _ad: back: "Back" reduceFrequencyOfThisAd: "Show this ad less" _forgotPassword: - enterEmail: "Enter the email address you used to register. A link with which you can reset your password will then be sent to it." - ifNoEmail: "If you did not use an email during registration, please contact the instance administrator instead." - contactAdmin: "This instance does not support using email addresses, please contact the instance administrator to reset your password instead." + enterEmail: "Enter the email address you used to register. A link with which you + can reset your password will then be sent to it." + ifNoEmail: "If you did not use an email during registration, please contact the + server administrator instead." + contactAdmin: "This server does not support using email addresses, please contact + the server administrator to reset your password instead." _gallery: my: "My Gallery" liked: "Liked Posts" @@ -990,12 +1196,15 @@ _preferencesBackups: save: "Save changes" inputName: "Please enter a name for this backup" cannotSave: "Saving failed" - nameAlreadyExists: "A backup called \"{name}\" already exists. Please enter a different name." - applyConfirm: "Do you really want to apply the \"{name}\" backup to this device? Existing settings of this device will be overwritten." + nameAlreadyExists: "A backup called \"{name}\" already exists. Please enter a different + name." + applyConfirm: "Do you really want to apply the \"{name}\" backup to this device? + Existing settings of this device will be overwritten." saveConfirm: "Save backup as {name}?" deleteConfirm: "Delete the {name} backup?" renameConfirm: "Rename this backup from \"{old}\" to \"{new}\"?" - noBackups: "No backups exist. You may backup your client settings on this server by using \"Create new backup\"." + noBackups: "No backups exist. You may backup your client settings on this server + by using \"Create new backup\"." createdAt: "Created at: {date} {time}" updatedAt: "Updated at: {date} {time}" cannotLoad: "Loading failed" @@ -1006,23 +1215,38 @@ _registry: keys: "Keys" domain: "Domain" createKey: "Create key" -_aboutMisskey: - about: "Calckey is a fork of Misskey made by ThatOneCalculator, which has been in development since 2022." +_aboutFirefish: + about: "Firefish is a fork of Misskey made by ThatOneCalculator, which has been in + development since 2022." contributors: "Main contributors" allContributors: "All contributors" source: "Source code" - translation: "Translate Calckey" - donate: "Donate to Calckey" - morePatrons: "We also appreciate the support of many other helpers not listed here. Thank you! 🥰" - patrons: "Calckey patrons" + translation: "Translate Firefish" + donate: "Donate to Firefish" + donateTitle: "Enjoying Firefish?" + pleaseDonateToFirefish: "Please consider donating to Firefish to support its development." + pleaseDonateToHost: "Please also consider donating to your home server, {host}, to help support its operation costs." + donateHost: "Donate to {host}" + morePatrons: "We also appreciate the support of many other helpers not listed here. + Thank you! 🥰" + sponsors: "Firefish sponsors" + patrons: "Firefish patrons" + patronsList: "Listed chronologically, not by donation size. Donate with the link above to get your name on here!" _nsfw: respect: "Hide NSFW media" ignore: "Don't hide NSFW media" force: "Hide all media" _mfm: + play: "Play MFM" + stop: "Stop MFM" + warn: "MFM may contain rapidly moving or flashy animations" + alwaysPlay: "Always autoplay all animated MFM" cheatSheet: "MFM Cheatsheet" - intro: "MFM is a markup language used on Misskey, Calckey, Akkoma, and more that can be used in many places. Here you can view a list of all available MFM syntax." - dummy: "Calckey expands the world of the Fediverse" + intro: "MFM is a markup language used on Misskey, Firefish, Akkoma, and more that + can be used in many places. Here you can view a list of all available MFM syntax." + dummy: "Firefish expands the world of the Fediverse" + advanced: "Advanced MFM" + advancedDescription: "If disabled, only allows for basic markup unless animated MFM is playing" mention: "Mention" mentionDescription: "You can specify a user by using an At-Symbol and a username." hashtag: "Hashtag" @@ -1040,15 +1264,17 @@ _mfm: inlineCode: "Code (Inline)" inlineCodeDescription: "Displays inline syntax highlighting for (program) code." blockCode: "Code (Block)" - blockCodeDescription: "Displays syntax highlighting for multi-line (program) code in a block." + blockCodeDescription: "Displays syntax highlighting for multi-line (program) code + in a block." inlineMath: "Math (Inline)" inlineMathDescription: "Display math formulas (KaTeX) in-line" blockMath: "Math (Block)" - blockMathDescription: "Display multi-line math formulas (KaTeX) in a block" + blockMathDescription: "Display math formulas (KaTeX) in a block" quote: "Quote" quoteDescription: "Displays content as a quote." emoji: "Custom Emoji" - emojiDescription: "By surrounding a custom emoji name with colons, custom emoji can be displayed." + emojiDescription: "By surrounding a custom emoji name with colons, custom emoji + can be displayed." search: "Search" searchDescription: "Displays a search box with pre-entered text." flip: "Flip" @@ -1083,8 +1309,21 @@ _mfm: sparkleDescription: "Gives content a sparkling particle effect." rotate: "Rotate" rotateDescription: "Turns content by a specified angle." + fade: "Fade" + fadeDescription: "Fades content in and out." + position: "Position" + positionDescription: "Move content by a specified amount." + crop: "Crop" + cropDescription: "Crop content." + scale: "Scale" + scaleDescription: "Scale content by a specified amount." + foreground: "Foreground color" + foregroundDescription: "Change the foreground color of text." + background: "Background color" + backgroundDescription: "Change the background color of text." plain: "Plain" - plainDescription: "Deactivates the effects of all MFM contained within this MFM effect." + plainDescription: "Deactivates the effects of all MFM contained within this MFM + effect." _instanceTicker: none: "Never show" remote: "Show for remote users" @@ -1104,6 +1343,8 @@ _channel: following: "Followed" usersCount: "{n} Participants" notesCount: "{n} Posts" + nameAndDescription: "Name and description" + nameOnly: "Name only" _messaging: dms: "Private" groups: "Groups" @@ -1114,18 +1355,22 @@ _menuDisplay: hide: "Hide" _wordMute: muteWords: "Muted words" - muteWordsDescription: "Separate with spaces for an AND condition or with line breaks for an OR condition." + muteWordsDescription: "Separate with spaces for an AND condition or with line breaks + for an OR condition." muteWordsDescription2: "Surround keywords with slashes to use regular expressions." softDescription: "Hide posts that fulfil the set conditions from the timeline." - hardDescription: "Prevents posts fulfilling the set conditions from being added to the timeline. In addition, these posts will not be added to the timeline even if the conditions are changed." + hardDescription: "Prevents posts fulfilling the set conditions from being added + to the timeline. In addition, these posts will not be added to the timeline even + if the conditions are changed." soft: "Soft" hard: "Hard" mutedNotes: "Muted posts" _instanceMute: - instanceMuteDescription: "This will mute any posts/boosts from the listed instances, including those of users replying to a user from a muted instance." + instanceMuteDescription: "This will mute any posts/boosts from the listed servers, + including those of users replying to a user from a muted server." instanceMuteDescription2: "Separate with newlines" - title: "Hides posts from listed instances." - heading: "List of instances to be muted" + title: "Hides posts from listed servers." + heading: "List of servers to be muted" _theme: explore: "Explore Themes" install: "Install a theme" @@ -1211,49 +1456,83 @@ _sfx: _ago: future: "Future" justNow: "Just now" - secondsAgo: "{n} second(s) ago" - minutesAgo: "{n} minute(s) ago" - hoursAgo: "{n} hour(s) ago" - daysAgo: "{n} day(s) ago" - weeksAgo: "{n} week(s) ago" - monthsAgo: "{n} month(s) ago" - yearsAgo: "{n} year(s) ago" + secondsAgo: "{n}s ago" + minutesAgo: "{n}m ago" + hoursAgo: "{n}h ago" + daysAgo: "{n}d ago" + weeksAgo: "{n}w ago" + monthsAgo: "{n}mo ago" + yearsAgo: "{n}y ago" _time: second: "Second(s)" minute: "Minute(s)" hour: "Hour(s)" day: "Day(s)" +_filters: + fromUser: "From user" + withFile: "With file" + fromDomain: "From domain" + notesBefore: "Posts before" + notesAfter: "Posts after" + followingOnly: "Following only" + followersOnly: "Followers only" _tutorial: - title: "How to use Calckey" + title: "How to use Firefish" step1_1: "Welcome!" step1_2: "Let's get you set up. You'll be up and running in no time!" step2_1: "First, please fill out your profile." - step2_2: "Providing some information about who you are will make it easier for others to tell if they want to see your posts or follow you." - step3_1: "Now time to follow some people!" - step3_2: "Your home and social timelines are based off of who you follow, so try following a couple accounts to get started.\nClick the plus circle on the top right of a profile to follow them." + step2_2: "Providing some information about who you are will make it easier for others + to tell if they want to see your posts or follow you." + step3_1: "Now it's time to follow some people!" + step3_2: "Your home and social timelines are based off of who you follow, so try + following a couple accounts to get started.\nClick the plus circle on the top + right of a profile to follow them." step4_1: "Let's get you out there." - step4_2: "For your first post, some people like to made a {introduction} post or a simple \"Hello world!\"" + step4_2: "For your first post, some people like to make an {introduction} post or + a simple \"Hello world!\"" step5_1: "Timelines, timelines everywhere!" - step5_2: "Your instance has {timelines} different timelines enabled." - step5_3: "The Home {icon} timeline is where you can see posts from your followers." - step5_4: "The Local {icon} timeline is where you can see posts from everyone else on this instance." - step5_5: "The Recommended {icon} timeline is where you can see posts from instances the admins recommend." - step5_6: "The Social {icon} timeline is your home + local." - step5_7: "The Global {icon} timeline is where you can see posts from every other connected instance." + step5_2: "Your server has {timelines} different timelines enabled." + step5_3: "The Home {icon} timeline is where you can see posts from the accounts + you follow." + step5_4: "The Local {icon} timeline is where you can see posts from everyone else + on this server." + step5_5: "The Social {icon} timeline is a combination of the Home and Local timelines." + step5_6: "The Recommended {icon} timeline is where you can see posts from servers\ + \ the admins recommend." + step5_7: "The Global {icon} timeline is where you can see posts from every other\ + \ connected server." step6_1: "So, what is this place?" - step6_2: "Well, you didn't just join Calckey. You joined a portal to the Fediverse, an interconnected network of thousands of servers, called \"instances\"." - step6_3: "Each server works in different ways, and not all servers run Calckey. This one does though! It's a bit complicated, but you'll get the hang of it in no time." + step6_2: "Well, you didn't just join Firefish. You joined a portal to the Fediverse, + an interconnected network of thousands of servers." + step6_3: "Each server works in different ways, and not all servers run Firefish. + This one does though! It's a bit complicated, but you'll get the hang of it in + no time." step6_4: "Now go, explore, and have fun!" _2fa: alreadyRegistered: "You have already registered a 2-factor authentication device." - registerDevice: "Register a new device" - registerKey: "Register a security key" + registerTOTP: "Register authenticator app" step1: "First, install an authentication app (such as {a} or {b}) on your device." step2: "Then, scan the QR code displayed on this screen." + step2Click: "Clicking on this QR code will allow you to register 2FA to your security key or phone authenticator app." step2Url: "You can also enter this URL if you're using a desktop program:" + step3Title: "Enter an authentication code" step3: "Enter the token provided by your app to finish setup." step4: "From now on, any future login attempts will ask for such a login token." + securityKeyNotSupported: "Your browser does not support security keys." + registerTOTPBeforeKey: "Please set up an authenticator app to register a security or pass key." securityKeyInfo: "Besides fingerprint or PIN authentication, you can also setup authentication via hardware security keys that support FIDO2 to further secure your account." + chromePasskeyNotSupported: "Chrome passkeys are currently not supported." + registerSecurityKey: "Register a security or pass key" + securityKeyName: "Enter a key name" + tapSecurityKey: "Please follow your browser to register the security or pass key" + removeKey: "Remove security key" + removeKeyConfirm: "Really delete the {name} key?" + whyTOTPOnlyRenew: "The authenticator app cannot be removed as long as a security key is registered." + renewTOTP: "Reconfigure authenticator app" + renewTOTPConfirm: "This will cause verification codes from your previous app to stop working" + renewTOTPOk: "Reconfigure" + renewTOTPCancel: "Cancel" + token: "2FA Token" _permissions: "read:account": "View your account information" "write:account": "Edit your account information" @@ -1289,17 +1568,21 @@ _permissions: "write:gallery-likes": "Edit your list of liked gallery posts" _auth: shareAccess: "Would you like to authorize \"{name}\" to access this account?" - shareAccessAsk: "Are you sure you want to authorize this application to access your account?" - permissionAsk: "This application requests the following permissions" + shareAccessAsk: "Are you sure you want to authorize this application to access your + account?" + permissionAsk: "This application requests the following permissions:" pleaseGoBack: "Please go back to the application" callback: "Returning to the application" denied: "Access denied" + copyAsk: "Please paste the following authorization code to the application:" + allPermissions: "Full account access" _antennaSources: all: "All posts" homeTimeline: "Posts from followed users" users: "Posts from specific users" userList: "Posts from a specified list of users" userGroup: "Posts from users in a specified group" + instances: "Posts from all users on an server" _weekday: sunday: "Sunday" monday: "Monday" @@ -1309,30 +1592,35 @@ _weekday: friday: "Friday" saturday: "Saturday" _widgets: - memo: "Sticky notes" + memo: "Sticky Notes" notifications: "Notifications" timeline: "Timeline" calendar: "Calendar" trends: "Trending" clock: "Clock" - rss: "RSS reader" - rssTicker: "RSS-Ticker" + rss: "RSS Reader" + rssTicker: "RSS Ticker" activity: "Activity" photos: "Photos" - digitalClock: "Digital clock" - unixClock: "UNIX clock" + digitalClock: "Digital Clock" + unixClock: "UNIX Clock" federation: "Federation" - instanceCloud: "Instance cloud" - postForm: "Posting form" + instanceCloud: "Server Cloud" + postForm: "Posting Form" slideshow: "Slideshow" button: "Button" - onlineUsers: "Online users" + onlineUsers: "Online Users" jobQueue: "Job Queue" - serverMetric: "Server metrics" - aiscript: "AiScript console" - userList: "User list" + serverMetric: "Server Metrics" + aiscript: "AiScript Console" + userList: "User List" + serverInfo: "Server Info" _userList: chooseList: "Select a list" + meiliStatus: "Server Status" + meiliSize: "Index size" + meiliIndexCount: "Indexed posts" + _cw: hide: "Hide" show: "Show content" @@ -1362,11 +1650,11 @@ _poll: remainingSeconds: "{s} second(s) remaining" _visibility: public: "Public" - publicDescription: "Your post will be visible for all users" + publicDescription: "Your post will be visible in all public timelines" home: "Unlisted" homeDescription: "Post to home timeline only" followers: "Followers" - followersDescription: "Make visible to your followers only" + followersDescription: "Make visible to your followers and mentioned users only" specified: "Direct" specifiedDescription: "Make visible for specified users only" localOnly: "Local only" @@ -1389,11 +1677,16 @@ _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" changeBanner: "Change banner" + locationDescription: "If you enter your city first, it will display your local time + to other users." _exportOrImport: allNotes: "All posts" followingList: "Followed users" @@ -1711,7 +2004,8 @@ _pages: _for: arg1: "Number of times to repeat" arg2: "Action" - typeError: "Slot {slot} accepts values of type \"{expect}\", but the provided value is of type \"{actual}\"!" + typeError: "Slot {slot} accepts values of type \"{expect}\", but the provided + value is of type \"{actual}\"!" thereIsEmptySlot: "Slot {slot} is empty!" types: string: "Text" @@ -1742,6 +2036,9 @@ _notification: youWereInvitedToGroup: "{userName} invited you to a group" pollEnded: "Poll results have become available" emptyPushNotificationMessage: "Push notifications have been updated" + reacted: "reacted to your post" + renoted: "boosted your post" + voted: "voted on your poll" _types: all: "All" follow: "New followers" @@ -1773,33 +2070,42 @@ _deck: popRight: "Pop column to the right" profile: "Workspace" newProfile: "New workspace" + renameProfile: "Rename workspace" deleteProfile: "Delete workspace" + nameAlreadyExists: "This workspace name already exists." introduction: "Create the perfect interface for you by arranging columns freely!" - introduction2: "Click on the + on the right of the screen to add new colums whenever you want." - widgetsIntroduction: "Please select \"Edit widgets\" in the column menu and add a widget." + introduction2: "Click on the + on the right of the screen to add new colums whenever + you want." + widgetsIntroduction: "Please select \"Edit widgets\" in the column menu and add + a widget." _columns: main: "Main" widgets: "Widgets" notifications: "Notifications" tl: "Timeline" - antenna: "Antennas" + antenna: "Antenna" list: "List" + channel: "Channel" mentions: "Mentions" direct: "Direct messages" -_apps: - apps: "Apps" - crossPlatform: "Cross platform" - mobile: "Mobile" - firstParty: "First party" - firstClass: "First class" - secondClass: "Second class" - thirdClass: "Third class" - free: "Free" - paid: "Paid" - pwa: "Install PWA" - kaiteki: "Kaiteki" - milktea: "Milktea" - subwayTooter: "Subway Tooter" - kimis: "Kimis" - theDesk: "TheDesk" - lesskey: "Lesskey" +_experiments: + title: "Experiments" + enablePostImports: "Enable post imports" + postImportsCaption: "Allows users to import their posts from past Firefish,\ + \ Misskey, Mastodon, Akkoma, and Pleroma accounts. It may cause slowdowns during\ + \ load if your queue is bottlenecked." +_dialog: + charactersExceeded: "Max characters exceeded! Current: {current}/Limit: {max}" + charactersBelow: "Not enough characters! Current: {current}/Limit: {min}" +_skinTones: + yellow: "Yellow" + light: "Light" + mediumLight: "Medium Light" + medium: "Medium" + mediumDark: "Medium Dark" + dark: "Dark" +_feeds: + copyFeed: "Copy feed" + rss: "RSS" + atom: "Atom" + jsonFeed: "JSON feed" diff --git a/locales/es-ES.yml b/locales/es-ES.yml index 1d35c878c4..49ce22f20f 100644 --- a/locales/es-ES.yml +++ b/locales/es-ES.yml @@ -1,7 +1,8 @@ ---- _lang_: "Español" -headlineMisskey: "Red conectada por notas" -introMisskey: "¡Bienvenido/a! Misskey es un servicio de microblogging descentralizado de código abierto.\nEscribe \"notas\" para compartir lo que te ocurre ahora o para contar sobre ti a todos 📡\nCon la función de \"reacciones\", puedes también añadir una reacción rápida a las notas de todos 👍\n¡Exploremos juntos un nuevo mundo! 🚀" +headlineMisskey: "¡Un proyecto de código abierto y una plataforma de medios de comunicación + descentralizada que es gratis para siempre! 🚀" +introMisskey: "¡Bienvenido! ¡Firefish es un proyecto de código abierto, plataforma + descentralizado medios de comunicación social que es gratis para siempre! 🚀" monthAndDay: "{day}/{month}" search: "Buscar" notifications: "Notificaciones" @@ -13,17 +14,17 @@ ok: "OK" gotIt: "¡Lo tengo!" cancel: "Cancelar" enterUsername: "Introduce el nombre de usuario" -renotedBy: "Renotado por {user}" -noNotes: "No hay notas" +renotedBy: "Impulsado por {user}" +noNotes: "No hay publicaciones" noNotifications: "No hay notificaciones" -instance: "Instancia" +instance: "Servidor" settings: "Configuración" basicSettings: "Configuración Básica" otherSettings: "Configuración avanzada" openInWindow: "Abrir en una ventana" profile: "Perfil" timeline: "Línea de tiempo" -noAccountDescription: "Este usuario no ha escrito su biografía aún" +noAccountDescription: "Este usuario no ha escrito su biografía aún." login: "Iniciar sesión" loggingIn: "Iniciando sesión" logout: "Cerrar sesión" @@ -36,7 +37,7 @@ favorite: "Añadir a favoritos" favorites: "Favoritos" unfavorite: "Quitar de favoritos" favorited: "Añadido a favoritos." -alreadyFavorited: "Ya había sido añadido a favoritos" +alreadyFavorited: "Ya había sido añadido a favoritos." cantFavorite: "No se puede añadir a favoritos." pin: "Fijar al perfil" unpin: "Desfijar" @@ -44,7 +45,8 @@ copyContent: "Copiar contenido" copyLink: "Copiar enlace" delete: "Borrar" deleteAndEdit: "Borrar y editar" -deleteAndEditConfirm: "¿Estás seguro de que quieres borrar esta nota y editarla? Perderás todas las reacciones, renotas y respuestas." +deleteAndEditConfirm: "¿Estás seguro de que quieres borrar esta publicación y editarla? + Perderás todas las reacciones, impulsos y respuestas." addToList: "Agregar a lista" sendMessage: "Enviar un mensaje" copyUsername: "Copiar nombre de usuario" @@ -58,20 +60,22 @@ receiveFollowRequest: "Recibiste una solicitud de seguimiento" followRequestAccepted: "La solicitud de seguimiento fue aceptada" mention: "Menciones" mentions: "Menciones" -directNotes: "Notas directas" +directNotes: "Mensajes Directos" importAndExport: "Importar y Exportar" import: "Importar" export: "Exportar" files: "Archivos" download: "Descargar" -driveFileDeleteConfirm: "¿Desea borrar el archivo \"{name}\"? Las notas que tengan este archivo como adjunto serán eliminadas" +driveFileDeleteConfirm: "¿Desea borrar el archivo \"{name}\"? Será removido de todas + las publicaciones que tengan este archivo adjunto." unfollowConfirm: "¿Desea dejar de seguir a {name}?" -exportRequested: "Se ha solicitado la exportación. Puede tomar un tiempo. Cuando termine la exportación, se añadirá en el drive" +exportRequested: "Se ha solicitado la exportación. Puede tomar un tiempo. Cuando termine + la exportación, se añadirá en el drive." importRequested: "Se ha solicitado la importación. Puede tomar un tiempo." lists: "Listas" noLists: "No tiene listas" -note: "Notas" -notes: "Notas" +note: "Publicación" +notes: "Publicaciones" following: "Siguiendo" followers: "Seguidores" followsYou: "Te sigue" @@ -80,10 +84,12 @@ manageLists: "Administrar listas" error: "Error" somethingHappened: "Ocurrió un error" retry: "Reintentar" -pageLoadError: "Error al leer la página" -pageLoadErrorDescription: "Normalmente es debido a la red o al caché del navegador. Por favor limpie el caché o intente más tarde." +pageLoadError: "Error al cargar la página." +pageLoadErrorDescription: "Normalmente es debido a la red o al caché del navegador. + Por favor limpie el caché o intente más tarde." serverIsDead: "No hay respuesta del servidor. Espere un momento y vuelva a intentarlo." -youShouldUpgradeClient: "Para ver esta página, por favor refrezca el navegador y utiliza una versión más reciente del cliente." +youShouldUpgradeClient: "Para ver esta página, por favor refrezca el navegador y utiliza + una versión más reciente del cliente." enterListName: "Ingrese nombre de lista" privacy: "Privacidad" makeFollowManuallyApprove: "Aprobar manualmente las solicitudes de seguimiento" @@ -94,13 +100,13 @@ followRequests: "Solicitudes de seguimiento" unfollow: "Dejar de seguir" followRequestPending: "Solicitudes de seguimiento pendiente" enterEmoji: "Ingresar emojis" -renote: "Renotar" -unrenote: "Quitar renota" -renoted: "Renotado" -cantRenote: "No se puede renotar este post" -cantReRenote: "No se puede renotar una renota" +renote: "Impulsar" +unrenote: "Quitar impulso" +renoted: "Impulsado." +cantRenote: "No se puede impulsar esta publicación." +cantReRenote: "No se puede impulsar un impulso." quote: "Citar" -pinnedNote: "Nota fijada" +pinnedNote: "Publicación fijada" pinned: "Fijar al perfil" you: "Tú" clickToShow: "Click para ver" @@ -108,8 +114,9 @@ sensitive: "Marcado como sensible" add: "Agregar" reaction: "Reacción" reactionSetting: "Reacciones para mostrar en el menú de reacciones" -reactionSettingDescription2: "Arrastre para reordenar, click para borrar, apriete la tecla + para añadir." -rememberNoteVisibility: "Recordar visibilidad" +reactionSettingDescription2: "Arrastre para reordenar, click para borrar, apriete + la tecla + para añadir." +rememberNoteVisibility: "Recordar la configuración de visibilidad de la publicación" attachCancel: "Quitar adjunto" markAsSensitive: "Marcar como sensible" unmarkAsSensitive: "Desmarcar como sensible" @@ -137,17 +144,24 @@ emojiUrl: "URL de la imágen del emoji" addEmoji: "Agregar emoji" settingGuide: "Configuración sugerida" cacheRemoteFiles: "Mantener en cache los archivos remotos" -cacheRemoteFilesDescription: "Si desactiva esta configuración, Los archivos remotos se cargarán desde el link directo sin usar la caché. Con eso se puede ahorrar almacenamiento del servidor, pero eso aumentará el tráfico al no crear miniaturas." +cacheRemoteFilesDescription: "Si desactiva esta configuración, los archivos remotos + se cargarán desde el servidor remoto sin usar la caché. Con eso se puede ahorrar + almacenamiento del servidor, pero eso aumentará el tráfico al no crear miniaturas." flagAsBot: "Esta cuenta es un bot" -flagAsBotDescription: "En caso de que esta cuenta fuera usada por un programa, active esta opción. Al hacerlo, esta opción servirá para otros desarrolladores para evitar cadenas infinitas de reacciones, y ajustará los sistemas internos de Misskey para que trate a esta cuenta como un bot." +flagAsBotDescription: "En caso de que esta cuenta fuera usada por un programa, active + esta opción. Al hacerlo, esta opción servirá para otros desarrolladores para evitar + cadenas infinitas de reacciones, y ajustará los sistemas internos de Firefish para + que trate a esta cuenta como un bot." flagAsCat: "Esta cuenta es un gato" -flagAsCatDescription: "En caso de que declare que esta cuenta es de un gato, active esta opción." +flagAsCatDescription: "Vas a tener orejas de gato y hablar como un gato!" flagShowTimelineReplies: "Mostrar respuestas a las notas en la biografía" -flagShowTimelineRepliesDescription: "Cuando se marca, la línea de tiempo muestra respuestas a otras notas además de las notas del usuario" -autoAcceptFollowed: "Aceptar automáticamente las solicitudes de seguimiento de los usuarios que sigues" +flagShowTimelineRepliesDescription: "Cuando se marca, la línea de tiempo muestra respuestas + a otras publicaciones además de las publicaciones del usuario." +autoAcceptFollowed: "Aceptar automáticamente las solicitudes de seguimiento de los + usuarios que sigues" addAccount: "Agregar Cuenta" -loginFailed: "Error al iniciar sesión." -showOnRemote: "Ver en una instancia remota" +loginFailed: "Error al iniciar sesión" +showOnRemote: "Ver en servidor remoto" general: "General" wallpaper: "Fondo de pantalla" setWallpaper: "Establecer fondo de pantalla" @@ -156,13 +170,17 @@ searchWith: "Buscar: {q}" youHaveNoLists: "No tienes listas" followConfirm: "¿Desea seguir a {name}?" proxyAccount: "Cuenta proxy" -proxyAccountDescription: "Una cuenta proxy es una cuenta que actúa como un seguidor remoto de un usuario bajo ciertas condiciones. Por ejemplo, cuando un usuario añade un usuario remoto a una lista, si ningún usuario local sigue al usuario agregado a la lista, la instancia no puede obtener su actividad. Así que la cuenta proxy sigue al usuario añadido a la lista" +proxyAccountDescription: "Una cuenta proxy es una cuenta que actúa como un seguidor + remoto de un usuario bajo ciertas condiciones. Por ejemplo, cuando un usuario añade + un usuario remoto a una lista, si ningún usuario local sigue al usuario agregado + a la lista, el servidor no puede obtener su actividad. Así que la cuenta proxy sigue + al usuario añadido a la lista." host: "Host" selectUser: "Elegir usuario" recipient: "Recipiente" annotation: "Anotación" federation: "Federación" -instances: "Instancia" +instances: "Servidores" registeredAt: "Registrado en" latestRequestSentAt: "Ultimo pedido enviado" latestRequestReceivedAt: "Ultimo pedido recibido" @@ -172,34 +190,35 @@ charts: "Chat" perHour: "por hora" perDay: "por día" stopActivityDelivery: "Dejar de enviar actividades" -blockThisInstance: "Bloquear instancia" +blockThisInstance: "Bloquear este servidor" operations: "Operaciones" software: "Software" version: "Versión" metadata: "Metadatos" -withNFiles: "{n} archivos" monitor: "Monitor" jobQueue: "Cola de trabajos" cpuAndMemory: "CPU y Memoria" network: "Red" disk: "Disco" -instanceInfo: "información de la instancia" +instanceInfo: "Información del servidor" statistics: "Estadísticas" clearQueue: "Limpiar cola" clearQueueConfirmTitle: "¿Desea limpiar la cola?" -clearQueueConfirmText: "Las notas aún no entregadas no se federarán. Normalmente no se necesita ejecutar esta operación" +clearQueueConfirmText: "Las publicaciones aún no entregadas no se federarán. Normalmente + no se necesita ejecutar esta operación." clearCachedFiles: "Limpiar caché" clearCachedFilesConfirm: "¿Desea borrar todos los archivos remotos cacheados?" -blockedInstances: "Instancias bloqueadas" -blockedInstancesDescription: "Seleccione los hosts de las instancias que desea bloquear, separadas por una linea nueva. Las instancias bloqueadas no podrán comunicarse con esta instancia." +blockedInstances: "Servidores bloqueados" +blockedInstancesDescription: "Escriba los hosts de los servidores que desea bloquear. + Los servidores bloqueados no podrán comunicarse con este servidor." muteAndBlock: "Silenciar y bloquear" mutedUsers: "Usuarios silenciados" blockedUsers: "Usuarios bloqueados" noUsers: "No hay usuarios" editProfile: "Editar perfil" -noteDeleteConfirm: "¿Desea borrar esta nota?" -pinLimitExceeded: "Ya no se pueden fijar más posts" -intro: "¡La instalación de Misskey ha terminado! Crea el usuario administrador." +noteDeleteConfirm: "¿Desea borrar esta publicación?" +pinLimitExceeded: "Ya no se pueden fijar más publicaciones" +intro: "¡La instalación de Firefish ha terminado! Crea el usuario administrador." done: "Terminado" processing: "Procesando" preview: "Vista previa" @@ -214,12 +233,12 @@ all: "Todo" subscribing: "Suscribiendo" publishing: "Publicando" notResponding: "Sin respuestas" -instanceFollowing: "Siguiendo instancias" -instanceFollowers: "Seguidores de la instancia" -instanceUsers: "Usuarios de la instancia" +instanceFollowing: "Siguiendo en este servidor" +instanceFollowers: "Seguidores del servidor" +instanceUsers: "Usuarios de este servidor" changePassword: "Cambiar contraseña" security: "Seguridad" -retypedNotMatch: "No hay coincidencia" +retypedNotMatch: "No hay coincidencia." currentPassword: "Contraseña actual" newPassword: "Contraseña nueva" newPasswordRetype: "Contraseña nueva (repetir)" @@ -240,7 +259,9 @@ saved: "Guardado" messaging: "Chat" upload: "Subir" keepOriginalUploading: "Mantener la imagen original" -keepOriginalUploadingDescription: "Mantener la versión original al cargar imágenes. Si está desactivado, el navegador generará imágenes para la publicación web en el momento de recargar la página" +keepOriginalUploadingDescription: "Mantener la versión original al cargar imágenes. + Si está desactivado, el navegador generará imágenes para la publicación web en el + momento de recargar la página." fromDrive: "Desde el drive" fromUrl: "Desde la URL" uploadFromUrl: "Subir desde una URL" @@ -256,7 +277,7 @@ agreeTo: "De acuerdo con {0}" tos: "Términos de uso" start: "Comenzar" home: "Inicio" -remoteUserCaution: "Para el usuario remoto, la información está incompleta" +remoteUserCaution: "La información del usuario remoto tal vez esta incompleta." activity: "Actividad" images: "Imágenes" birthday: "Fecha de nacimiento" @@ -289,7 +310,8 @@ unableToDelete: "No se puede borrar" inputNewFileName: "Ingrese un nuevo nombre de archivo" inputNewDescription: "Ingrese nueva descripción" inputNewFolderName: "Ingrese un nuevo nombre de la carpeta" -circularReferenceFolder: "La carpeta de destino es una sub-carpeta de la carpeta que quieres mover." +circularReferenceFolder: "La carpeta de destino es una sub-carpeta de la carpeta que + quieres mover." hasChildFilesOrFolders: "No se puede borrar esta carpeta. No está vacía." copyUrl: "Copiar URL" rename: "Renombrar" @@ -306,8 +328,8 @@ unwatch: "Dejar de ver" accept: "Aceptar" reject: "Rechazar" normal: "Normal" -instanceName: "Nombre de la instancia" -instanceDescription: "Descripción de la instancia" +instanceName: "Nombre del servidor" +instanceDescription: "Descripción del servidor" maintainerName: "Nombre del administrador" maintainerEmail: "Correo del administrador" tosUrl: "URL de los términos de uso" @@ -318,12 +340,13 @@ dayX: "Día {day}" monthX: "Mes {month}" yearX: "Año {year}" pages: "Páginas" -integration: "Integración" +integration: "Integraciones" connectService: "Conectar" disconnectService: "Desconectar" enableLocalTimeline: "Habilitar linea de tiempo local" enableGlobalTimeline: "Habilitar linea de tiempo global" -disablingTimelinesInfo: "Aunque se desactiven estas lineas de tiempo, por conveniencia el administrador y los moderadores pueden seguir usándolos" +disablingTimelinesInfo: "Aunque se desactiven estas lineas de tiempo, por conveniencia + el administrador y los moderadores pueden seguir usándolos" registration: "Registro" enableRegistration: "Permitir nuevos registros" invite: "Invitar" @@ -335,11 +358,13 @@ bannerUrl: "URL de la imagen del banner" backgroundImageUrl: "URL de la imagen de fondo" basicInfo: "Información básica" pinnedUsers: "Usuarios fijados" -pinnedUsersDescription: "Describir los usuarios que quiere fijar en la página \"Descubrir\" separados por una linea nueva" +pinnedUsersDescription: "Describir los usuarios que quiere fijar en la pestaña \"\ + Explorar\" separados por líneas nuevas." pinnedPages: "Páginas fijadas" -pinnedPagesDescription: "Describa las rutas de las páginas que desea fijar a la página principal de la instancia, separadas por lineas nuevas" +pinnedPagesDescription: "Describa las rutas de las páginas que desea fijar a la página + principal del servidor, separadas por líneas nuevas." pinnedClipId: "Id del clip fijado" -pinnedNotes: "Nota fijada" +pinnedNotes: "Publicación fijada" hcaptcha: "hCaptcha" enableHcaptcha: "Habilitar hCaptcha" hcaptchaSiteKey: "Clave del sitio" @@ -348,22 +373,25 @@ recaptcha: "reCAPTCHA" enableRecaptcha: "activar reCAPTCHA" recaptchaSiteKey: "Clave del sitio" recaptchaSecretKey: "Clave secreta" -avoidMultiCaptchaConfirm: "El uso de múltiples Captchas puede causar interferencia. ¿Desea desactivar el otro Captcha? Puede dejar múltiples Captchas habilitadas presionando cancelar." +avoidMultiCaptchaConfirm: "El uso de múltiples Captchas puede causar interferencia. + ¿Desea desactivar el otro Captcha? Puede dejar múltiples Captchas habilitadas presionando + cancelar." antennas: "Antenas" manageAntennas: "Administrar antenas" name: "Nombre" antennaSource: "Origen de la antena" antennaKeywords: "Palabras clave para recibir" antennaExcludeKeywords: "Palabras clave para excluir" -antennaKeywordsDescription: "Separar con espacios es una declaración AND, separar con una linea nueva es una declaración OR" -notifyAntenna: "Notificar nueva nota" -withFileAntenna: "Sólo notas con archivos adjuntados" +antennaKeywordsDescription: "Separar con espacios es una declaración AND, separar + con una linea nueva es una declaración OR" +notifyAntenna: "Notificar nueva publicación" +withFileAntenna: "Sólo publicaciones con archivos adjuntados" enableServiceworker: "Activar ServiceWorker" antennaUsersDescription: "Elegir nombres de usuarios separados por una linea nueva" caseSensitive: "Distinguir mayúsculas de minúsculas" withReplies: "Incluir respuestas" connectedTo: "Estas cuentas están conectadas" -notesAndReplies: "Notas y respuestas" +notesAndReplies: "Publicaciones y respuestas" withFiles: "Adjuntos" silence: "Silenciar" silenceConfirm: "¿Desea silenciar al usuario?" @@ -378,7 +406,7 @@ exploreFediverse: "Explorar fediverso" popularTags: "Etiquetas populares" userList: "Lista" about: "Información" -aboutMisskey: "Sobre Misskey" +aboutFirefish: "Sobre Firefish" administrator: "Administrador" token: "Token" twoStepAuthentication: "Autenticación de dos factores" @@ -400,7 +428,7 @@ notFoundDescription: "No se encontró la página correspondiente a la URL elegid uploadFolder: "Carpeta de subidas por defecto" cacheClear: "Borrar caché" markAsReadAllNotifications: "Marcar todas las notificaciones como leídas" -markAsReadAllUnreadNotes: "Marcar todas las notas como leídas" +markAsReadAllUnreadNotes: "Marcar todas las publicaciones como leídas" markAsReadAllTalkMessages: "Marcar todos los chats como leídos" help: "Ayuda" inputMessageHere: "Escribe el mensaje aquí" @@ -421,7 +449,7 @@ text: "Texto" enable: "Activar" next: "Siguiente" retype: "Intentar de nuevo" -noteOf: "Notas de {user}" +noteOf: "Publicaciones de {user}" inviteToGroup: "Invitar al grupo" quoteAttached: "Cita añadida" quoteQuestion: "¿Quiere añadir una cita?" @@ -443,7 +471,8 @@ strongPassword: "Muy buena contraseña" passwordMatched: "Correcto" passwordNotMatched: "Las contraseñas no son las mismas" signinWith: "Inicie sesión con {x}" -signinFailed: "Autenticación fallida. Asegúrate de haber usado el nombre de usuario y contraseña correctos." +signinFailed: "Autenticación fallida. Asegúrate de haber usado el nombre de usuario + y contraseña correctos." tapSecurityKey: "Toque la clave de seguridad" or: "O" language: "Idioma" @@ -453,7 +482,8 @@ aboutX: "Acerca de {x}" useOsNativeEmojis: "Usa los emojis nativos de la plataforma" disableDrawer: "No mostrar los menús en cajones" youHaveNoGroups: "Sin grupos" -joinOrCreateGroup: "Obtenga una invitación para unirse al grupos o puede crear su propio grupo." +joinOrCreateGroup: "Obtenga una invitación para unirse al grupos o puede crear su + propio grupo." noHistory: "No hay datos en el historial" signinHistory: "Historial de ingresos" disableAnimatedMfm: "Deshabilitar MFM que tiene animaciones" @@ -479,29 +509,38 @@ accountSettings: "Ajustes de cuenta" promotion: "Promovido" promote: "Promover" numberOfDays: "Cantidad de dias" -hideThisNote: "Ocultar esta nota" -showFeaturedNotesInTimeline: "Mostrar notas destacadas en la línea de tiempo" +hideThisNote: "Ocultar esta publicación" +showFeaturedNotesInTimeline: "Mostrar publicaciones destacadas en la línea de tiempo" objectStorage: "Almacenamiento de objetos" useObjectStorage: "Usar almacenamiento de objetos" objectStorageBaseUrl: "Base URL" -objectStorageBaseUrlDesc: "Prefijo de URL utilizado para construir URL para hacer referencia a objetos (medios). Especifique su URL si está utilizando un CDN o Proxy; de lo contrario, especifique la dirección a la que se puede acceder públicamente de acuerdo con la guía de servicio que va a utilizar. i.g 'https://.s3.amazonaws.com' para AWS S3 y 'https://storage.googleapis.com/' para GCS." +objectStorageBaseUrlDesc: "Prefijo de URL utilizado para construir URL para hacer + referencia a objetos (medios). Especifique su URL si está utilizando un CDN o Proxy; + de lo contrario, especifique la dirección a la que se puede acceder públicamente + de acuerdo con la guía de servicio que va a utilizar. i.g 'https://.s3.amazonaws.com' + para AWS S3 y 'https://storage.googleapis.com/' para GCS." objectStorageBucket: "Bucket" -objectStorageBucketDesc: "Especifique el nombre del depósito utilizado en el servicio configurado." +objectStorageBucketDesc: "Especifique el nombre del depósito utilizado en el servicio + configurado." objectStoragePrefix: "Prefix" objectStoragePrefixDesc: "Los archivos se almacenarán en el directorio de este prefijo." objectStorageEndpoint: "Endpoint" -objectStorageEndpointDesc: "Deje esto en blanco si está utilizando AWS S3; de lo contrario, especifique el punto final como '' o ': ' de acuerdo con la guía de servicio que va a utilizar." +objectStorageEndpointDesc: "Deje esto en blanco si está utilizando AWS S3; de lo contrario, + especifique el punto final como '' o ': ' de acuerdo con la guía + de servicio que va a utilizar." objectStorageRegion: "Region" -objectStorageRegionDesc: "Especifique una región como 'xx-east-1'. Si su servicio no tiene distinción sobre regiones, déjelo en blanco o complete con 'us-east-1'." +objectStorageRegionDesc: "Especifique una región como 'xx-east-1'. Si su servicio + no tiene distinción sobre regiones, déjelo en blanco o complete con 'us-east-1'." objectStorageUseSSL: "Usar SSL" objectStorageUseSSLDesc: "Desactive esto si no va a usar HTTPS para la conexión API" objectStorageUseProxy: "Conectarse a través de Proxy" -objectStorageUseProxyDesc: "Desactive esto si no va a usar Proxy para la conexión de Almacenamiento de objetos" +objectStorageUseProxyDesc: "Desactive esto si no va a usar Proxy para la conexión + de Almacenamiento de objetos" objectStorageSetPublicRead: "Seleccionar \"public-read\" al subir " serverLogs: "Registros del servidor" deleteAll: "Eliminar todos" showFixedPostForm: "Mostrar el formulario de las entradas encima de la línea de tiempo" -newNoteRecived: "Tienes una nota nuevo" +newNoteRecived: "Tienes unas publicaciones nuevas" sounds: "Sonidos" listen: "Escuchar" none: "Ninguna" @@ -524,7 +563,8 @@ sort: "Ordenar" ascendingOrder: "Ascendente" descendingOrder: "Descendente" scratchpad: "Scratch pad" -scratchpadDescription: "Scratchpad proporciona un entorno experimental para AiScript. Puede escribir, ejecutar y verificar los resultados que interactúan con Misskey." +scratchpadDescription: "Scratchpad proporciona un entorno experimental para AiScript. + Puede escribir, ejecutar y verificar los resultados que interactúan con Firefish." output: "Salida" script: "Script" disablePagesScript: "Deshabilitar AiScript en Páginas" @@ -532,11 +572,14 @@ updateRemoteUser: "Actualizar información de usuario remoto" deleteAllFiles: "Borrar todos los archivos" deleteAllFilesConfirm: "¿Desea borrar todos los archivos?" removeAllFollowing: "Retener todos los siguientes" -removeAllFollowingDescription: "Cancelar todos los siguientes del servidor {host}. Ejecutar en caso de que esta instancia haya dejado de existir" +removeAllFollowingDescription: "Cancelar todos los siguientes del servidor {host}. + Ejecutar en caso de que esta instancia haya dejado de existir." userSuspended: "Este usuario ha sido suspendido." userSilenced: "Este usuario ha sido silenciado." yourAccountSuspendedTitle: "Esta cuenta ha sido suspendida" -yourAccountSuspendedDescription: "Esta cuenta ha sido suspendida debido a violaciones de los términos de servicio del servidor y otras razones. Para más información, póngase en contacto con el administrador. Por favor, no cree una nueva cuenta." +yourAccountSuspendedDescription: "Esta cuenta ha sido suspendida debido a violaciones + de los términos de servicio del servidor y otras razones. Para más información, + póngase en contacto con el administrador. Por favor, no cree una nueva cuenta." menu: "Menú" divider: "Divisor" addItem: "Agregar elemento" @@ -545,8 +588,8 @@ addRelay: "Agregar relé" inboxUrl: "Inbox URL" addedRelays: "Relés añadidos" serviceworkerInfo: "Se necesita activar para usar las notificaciones push" -deletedNote: "Nota eliminada" -invisibleNote: "Nota oculta" +deletedNote: "Publicación eliminada" +invisibleNote: "Publicación oculta" enableInfiniteScroll: "Activar scroll infinito" visibility: "Visibilidad" poll: "Encuesta" @@ -590,14 +633,16 @@ smtpHost: "Host" smtpPort: "Puerto" smtpUser: "Nombre de usuario" smtpPass: "Contraseña" -emptyToDisableSmtpAuth: "Deje el nombre del usuario y la contraseña en blanco para deshabilitar la autenticación SMTP" +emptyToDisableSmtpAuth: "Deje el nombre del usuario y la contraseña en blanco para + deshabilitar la autenticación SMTP" smtpSecure: "Usar SSL/TLS implícito en la conexión SMTP" smtpSecureInfo: "Apagar cuando se use STARTTLS" testEmail: "Prueba de envío" wordMute: "Silenciar palabras" regexpError: "Error de la expresión regular" -regexpErrorDescription: "Ocurrió un error en la expresión regular en la linea {line} de las palabras muteadas {tab}" -instanceMute: "Instancias silenciadas" +regexpErrorDescription: "Ocurrió un error en la expresión regular en la linea {line} + de las palabras muteadas {tab}" +instanceMute: "Servidores silenciados" userSaysSomething: "{name} dijo algo" makeActive: "Activar" display: "Apariencia" @@ -612,10 +657,13 @@ create: "Crear" notificationSetting: "Ajustes de Notificaciones" notificationSettingDesc: "Por favor elija el tipo de notificación a mostrar" useGlobalSetting: "Usar ajustes globales" -useGlobalSettingDesc: "Al activarse, se usará la configuración de notificaciones de la cuenta, al desactivarse se pueden hacer configuraciones particulares." +useGlobalSettingDesc: "Al activarse, se usará la configuración de notificaciones de + la cuenta, al desactivarse se pueden hacer configuraciones particulares." other: "Otro" regenerateLoginToken: "Regenerar token de login" -regenerateLoginTokenDescription: "Regenerar el token usado internamente durante el login. No siempre es necesario hacerlo. Al hacerlo de nuevo, se deslogueará en todos los dispositivos." +regenerateLoginTokenDescription: "Regenerar el token usado internamente durante el + login. No siempre es necesario hacerlo. Al hacerlo de nuevo, se deslogueará en todos + los dispositivos." setMultipleBySeparatingWithSpace: "Puedes añadir mas de uno, separado por espacios." fileIdOrUrl: "Id del archivo o URL" behavior: "Comportamiento" @@ -623,20 +671,22 @@ sample: "Muestra" abuseReports: "Reportes" reportAbuse: "Reportar" reportAbuseOf: "Reportar a {name}" -fillAbuseReportDescription: "Ingrese los detalles del reporte. Si hay una nota en particular, ingrese la URL de esta." +fillAbuseReportDescription: "Ingrese los detalles del reporte. Si hay una publicación + en particular, ingrese la URL de esta." abuseReported: "Se ha enviado el reporte. Muchas gracias." reporter: "Reportador" reporteeOrigin: "Reportar a" reporterOrigin: "Origen del reporte" -forwardReport: "Transferir un informe a una instancia remota" -forwardReportIsAnonymous: "No puede ver su información de la instancia remota y aparecerá como una cuenta anónima del sistema" +forwardReport: "Transferir reporte a un servidor remoto" +forwardReportIsAnonymous: "No puede ver su información del servidor remoto y aparecerá + como una cuenta anónima del sistema" send: "Enviar" abuseMarkAsResolved: "Marcar reporte como resuelto" openInNewTab: "Abrir en una Nueva Pestaña" openInSideView: "Abrir en una vista al costado" defaultNavigationBehaviour: "Navegación por defecto" editTheseSettingsMayBreakAccount: "Editar estas configuraciones puede dañar su cuenta." -instanceTicker: "Información de notas de la instancia" +instanceTicker: "Información de publicaciones de el servidor" waitingFor: "Esperando a {x}" random: "Aleatorio" system: "Sistema" @@ -647,12 +697,14 @@ createNew: "Crear" optional: "Opcional" createNewClip: "Crear clip nuevo" unclip: "Quitar clip" -confirmToUnclipAlreadyClippedNote: "Esta nota ya está incluida en el clip \"{name}\". ¿Quiere quitar la nota del clip?" +confirmToUnclipAlreadyClippedNote: "Esta publicación ya está incluida en el clip \"\ + {name}\". ¿Quiere quitar la nota del clip?" public: "Público" -i18nInfo: "Calckey está siendo traducido a varios idiomas gracias a voluntarios. Se puede colaborar traduciendo en {link}" +i18nInfo: "Firefish está siendo traducido a varios idiomas gracias a voluntarios. Se + puede colaborar traduciendo en {link}" manageAccessTokens: "Administrar tokens de acceso" accountInfo: "Información de la Cuenta" -notesCount: "Cantidad de notas" +notesCount: "Cantidad de publicaciones" repliesCount: "Cantidad de respuestas hechas" renotesCount: "Cantidad de renotas hechas" repliedCount: "Cantidad de respuestas recibidas" @@ -668,15 +720,21 @@ no: "No" driveFilesCount: "Cantidad de archivos en el drive" driveUsage: "Uso del drive" noCrawle: "Rechazar indexación del crawler" -noCrawleDescription: "Pedir a los motores de búsqueda que no indexen tu perfil, notas, páginas, etc." -lockedAccountInfo: "A menos que configures la visibilidad de tus notas como \"Sólo seguidores\", tus notas serán visibles para cualquiera, incluso si requieres que los seguidores sean aprobados manualmente." -alwaysMarkSensitive: "Marcar los medios de comunicación como contenido sensible por defecto" +noCrawleDescription: "Pedir a los motores de búsqueda que no indexen tu perfil, publicaciones, + páginas, etc." +lockedAccountInfo: "A menos que configures la visibilidad de tus notas como \"Sólo + seguidores\", tus notas serán visibles para cualquiera, incluso si requieres que + los seguidores sean aprobados manualmente." +alwaysMarkSensitive: "Marcar los medios de comunicación como contenido sensible por + defecto" loadRawImages: "Cargar las imágenes originales en lugar de mostrar las miniaturas" disableShowingAnimatedImages: "No reproducir imágenes animadas" -verificationEmailSent: "Se le ha enviado un correo electrónico de confirmación. Por favor, acceda al enlace proporcionado en el correo electrónico para completar la configuración." +verificationEmailSent: "Se le ha enviado un correo electrónico de confirmación. Por + favor, acceda al enlace proporcionado en el correo electrónico para completar la + configuración." notSet: "Sin especificar" emailVerified: "Su dirección de correo electrónico ha sido verificada." -noteFavoritesCount: "Número de notas favoritas" +noteFavoritesCount: "Número de publicaciones favoritas" pageLikesCount: "Número de favoritos en la página" pageLikedCount: "Número de favoritos de su página" contact: "Contacto" @@ -685,14 +743,16 @@ clips: "Clip" experimentalFeatures: "Características experimentales" developer: "Desarrolladores" makeExplorable: "Hacer visible la cuenta en \"Explorar\"" -makeExplorableDescription: "Si desactiva esta opción, su cuenta no aparecerá en la sección \"Explorar\"." +makeExplorableDescription: "Si desactiva esta opción, su cuenta no aparecerá en la + sección \"Explorar\"." showGapBetweenNotesInTimeline: "Mostrar un intervalo entre notas en la línea de tiempo" duplicate: "Duplicar" left: "Izquierda" center: "Centrar" wide: "Ancho" narrow: "Estrecho" -reloadToApplySetting: "Esta configuración sólo se aplicará después de recargar la página. ¿Recargar ahora?" +reloadToApplySetting: "Esta configuración sólo se aplicará después de recargar la + página. ¿Recargar ahora?" needReloadToApply: "Se requiere un reinicio para la aplicar los cambios" showTitlebar: "Mostrar la barra de título" clearCache: "Limpiar caché" @@ -700,7 +760,11 @@ onlineUsersCount: "{n} usuarios en línea" nUsers: "{n} Usuarios" nNotes: "{n} Notas" sendErrorReports: "Envíar informe de errores" -sendErrorReportsDescription: "Si habilita esta opción, ayudará a mejorar la calidad de Misskey compartiendo información detallada sobre los errores cuando se produzca un problema.\nEsto incluye información como la versión de su sistema operativo, el tipo de navegador que utiliza, su historial de actividad, etc." +sendErrorReportsDescription: "Si habilita esta opción, los detalles de los errores + serán compartidos con Firefish cuando ocurra un problema, lo que ayudará a mejorar + la calidad de Firefish. \nEsto incluye información como la versión del sistema operativo, + el tipo de navegador que está utilizando y su historial en Firefish, entre otros + datos." myTheme: "Mi Tema" backgroundColor: "Fondo" accentColor: "Acento" @@ -728,7 +792,8 @@ receiveAnnouncementFromInstance: "Recibir notificaciones de la instancia" emailNotification: "Notificaciones por correo electrónico" publish: "Publicar" inChannelSearch: "Buscar en el canal" -useReactionPickerForContextMenu: "Haga clic con el botón derecho para abrir el menu de reacciones" +useReactionPickerForContextMenu: "Haga clic con el botón derecho para abrir el menu + de reacciones" typingUsers: "{users} está escribiendo" jumpToSpecifiedDate: "Saltar a una fecha específica" showingPastTimeline: "Mostrar líneas de tiempo antiguas" @@ -739,14 +804,16 @@ unlikeConfirm: "¿Quitar como favorito?" fullView: "Vista completa" quitFullView: "quitar vista completa" addDescription: "Agregar descripción" -userPagePinTip: "Puede mantener sus notas visibles aquí seleccionando Pin en el menú de notas individuales" +userPagePinTip: "Puede mantener sus notas visibles aquí seleccionando Pin en el menú + de notas individuales" notSpecifiedMentionWarning: "Algunas menciones no están incluidas en el destino" info: "Información" userInfo: "Información del usuario" unknown: "Desconocido" onlineStatus: "En línea" hideOnlineStatus: "mostrarse como desconectado" -hideOnlineStatusDescription: "Ocultar su estado en línea puede reducir la eficacia de algunas funciones, como la búsqueda" +hideOnlineStatusDescription: "Ocultar su estado en línea puede reducir la eficacia + de algunas funciones, como la búsqueda" online: "En línea" active: "Activo" offline: "Sin conexión" @@ -781,7 +848,8 @@ emailNotConfiguredWarning: "No se ha configurado una dirección de correo electr ratio: "Proporción" previewNoteText: "Mostrar vista preliminar" customCss: "CSS personalizado" -customCssWarn: "Este ajuste sólo debe utilizarse si se sabe lo que hace. Introducir valores inadecuados puede hacer que el cliente deje de funcionar con normalidad." +customCssWarn: "Este ajuste sólo debe utilizarse si se sabe lo que hace. Introducir + valores inadecuados puede hacer que el cliente deje de funcionar con normalidad." global: "Global" squareAvatars: "Mostrar iconos cuadrados" sent: "Enviar" @@ -791,12 +859,14 @@ hashtags: "Hashtag" troubleshooting: "Solución de problemas" useBlurEffect: "Utilizar efecto de desenfoque en la interfaz de usuario" learnMore: "Ver más" -misskeyUpdated: "¡Misskey ha sido actualizado!" +misskeyUpdated: "¡Firefish ha sido actualizado!" whatIsNew: "Mostrar cambios" translate: "Traducir" translatedFrom: "Traducido de {x}" accountDeletionInProgress: "La eliminación de la cuenta está en curso" -usernameInfo: "Un nombre que identifique su cuenta de otras en este servidor. Puede utilizar el alfabeto (a~z, A~Z), dígitos (0~9) o guiones bajos (_). Los nombres de usuario no se pueden cambiar posteriormente." +usernameInfo: "Un nombre que identifique su cuenta de otras en este servidor. Puede + utilizar el alfabeto (a~z, A~Z), dígitos (0~9) o guiones bajos (_). Los nombres + de usuario no se pueden cambiar posteriormente." aiChanMode: "Modo Ai" keepCw: "Mantener la advertencia de contenido" pubSub: "Cuentas Pub/Sub" @@ -806,18 +876,21 @@ unresolved: "Sin resolver" breakFollow: "Dejar de seguir" itsOn: "¡Está encendido!" itsOff: "¡Está apagado!" -emailRequiredForSignup: "Se requere una dirección de correo electrónico para el registro de la cuenta" +emailRequiredForSignup: "Se requere una dirección de correo electrónico para el registro + de la cuenta" unread: "No leído" filter: "Filtro" controlPanel: "Panel de control" manageAccounts: "Administrar cuenta" makeReactionsPublic: "Hacer el historial de reacciones público" -makeReactionsPublicDescription: "Todas las reacciones que hayas hecho serán públicamente visibles." +makeReactionsPublicDescription: "Todas las reacciones que hayas hecho serán públicamente + visibles." classic: "Clásico" muteThread: "Ocultar hilo" unmuteThread: "Mostrar hilo" ffVisibility: "Visibilidad de seguidores y seguidos" -ffVisibilityDescription: "Puedes configurar quien puede ver a quienes sigues y quienes te siguen" +ffVisibilityDescription: "Puedes configurar quien puede ver a quienes sigues y quienes + te siguen" continueThread: "Ver la continuación del hilo" deleteAccountConfirm: "La cuenta será borrada. ¿Está seguro?" incorrectPassword: "La contraseña es incorrecta" @@ -858,14 +931,16 @@ thereIsUnresolvedAbuseReportWarning: "Hay reportes sin resolver" recommended: "Recomendado" check: "Verificar" driveCapOverrideLabel: "Cambiar la capacidad de la unidad para este usuario" -driveCapOverrideCaption: "Restablecer la capacidad a su predeterminado ingresando un valor de 0 o menos" +driveCapOverrideCaption: "Restablecer la capacidad a su predeterminado ingresando + un valor de 0 o menos" requireAdminForView: "Necesitas iniciar sesión como administrador para ver esto." isSystemAccount: "Cuenta creada y operada automáticamente por el sistema" typeToConfirm: "Ingrese {x} para confirmar" deleteAccount: "Borrar cuenta" document: "Documento" numberOfPageCache: "Cantidad de páginas cacheadas" -numberOfPageCacheDescription: "Al aumentar el número mejora la conveniencia pero tambien puede aumentar la carga y la memoria a usarse" +numberOfPageCacheDescription: "Al aumentar el número mejora la conveniencia pero tambien + puede aumentar la carga y la memoria a usarse" logoutConfirm: "¿Cerrar sesión?" lastActiveDate: "Utilizado por última vez el" statusbar: "Barra de estado" @@ -882,42 +957,58 @@ sensitiveMediaDetection: "Detección de contenido NSFW" localOnly: "Solo local" remoteOnly: "Sólo remoto" failedToUpload: "La subida falló" -cannotUploadBecauseInappropriate: "Este archivo no se puede subir debido a que algunas partes han sido detectadas comoNSFW." -cannotUploadBecauseNoFreeSpace: "La subida falló debido a falta de espacio libre en la unidad del usuario." +cannotUploadBecauseInappropriate: "Este archivo no se puede subir debido a que algunas + partes han sido detectadas comoNSFW." +cannotUploadBecauseNoFreeSpace: "La subida falló debido a falta de espacio libre en + la unidad del usuario." beta: "Beta" enableAutoSensitive: "Marcar automáticamente contenido NSFW" -enableAutoSensitiveDescription: "Permite la detección y marcado automático de contenido NSFW usando 'Machine Learning' cuando sea posible. Incluso si esta opción está desactivada, puede ser activado para toda la instancia." -activeEmailValidationDescription: "Habilita la validación estricta de direcciones de correo electrónico, lo cual incluye la revisión de direcciones desechables y si se puede comunicar con éstas. Cuando está deshabilitado, sólo el formato de la dirección es validado." +enableAutoSensitiveDescription: "Permite la detección y marcado automático de contenido + NSFW usando 'Machine Learning' cuando sea posible. Incluso si esta opción está desactivada, + puede ser activado para toda la instancia." +activeEmailValidationDescription: "Habilita la validación estricta de direcciones + de correo electrónico, lo cual incluye la revisión de direcciones desechables y + si se puede comunicar con éstas. Cuando está deshabilitado, sólo el formato de la + dirección es validado." navbar: "Barra de navegación" shuffle: "Aleatorio" account: "Cuentas" move: "Mover" _sensitiveMediaDetection: - description: "Reduce el esfuerzo de la moderación el el servidor a través del reconocimiento automático de contenido NSFW usando 'Machine Learning'. Esto puede incrementar ligeramente la carga en el servidor." + description: "Reduce el esfuerzo de la moderación de el servidor a través del reconocimiento + automático de contenido NSFW usando 'Machine Learning'. Esto puede incrementar + ligeramente la carga en el servidor." sensitivity: "Sensibilidad de detección" - sensitivityDescription: "Reducir la sensibilidad puede acarrear a varios falsos positivos, mientras que incrementarla puede reducir las detecciones (falsos negativos)." + sensitivityDescription: "Reducir la sensibilidad puede acarrear a varios falsos + positivos, mientras que incrementarla puede reducir las detecciones (falsos negativos)." setSensitiveFlagAutomatically: "Marcar como NSFW" - setSensitiveFlagAutomaticallyDescription: "Los resultados de la detección interna pueden ser retenidos incluso si la opción está desactivada." + setSensitiveFlagAutomaticallyDescription: "Los resultados de la detección interna + pueden ser retenidos incluso si la opción está desactivada." analyzeVideos: "Habilitar el análisis de videos" - analyzeVideosDescription: "Analizar videos en adición a las imágenes. Esto puede incrementar ligeramente la carga del servidor." + analyzeVideosDescription: "Analizar videos en adición a las imágenes. Esto puede + incrementar ligeramente la carga del servidor." _emailUnavailable: used: "Ya fue usado" - format: "Formato no válido." - disposable: "No es un correo reutilizable" + format: "El formato de este correo electrónico no es válido" + disposable: "No se pueden utilizar direcciones de correo electrónico desechables" mx: "Servidor de correo inválido" smtp: "Servidor de correo no disponible" _ffVisibility: - public: "Publicar" + public: "Público" followers: "Visible solo para seguidores" private: "Privado" _signup: almostThere: "Ya falta poco" emailAddressInfo: "Ingrese el correo electrónico que usa. Este no se hará público." - emailSent: "Se envió un correo de verificación a la dirección {email}. Acceda al link enviado en el correo para completar el ingreso." + emailSent: "Se envió un correo de verificación a la dirección {email}. Acceda al + link enviado en el correo para completar el ingreso." _accountDelete: accountDelete: "Eliminar Cuenta" - mayTakeTime: "La eliminación de la cuenta es un proceso que precisa de carga. Puede pasar un tiempo hasta que se complete si es mucho el contenido creado y los archivos subidos." - sendEmail: "Cuando se termine de borrar la cuenta, se enviará un correo a la dirección usada para el registro." + mayTakeTime: "La eliminación de la cuenta es un proceso que precisa de carga. Puede + pasar un tiempo hasta que se complete si es mucho el contenido creado y los archivos + subidos." + sendEmail: "Cuando se termine de borrar la cuenta, se enviará un correo a la dirección + usada para el registro." requestAccountDelete: "Pedir la eliminación de la cuenta." started: "El proceso de eliminación ha comenzado." inProgress: "La eliminación está en proceso." @@ -925,9 +1016,11 @@ _ad: back: "Deseleccionar" reduceFrequencyOfThisAd: "Mostrar menos este anuncio." _forgotPassword: - enterEmail: "Ingrese el correo usado para registrar la cuenta. Se enviará un link para resetear la contraseña." + enterEmail: "Ingrese el correo usado para registrar la cuenta. Se enviará un link + para resetear la contraseña." ifNoEmail: "Si no utilizó un correo para crear la cuenta, contáctese con el administrador." - contactAdmin: "Esta instancia no admite el uso de direcciones de correo electrónico, póngase en contacto con el administrador de la instancia para restablecer su contraseña" + contactAdmin: "Esta instancia no admite el uso de direcciones de correo electrónico, + póngase en contacto con el administrador de la instancia para restablecer su contraseña" _gallery: my: "Mi galería" liked: "Publicaciones que me gustan" @@ -950,12 +1043,15 @@ _preferencesBackups: save: "Guardar cambios" inputName: "Por favor, ingresa un nombre para este respaldo" cannotSave: "Fallo al guardar" - nameAlreadyExists: "Un respaldo llamado \"{name}\" ya existe. Por favor ingresa un nombre diferente" - applyConfirm: "¿Realmente quieres aplicar los cambios desde el archivo \"{name}\" a este dispositivo? Las configuraciones existentes serán sobreescritas. " + nameAlreadyExists: "Un respaldo llamado \"{name}\" ya existe. Por favor ingresa + un nombre diferente" + applyConfirm: "¿Realmente quieres aplicar los cambios desde el archivo \"{name}\"\ + \ a este dispositivo? Las configuraciones existentes serán sobreescritas. " saveConfirm: "¿Guardar respaldo como \"{name}\"?" deleteConfirm: "¿Borrar el respaldo \"{name}\"?" renameConfirm: "¿Renombrar este respaldo de \"{old}\" a \"{new}\"?" - noBackups: "No existen respaldos. Deberás respaldar las configuraciones del cliente en este servidor usando \"Crear nuevo respaldo\"" + noBackups: "No existen respaldos. Deberás respaldar las configuraciones del cliente + en este servidor usando \"Crear nuevo respaldo\"" createdAt: "Creado: {date} {time}" updatedAt: "Actualizado: {date} {time}" cannotLoad: "La carga falló" @@ -966,25 +1062,30 @@ _registry: keys: "Clave" domain: "Dominio" createKey: "Crear una llave" -_aboutMisskey: - about: "Misskey es un software de código abierto, desarrollado por syuilo desde el 2014" +_aboutFirefish: + about: "Firefish es una bifurcación de Misskey creada por ThatOneCalculator, que + ha estado en desarrollo desde el 2022." contributors: "Principales colaboradores" allContributors: "Todos los colaboradores" source: "Código fuente" - translation: "Traducir Misskey" - donate: "Donar a Misskey" - morePatrons: "Muchas más personas nos apoyan. Muchas gracias🥰" - patrons: "Patrocinadores" + translation: "Traducir Firefish" + donate: "Donar a Firefish" + morePatrons: "También apreciamos el apoyo de muchos más que no están enlistados + aquí. ¡Gracias! 🥰" + patrons: "Mecenas de Firefish" _nsfw: respect: "Ocultar medios NSFW" ignore: "No esconder medios NSFW " force: "Ocultar todos los medios" _mfm: cheatSheet: "Hoja de referencia de MFM" - intro: "MFM es un lenguaje de marcado dedicado que se puede usar en varios lugares dentro de Misskey. Aquí puede ver una lista de sintaxis disponibles en MFM." - dummy: "Misskey expande el mundo de la Fediverso" + intro: "MFM es un lenguaje de marcado dedicado que se puede usar en varios lugares + dentro de Misskey, Firefish, Akkoma, y mucho más. Aquí puede ver una lista de sintaxis + disponibles en MFM." + dummy: "Firefish expande el mundo de la Fediverso" mention: "Menciones" - mentionDescription: "El signo @ seguido de un nombre de usuario se puede utilizar para notificar a un usuario en particular." + mentionDescription: "El signo @ seguido de un nombre de usuario se puede utilizar + para notificar a un usuario en particular." hashtag: "Hashtag" hashtagDescription: "Puede especificar un hashtag con un numeral y el texto." url: "URL" @@ -1000,7 +1101,8 @@ _mfm: inlineCode: "Código (insertado)" inlineCodeDescription: "Muestra el código de un programa resaltando su sintaxis" blockCode: "Código (bloque)" - blockCodeDescription: "Código de resaltado de sintaxis, como programas de varias líneas con bloques." + blockCodeDescription: "Código de resaltado de sintaxis, como programas de varias + líneas con bloques." inlineMath: "Fórmula (insertado)" inlineMathDescription: "Muestra fórmulas (KaTeX) insertadas" blockMath: "Fórmula (bloque)" @@ -1012,7 +1114,8 @@ _mfm: search: "Buscar" searchDescription: "Muestra una caja de búsqueda con texto pre-escrito" flip: "Echar de un capirotazo" - flipDescription: "Voltea el contenido hacia arriba / abajo o hacia la izquierda / derecha." + flipDescription: "Voltea el contenido hacia arriba / abajo o hacia la izquierda + / derecha." jelly: "Animación (gelatina)" jellyDescription: "Aplica un efecto de animación tipo gelatina" tada: "Animación (tadá)" @@ -1034,7 +1137,8 @@ _mfm: x4: "Totalmente grande" x4Description: "Muestra el contenido totalmente grande" blur: "Desenfoque" - blurDescription: "Para desenfocar el contenido. Se muestra claramente al colocar el puntero encima." + blurDescription: "Para desenfocar el contenido. Se muestra claramente al colocar + el puntero encima." font: "Fuente" fontDescription: "Elegir la fuente del contenido" rainbow: "Arcoíris" @@ -1044,7 +1148,9 @@ _mfm: rotate: "Rotar" rotateDescription: "Rota el contenido a un ángulo especificado." plain: "Plano" - plainDescription: "Desactiva los efectos de todo el contenido MFM con este efecto MFM." + plainDescription: "Desactiva los efectos de todo el contenido MFM con este efecto + MFM." + position: Posición _instanceTicker: none: "No mostrar" remote: "Mostrar a usuarios remotos" @@ -1053,6 +1159,7 @@ _serverDisconnectedBehavior: reload: "Recargar automáticamente" dialog: "Mostrar diálogo de advertencia" quiet: "Advertencia discreta" + nothing: Hacer nada _channel: create: "Crear canal" edit: "Editar canal" @@ -1063,6 +1170,8 @@ _channel: following: "Siguiendo" usersCount: "{n} participantes" notesCount: "{n} notas" + nameOnly: Nombre solamente + nameAndDescription: Nombre y descripción _menuDisplay: sideFull: "Horizontal" sideIcon: "Horizontal (ícono)" @@ -1070,15 +1179,19 @@ _menuDisplay: hide: "Ocultar" _wordMute: muteWords: "Palabras que silenciar" - muteWordsDescription: "Separar con espacios indica una declaracion And, separar con lineas nuevas indica una declaracion Or。" - muteWordsDescription2: "Encerrar las palabras clave entre numerales para usar expresiones regulares" + muteWordsDescription: "Separar con espacios indica una declaracion And, separar + con lineas nuevas indica una declaracion Or。" + muteWordsDescription2: "Encerrar las palabras clave entre numerales para usar expresiones + regulares" softDescription: "Ocultar en la linea de tiempo las notas que cumplen las condiciones" - hardDescription: "Evitar que se agreguen a la linea de tiempo las notas que cumplen las condiciones. Las notas no agregadas seguirán quitadas aunque cambien las condiciones." + hardDescription: "Evitar que se agreguen a la linea de tiempo las notas que cumplen + las condiciones. Las notas no agregadas seguirán quitadas aunque cambien las condiciones." soft: "Suave" hard: "Duro" mutedNotes: "Notas silenciadas" _instanceMute: - instanceMuteDescription: "Silencia todas las notas y reposts de la instancias seleccionadas, incluyendo respuestas a los usuarios de las mismas" + instanceMuteDescription: "Silencia todas las notas y reposts de la instancias seleccionadas, + incluyendo respuestas a los usuarios de las mismas" instanceMuteDescription2: "Separar por líneas" title: "Oculta las notas de las instancias listadas." heading: "Instancias a silenciar" @@ -1180,36 +1293,51 @@ _time: hour: "Horas" day: "Días" _tutorial: - title: "Cómo usar Calckey" + title: "Cómo usar Firefish" step1_1: "¡Bienvenido!" - step1_2: "Vamos a configurarte. Estarás listo y funcionando en poco tiempo" + step1_2: "Vamos a configurarte. ¡Estarás listo y funcionando en poco tiempo!" step2_1: "En primer lugar, rellena tu perfil" - step2_2: "Proporcionar algo de información sobre quién eres hará que sea más fácil para los demás saber si quieren ver tus notas o seguirte." + step2_2: "Proporcionar algo de información sobre quién eres hará que sea más fácil + para los demás saber si quieren ver tus notas o seguirte." step3_1: "¡Ahora es el momento de seguir a algunas personas!" - step3_2: "Tu página de inicio y tus líneas de tiempo sociales se basan en quién sigues, así que intenta seguir un par de cuentas para empezar.\nHaz clic en el círculo más en la parte superior derecha de un perfil para seguirlos." + step3_2: "Tu página de inicio y tus líneas de tiempo sociales se basan en quién + sigues, así que intenta seguir un par de cuentas para empezar.\nHaz clic en el + círculo más en la parte superior derecha de un perfil para seguirlos." step4_1: "Vamos a salir a la calle" - step4_2: "Para tu primer post, a algunas personas les gusta hacer un post de {introduction} o un simple \"¡Hola mundo!\"" + step4_2: "Para tu primer post, a algunas personas les gusta hacer un post de {introduction} + o un simple \"¡Hola mundo!\"" step5_1: "¡Líneas de tiempo, líneas de tiempo por todas partes!" step5_2: "Su instancia tiene {timelines} diferentes líneas de tiempo habilitadas" - step5_3: "La línea de tiempo Inicio {icon} es donde puedes ver las publicaciones de tus seguidores." - step5_4: "La línea de tiempo Local {icon} es donde puedes ver las publicaciones de todos los demás en esta instancia." - step5_5: "La línea de tiempo {icon} recomendada es donde puedes ver las publicaciones de las instancias que los administradores recomiendan." - step5_6: "La línea de tiempo Social {icon} es donde puedes ver las publicaciones de los amigos de tus seguidores." - step5_7: "La línea de tiempo Global {icon} es donde puedes ver las publicaciones de todas las demás instancias conectadas." + step5_3: "La línea de tiempo Inicio {icon} es donde puedes ver las publicaciones + de tus seguidores." + step5_4: "La línea de tiempo Local {icon} es donde puedes ver las publicaciones + de todos los demás en esta instancia." + step5_5: "La línea de tiempo {icon} recomendada es donde puedes ver las publicaciones + de las instancias que los administradores recomiendan." + step5_6: "La línea de tiempo Social {icon} es donde puedes ver las publicaciones + de los amigos de tus seguidores." + step5_7: "La línea de tiempo Global {icon} es donde puedes ver las publicaciones + de todas las demás instancias conectadas." step6_1: "Entonces, ¿qué es este lugar?" - step6_2: "Bueno, no sólo te has unido a Calckey. Te has unido a un portal del Fediverso, una red interconectada de miles de servidores, llamada \"instancias\"" - step6_3: "Cada servidor funciona de forma diferente, y no todos los servidores ejecutan Calckey. Sin embargo, ¡éste lo hace! Es un poco complicado, pero le cogerás el tranquillo enseguida" + step6_2: "Bueno, no sólo te has unido a Firefish. Te has unido a un portal del Fediverso, + una red interconectada de miles de servidores, llamada \"instancias\"" + step6_3: "Cada servidor funciona de forma diferente, y no todos los servidores ejecutan + Firefish. Sin embargo, ¡éste lo hace! Es un poco complicado, pero le cogerás el + tranquillo enseguida" step6_4: "¡Ahora ve, explora y diviértete!" _2fa: alreadyRegistered: "Ya has completado la configuración." - registerDevice: "Registrar dispositivo" - registerKey: "Registrar clave" - step1: "Primero, instale en su dispositivo la aplicación de autenticación {a} o {b} u otra." + registerTOTP: "Registrar dispositivo" + registerSecurityKey: "Registrar clave" + step1: "Primero, instale en su dispositivo la aplicación de autenticación {a} o + {b} u otra." step2: "Luego, escanee con la aplicación el código QR mostrado en pantalla." step2Url: "En una aplicación de escritorio se puede ingresar la siguiente URL:" step3: "Para terminar, ingrese el token mostrado en la aplicación." step4: "Ahora cuando inicie sesión, ingrese el mismo token" - securityKeyInfo: "Se puede configurar el inicio de sesión usando una clave de seguridad de hardware que soporte FIDO2 o con un certificado de huella digital o con un PIN" + securityKeyInfo: "Se puede configurar el inicio de sesión usando una clave de seguridad + de hardware que soporte FIDO2 o con un certificado de huella digital o con un + PIN" _permissions: "read:account": "Ver información de la cuenta" "write:account": "Editar información de la cuenta" @@ -1245,7 +1373,8 @@ _permissions: "write:gallery-likes": "Editar favoritos de la galería" _auth: shareAccess: "¿Desea permitir el acceso a la cuenta \"{name}\"?" - shareAccessAsk: "¿Está seguro de que desea autorizar esta aplicación para acceder a su cuenta?" + shareAccessAsk: "¿Está seguro de que desea autorizar esta aplicación para acceder + a su cuenta?" permissionAsk: "Esta aplicación requiere los siguientes permisos" pleaseGoBack: "Por favor, vuelve a la aplicación" callback: "Volviendo a la aplicación" @@ -1287,6 +1416,9 @@ _widgets: serverMetric: "Estadísticas del servidor" aiscript: "Consola de AiScript" aichan: "indigo" + userList: Lista Usuarios + _userList: + chooseList: Seleccione una lista _cw: hide: "Ocultar" show: "Ver más" @@ -1343,11 +1475,13 @@ _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" changeBanner: "Cambiar banner" + locationDescription: Si ingresas tu ciudad primero, el tiempo local tuyo será visible + para otros usuarios. _exportOrImport: allNotes: "Todas las notas" followingList: "Siguiendo" @@ -1387,6 +1521,7 @@ _timelines: local: "Local" social: "Social" global: "Global" + recommended: Recomendado _pages: newPage: "Crear página" editPage: "Editar página" @@ -1633,7 +1768,8 @@ _pages: _seedRandomPick: arg1: "Semilla" arg2: "Listas" - DRPWPM: "Elegir aleatoriamente de la lista ponderada (Diariamente para cada usuario)" + DRPWPM: "Elegir aleatoriamente de la lista ponderada (Diariamente para cada + usuario)" _DRPWPM: arg1: "Lista de texto" pick: "Elegir de la lista" @@ -1653,7 +1789,7 @@ _pages: splitStrByLine: "Separar texto en lineas" _splitStrByLine: arg1: "Texto" - ref: "Variables" + ref: "Variable" aiScriptVar: "Variable de AiScript" fn: "funciones" _fn: @@ -1664,7 +1800,8 @@ _pages: _for: arg1: "Cantidad de repeticiones" arg2: "Acción" - typeError: "El slot {slot} acepta el tipo {expect} pero fue ingresado el tipo {actual}" + typeError: "El slot {slot} acepta el tipo \"{expect}\" pero fue ingresado el tipo + \"{actual}\"" thereIsEmptySlot: "El slot {slot} está vacío" types: string: "Texto" @@ -1728,8 +1865,10 @@ _deck: newProfile: "Nuevo perfil" deleteProfile: "Eliminar perfil" introduction: "¡Crea la interfaz perfecta para tí organizando las columnas libremente!" - introduction2: "Presiona en la + de la derecha de la pantalla para añadir nuevas columnas donde quieras." - widgetsIntroduction: "Por favor selecciona \"Editar Widgets\" en el menú columna y agrega un widget." + introduction2: "Presiona en la + de la derecha de la pantalla para añadir nuevas + columnas donde quieras." + widgetsIntroduction: "Por favor selecciona \"Editar Widgets\" en el menú columna + y agrega un widget." _columns: main: "Principal" widgets: "Widgets" @@ -1739,3 +1878,66 @@ _deck: list: "Listas" mentions: "Menciones" direct: "Mensaje directo" +manageGroups: Administrar grupos +replayTutorial: Repetir Tutorial +privateMode: Modo privado +addInstance: Añadir un servidor +renoteMute: Silenciar impulsos +renoteUnmute: Dejar de silenciar impulsos +flagSpeakAsCat: Habla como un gato +selectInstance: Selecciona un servidor +flagSpeakAsCatDescription: Tu publicación se "nyanified" cuando esté en modo gato +allowedInstances: Instancias en la lista blanca +breakFollowConfirm: ¿Estás seguro de que quieres eliminar el seguidor? +subscribePushNotification: Habilitar notificaciones +unsubscribePushNotification: Desactivar notificaciones +pushNotificationAlreadySubscribed: Las notificaciones ya están activados +pushNotificationNotSupported: Su navegador o instancia no admite notificaciones +moveAccount: ¡Mover cuenta! +moveFrom: Mueve a esta cuenta de una cuenta antigua +moveFromLabel: 'La cuenta que estás moviendo de:' +moveAccountDescription: '' +license: Licencia +noThankYou: No gracias +userSaysSomethingReason: '{name} dijo {reason}' +hiddenTags: Etiquetas Ocultas +noInstances: No hay servidores +accountMoved: 'Usuario ha movido a una cuenta nueva:' +caption: Auto Subtítulos +showAds: Mostrar Anuncios +enterSendsMessage: Presione "RETORNO" en los mensajes para enviar el mensaje (para + apagarlo es Ctrl + RETORNO) +recommendedInstances: Instancias Recomendadas +instanceSecurity: Seguridad de la instancia +seperateRenoteQuote: Separar impulsados y Citar botones +_messaging: + groups: Grupos + dms: Privado +pushNotification: Notificaciones +apps: Aplicaciones +migration: Migración +silenced: Silenciado +deleted: Eliminado +edited: 'Editado a las {date} {time}' +editNote: Editar nota +silenceThisInstance: Silenciar este servidor +findOtherInstance: Buscar otro servidor +userSaysSomethingReasonRenote: '{name} impulsó una publicación que contiene {reason]' +enableRecommendedTimeline: Habilitar línea de tiempo "Recomendado" +searchPlaceholder: Buscar en Firefish +listsDesc: Las listas te permiten crear líneas de tiempo con usuarios específicos. + Puedes acceder a ellas desde la pestaña "Línea de tiempo". +removeReaction: Quitar tu reacción +selectChannel: Seleccionar canal +showEmojisInReactionNotifications: Mostrar emojis en notificaciones de reacciones +silencedInstancesDescription: Escriba los hosts de los servidores que desea bloquear. + Las cuentas en estos servidores serán tratadas como "silenciadas", solo podrán hacer + solicitudes de seguimiento, y no podrán mencionar a usuarios de este servidor si + no les siguen. Esto no afecta los servidores bloqueados. +silencedInstances: Servidores silenciados +hiddenTagsDescription: 'Escriba los hashtags (sin el #) que desea ocultar de las secciones + de Tendencias y Explorar. Los hashtags ocultos seguirán siendo descubribles por + otros métodos.' +jumpToPrevious: Ver anterior +enableEmojiReactions: Habilitar reacciones de emoji +cw: Aviso de contenido diff --git a/locales/fi.yml b/locales/fi.yml new file mode 100644 index 0000000000..efbbd495b5 --- /dev/null +++ b/locales/fi.yml @@ -0,0 +1,970 @@ +_lang_: "Suomi" +username: Käyttäjänimi +fetchingAsApObject: Hae Fedeversestä +gotIt: Selvä! +cancel: Peruuta +enterUsername: Anna käyttäjänimi +renotedBy: Buustannut {user} +noNotes: Ei lähetyksiä +noNotifications: Ei ilmoituksia +instance: Instanssi +settings: Asetukset +basicSettings: Perusasetukset +otherSettings: Muut asetukset +openInWindow: Avaa ikkunaan +profile: Profiili +timeline: Aikajana +noAccountDescription: Käyttäjä ei ole vielä kirjoittanut kuvaustaan vielä. +login: Kirjaudu sisään +loggingIn: Kirjautuu sisään +logout: Kirjaudu ulos +uploading: Tallentaa ylös... +save: Tallenna +favorites: Kirjanmerkit +unfavorite: Poista kirjanmerkeistä +favorited: Lisätty kirjanmerkkeihin. +alreadyFavorited: Lisätty jo kirjanmerkkeihin. +cantFavorite: Ei voitu lisätä kirjanmerkkeihin. +pin: Kiinnitä profiiliin +unpin: Irroita profiilista +delete: Poista +forgotPassword: Unohtunut salasana +search: Etsi +notifications: Ilmoitukset +password: Salasana +ok: OK +noThankYou: Ei kiitos +signup: Rekisteröidy +users: Käyttäjät +addUser: Lisää käyttäjä +addInstance: Lisää instanssi +favorite: Lisää kirjanmerkkeihin +copyContent: Kopioi sisältö +deleteAndEdit: Poista ja muokkaa +copyLink: Kopioi linkki +makeFollowManuallyApprove: Seuraajapyyntö vaatii hyväksymistä +follow: Seuraa +pinned: Kiinnitä profiiliin +followRequestPending: Seuraajapyyntö odottaa +you: Sinä +unrenote: Peruuta buustaus +reaction: Reaktiot +reactionSettingDescription2: Vedä uudelleenjärjestelläksesi, napsauta poistaaksesi, + paina "+" lisätäksesi. +attachCancel: Poista liite +enterFileName: Anna tiedostonimi +mute: Hiljennä +unmute: Poista hiljennys +headlineMisskey: Avoimen lähdekoodin, hajautettu sosiaalisen median alusta, joka on + ikuisesti ilmainen! 🚀 +monthAndDay: '{day}/{month}' +deleteAndEditConfirm: Oletko varma, että haluat poistaa tämän lähetyksen ja muokata + sitä? Menetät kaikki reaktiot, buustaukset ja vastaukset lähetyksestäsi. +addToList: Lisää listaan +sendMessage: Lähetä viesti +reply: Vastaa +loadMore: Lataa enemmän +showMore: Näytä enemmän +receiveFollowRequest: Seuraajapyyntö vastaanotettu +followRequestAccepted: Seuraajapyyntö hyväksytty +mentions: Maininnat +importAndExport: Tuo/Vie Tietosisältö +import: Tuo +export: Vie +files: Tiedostot +download: Lataa +unfollowConfirm: Oletko varma, ettet halua seurata enää käyttäjää {name}? +noLists: Sinulla ei ole listoja +note: Viesti +notes: Viestit +following: Seuraa +createList: Luo lista +manageLists: Hallitse listoja +error: Virhe +somethingHappened: On tapahtunut virhe +retry: Yritä uudelleen +pageLoadError: Virhe ladattaessa sivua. +serverIsDead: Tämä palvelin ei vastaa. Yritä hetken kuluttua uudelleen. +youShouldUpgradeClient: Nähdäksesi tämän sivun, virkistä päivittääksesi asiakasohjelmasi. +privacy: Tietosuoja +defaultNoteVisibility: Oletusnäkyvyys +followRequest: Seuraajapyyntö +followRequests: Seuraajapyynnöt +unfollow: Poista seuraaminen +enterEmoji: Syötä emoji +renote: Buustaa +renoted: Buustattu. +cantRenote: Tätä lähetystä ei voi buustata. +cantReRenote: Buustausta ei voi buustata. +quote: Lainaus +pinnedNote: Lukittu lähetys +clickToShow: Napsauta nähdäksesi +sensitive: Herkkää sisältöä (NSFW) +add: Lisää +enableEmojiReactions: Ota käyttöön emoji-reaktiot +showEmojisInReactionNotifications: Näytä emojit reaktioilmoituksissa +reactionSetting: Reaktiot näytettäväksi reaktiovalitsimessa +rememberNoteVisibility: Muista lähetyksen näkyvyysasetukset +markAsSensitive: Merkitse herkäksi sisällöksi (NSFW) +unmarkAsSensitive: Poista merkintä herkkää sisältöä (NSFW) +renoteMute: Hiljennä buustit +renoteUnmute: Poista buustien hiljennys +block: Estä +unblock: Poista esto +unsuspend: Poista keskeytys +suspend: Keskeytys +blockConfirm: Oletko varma, että haluat estää tämän tilin? +unblockConfirm: Oletko varma, että haluat poistaa tämän tilin eston? +selectAntenna: Valitse antenni +selectWidget: Valitse vimpain +editWidgets: Muokkaa vimpaimia +editWidgetsExit: Valmis +emoji: Emoji +emojis: Emojit +emojiName: Emojin nimi +emojiUrl: Emojin URL-linkki +cacheRemoteFiles: Taltioi etätiedostot välimuistiin +flagAsBot: Merkitse tili botiksi +flagAsBotDescription: Ota tämä vaihtoehto käyttöön, jos tätä tiliä ohjaa ohjelma. + Jos se on käytössä, se toimii lippuna muille kehittäjille, jotta estetään loputtomat + vuorovaikutusketjut muiden bottien kanssa ja säädetään Firefishn sisäiset järjestelmät + käsittelemään tätä tiliä botina. +flagAsCat: Oletko kissa? 🐱 +flagAsCatDescription: Saat kissan korvat ja puhut kuin kissa! +flagSpeakAsCat: Puhu kuin kissa +flagShowTimelineReplies: Näytä vastaukset aikajanalla +addAccount: Lisää tili +loginFailed: Kirjautuminen epäonnistui +showOnRemote: Katsele etäinstanssilla +general: Yleistä +accountMoved: 'Käyttäjä on muuttanut uuteen tiliin:' +wallpaper: Taustakuva +setWallpaper: Aseta taustakuva +searchWith: 'Etsi: {q}' +youHaveNoLists: Sinulla ei ole listoja +followConfirm: Oletko varma, että haluat seurata käyttäjää {name}? +host: Isäntä +selectUser: Valitse käyttäjä +annotation: Kommentit +registeredAt: Rekisteröity +latestRequestReceivedAt: Viimeisin pyyntö vastaanotettu +latestRequestSentAt: Viimeisin pyyntö lähetetty +storageUsage: Tallennustilan käyttö +charts: Kaaviot +stopActivityDelivery: Lopeta toimintojen lähettäminen +blockThisInstance: Estä tämä instanssi +operations: Toiminnot +metadata: Metatieto +monitor: Seuranta +jobQueue: Työjono +cpuAndMemory: Prosessori ja muisti +network: Verkko +disk: Levy +clearCachedFiles: Tyhjennä välimuisti +clearCachedFilesConfirm: Oletko varma, että haluat tyhjentää kaikki välimuistiin tallennetut + etätiedostot? +blockedInstances: Estetyt instanssit +hiddenTags: Piilotetut asiatunnisteet +mention: Maininta +copyUsername: Kopioi käyttäjänimi +searchUser: Etsi käyttäjää +showLess: Sulje +youGotNewFollower: seurasi sinua +directNotes: Yksityisviestit +driveFileDeleteConfirm: Oletko varma, että haluat poistaa tiedoston " {name}"? Se + poistetaan kaikista viesteistä, jotka sisältävät sen liitetiedostona. +importRequested: Olet pyytänyt viemistä. Tämä voi viedä hetken. +exportRequested: Olet pyytänyt tuomista. Tämä voi viedä hetken. Se lisätään asemaan + kun tuonti valmistuu. +lists: Listat +followers: Seuraajat +followsYou: Seuraa sinua +pageLoadErrorDescription: Tämä yleensä johtuu verkkovirheistä tai selaimen välimuistista. + Kokeile tyhjentämällä välimuisti ja yritä sitten hetken kuluttua uudelleen. +enterListName: Anna listalle nimi +instanceInfo: Instanssin tiedot +clearQueue: Tyhjennä jono +suspendConfirm: Oletko varma, että haluat keskeyttää tämän tilin? +unsuspendConfirm: Oletko varma, että haluat poistaa tämän tilin keskeytyksen? +selectList: Valitse lista +customEmojis: Kustomoitu Emoji +addEmoji: Lisää +settingGuide: Suositellut asetukset +cacheRemoteFilesDescription: Kun tämä asetus ei ole käytössä, etätiedostot on ladattu + suoraan etäinstanssilta. Asetuksen poistaminen käytöstä vähentää tallennustilan + käyttöä, mutta lisää verkkoliikennettä kun pienoiskuvat eivät muodostu. +flagSpeakAsCatDescription: Lähetyksesi nyanifioidaan, kun olet kissatilassa +flagShowTimelineRepliesDescription: Näyttää käyttäjien vastaukset muiden käyttäjien + lähetyksiin aikajanalla, jos se on päällä. +autoAcceptFollowed: Automaattisesti hyväksy seuraamispyynnöt käyttäjiltä, joita seuraat +perHour: Tunnissa +removeWallpaper: Poista taustakuva +recipient: Vastaanottaja(t) +federation: Federaatio +software: Ohjelmisto +proxyAccount: Proxy-tili +proxyAccountDescription: Välitystili (Proxy-tili) on tili, joka toimii käyttäjien + etäseuraajana tietyin edellytyksin. Kun käyttäjä esimerkiksi lisää etäkäyttäjän + luetteloon, etäkäyttäjän toimintaa ei toimiteta instanssiin, jos yksikään paikallinen + käyttäjä ei seuraa kyseistä käyttäjää, joten välitystili seuraa sen sijaan. +latestStatus: Viimeisin tila +selectInstance: Valitse instanssi +instances: Instanssit +perDay: Päivässä +version: Versio +statistics: Tilastot +clearQueueConfirmTitle: Oletko varma, että haluat tyhjentää jonon? +introMisskey: Tervetuloa! Firefish on avoimen lähdekoodin, hajautettu sosiaalisen median + alusta, joka on ikuisesti ilmainen! 🚀 +clearQueueConfirmText: Mitkään välittämättömät lähetykset, jotka ovat jonossa, eivät + federoidu. Yleensä tätä toimintoa ei tarvita. +blockedInstancesDescription: Lista instanssien isäntänimistä, jotka haluat estää. + Listatut instanssit eivät kykene kommunikoimaan enää tämän instanssin kanssa. +security: Turvallisuus +retypedNotMatch: Syöte ei kelpaa. +fromDrive: Asemasta +keepOriginalUploading: Säilytä alkuperäinen kuva +uploadFromUrlDescription: Tiedoston URL, jonka haluat ylösladata +themeForLightMode: Teema vaaleassa tilassa +theme: Teemat +themeForDarkMode: Teema tummassa tilassa +drive: Asema +darkThemes: Tummat teemat +copyUrl: Kopioi URL-linkki +rename: Uudelleennimeä +maintainerName: Ylläpitäjä +maintainerEmail: Ylläpitäjän sähköposti +tosUrl: Palvelun ehdot URL-linkki +thisYear: Vuosi +backgroundImageUrl: Taustakuvan URL-linkki +basicInfo: Perustiedot +pinnedPagesDescription: Kirjoita niiden sivujen polut, jotka haluat liittää tämän + instanssin yläsivulle rivinvaihdoin erotettuna. +hcaptchaSiteKey: Sivuston avain +hcaptchaSecretKey: Salausavain +silencedInstances: Hiljennetyt instanssit +muteAndBlock: Hiljennykset ja estetyt +mutedUsers: Hiljennetyt käyttäjät +blockedUsers: Estetyt käyttäjät +noUsers: Ei yhtään käyttäjää +noInstances: Ei yhtään instanssia +editProfile: Muokkaa profiilia +noteDeleteConfirm: Oletko varma, että haluat poistaa tämän viestin? +pinLimitExceeded: Et voi kiinnittää enempää viestejä +intro: Firefish -asennus valmis! Ole hyvä ja luo admin-käyttäjä. +done: Valmis +processing: Suorittaa +preview: Esikatselu +default: Oletus +defaultValueIs: 'Oletus: {value}' +noCustomEmojis: Ei emojia +noJobs: Ei töitä +federating: Federoi +blocked: Estetty +silenced: Hiljennetty +suspended: Keskeytetty +all: Kaikki +publishing: Julkaisee +subscribing: Tilaa +notResponding: Ei vastaa +instanceFollowing: Seuraa instanssia +instanceFollowers: Instanssin seuraajat +instanceUsers: Instanssin käyttäjät +changePassword: Muuta salasana +newPasswordRetype: Uudelleensyötä uusi salasana +more: Lisää! +featured: Esillä +usernameOrUserId: Käyttäjänimi tai käyttäjä id +noSuchUser: Käyttäjää ei löydy +lookup: Hae +announcements: Tiedoitteet +imageUrl: Kuva URL-linkki +removed: Onnistuneesti poistettu +removeAreYouSure: Oletko varma, että haluat poistaa " {x}"? +resetAreYouSure: Haluatko nollata? +saved: Tallennettu +messaging: Juttele +upload: Lataa ylös +fromUrl: URL:stä +uploadFromUrl: Ylöslataa URL:stä +uploadFromUrlRequested: Ylöslataus pyydetty +uploadFromUrlMayTakeTime: Voi viedä hetki, kun ylöslataus on valmis. +explore: Tutustu +messageRead: Lue +noMoreHistory: Ei lisää historiaa +startMessaging: Aloita uusi juttelu +manageGroups: Hallitse ryhmiä +nUsersRead: lukenut {n} +agreeTo: Hyväksyn {0} +tos: Palvelun ehdot +start: Aloita +home: Koti +remoteUserCaution: Etäkäyttäjän tiedot saattavat olla puutteellisia. +light: Vaalea +dark: Tumma +lightThemes: Vaaleat teemat +syncDeviceDarkMode: Synkronoi tumma tila laitteen asetuksen mukaan +fileName: Tiedostonimi +selectFile: Valitse tiedosto +selectFiles: Valitse tiedostot +selectFolder: Valitse kansio +selectFolders: Valitse kansiot +renameFile: Uudelleennimeä tiedosto +folderName: Kansionimi +createFolder: Luo kansio +renameFolder: Uudelleennimeä kansio +deleteFolder: Poista kansio +addFile: Lisää tiedosto +emptyDrive: Asemasi on tyhjä +emptyFolder: Tämä kansio on tyhjä +unableToDelete: Ei voitu poistaa +inputNewFileName: Syötä uusi tiedostonimi +inputNewDescription: Syötä uusi kuvateksti +inputNewFolderName: Syötä uusi kansionimi +hasChildFilesOrFolders: Koska kansio ei ole tyhjä, sitä ei voi poistaa. +avatar: Kuvake +banner: Banneri +nsfw: Herkkää sisältöä (NSFW) +whenServerDisconnected: Kun yhteys palvelimeen menetetään +disconnectedFromServer: Yhteys palvelimeen katkennut +reload: Päivitä +doNothing: Hylkää +reloadConfirm: Haluaisitko päivittää aikajanan? +unwatch: Lopeta katselu +watch: Katsele +accept: Hyväksy +reject: Hylkää +normal: Normaali +instanceName: Instanssin nimi +thisMonth: Kuukausi +today: Tänään +monthX: '{month}' +connectService: Yhdistä +disconnectService: Katkaise yhteys +enableLocalTimeline: Ota käyttöön paikallinen aikajana +enableGlobalTimeline: Ota käyttöön globaali aikajana +enableRecommendedTimeline: Ota käyttöön suositellut -aikajana +registration: Rekisteröinti +enableRegistration: Ota käyttöön uuden käyttäjän rekisteröinti +driveCapacityPerLocalAccount: Aseman kapasiteetti paikallista käyttäjää kohti +driveCapacityPerRemoteAccount: Aseman kapasiteetti etäkäyttäjää kohti +inMb: megatavuissa +bannerUrl: Bannerikuvan URL-linkki +pinnedUsers: Kiinnitetyt käyttäjät +pinnedPages: Kiinnitetyt sivut +pinnedClipId: Kiinnitettävän leikkeen ID +enableHcaptcha: Ota käyttöön hCaptcha-tunnistus +recaptcha: CAPTCHA uudelleen +enableRecaptcha: Ota käyttöön CAPTCHA uudelleen +recaptchaSiteKey: Sivuston avain +recaptchaSecretKey: Salausavain +silenceThisInstance: Hiljennä tämä instanssi +silencedInstancesDescription: Lista isäntänimistä, joka haluat hiljentää. Tilejä listassa + kohdellaan "hiljennettynä", ne voivat tehdä seuraajapyyntöjä ja eivät voi tehdä + mainintoja paikallistileistä jossei seurattu. Tämä ei vaikuta estettyihin instansseihin. +hiddenTagsDescription: 'Listaa aihetunnisteet (ilman #-merkkiä) aihetunnisteet, jotka + haluat piilottaa trendaavista ja Tutustu-osiosta. Piilotetut aihetunnisteet ovat + kuitenkin löydettävissä muilla keinoilla. Estetyt instanssit eivät vaikuta, vaikka + listattu tähän.' +currentPassword: Nykyinen salasana +newPassword: Uusi salasana +attachFile: Liitetyt tiedostot +keepOriginalUploadingDescription: Tallentaa alkuperäisen kuvan sellaisenaan. Jos kytketty + päältä, webissä näytettävä versio luodaan ylöslatauksen yhteydessä. +remove: Poista +circularReferenceFolder: Kohdekansio on kansion alikansio, jonka haluat siirtää. +deleteAreYouSure: Oletko varma, että haluat poistaa kokonaan" {x}"? +yearsOld: '{age} vuotias' +activity: Aktiivisuus +images: Kuvat +birthday: Syntymäpäivä +registeredDate: Liittynyt +location: Sijainti +disablingTimelinesInfo: Järjestelmänvalvojilla ja moderaattoreilla on aina pääsy kaikille + aikajanoille, vaikka olisikin poistettu käytöstä. +dayX: '{day}' +yearX: '{year}' +pages: Sivut +integration: Integraatiot +instanceDescription: Instanssin kuvaus +invite: Kutsu +iconUrl: Ikoni URL-linkki +pinnedUsersDescription: Listaa käyttäjänimet eroteltuna rivivaihdoin kiinnittääksesi + ne "Tutustu" välilehteen. +pinnedNotes: Kiinnitetyt viestit +hcaptcha: hCaptcha-tunnistus +antennaSource: Antennin lähde +invitationCode: Kutsukoodi +checking: Tarkistetaan... +passwordNotMatched: Ei vastaa +doing: Käsittelee... +category: Kategoria +tags: Tagit +disableAnimatedMfm: Poista MFM -animaatiot käytöstä +openImageInNewTab: Avaa kuvat uuteen välilehteen +dashboard: Kojelauta +local: Paikallinen +remote: Etä +total: Yhteensä +weekOverWeekChanges: Muutokset viime viikkoon +objectStorageRegion: Alue +popout: Ulosvedettävä +volume: Äänenvoimakkuus +masterVolume: Master äänenvoimakkuus +details: Yksityiskohdat +chooseEmoji: Valitse emoji +descendingOrder: Laskevasti +scratchpad: Raaputusalusta +output: Ulostulo +invisibleNote: Näkymätön viesti +enableInfiniteScroll: Lataa enemmän automaattisesti +visibility: Näkyvyys +useCw: Piilota sisältö +poll: Kysely +enablePlayer: Avaa videotoistimeen +enterFileDescription: Syötä tiedostokuvaus +author: Kirjoittaja +manage: Hallinta +description: Kuvaus +describeFile: Lisää tiedostokuvaus +height: Korkeus +large: Suuri +medium: Keskikokoinen +small: Pieni +other: Muu +create: Luo +regenerateLoginTokenDescription: Luo uudelleen kirjautumisen aikana sisäisesti käytettävän + tunnuksen. Normaalisti tämä toiminto ei ole tarpeen. Jos tunniste luodaan uudelleen, + kaikki laitteet kirjautuvat ulos. +setMultipleBySeparatingWithSpace: Erottele useat merkinnät välilyönneillä. +fileIdOrUrl: Tiedosto ID tai URL-linkki +behavior: Käytös +instanceTicker: Viestejä koskevat instanssitiedot +waitingFor: Odottaa {x} +random: Satunnainen +system: Järjestelmä +switchUi: Ulkoasu +createNew: Luo uusi +followersCount: Seuraajien määrä +renotedCount: Saatujen buustausten määrä +followingCount: Seurattujen tilien määrä +notSet: Ei asetettu +nUsers: '{n} Käyttäjää' +nNotes: '{n} Viestiä' +sendErrorReports: Lähetä virheraportteja +backgroundColor: Taustaväri +accentColor: Korostusväri +textColor: Tekstin väri +advanced: Edistynyt +saveAs: Tallenna nimellä... +invalidValue: Epäkelpo arvo. +registry: Rekisteri +closeAccount: Sulje tili +currentVersion: Nykyinen versio +capacity: Kapasiteetti +clear: Palaa +_theme: + explore: Tutustu teemoihin +silenceConfirm: Oletko varma, että haluat hiljentää tämän käyttäjän? +notesAndReplies: Viestit ja vastaukset +withFiles: Tiedostot sisältyvät +silence: Hiljennä +popularTags: Suositut tagit +userList: Listat +about: Tietoja +aboutFirefish: Tietoja Firefishstä +exploreFediverse: Tutustu fediverseen +recentlyUpdatedUsers: Vastikään lisätyt käyttäjät +recentlyRegisteredUsers: Uudet liittyneet jäyttäjät +recentlyDiscoveredUsers: Vastikään löydetyt käyttäjät +exploreUsersCount: Täällä on {count} käyttäjää +share: Jaa +moderation: Sisällön valvonta +nUsersMentioned: Mainittu {n} käyttäjältä +securityKey: Turva-avain +securityKeyName: Avainnimi +registerSecurityKey: Rekisteröi turva-avain +lastUsed: Viimeksi käytetty +unregister: Poista rekisteröinti +passwordLessLogin: Salasanaton sisäänkirjautuminen +cacheClear: Tyhjennä välimuisti +markAsReadAllNotifications: Merkitse kaikki ilmoitukset luetuksi +markAsReadAllUnreadNotes: Merkitse kaikki viestit luetuiksi +uploadFolder: Oletuskansio ylöslatauksille +createGroup: Luo ryhmä +group: Ryhmä +groups: Ryhmät +ownedGroups: Omistetut ryhmät +help: Apua +inputMessageHere: Syötä viesti tähän +close: Sulje +joinedGroups: Liittyneet ryhmät +invites: Kutsut +groupName: Ryhmänimi +members: Jäsenet +language: Kieli +signinHistory: Kirjautumishistoria +docSource: Tämän dokumentin lähde +createAccount: Luo tili +existingAccount: Olemassa oleva tili +promotion: Edistetty +promote: Edistää +numberOfDays: Päivien määrä +accountSettings: Tilin asetukset +objectStorage: Objektitallennus +useObjectStorage: Käytä objektitallennusta +objectStorageBaseUrl: Perus URL-linkki +objectStorageBaseUrlDesc: "Viitteenä käytetty URL-linkki. Määritä CDN:n tai välityspalvelimen\ + \ URL-linkki, jos käytät kumpaakin.\nKäytä S3:lle 'https://.s3.amazonaws.com'\ + \ ja GCS:lle tai vastaaville palveluille 'https://storage.googleapis.com/'\ + \ jne." +objectStorageBucket: Kauha +newNoteRecived: Uusia viestejä +smtpPort: Portti +instanceMute: Instanssin mykistys +repliesCount: Lähetettyjen vastausten määrä +updatedAt: Päivitetty +notFound: Ei löydy +useOsNativeEmojis: Käytä käyttöjärjestelmän natiivi-Emojia +joinOrCreateGroup: Tule kutsutuksi ryhmään tai luo oma ryhmä. +text: Teksti +usernameInvalidFormat: Käytä isoja ja pieniä kirjaimia, numeroita ja erikoismerkkejä. +unsilenceConfirm: Oletko varma, että haluat poistaa käyttäjän hiljennyksen? +popularUsers: Suositut käyttäjät +moderator: Moderaattori +twoStepAuthentication: Kaksivaiheinen tunnistus +notFoundDescription: URL-linkkiin liittyvää sivua ei löytynyt. +antennaKeywords: Kuunneltavat avainsanat +antennaExcludeKeywords: Poislasketut avainsanat +antennaKeywordsDescription: Erottele välilyönneillä AND-ehtoa varten tai rivinvaihdolla + OR-ehtoa varten. +notifyAntenna: Ilmoita uusista viesteistä +withFileAntenna: Vain viestit tiedoston kanssa +enableServiceworker: Ota käyttöön Push-notifikaatiot selaimessasi +antennaUsersDescription: Luettele yksi käyttäjänimi rivi kohti +antennaInstancesDescription: Luettele yksi instanssi riviä kohti +caseSensitive: Isot ja pienet kirjaimet +withReplies: Sisällytä vastaukset +connectedTo: Seuraavat tili(t) on yhdistetty +unsilence: Poista hiljennys +administrator: Järjestelmänvalvoja +token: Merkki +resetPassword: Resetoi salasana +reduceUiAnimation: Vähennä käyttöliittymän animaatioita +transfer: Siirrä +messagingWithUser: Yksityisjuttelu +title: Otsikko +enable: Ota käyttöön +next: Seuraava +retype: Syötä uudelleen +noteOf: Lähettänyt {user} +inviteToGroup: Kutsu ryhmään +quoteAttached: Lainaus +quoteQuestion: Liitä lainauksena? +noMessagesYet: Ei vielä viestejä +newMessageExists: Uusia viestejä +onlyOneFileCanBeAttached: Voit liittää vain yhden tiedoston viestiin +signinRequired: Ole hyvä ja rekisteröidy tai kirjaudu sisään jatkaaksesi +invitations: Kutsut +available: Saatavilla +unavailable: Ei saatavissa +tooShort: Liian lyhyt +tooLong: Liian pitkä +weakPassword: Heikko salasana +normalPassword: Kohtalainen salasana +strongPassword: Vahva salasana +passwordMatched: Vastaa +signinWith: Kirjaudu sisään {x} +signinFailed: Ei voitu kirjautua sisään. Annettu käyttäjänimi tai salasana virheellinen. +tapSecurityKey: Napsauta turva-avaintasi +or: Tai +uiLanguage: Anna käyttöliittymän kieli +groupInvited: Sinut on kutsuttu ryhmään +aboutX: Tietoja {x} +disableDrawer: Älä käytä laatikkotyyppisiä valikoita +youHaveNoGroups: Sinulla ei ole ryhmiä +noHistory: Ei historiaa saatavilla +regenerate: Uudelleenluo +fontSize: Kirjasinkoko +dayOverDayChanges: Muutokset eiliseen +clientSettings: Asiakkaan asetukset +hideThisNote: Piilota tämä viesti +showFeaturedNotesInTimeline: Näytä esillä olevat viestit aikajanalla +objectStorageBucketDesc: Määritä palveluntarjoajasi käyttämä kauhan nimi. +objectStoragePrefix: Etuliite +objectStorageEndpoint: Päätepiste +objectStorageRegionDesc: Määritä alue, kuten "xx-east-1". Jos palvelusi ei tee eroa + alueiden välillä, jätä tämä kohta tyhjäksi tai kirjoita "us-east-1". +objectStorageUseSSL: Käytä SSL-salausta +objectStorageUseSSLDesc: Poista tämä käytöstä, jos et aio käyttää HTTPS:ää API-yhteyksissä +objectStorageUseProxy: Yhdistä välityspalvelimen kautta +objectStorageUseProxyDesc: Poista tämä käytöstä, jos et aio käyttää välityspalvelinta + API-yhteyksiä varten +objectStorageSetPublicRead: Aseta "public-read" ylöslataukseen +serverLogs: Palvelimen lokit +deleteAll: Poista kaikki +showFixedPostForm: Näytä viesti-ikkuna aikajanan yläpuolella +sounds: Äänet +listen: Kuuntele +none: Ei mitään +showInPage: Näytä sivulla +recentUsed: Vastikään käytetty +install: Asenna +uninstall: Poista asennus +installedApps: Hyväksytyt sovellukset +nothing: Ei nähtävää täällä +state: Tila +sort: Järjestä +ascendingOrder: Nousevasti +scratchpadDescription: Raaputusalusta tarjoaa ympäristön AiScript-kokeiluja varten. + Voit kirjoittaa, suorittaa ja tarkistaa sen tulokset vuorovaikutuksessa siinä olevan + Firefishn kanssa. +script: Skripti +disablePagesScript: Poista AiScript käytöstä sivuilla +updateRemoteUser: Päivitä etäkäyttäjän tiedot +deleteAllFiles: Poista kaikki tiedostot +deleteAllFilesConfirm: Oletko varma, että haluat poistaa kaikki tiedostot? +removeAllFollowing: Poista seuraaminen kaikista seuratuista käyttäjistä +removeAllFollowingDescription: Tämän suorittaminen poistaa kaikki {host}:n tilit. + Suorita tämä, jos instanssia ei esimerkiksi enää ole olemassa. +userSuspended: Tämä käyttäjä on hyllytetty. +userSilenced: Tämä käyttäjä on hiljennetty. +yourAccountSuspendedTitle: Tämä tili on hyllytetty +yourAccountSuspendedDescription: Tämä tili on hyllytetty palvelimen palveluehtojen + tai vastaavien rikkomisen vuoksi. Ota yhteyttä ylläpitäjään, jos haluat tietää tarkemman + syyn. Älä luo uutta tiliä. +menu: Valikko +divider: Jakaja +addItem: Lisää kohde +relays: Releet +addRelay: Lisää rele +inboxUrl: Saavuneen postin URL +addedRelays: Lisätyt releet +serviceworkerInfo: Pitää ottaa käyttöön Push-notifikaatioissa. +deletedNote: Poistetut viestit +disablePlayer: Sulje videotoistin +expandTweet: Laajenna twiittiä +themeEditor: Teemaeditori +leaveConfirm: Tallentamattomia muutoksia olemassa. Hylätäänkö ne? +plugins: Liitännäiset +preferencesBackups: Asetusten varmuuskopiot +deck: Kansi +undeck: Jätä kansi +useBlurEffectForModal: Käytä blur-efektiä modaaleissa +useFullReactionPicker: Käytä täysikokoista reaktiovalitsinta +width: Leveys +generateAccessToken: Luo käyttöoikeustunniste +enableAll: Ota käyttöön kaikki +disableAll: Poista käytöstä kaikki +tokenRequested: Myönnä oikeus tiliin +notificationType: Ilmoituksen tyyppi +edit: Muokkaa +emailServer: Sähköpostipalvelin +enableEmail: Ota sähköpostin jakelu käyttöön +emailConfigInfo: Käytetään vahvistamaan sähköpostiosoitteesi rekisteröitymisen yhteydessä + tai jos unohdat salasanasi +email: Sähköposti +smtpHost: Isäntä +smtpUser: Käyttäjänimi +smtpPass: Salasana +emptyToDisableSmtpAuth: Jätä käyttäjänimi ja salasana tyhjäksi ohittaaksesi SMTP verifioinnin +smtpSecureInfo: Kytke tämä päältä kun käytät STARTTLS +testEmail: Kokeile email-lähetystä +wordMute: Sanan hiljennys +regexpError: Säännöllinen lausekevirhe +userSaysSomething: '{name} sanoi jotakin' +userSaysSomethingReason: '{name} sanoi {reason}' +makeActive: Aktivoi +display: Näyttö +copy: Kopioi +metrics: Mittarit +overview: Yleiskatsaus +logs: Lokit +delayed: Viivästynyt +database: Tietokanta +channel: Kanavat +notificationSetting: Ilmoitusasetukset +notificationSettingDesc: Valitse näytettävät ilmoitustyypit. +useGlobalSetting: Käytä globaaleja asetuksia +regenerateLoginToken: Luo kirjautumistunniste uudelleen +sample: Näyte +abuseReports: Raportit +reportAbuse: Raportti +reportAbuseOf: Raportti {name} +fillAbuseReportDescription: Täytä tätä raporttia koskevat tiedot. Jos se koskee tiettyä + viestiä, ilmoita sen URL-linkki. +abuseReported: Raporttisi on lähetetty. Kiitoksia paljon. +reporter: Raportoija +reporteeOrigin: Ilmoittajan alkuperä +reporterOrigin: Raportoijan alkuperä +forwardReport: Välitä raportti etäinstanssille +forwardReportIsAnonymous: Tilisi sijasta anonyymi järjestelmätili näytetään toimittajana + etäinstanssissa. +send: Lähetä +abuseMarkAsResolved: Merkitse raportti ratkaistuksi +openInNewTab: Avaa uuteen välilehteen +openInSideView: Avaa sivunäkymään +defaultNavigationBehaviour: Navigoinnin oletuskäyttäytyminen +editTheseSettingsMayBreakAccount: Näiden asetusten muuttaminen voi vahingoittaa tiliäsi. +desktop: Työpöytä +clip: Leike +optional: Vaihtoehtoinen +createNewClip: Luo uusi leike +unclip: Poista leike +confirmToUnclipAlreadyClippedNote: Tämä viesti on jo osa "{name}"-leikettä. Haluatko + sen sijaan poistaa sen tästä leikkeestä? +manageAccessTokens: Hallitse käyttöoikeuskoodeja +accountInfo: Tilin tiedot +notesCount: Viestien määrä +renotesCount: Lähetettyjen buustausten määrä +repliedCount: Saatujen vastausten määrä +sentReactionsCount: Lähetettyjen reaktioiden määrä +receivedReactionsCount: Saatujen reaktioiden määrä +pollVotesCount: Lähetettyjen kyselyäänien määrä +pollVotedCount: Saatujen kyselyäänien määrä +yes: Kyllä +no: Ei +driveFilesCount: Tiedostojen määrä asemalla +driveUsage: Aseman tilankäyttö +noCrawle: Hylkää hakukoneindeksointi +noCrawleDescription: Pyydä hakukoneita olemaan indeksoimatta profiilisivuasi, viestejäsi, + sivujasi jne. +alwaysMarkSensitive: Merkitse oletusarvoisesti herkäksi sisällöksi (NSFW) +loadRawImages: Alkuperäisten kuvien lataaminen pikkukuvien näyttämisen sijaan +disableShowingAnimatedImages: Älä näytä animoituja kuvia +verificationEmailSent: Vahvistussähköposti on lähetetty. Seuraa mukana olevaa linkkiä + suorittaaksesi vahvistuksen loppuun. +emailVerified: Sähköposti on vahvistettu +noteFavoritesCount: Kirjanmerkittyjen viestien määrä +pageLikedCount: Saatujen Sivu-tykkäysten määrä +pageLikesCount: Sivut-tykkäysten määrä +contact: Yhteystieto +useSystemFont: Käytä järjestelmän oletuskirjasinta +clips: Leikkeet +experimentalFeatures: Kokeiluluontoiset ominaisuudet +developer: Kehittäjä +makeExplorable: Tee tili näkyväksi osiossa "Tutustu" +makeExplorableDescription: Jos otat tämän pois käytöstä, tilisi ei näy "Tutustu"-osiossa. +showGapBetweenNotesInTimeline: Näytä väli viestien välissä aikajanalla +duplicate: Monista +left: Vasen +center: Keskellä +wide: Leveä +narrow: Kapea +reloadToApplySetting: Asetus otetaan käyttöön vain uudelleenladattaessa. Ladataanko + uudelleen nyt? +showTitlebar: Näytä otsikkorivi +clearCache: Tyhjennä välimuisti +onlineUsersCount: '{n} käyttäjää online-tilassa' +myTheme: Minun teemani +value: Arvo +saveConfirm: Tallenna muutokset? +deleteConfirm: Poistetaanko tosiaan? +latestVersion: Uusin versio +newVersionOfClientAvailable: Asiakasohjelmiston uudempi versio saatavilla. +usageAmount: Käyttö +inUse: Käytetty +editCode: Muokkaa koodia +apply: Käytä +receiveAnnouncementFromInstance: Vastaanota ilmoituksia tästä instanssista +emailNotification: Sähköposti-ilmoitukset +publish: Julkaise +inChannelSearch: Etsi kanavalta +useReactionPickerForContextMenu: Avaa reaktiovalitsin napsauttamalla oikeaa +typingUsers: '{users} kirjoittaa' +jumpToSpecifiedDate: Hyppää tiettyyn päivään +markAllAsRead: Merkitse kaikki luetuksi +goBack: Takaisin +unlikeConfirm: Poistatko todella tykkäyksesi? +fullView: Täysi koko +quitFullView: Poistu täydestä koosta +addDescription: Lisää kuvaus +markAsReadAllTalkMessages: Merkitse kaikki yksityisviestit luetuiksi +appearance: Ulkonäkö +messagingWithGroup: Ryhmäjuttelu +newPasswordIs: Uusi salasana on "{password}" +noFollowRequests: Sinulla ei ole odottavia seuraajapyyntöjä +objectStoragePrefixDesc: Tiedostot tallennetaan hakemistoihin tällä etuliitteellä. +objectStorageEndpointDesc: Jätä tämä tyhjäksi, jos käytät AWS S3:a. Muuten määritä + päätepisteeksi '' tai ':' käyttämästäsi palvelusta riippuen. +unableToProcess: Toimenpidettä ei voida suorittaa loppuun +installedDate: Hyväksynyt +lastUsedDate: Viimeksi käytetty +pluginTokenRequestedDescription: Tämä litännäinen voi käyttää tässä asetettuja käyttöoikeuksia. +permission: Oikeudet +smtpConfig: Lähtevän sähköpostin palvelimen (SMTP) asetukset +regexpErrorDescription: 'Säännöllisessä lausekkeessa tapahtui virhe rivillä {line} + sanan {tab} sanan mykistäminen rivillä {line}:' +emailAddress: Sähköpostiosoite +smtpSecure: Käytä implisiittistä SSL/TLS:ää SMTP-yhteyksissä +useGlobalSettingDesc: Jos se on päällä, käytetään tilisi ilmoitusasetuksia. Jos se + on pois päältä, voit tehdä yksilöllisiä asetuksia. +public: Julkinen +i18nInfo: Vapaaehtoiset kääntävät Firefishta eri kielille. Voit auttaa osoitteessa + {link}. +lockedAccountInfo: Ellet aseta postauksen näkyvyydeksi "Vain seuraajille", postauksesi + näkyvät kaikille, vaikka vaatisitkin seuraajilta manuaalista hyväksyntää. +sendErrorReportsDescription: "Kun tämä on päällä, yksityiskohtaiset virhetiedot jaetaan\ + \ Firefishn kanssa ongelman ilmetessä, mikä auttaa parantamaan Firefishn laatua.\n\ + Näihin tietoihin sisältyy esimerkiksi käyttöjärjestelmäversio, käyttämäsi selain,\ + \ toimintasi Firefishssä jne." +createdAt: Luotu +youAreRunningUpToDateClient: Käytössäsi on asiakasohjelman uusin versio. +needReloadToApply: Uudelleenlataus vaaditaan, jotta tämä näkyy. +showingPastTimeline: Näytetään parhaillaan vanhaa aikajanaa +userPagePinTip: Voit näyttää viestit täällä valitsemalla yksittäisten viestien valikosta + "Kiinnitä profiiliin". +notSpecifiedMentionWarning: Tämä viesti sisältää mainintoja käyttäjistä, joita ei + ole mainittu vastaanottajina +name: Nimi +allowedInstances: Sallitut (whitelisted) instanssit +hashtags: Aihetunnisteet +troubleshooting: Vianetsintä +received: Vastaanotettu +searchResult: Hakutulokset +filter: Suodatin +antennas: Antennit +noMaintainerInformationWarning: Ylläpitäjän tietoja ei ole konfiguroitu. +controlPanel: Hallintapaneeli +manageAccounts: Hallitse tilejä +makeReactionsPublic: Aseta reaktiohistoria julkiseksi +unread: Lukematon +deleted: Poistettu +editNote: Muokkaa viestiä +edited: 'Muokattu klo {date} {time}' +avoidMultiCaptchaConfirm: Useiden Captcha-järjestelmien käyttö voi aiheuttaa häiriöitä + niiden välillä. Haluatko poistaa käytöstä muut tällä hetkellä käytössä olevat Captcha-järjestelmät? + Jos haluat, että ne pysyvät käytössä, paina peruutusnäppäintä. +manageAntennas: Hallitse antenneja +info: Tietoja +userInfo: Käyttäjätiedot +unknown: Tuntematon +onlineStatus: Online-tila +hideOnlineStatus: Piilota Online-tila +hideOnlineStatusDescription: Online-tilasi piilottaminen vähentää joidenkin toimintojen, + kuten haun, käyttömukavuutta. +online: Online +active: Aktiivinen +offline: Offline +botProtection: Botti-suojaus +instanceBlocking: Federaatio Esto/Hiljennys +enabled: Otettu käyttöön +quickAction: Pikatoiminnot +user: Käyttäjä +accounts: Tilit +switch: Vaihda +noBotProtectionWarning: Botti-suojausta ei ole konfiguroitu. +configure: Konfiguroi +postToGallery: Luo uusi galleriaviesti +gallery: Galleria +recentPosts: Viimeaikaiset sivut +popularPosts: Suositut sivut +ads: Mainokset +expiration: Aikaraja +memo: Muistio +priority: Prioriteetti +high: Korkea +middle: Keskitaso +low: Alhainen +emailNotConfiguredWarning: Sähköpostiosoitetta ei ole asetettu. +ratio: Suhde +secureMode: Suojattu moodi (Valtuutettu nouto) +instanceSecurity: Instanssiturvallisuus +allowedInstancesDescription: Federaatiota varten sallitulle listalle (whitelisted) + otettavien instanssien isännät, kukin erotettuna uudella rivillä (sovelletaan vain + yksityisessä tilassa). +previewNoteText: Näytä esikatselu +customCss: Kustomoitu CSS +customCssWarn: Tätä asetusta tulisi käyttää vain, jos tiedät, mitä se tekee. Vääränlaisten + arvojen syöttäminen voi aiheuttaa sen, että asiakasohjelma lakkaa toimimasta normaalisti. +recommended: Suositeltu +squareAvatars: Näytä neliön malliset kuvakkeet +seperateRenoteQuote: Erilliset buustaa ja lainaa -napit +sent: Lähetetty +useBlurEffect: Käytä blur-efektejä käyttöliittymässä +misskeyUpdated: Firefish on päivitetty! +whatIsNew: Näytä muutokset +translate: Käännä +translatedFrom: Käännetty kielestä {x} +accountDeletionInProgress: Tilin poistaminen on parhaillaan menossa +usernameInfo: Nimi, joka erottaa tilisi muista tällä palvelimella olevista tileistä. Voit + käyttää aakkosia (a~z, A~Z), numeroita (0~9) tai alaviivoja (_). Käyttäjätunnuksia + ei voi muuttaa myöhemmin. +aiChanMode: Ai-chan klassisessa käyttöliittymässä +keepCw: Pidä sisältövaroitukset +pubSub: Pub/Sub tilit +lastCommunication: Viimeisin kommunikaatio +unresolved: Ratkaisematon +breakFollow: Poista seuraaja +breakFollowConfirm: Oletko varma, että haluat poistaa seuraajan? +itsOn: Otettu käyttöön +itsOff: Poistettu käytöstä +emailRequiredForSignup: Vaadi sähköpostiosoitetta sisäänkirjautumiseen +makeReactionsPublicDescription: Tämä laittaa viimeisimmät reaktiosi julkisesti näkyväksi. +classic: Klassinen +muteThread: Mykistä lanka +unmuteThread: Poista langan mykistys +ffVisibility: Seurataan/Seurattavien näkyvyys +notRecommended: Ei suositeltu +disabled: Poistettu käytöstä +selectAccount: Valitse tili +switchAccount: Vaihda tili +administration: Hallinta +shareWithNote: Jaa viestin kanssa +secureModeInfo: Kun pyydät muista instansseista, älä lähetä takaisin ilman todisteita. +privateMode: Yksityinen moodi +privateModeInfo: Kun tämä on käytössä, vain sallittujen (whitelisted) luetteloon merkityt + instanssit voivat liittyä instansseihisi. Kaikki viestit piilotetaan yleisöltä. +global: Globaali +resolved: Ratkaistu +learnMore: Opi lisää +continueThread: Jatka lankaa +file: Tiedosto +cropImageAsk: Haluatko rajata tätä kuvaa? +recentNHours: Viimeiset {n} tuntia +rateLimitExceeded: Nopeusraja ylittynyt +cropImage: Rajaa kuvaa +socialTimeline: Sosiaalinen aikajana +themeColor: Instanssi Ticker Väri +check: Tarkista +ffVisibilityDescription: Antaa sinun konfiguroida, kuka voi nähdä ketä seuraat ja + kuka seuraa sinua. +homeTimeline: Koti aikajana +size: Koko +showLocalPosts: 'Näytä paikalliset viestit:' +oneDay: Päivä +instanceDefaultDarkTheme: Instanssikattava tumma oletusteema +recentNDays: Viimeiset {n} päivää +reflectMayTakeTime: Voi kestää jonkin aikaa, ennen kuin tämä näkyy. +failedToFetchAccountInformation: Ei voitu hakea tietoja +requireAdminForView: Sinun tulee kirjautua järjestelmänvalvojana nähdäksesi tämän. +driveCapOverrideCaption: Resetoi oletusarvoon syöttämällä arvo 0 tai alempi. +isSystemAccount: Järjestelmän luoma ja automaattisesti käyttämä tili. +userSaysSomethingReasonReply: '{name} vastasi viestiin sisältäen {reason}' +userSaysSomethingReasonRenote: '{name} buustasi viestiin sisältäen {reason}' +voteConfirm: Vahvista äänesi vaihtoehdolle "{choice}"? +hide: Piilota +leaveGroup: Poistu ryhmästä +leaveGroupConfirm: Oletko varma, että haluat poistua ryhmästä "{name}"? +welcomeBackWithName: Tervetuloa takaisin, {name} +clickToFinishEmailVerification: Klikkaa [{ok}] viimeistelläksesi sähköpostivahvistuksen. +overridedDeviceKind: Laitetyyppi +tablet: Tabletti +numberOfColumn: Sarakkeiden määrä +searchByGoogle: Etsi +mutePeriod: Vaiennuksen kesto +indefinitely: Pysyvästi +tenMinutes: 10 minuuttia +oneHour: Tunti +thereIsUnresolvedAbuseReportWarning: On ratkaisemattomia raportteja. +driveCapOverrideLabel: Muuta aseman kapasiteetti tälle käyttäjälle +userSaysSomethingReasonQuote: '{name} lainasi viestiä sisältäen {reason}' +deleteAccountConfirm: Tämä peruuttamattomasti poistaa tilisi. Jatketaanko? +incorrectPassword: Väärä salasana. +useDrawerReactionPickerForMobile: Näytä reaktiovalitsin mobiilissa laatikkomallisena +smartphone: Älypuhelin +auto: Automaattinen +oneWeek: Viikko +instanceDefaultLightTheme: Instanssin kattava vaalea oletusteema +instanceDefaultThemeDescription: Anna teemakoodi objektiformaatille. +noEmailServerWarning: Sähköpostipalvelinta ei konfiguroituna. diff --git a/locales/fr-FR.yml b/locales/fr-FR.yml index 9f7028d3b1..f2333f731f 100644 --- a/locales/fr-FR.yml +++ b/locales/fr-FR.yml @@ -1,7 +1,10 @@ ---- _lang_: "Français" headlineMisskey: "Réseau relié par des notes" -introMisskey: "Bienvenue ! Misskey est un service de microblogage décentralisé, libre et ouvert.\nÉcrivez des « notes » et partagez ce qui se passe à l’instant présent, autour de vous avec les autres 📡\nLa fonction « réactions », vous permet également d’ajouter une réaction rapide aux notes des autres utilisateur·rice·s 👍\nExplorons un nouveau monde 🚀" +introMisskey: "Bienvenue ! Firefish est un service de microblogage décentralisé, libre + et ouvert.\nÉcrivez des « notes » et partagez ce qui se passe à l’instant présent, + autour de vous avec les autres 📡\nLa fonction « réactions », vous permet également + d’ajouter une réaction rapide aux notes des autres utilisateur·rice·s 👍\nExplorons + un nouveau monde 🚀" monthAndDay: "{day}/{month}" search: "Rechercher" notifications: "Notifications" @@ -23,7 +26,8 @@ otherSettings: "Paramètres avancés" openInWindow: "Ouvrir dans une nouvelle fenêtre" profile: "Profil" timeline: "Fil" -noAccountDescription: "L’utilisateur·rice n’a pas encore renseigné de biographie de présentation sur son profil." +noAccountDescription: "L’utilisateur·rice n’a pas encore renseigné de biographie de + présentation sur son profil." login: "Se connecter" loggingIn: "Connexion en cours" logout: "Se déconnecter" @@ -44,7 +48,8 @@ copyContent: "Copier le contenu" copyLink: "Copier le lien" delete: "Supprimer" deleteAndEdit: "Supprimer et réécrire" -deleteAndEditConfirm: "Êtes-vous sûr·e de vouloir supprimer cette note et la reformuler ? Vous perdrez toutes les réactions, renotes et réponses y afférentes." +deleteAndEditConfirm: "Êtes-vous sûr·e de vouloir supprimer cette note et la reformuler + ? Vous perdrez toutes les réactions, renotes et réponses y afférentes." addToList: "Ajouter à une liste" sendMessage: "Envoyer un message" copyUsername: "Copier le nom d’utilisateur·rice" @@ -64,14 +69,16 @@ import: "Importer" export: "Exporter" files: "Fichiers" download: "Télécharger" -driveFileDeleteConfirm: "Êtes-vous sûr·e de vouloir supprimer le fichier \"{name}\" ? Les notes liées à ce fichier seront aussi supprimées." +driveFileDeleteConfirm: "Êtes-vous sûr·e de vouloir supprimer le fichier \"{name}\"\ + \ ? Il sera retiré de toutes ses notes liées." unfollowConfirm: "Désirez-vous vous désabonner de {name} ?" -exportRequested: "Vous avez demandé une exportation. L’opération pourrait prendre un peu de temps. Une terminée, le fichier résultant sera ajouté au Drive." +exportRequested: "Vous avez demandé une exportation. L’opération pourrait prendre + un peu de temps. Une terminée, le fichier résultant sera ajouté au Drive." importRequested: "Vous avez initié un import. Cela pourrait prendre un peu de temps." lists: "Listes" noLists: "Vous n’avez aucune liste" -note: "Notes" -notes: "Notes" +note: "Post" +notes: "Posts" following: "Abonnements" followers: "Abonné·e·s" followsYou: "Vous suit" @@ -80,10 +87,13 @@ manageLists: "Gérer les listes" error: "Erreur" somethingHappened: "Une erreur est survenue" retry: "Réessayer" -pageLoadError: "Le chargement de la page a échoué" -pageLoadErrorDescription: "Cela est généralement causé par le cache du navigateur ou par un problème réseau. Veuillez vider votre cache ou attendre un peu et réessayer." -serverIsDead: "Le serveur ne répond pas. Patientez quelques instants puis essayez à nouveau." -youShouldUpgradeClient: "Si la page ne s'affiche pas correctement, rechargez-la pour mettre votre client à jour." +pageLoadError: "Le chargement de la page a échoué." +pageLoadErrorDescription: "Cela est généralement causé par le cache du navigateur + ou par un problème réseau. Veuillez vider votre cache ou attendre un peu et réessayer." +serverIsDead: "Le serveur ne répond pas. Patientez quelques instants puis essayez + à nouveau." +youShouldUpgradeClient: "Si la page ne s'affiche pas correctement, rechargez-la pour + mettre votre client à jour." enterListName: "Nom de la liste" privacy: "Confidentialité" makeFollowManuallyApprove: "Accepter manuellement les demandes d’abonnement" @@ -96,7 +106,7 @@ followRequestPending: "Demande d'abonnement en attente de confirmation" enterEmoji: "Insérer un émoji" renote: "Renoter" unrenote: "Annuler la Renote" -renoted: "Renoté !" +renoted: "Renoté." cantRenote: "Ce message ne peut pas être renoté." cantReRenote: "Impossible de renoter une Renote." quote: "Citer" @@ -108,8 +118,11 @@ sensitive: "Contenu sensible" add: "Ajouter" reaction: "Réactions" reactionSetting: "Réactions à afficher dans le sélecteur de réactions" -reactionSettingDescription2: "Déplacer pour réorganiser, cliquer pour effacer, utiliser « + » pour ajouter." -rememberNoteVisibility: "Activer l'option \" se souvenir de la visibilité des notes \" vous permet de réutiliser automatiquement la visibilité utilisée lors de la publication de votre note précédente." +reactionSettingDescription2: "Déplacer pour réorganiser, cliquer pour effacer, utiliser + « + » pour ajouter." +rememberNoteVisibility: "Activer l'option \" se souvenir de la visibilité des notes + \" vous permet de réutiliser automatiquement la visibilité utilisée lors de la publication + de votre note précédente." attachCancel: "Supprimer le fichier attaché" markAsSensitive: "Marquer comme sensible" unmarkAsSensitive: "Supprimer le marquage comme sensible" @@ -137,13 +150,20 @@ emojiUrl: "URL de l’émoji" addEmoji: "Ajouter un émoji" settingGuide: "Configuration proposée" cacheRemoteFiles: "Mise en cache des fichiers distants" -cacheRemoteFilesDescription: "Lorsque cette option est désactivée, les fichiers distants sont chargés directement depuis l’instance distante. La désactiver diminuera certes l’utilisation de l’espace de stockage local mais augmentera le trafic réseau puisque les miniatures ne seront plus générées." +cacheRemoteFilesDescription: "Lorsque cette option est désactivée, les fichiers distants + sont chargés directement depuis l’instance distante. La désactiver diminuera certes + l’utilisation de l’espace de stockage local mais augmentera le trafic réseau puisque + les miniatures ne seront plus générées." flagAsBot: "Ce compte est un robot" -flagAsBotDescription: "Si ce compte est géré de manière automatisée, choisissez cette option. Si elle est activée, elle agira comme un marqueur pour les autres développeurs afin d'éviter des chaînes d'interaction sans fin avec d'autres robots et d'ajuster les systèmes internes de Misskey pour traiter ce compte comme un robot." +flagAsBotDescription: "Si ce compte est géré de manière automatisée, choisissez cette + option. Si elle est activée, elle agira comme un marqueur pour les autres développeurs + afin d'éviter des chaînes d'interaction sans fin avec d'autres robots et d'ajuster + les systèmes internes de Firefish pour traiter ce compte comme un robot." flagAsCat: "Ce compte est un chat" flagAsCatDescription: "Activer l'option \" Je suis un chat \" pour ce compte." flagShowTimelineReplies: "Afficher les réponses dans le fil" -autoAcceptFollowed: "Accepter automatiquement les demandes d’abonnement venant d’utilisateur·rice·s que vous suivez" +autoAcceptFollowed: "Accepter automatiquement les demandes d’abonnement venant d’utilisateur·rice·s + que vous suivez" addAccount: "Ajouter un compte" loginFailed: "Échec de la connexion" showOnRemote: "Voir sur l’instance distante" @@ -155,7 +175,12 @@ searchWith: "Recherche : {q}" youHaveNoLists: "Vous n’avez aucune liste" followConfirm: "Êtes-vous sûr·e de vouloir suivre {name} ?" proxyAccount: "Compte proxy" -proxyAccountDescription: "Un compte proxy se comporte, dans certaines conditions, comme un·e abonné·e distant·e pour les utilisateurs d'autres instances. Par exemple, quand un·e utilisateur·rice ajoute un·e utilisateur·rice distant·e à une liste, ses notes ne seront pas visibles sur l'instance si personne ne suit cet·te utilisateur·rice. Le compte proxy va donc suivre cet·te utilisateur·rice pour que ses notes soient acheminées." +proxyAccountDescription: "Un compte proxy se comporte, dans certaines conditions, + comme un·e abonné·e distant·e pour les utilisateurs d'autres instances. Par exemple, + quand un·e utilisateur·rice ajoute un·e utilisateur·rice distant·e à une liste, + ses notes ne seront pas visibles sur l'instance si personne ne suit cet·te utilisateur·rice. + Le compte proxy va donc suivre cet·te utilisateur·rice pour que ses notes soient + acheminées." host: "Serveur distant" selectUser: "Sélectionner un·e utilisateur·rice" recipient: "Destinataire" @@ -176,7 +201,6 @@ operations: "Opérations" software: "Logiciel" version: "Version" metadata: "Métadonnées" -withNFiles: "{n} fichier(s)" monitor: "Contrôle" jobQueue: "File d’attente" cpuAndMemory: "Processeur et mémoire" @@ -186,11 +210,14 @@ instanceInfo: "Informations sur l’instance" statistics: "Statistiques" clearQueue: "Vider la file d’attente" clearQueueConfirmTitle: "Êtes-vous sûr·e de vouloir vider la file d’attente ?" -clearQueueConfirmText: "Les notes non distribuées ne seront pas délivrées. Normalement, vous n'avez pas besoin d'effectuer cette opération." +clearQueueConfirmText: "Les notes non distribuées ne seront pas délivrées. Normalement, + vous n'avez pas besoin d'effectuer cette opération." clearCachedFiles: "Vider le cache" -clearCachedFilesConfirm: "Êtes-vous sûr·e de vouloir vider tout le cache de fichiers distants ?" +clearCachedFilesConfirm: "Êtes-vous sûr·e de vouloir vider tout le cache de fichiers + distants ?" blockedInstances: "Instances bloquées" -blockedInstancesDescription: "Listez les instances que vous désirez bloquer, une par ligne. Ces instances ne seront plus en capacité d'interagir avec votre instance." +blockedInstancesDescription: "Listez les instances que vous désirez bloquer, une par + ligne. Ces instances ne seront plus en capacité d'interagir avec votre instance." muteAndBlock: "Masqué·e·s / Bloqué·e·s" mutedUsers: "Utilisateur·rice·s en sourdine" blockedUsers: "Utilisateur·rice·s bloqué·e·s" @@ -198,7 +225,7 @@ noUsers: "Il n’y a pas d’utilisateur·rice·s" editProfile: "Modifier votre profil" noteDeleteConfirm: "Êtes-vous sûr·e de vouloir supprimer cette note ?" pinLimitExceeded: "Vous ne pouvez plus épingler d’autres notes." -intro: "L’installation de Misskey est terminée ! Veuillez créer un compte administrateur." +intro: "L’installation de Firefish est terminée ! Veuillez créer un compte administrateur." done: "Terminé" processing: "Traitement en cours" preview: "Aperçu" @@ -243,7 +270,8 @@ fromUrl: "Depuis une URL" uploadFromUrl: "Téléverser via une URL" uploadFromUrlDescription: "URL du fichier que vous souhaitez téléverser" uploadFromUrlRequested: "Téléversement demandé" -uploadFromUrlMayTakeTime: "Le téléversement de votre fichier peut prendre un certain temps." +uploadFromUrlMayTakeTime: "Le téléversement de votre fichier peut prendre un certain + temps." explore: "Découvrir" messageRead: "Lu" noMoreHistory: "Il n’y a plus d’historique" @@ -253,7 +281,8 @@ agreeTo: "J’accepte {0}" tos: "les conditions d’utilisation" start: "Commencer" home: "Principal" -remoteUserCaution: "Les informations de ce compte risqueraient d’être incomplètes du fait que l’utilisateur·rice provient d’une instance distante." +remoteUserCaution: "Les informations de ce compte risqueraient d’être incomplètes + du fait que l’utilisateur·rice provient d’une instance distante." activity: "Activité" images: "Images" birthday: "Date de naissance" @@ -286,7 +315,8 @@ unableToDelete: "Suppression impossible" inputNewFileName: "Entrez un nouveau nom de fichier" inputNewDescription: "Veuillez entrer une nouvelle description" inputNewFolderName: "Entrez un nouveau nom de dossier" -circularReferenceFolder: "Le dossier de destination est un sous-dossier du dossier que vous souhaitez déplacer." +circularReferenceFolder: "Le dossier de destination est un sous-dossier du dossier + que vous souhaitez déplacer." hasChildFilesOrFolders: "Impossible de supprimer ce dossier car il n'est pas vide." copyUrl: "Copier l’URL" rename: "Renommer" @@ -320,7 +350,8 @@ connectService: "Connexion" disconnectService: "Déconnexion" enableLocalTimeline: "Activer le fil local" enableGlobalTimeline: "Activer le fil global" -disablingTimelinesInfo: "Même si vous désactivez ces fils, les administrateur·rice·s et les modérateur·rice·s pourront toujours y accéder." +disablingTimelinesInfo: "Même si vous désactivez ces fils, les administrateur·rice·s + et les modérateur·rice·s pourront toujours y accéder." registration: "S’inscrire" enableRegistration: "Autoriser les nouvelles inscriptions" invite: "Inviter" @@ -332,9 +363,11 @@ bannerUrl: "URL de l’image de la bannière" backgroundImageUrl: "URL de l'image d'arrière-plan" basicInfo: "Informations basiques" pinnedUsers: "Utilisateur·rice épinglé·e" -pinnedUsersDescription: "Listez les utilisateur·rice·s que vous souhaitez voir épinglé·e·s sur la page \"Découvrir\", un·e par ligne." +pinnedUsersDescription: "Listez les utilisateur·rice·s que vous souhaitez voir épinglé·e·s + sur la page \"Découvrir\", un·e par ligne." pinnedPages: "Pages épinglées" -pinnedPagesDescription: "Inscrivez le chemin des pages que vous souhaitez épingler en haut de la page de l'instance. Séparez les pages d'un retour à la ligne." +pinnedPagesDescription: "Inscrivez le chemin des pages que vous souhaitez épingler + en haut de la page de l'instance. Séparez les pages d'un retour à la ligne." pinnedClipId: "Identifiant du clip épinglé" pinnedNotes: "Note épinglée" hcaptcha: "hCaptcha" @@ -345,14 +378,17 @@ recaptcha: "reCAPTCHA" enableRecaptcha: "Activer reCAPTCHA" recaptchaSiteKey: "Clé du site" recaptchaSecretKey: "Clé secrète" -avoidMultiCaptchaConfirm: "L’utilisation de plusieurs Captchas peut provoquer des interférences. Souhaitez-vous désactiver l’autre Captcha ? Vous pouvez laisser plusieurs Captcha activés en appuyant sur Annuler." +avoidMultiCaptchaConfirm: "L’utilisation de plusieurs Captchas peut provoquer des + interférences. Souhaitez-vous désactiver l’autre Captcha ? Vous pouvez laisser plusieurs + Captcha activés en appuyant sur Annuler." antennas: "Antennes" manageAntennas: "Gestion des antennes" name: "Nom" antennaSource: "Source de l’antenne" antennaKeywords: "Mots clés à recevoir" antennaExcludeKeywords: "Mots clés à exclure" -antennaKeywordsDescription: "Séparer avec des espaces pour la condition AND. Séparer avec un saut de ligne pour une condition OR." +antennaKeywordsDescription: "Séparer avec des espaces pour la condition AND. Séparer + avec un saut de ligne pour une condition OR." notifyAntenna: "Je souhaite recevoir les notifications des nouvelles notes" withFileAntenna: "Notes ayant des attachements uniquement" enableServiceworker: "Activer ServiceWorker" @@ -363,9 +399,11 @@ connectedTo: "Vous êtes connectés aux services suivants" notesAndReplies: "Notes et Réponses" withFiles: "Avec fichiers joints" silence: "Mettre en sourdine" -silenceConfirm: "Êtes-vous sûr·e de vouloir mettre l’utilisateur·rice en sourdine ?" +silenceConfirm: "Êtes-vous sûr·e de vouloir mettre l’utilisateur·rice en sourdine + ?" unsilence: "Annuler la sourdine" -unsilenceConfirm: "Êtes-vous sûr·e de vouloir annuler la mise en sourdine de cet·te utilisateur·rice ?" +unsilenceConfirm: "Êtes-vous sûr·e de vouloir annuler la mise en sourdine de cet·te + utilisateur·rice ?" popularUsers: "Utilisateur·rice·s populaires" recentlyUpdatedUsers: "Utilisateur·rice·s actif·ve·s récemment" recentlyRegisteredUsers: "Utilisateur·rice·s récemment inscrit·e·s" @@ -375,7 +413,7 @@ exploreFediverse: "Explorer le Fediverse" popularTags: "Mots-clés populaires" userList: "Listes" about: "Informations" -aboutMisskey: "À propos de Misskey" +aboutFirefish: "À propos de Firefish" administrator: "Administrateur" token: "Jeton" twoStepAuthentication: "Authentification à deux facteurs" @@ -430,7 +468,8 @@ invitationCode: "Code d’invitation" checking: "Vérification en cours..." available: "Disponible" unavailable: "Non disponible" -usernameInvalidFormat: "Le nom d'utilisateur peut contenir uniquement des lettres (minuscules et/ou majuscules), des chiffres et des _" +usernameInvalidFormat: "Le nom d'utilisateur peut contenir uniquement des lettres + (minuscules et/ou majuscules), des chiffres et des _" tooShort: "Trop court" tooLong: "Trop long" weakPassword: "Mot de passe faible" @@ -439,7 +478,8 @@ strongPassword: "Mot de passe fort" passwordMatched: "Les mots de passe correspondent" passwordNotMatched: "Les mots de passe ne correspondent pas" signinWith: "Se connecter avec {x}" -signinFailed: "Échec d’authentification. Veuillez vérifier que votre nom d’utilisateur et mot de passe sont corrects." +signinFailed: "Échec d’authentification. Veuillez vérifier que votre nom d’utilisateur + et mot de passe sont corrects." tapSecurityKey: "Appuyez sur votre clé de sécurité" or: "OU" language: "Langue" @@ -448,7 +488,8 @@ groupInvited: "Invité au groupe" aboutX: "À propos de {x}" useOsNativeEmojis: "Utiliser les émojis natifs du système" youHaveNoGroups: "Vous n’avez aucun groupe" -joinOrCreateGroup: "Vous pouvez être invité·e à rejoindre des groupes existants ou créer votre propre nouveau groupe." +joinOrCreateGroup: "Vous pouvez être invité·e à rejoindre des groupes existants ou + créer votre propre nouveau groupe." noHistory: "Pas d'historique" signinHistory: "Historique de connexion" disableAnimatedMfm: "Désactiver MFM ayant des animations" @@ -479,19 +520,29 @@ showFeaturedNotesInTimeline: "Afficher les notes des Tendances dans le fil d'act objectStorage: "Stockage d'objets" useObjectStorage: "Utiliser le stockage d'objets" objectStorageBaseUrl: "Base URL" -objectStorageBaseUrlDesc: "Préfixe d’URL utilisé pour construire l’URL vers le référencement d’objet (média). Spécifiez son URL si vous utilisez un CDN ou un proxy, sinon spécifiez l’adresse accessible au public selon le guide de service que vous allez utiliser. P.ex. 'https://.s3.amazonaws.com' pour AWS S3 et 'https://storage.googleapis.com/' pour GCS." +objectStorageBaseUrlDesc: "Préfixe d’URL utilisé pour construire l’URL vers le référencement + d’objet (média). Spécifiez son URL si vous utilisez un CDN ou un proxy, sinon spécifiez + l’adresse accessible au public selon le guide de service que vous allez utiliser. + P.ex. 'https://.s3.amazonaws.com' pour AWS S3 et 'https://storage.googleapis.com/' + pour GCS." objectStorageBucket: "Bucket" -objectStorageBucketDesc: "Veuillez spécifier le nom du compartiment utilisé sur le service configuré." +objectStorageBucketDesc: "Veuillez spécifier le nom du compartiment utilisé sur le + service configuré." objectStoragePrefix: "Prefix" objectStoragePrefixDesc: "Les fichiers seront stockés sous le répertoire de ce préfixe." objectStorageEndpoint: "Endpoint" -objectStorageEndpointDesc: "Laissez ce champ vide si vous utilisez AWS S3, sinon spécifiez le point de terminaison comme '' ou ': ' selon le guide de service que vous allez utiliser." +objectStorageEndpointDesc: "Laissez ce champ vide si vous utilisez AWS S3, sinon spécifiez + le point de terminaison comme '' ou ': ' selon le guide de service + que vous allez utiliser." objectStorageRegion: "Région" -objectStorageRegionDesc: "Spécifiez une région comme 'xx-east-1'. Si votre service ne fait pas de distinction entre les régions, laissez-le vide ou remplissez 'us-east-1'." +objectStorageRegionDesc: "Spécifiez une région comme 'xx-east-1'. Si votre service + ne fait pas de distinction entre les régions, laissez-le vide ou remplissez 'us-east-1'." objectStorageUseSSL: "Utiliser SSL" -objectStorageUseSSLDesc: "Désactivez cette option si vous n'utilisez pas HTTPS pour la connexion API" +objectStorageUseSSLDesc: "Désactivez cette option si vous n'utilisez pas HTTPS pour + la connexion API" objectStorageUseProxy: "Se connecter via proxy" -objectStorageUseProxyDesc: "Désactivez cette option si vous n'utilisez pas de proxy pour la connexion API" +objectStorageUseProxyDesc: "Désactivez cette option si vous n'utilisez pas de proxy + pour la connexion API" objectStorageSetPublicRead: "Régler sur « public » lors de l'envoi" serverLogs: "Journal du serveur" deleteAll: "Supprimer tout" @@ -519,7 +570,9 @@ sort: "Trier" ascendingOrder: "Ascendant" descendingOrder: "Descendant" scratchpad: "ScratchPad" -scratchpadDescription: "ScratchPad fournit un environnement expérimental pour AiScript. Vous pouvez vérifier la rédaction de votre code, sa bonne exécution et le résultat de son interaction avec Misskey." +scratchpadDescription: "ScratchPad fournit un environnement expérimental pour AiScript. + Vous pouvez vérifier la rédaction de votre code, sa bonne exécution et le résultat + de son interaction avec Firefish." output: "Sortie" script: "Script" disablePagesScript: "Désactiver AiScript sur les Pages" @@ -527,11 +580,15 @@ updateRemoteUser: "Mettre à jour les informations de l’utilisateur·rice dist deleteAllFiles: "Supprimer tous les fichiers" deleteAllFilesConfirm: "Êtes-vous sûr·e de vouloir supprimer tous les fichiers ?" removeAllFollowing: "Retenir tous les abonnements" -removeAllFollowingDescription: "Se désabonner de tous les comptes de {host}. Veuillez lancer cette action uniquement si l’instance n’existe plus." +removeAllFollowingDescription: "Se désabonner de tous les comptes de {host}. Veuillez + lancer cette action uniquement si l’instance n’existe plus." userSuspended: "Cet·te utilisateur·rice a été suspendu·e." userSilenced: "Cette utilisateur·trice a été mis·e en sourdine." yourAccountSuspendedTitle: "Ce compte est suspendu" -yourAccountSuspendedDescription: "Ce compte est suspendu car vous avez enfreint les conditions d'utilisation de l'instance, ou pour un motif similaire. Si vous souhaitez connaître en détail les raisons de cette suspension, renseignez-vous auprès de l'administrateur·rice de votre instance. Merci de ne pas créer de nouveau compte." +yourAccountSuspendedDescription: "Ce compte est suspendu car vous avez enfreint les + conditions d'utilisation de l'instance, ou pour un motif similaire. Si vous souhaitez + connaître en détail les raisons de cette suspension, renseignez-vous auprès de l'administrateur·rice + de votre instance. Merci de ne pas créer de nouveau compte." menu: "Menu" divider: "Séparateur" addItem: "Ajouter un élément" @@ -554,7 +611,8 @@ description: "Description" describeFile: "Ajouter une description d'image" enterFileDescription: "Saisissez une description" author: "Auteur·rice" -leaveConfirm: "Vous avez des modifications non-sauvegardées. Voulez-vous les ignorer ?" +leaveConfirm: "Vous avez des modifications non-sauvegardées. Voulez-vous les ignorer + ?" manage: "Gestion" plugins: "Extensions" deck: "Deck" @@ -571,12 +629,14 @@ permission: "Autorisations" enableAll: "Tout activer" disableAll: "Tout désactiver" tokenRequested: "Autoriser l'accès au compte" -pluginTokenRequestedDescription: "Ce plugin pourra utiliser les autorisations définies ici." +pluginTokenRequestedDescription: "Ce plugin pourra utiliser les autorisations définies + ici." notificationType: "Type de notifications" edit: "Editer" emailServer: "Serveur mail" enableEmail: "Activer la distribution de courriel" -emailConfigInfo: "Utilisé pour confirmer votre adresse de courriel et la réinitialisation de votre mot de passe en cas d’oubli." +emailConfigInfo: "Utilisé pour confirmer votre adresse de courriel et la réinitialisation + de votre mot de passe en cas d’oubli." email: "E-mail " emailAddress: "Adresses e-mail" smtpConfig: "Paramètres du serveur SMTP" @@ -584,7 +644,8 @@ smtpHost: "Serveur distant" smtpPort: "Port" smtpUser: "Nom d’utilisateur·rice" smtpPass: "Mot de passe" -emptyToDisableSmtpAuth: "Laisser le nom d’utilisateur et le mot de passe vides pour désactiver la vérification SMTP" +emptyToDisableSmtpAuth: "Laisser le nom d’utilisateur et le mot de passe vides pour + désactiver la vérification SMTP" smtpSecure: "Utiliser SSL/TLS implicitement dans les connexions SMTP" smtpSecureInfo: "Désactiver cette option lorsque STARTTLS est utilisé" testEmail: "Tester la distribution de courriel" @@ -605,18 +666,24 @@ create: "Créer" notificationSetting: "Paramètres des notifications " notificationSettingDesc: "Sélectionnez le type de notification à afficher" useGlobalSetting: "Utiliser paramètre général" -useGlobalSettingDesc: "S'il est activé, les paramètres de notification de votre compte seront utilisés. S'il est désactivé, des configurations individuelles peuvent être effectuées." +useGlobalSettingDesc: "S'il est activé, les paramètres de notification de votre compte + seront utilisés. S'il est désactivé, des configurations individuelles peuvent être + effectuées." other: "Autre" regenerateLoginToken: "Régénérer le jeton de connexion" -regenerateLoginTokenDescription: "Générer un nouveau jeton d'authentification. Cette opération ne devrait pas être nécessaire ; lors de la génération d'un nouveau jeton, tous les appareils seront déconnectés. " -setMultipleBySeparatingWithSpace: "Vous pouvez en définir plusieurs, en les séparant par des espaces." +regenerateLoginTokenDescription: "Générer un nouveau jeton d'authentification. Cette + opération ne devrait pas être nécessaire ; lors de la génération d'un nouveau jeton, + tous les appareils seront déconnectés. " +setMultipleBySeparatingWithSpace: "Vous pouvez en définir plusieurs, en les séparant + par des espaces." fileIdOrUrl: "ID du fichier ou URL" behavior: "Comportement" sample: "Exemple" abuseReports: "Signalements" reportAbuse: "Signaler" reportAbuseOf: "Signaler {name}" -fillAbuseReportDescription: "Veuillez expliquer les raisons du signalement. S'il s'agit d'une note précise, veuillez en donner le lien." +fillAbuseReportDescription: "Veuillez expliquer les raisons du signalement. S'il s'agit + d'une note précise, veuillez en donner le lien." abuseReported: "Le rapport est envoyé. Merci." reporter: "Signalé par" reporteeOrigin: "Origine du signalement" @@ -627,7 +694,8 @@ abuseMarkAsResolved: "Marquer le signalement comme résolu" openInNewTab: "Ouvrir dans un nouvel onglet" openInSideView: "Ouvrir en vue latérale" defaultNavigationBehaviour: "Navigation par défaut" -editTheseSettingsMayBreakAccount: "La modification de ces paramètres peut endommager votre compte." +editTheseSettingsMayBreakAccount: "La modification de ces paramètres peut endommager + votre compte." instanceTicker: "Nom de l'instance d'origine des notes" waitingFor: "En attente de {x}" random: "Aléatoire" @@ -639,7 +707,8 @@ createNew: "Créer nouveau" optional: "Facultatif" createNewClip: "Créer un nouveau clip" public: "Public" -i18nInfo: "Calckey est traduit dans différentes langues par des bénévoles. Vous pouvez contribuer à {link}." +i18nInfo: "Firefish est traduit dans différentes langues par des bénévoles. Vous pouvez + contribuer à {link}." manageAccessTokens: "Gérer les jetons d'accès" accountInfo: " Informations du compte " notesCount: "Nombre de notes" @@ -658,12 +727,16 @@ no: "Non" driveFilesCount: "Nombre de fichiers dans le Drive" driveUsage: "Utilisation du Drive" noCrawle: "Refuser l'indexation par les robots" -noCrawleDescription: "Demandez aux moteurs de recherche de ne pas indexer votre page de profil, vos notes, vos pages, etc." -lockedAccountInfo: "À moins que vous ne définissiez la visibilité de votre note sur \"Abonné-e-s\", vos notes sont visibles par tous, même si vous exigez que les demandes d'abonnement soient approuvées manuellement." +noCrawleDescription: "Demandez aux moteurs de recherche de ne pas indexer votre page + de profil, vos notes, vos pages, etc." +lockedAccountInfo: "À moins que vous ne définissiez la visibilité de votre note sur + \"Abonné-e-s\", vos notes sont visibles par tous, même si vous exigez que les demandes + d'abonnement soient approuvées manuellement." alwaysMarkSensitive: "Marquer les médias comme contenu sensible par défaut" loadRawImages: "Affichage complet des images jointes au lieu des vignettes" disableShowingAnimatedImages: "Désactiver l'animation des images" -verificationEmailSent: "Un e-mail de vérification a été envoyé. Veuillez accéder au lien pour compléter la vérification." +verificationEmailSent: "Un e-mail de vérification a été envoyé. Veuillez accéder au + lien pour compléter la vérification." notSet: "Non défini" emailVerified: "Votre adresse e-mail a été vérifiée." noteFavoritesCount: "Nombre de notes dans les favoris" @@ -675,14 +748,16 @@ clips: "Clips" experimentalFeatures: "Fonctionnalités expérimentales" developer: "Développeur" makeExplorable: "Rendre le compte visible sur la page \"Découvrir\"." -makeExplorableDescription: "Si vous désactivez cette option, votre compte n'apparaîtra pas sur la page \"Découvrir\"." +makeExplorableDescription: "Si vous désactivez cette option, votre compte n'apparaîtra + pas sur la page \"Découvrir\"." showGapBetweenNotesInTimeline: "Afficher un écart entre les notes sur la Timeline" duplicate: "Duliquer" left: "Gauche" center: "Centrer" wide: "Large" narrow: "Condensé" -reloadToApplySetting: "Vos paramètres seront appliqués lorsque vous rechargerez la page. Souhaitez-vous recharger ?" +reloadToApplySetting: "Vos paramètres seront appliqués lorsque vous rechargerez la + page. Souhaitez-vous recharger ?" needReloadToApply: "Ce paramètre s'appliquera après un rechargement." showTitlebar: "Afficher la barre de titre" clearCache: "Vider le cache" @@ -690,7 +765,11 @@ onlineUsersCount: "{n} utilisateur(s) en ligne" nUsers: "{n} utilisateur·rice·s" nNotes: "{n} Notes" sendErrorReports: "Envoyer les rapports d’erreur" -sendErrorReportsDescription: "Si vous activez l'envoi des rapports d'erreur, vous contribuerez à améliorer la qualité de Misskey grâce au partage d'informations détaillées sur les erreurs lorsqu'un problème survient.\nCela inclut des informations telles que la version de votre système d'exploitation, le type de navigateur que vous utilisez, votre historique d'activité, etc." +sendErrorReportsDescription: "Si vous activez l'envoi des rapports d'erreur, vous + contribuerez à améliorer la qualité de Firefish grâce au partage d'informations détaillées + sur les erreurs lorsqu'un problème survient.\nCela inclut des informations telles + que la version de votre système d'exploitation, le type de navigateur que vous utilisez, + votre historique d'activité, etc." myTheme: "Mes thèmes" backgroundColor: "Arrière-plan" accentColor: "Accentuation" @@ -729,20 +808,23 @@ unlikeConfirm: "Êtes-vous sûr·e de ne plus vouloir aimer cette publication ?" fullView: "Plein écran" quitFullView: "Quitter le plein écran" addDescription: "Ajouter une description" -userPagePinTip: "Vous pouvez afficher des notes ici en sélectionnant l'option « Épingler au profil » dans le menu de chaque note." -notSpecifiedMentionWarning: "Vous avez mentionné des utilisateur·rice·s qui ne font pas partie de la liste des destinataires" +userPagePinTip: "Vous pouvez afficher des notes ici en sélectionnant l'option « Épingler + au profil » dans le menu de chaque note." +notSpecifiedMentionWarning: "Vous avez mentionné des utilisateur·rice·s qui ne font + pas partie de la liste des destinataires" info: "Informations" userInfo: "Informations sur l'utilisateur" unknown: "Inconnu" onlineStatus: "Statut" hideOnlineStatus: "Se rendre invisible" -hideOnlineStatusDescription: "Rendre votre statut invisible peut diminuer les performances de certaines fonctionnalités, telles que la Recherche." +hideOnlineStatusDescription: "Rendre votre statut invisible peut diminuer les performances + de certaines fonctionnalités, telles que la Recherche." online: "En ligne" active: "Actif·ve" offline: "Hors ligne" notRecommended: "Déconseillé" botProtection: "Protection contre les bots" -instanceBlocking: "Instances bloquées" +instanceBlocking: "Instances bloquées/mise en sourdine" selectAccount: "Sélectionner un compte" switchAccount: "Changer de compte" enabled: "Activé" @@ -771,7 +853,9 @@ emailNotConfiguredWarning: "Vous n'avez pas configuré d'adresse e-mail." ratio: "Ratio" previewNoteText: "Voir l'aperçu" customCss: "CSS personnalisé" -customCssWarn: "Utilisez cette fonctionnalité uniquement si vous savez exactement ce que vous faites. Une configuration inadaptée peut empêcher le client de s'exécuter normalement." +customCssWarn: "Utilisez cette fonctionnalité uniquement si vous savez exactement + ce que vous faites. Une configuration inadaptée peut empêcher le client de s'exécuter + normalement." global: "Global" squareAvatars: "Avatars carrés" sent: "Envoyer" @@ -781,12 +865,15 @@ hashtags: "Hashtags" troubleshooting: "Résolution de problèmes" useBlurEffect: "Utiliser des effets de flou dans l'interface" learnMore: "Plus d'informations" -misskeyUpdated: "Misskey a été mis à jour !" +misskeyUpdated: "Firefish a été mis à jour !" whatIsNew: "Voir les derniers changements" translate: "Traduire" translatedFrom: "Traduit depuis {x}" accountDeletionInProgress: "La suppression de votre compte est en cours" -usernameInfo: "C'est un nom qui identifie votre compte sur l'instance de manière unique. Vous pouvez utiliser des lettres de l'alphabet (minuscules et majuscules), des chiffres (de 0 à 9), ou bien le tiret « _ ». Vous ne pourrez pas modifier votre nom d'utilisateur·rice par la suite." +usernameInfo: "C'est un nom qui identifie votre compte sur l'instance de manière unique. + Vous pouvez utiliser des lettres de l'alphabet (minuscules et majuscules), des chiffres + (de 0 à 9), ou bien le tiret « _ ». Vous ne pourrez pas modifier votre nom d'utilisateur·rice + par la suite." aiChanMode: "Mode Ai" keepCw: "Garder le CW" pubSub: "Comptes Pub/Sub" @@ -802,12 +889,14 @@ filter: "Filtre" controlPanel: "Panneau de contrôle" manageAccounts: "Gérer les comptes" makeReactionsPublic: "Rendre les réactions publiques" -makeReactionsPublicDescription: "Ceci rendra la liste de toutes vos réactions données publique." +makeReactionsPublicDescription: "Ceci rendra la liste de toutes vos réactions données + publique." classic: "Classique" muteThread: "Masquer cette discussion" unmuteThread: "Ne plus masquer le fil" ffVisibility: "Visibilité des abonnés/abonnements" -ffVisibilityDescription: "Permet de configurer qui peut voir les personnes que tu suis et les personnes qui te suivent." +ffVisibilityDescription: "Permet de configurer qui peut voir les personnes que tu + suis et les personnes qui te suivent." continueThread: "Afficher la suite du fil" deleteAccountConfirm: "Votre compte sera supprimé. Êtes vous certain ?" incorrectPassword: "Le mot de passe est incorrect." @@ -815,9 +904,11 @@ voteConfirm: "Confirmez-vous votre vote pour « {choice} » ?" hide: "Masquer" leaveGroup: "Quitter le groupe" leaveGroupConfirm: "Êtes vous sûr de vouloir quitter \"{name}\" ?" -useDrawerReactionPickerForMobile: "Afficher le sélecteur de réactions en tant que panneau sur mobile" +useDrawerReactionPickerForMobile: "Afficher le sélecteur de réactions en tant que + panneau sur mobile" welcomeBackWithName: "Heureux de vous revoir, {name}" -clickToFinishEmailVerification: "Veuillez cliquer sur [{ok}] afin de compléter la vérification par courriel." +clickToFinishEmailVerification: "Veuillez cliquer sur [{ok}] afin de compléter la + vérification par courriel." overridedDeviceKind: "Type d’appareil" smartphone: "Smartphone" tablet: "Tablette" @@ -857,11 +948,16 @@ _ffVisibility: _signup: almostThere: "Bientôt fini" emailAddressInfo: "Insérez votre adresse e-mail." - emailSent: "Un courriel de confirmation vient d'être envoyé à l'adresse que vous avez renseignée ({email}). Cliquez sur le lien contenu dans le message pour terminer la création de votre compte." + emailSent: "Un courriel de confirmation vient d'être envoyé à l'adresse que vous + avez renseignée ({email}). Cliquez sur le lien contenu dans le message pour terminer + la création de votre compte." _accountDelete: accountDelete: "Supprimer le compte" - mayTakeTime: "La suppression de compte nécessitant beaucoup de ressources, l'exécution du processus peut prendre du temps, en fonction de la quantité de contenus que vous avez créés et du nombre de fichiers que vous avez téléversés." - sendEmail: "Une fois la suppression de votre compte effectuée, un courriel sera envoyé à l'adresse que vous aviez enregistrée." + mayTakeTime: "La suppression de compte nécessitant beaucoup de ressources, l'exécution + du processus peut prendre du temps, en fonction de la quantité de contenus que + vous avez créés et du nombre de fichiers que vous avez téléversés." + sendEmail: "Une fois la suppression de votre compte effectuée, un courriel sera + envoyé à l'adresse que vous aviez enregistrée." requestAccountDelete: "Demander la suppression de votre compte" started: "La procédure de suppression a commencé." inProgress: "Suppression en cours" @@ -869,9 +965,14 @@ _ad: back: "Retour" reduceFrequencyOfThisAd: "Voir cette publicité moins souvent" _forgotPassword: - enterEmail: "Entrez ici l'adresse e-mail que vous avez enregistrée pour votre compte. Un lien vous permettant de réinitialiser votre mot de passe sera envoyé à cette adresse." - ifNoEmail: "Si vous n'avez pas enregistré d'adresse e-mail, merci de contacter l'administrateur·rice de votre instance." - contactAdmin: "Cette instance ne permettant pas l'utilisation d'adresses e-mail, prenez contact avec l'administrateur·rice pour procéder à la réinitialisation de votre mot de passe." + enterEmail: "Entrez ici l'adresse e-mail que vous avez enregistrée pour votre compte. + Un lien vous permettant de réinitialiser votre mot de passe sera envoyé à cette + adresse." + ifNoEmail: "Si vous n'avez pas enregistré d'adresse e-mail, merci de contacter l'administrateur·rice + de votre instance." + contactAdmin: "Cette instance ne permettant pas l'utilisation d'adresses e-mail, + prenez contact avec l'administrateur·rice pour procéder à la réinitialisation + de votre mot de passe." _gallery: my: "Mes publications" liked: " Publications que j'ai aimées" @@ -892,14 +993,15 @@ _registry: keys: "Clé " domain: "Domaine" createKey: "Créer une clé" -_aboutMisskey: +_aboutFirefish: about: "Misskey est un logiciel libre et ouvert, développé par syuilo depuis 2014." contributors: "Principaux contributeurs" allContributors: "Tous les contributeurs" source: "Code source" - translation: "Traduire Misskey" - donate: "Soutenir Misskey" - morePatrons: "Nous apprécions vraiment le soutien de nombreuses autres personnes non mentionnées ici. Merci à toutes et à tous ! 🥰" + translation: "Traduire Firefish" + donate: "Soutenir Firefish" + morePatrons: "Nous apprécions vraiment le soutien de nombreuses autres personnes + non mentionnées ici. Merci à toutes et à tous ! 🥰" patrons: "Contributeurs" _nsfw: respect: "Cacher les médias marqués comme contenu sensible" @@ -907,18 +1009,22 @@ _nsfw: force: "Cacher tous les médias" _mfm: cheatSheet: "Antisèche MFM" - intro: "MFM est un langage Markdown spécifique utilisable ici et là dans Misskey. Vous pouvez vérifier ici les structures utilisables avec MFM." - dummy: "La Fédiverse s'agrandit avec Misskey" + intro: "MFM est un langage Markdown spécifique utilisable ici et là dans Firefish. + Vous pouvez vérifier ici les structures utilisables avec MFM." + dummy: "La Fédiverse s'agrandit avec Firefish" mention: "Mentionner" - mentionDescription: "Vous pouvez afficher un utilisateur spécifique en indiquant une arobase suivie d'un nom d'utilisateur" + mentionDescription: "Vous pouvez afficher un utilisateur spécifique en indiquant + une arobase suivie d'un nom d'utilisateur" hashtag: "Hashtags" - hashtagDescription: "Vous pouvez afficher un mot-dièse en utilisant un croisillon et du texte" + hashtagDescription: "Vous pouvez afficher un mot-dièse en utilisant un croisillon + et du texte" url: "URL" urlDescription: "L'adresse web peut être affichée." link: "Lien" linkDescription: "Une partie précise d'une phrase peut être liée à l'adresse web." bold: "Gras" - boldDescription: "Il est possible de mettre le texte en exergue en le mettant en gras." + boldDescription: "Il est possible de mettre le texte en exergue en le mettant en + gras." small: "Diminuer l'emphase" smallDescription: "Le contenu peut être affiché en petit et fin." center: "Centrer" @@ -930,7 +1036,8 @@ _mfm: inlineMath: "Formule mathématique (inline)" inlineMathDescription: "Afficher les formules mathématiques (KaTeX)." blockMath: "Formule mathématique (bloc)" - blockMathDescription: "Afficher les formules mathématiques (KaTeX) multi-lignes dans un bloc." + blockMathDescription: "Afficher les formules mathématiques (KaTeX) multi-lignes + dans un bloc." quote: "Citer" quoteDescription: "Affiche le contenu sous forme de citation." emoji: "Émojis personnalisés" @@ -960,7 +1067,8 @@ _mfm: x4: "Plus grand" x4Description: "Afficher le contenu en plus grand." blur: "Flou" - blurDescription: "Le contenu peut être flouté ; il sera visible en le survolant avec le curseur." + blurDescription: "Le contenu peut être flouté ; il sera visible en le survolant + avec le curseur." font: "Police de caractères" fontDescription: "Il est possible de choisir la police." rainbow: "Arc-en-ciel" @@ -968,6 +1076,12 @@ _mfm: sparkle: "Paillettes" sparkleDescription: "Ajoute un effet scintillant au contenu." rotate: "Pivoter" + fade: "Apparaître/Disparaître" + fadeDescription: "Fait apparaître et disparaître le contenu." + plainDescription: Désactiver les effets de tous les MFM contenus dans cet effet + MFM. + rotateDescription: Pivoter le contenu d'un angle spécifique. + position: Position _instanceTicker: none: "Cacher " remote: "Montrer pour les utilisateur·ice·s distant·e·s" @@ -976,6 +1090,7 @@ _serverDisconnectedBehavior: reload: "Rechargement automatique" dialog: "Ouvrir une boîte de dialogue pour l'avertissement" quiet: "Afficher un avertissement discret" + nothing: Ne rien faire _channel: create: "Créer un canal" edit: "Éditer le canal" @@ -986,6 +1101,7 @@ _channel: following: "Abonné·e" usersCount: "{n} Participant·e·s" notesCount: "{n} Notes" + nameAndDescription: Nom et description _menuDisplay: sideFull: "Latéral" sideIcon: "Latéral (icônes)" @@ -993,10 +1109,14 @@ _menuDisplay: hide: "Masquer" _wordMute: muteWords: "Mots à filtrer" - muteWordsDescription: "Séparer avec des espaces pour la condition AND. Séparer avec un saut de ligne pour une condition OR." - muteWordsDescription2: "Pour utiliser des expressions régulières (regex), mettez les mots-clés entre barres obliques." + muteWordsDescription: "Séparer avec des espaces pour la condition AND. Séparer avec + un saut de ligne pour une condition OR." + muteWordsDescription2: "Pour utiliser des expressions régulières (regex), mettez + les mots-clés entre barres obliques." softDescription: "Masquez les notes de votre fil selon les paramètres que vous définissez." - hardDescription: "Empêchez votre fil de charger les notes selon les paramètres que vous définissez. Cette action est irréversible : si vous modifiez ces paramètres plus tard, les notes précédemment filtrées ne seront pas récupérées." + hardDescription: "Empêchez votre fil de charger les notes selon les paramètres que + vous définissez. Cette action est irréversible : si vous modifiez ces paramètres + plus tard, les notes précédemment filtrées ne seront pas récupérées." soft: "Doux" hard: "Strict" mutedNotes: "Notes filtrées" @@ -1004,6 +1124,9 @@ _instanceMute: instanceMuteDescription2: "Séparer avec de nouvelles lignes" title: "Masque les notes venant des instances listées." heading: "Instances à mettre en sourdine" + instanceMuteDescription: Ceci va masquer toute publication ou partage des instances + listées, incluant celles des personnes répondant à des personnes des instances + masquées. _theme: explore: "Explorer les thèmes" install: "Installer un thème" @@ -1032,8 +1155,10 @@ _theme: darken: "Sombre" lighten: "Clair" inputConstantName: "Insérez un nom de constante" - importInfo: "Vous pouvez importer un thème vers l’éditeur de thèmes en saisissant son code ici." - deleteConstantConfirm: "Êtes-vous sûr·e de vouloir supprimer la constante {const} ?" + importInfo: "Vous pouvez importer un thème vers l’éditeur de thèmes en saisissant + son code ici." + deleteConstantConfirm: "Êtes-vous sûr·e de vouloir supprimer la constante {const} + ?" keys: accent: "Accentuation" bg: "Arrière-plan" @@ -1102,36 +1227,55 @@ _time: hour: "h" day: "j" _tutorial: - title: "Comment utiliser Calckey" - step1_1 : "Bienvenue!" - step1_2 : "On va vous installer. Vous serez opérationnel en un rien de temps" - step2_1 : "Tout d'abord, remplissez votre profil" - step2_2 : "En fournissant quelques informations sur qui vous êtes, il sera plus facile pour les autres de savoir s'ils veulent voir vos notes ou vous suivre." - step3_1 : "Maintenant il est temps de suivre des gens !" - step3_2 : "Votre page d'accueil et vos timelines sociales sont basées sur les personnes que vous suivez, alors essayez de suivre quelques comptes pour commencer.\nCliquez sur le cercle plus en haut à droite d'un profil pour le suivre." - step4_1 : "On y va." - step4_2 : "Pour votre premier post, certaines personnes aiment faire un post {introduction} ou un simple post 'Hello world'." - step5_1 : "Lignes de temps, lignes de temps partout !" - step5_2 : "Votre instance a {timelines} différentes chronologies activées !" - step5_3 : "La timeline Home {icon} est l'endroit où vous pouvez voir les publications de vos followers." - step5_4 : "La timeline locale {icon} est l'endroit où vous pouvez voir les messages de tout le monde sur cette instance." - step5_5 : "La timeline {icon} recommandée est l'endroit où vous pouvez voir les messages des instances que les administrateurs recommandent." - step5_6 : "La timeline {icon} sociale est l'endroit où vous pouvez voir les publications des amis de vos followers." - step5_7 : "La timeline globale {icon} est l'endroit où vous pouvez voir les messages de toutes les autres instances connectées." - step6_1 : "Alors quel est cet endroit ?" - step6_2 : "Eh bien, vous ne venez pas de rejoindre Calckey. Vous avez rejoint un portail vers le Fediverse, un réseau interconnecté de milliers de serveurs, appelés \"instances\"." - step6_3 : "Chaque serveur fonctionne différemment, et tous les serveurs n'utilisent pas Calckey. Cependant, celui-ci le fait ! C'est un peu délicat, mais vous aurez le coup de main en un rien de temps." - step6_4 : "Maintenant, allez-y, explorez et amusez-vous !" + title: "Comment utiliser Firefish" + step1_1: "Bienvenue!" + step1_2: "On va vous installer. Vous serez opérationnel en un rien de temps" + step2_1: "Tout d'abord, remplissez votre profil" + step2_2: "En fournissant quelques informations sur qui vous êtes, il sera plus facile + pour les autres de savoir s'ils veulent voir vos notes ou vous suivre." + step3_1: "Maintenant il est temps de suivre des gens !" + step3_2: "Votre page d'accueil et vos timelines sociales sont basées sur les personnes + que vous suivez, alors essayez de suivre quelques comptes pour commencer.\nCliquez + sur le cercle plus en haut à droite d'un profil pour le suivre." + step4_1: "On y va." + step4_2: "Pour votre premier post, certaines personnes aiment faire un post {introduction} + ou un simple post 'Hello world'." + step5_1: "Lignes de temps, lignes de temps partout !" + step5_2: "Votre instance a {timelines} différentes chronologies activées !" + step5_3: "La timeline Home {icon} est l'endroit où vous pouvez voir les publications + de vos followers." + step5_4: "La timeline locale {icon} est l'endroit où vous pouvez voir les messages + de tout le monde sur cette instance." + step5_5: "La chronologie {icon} sociale est l'endroit où vous pouvez voir uniquement + les publications des comptes que vous suivez." + step5_6: "La chronologie {icon} recommandée est l'endroit où vous pouvez voir les + publications des instances recommandées par les administrateurs." + step5_7: "La timeline globale {icon} est l'endroit où vous pouvez voir les messages + de toutes les autres instances connectées." + step6_1: "Alors quel est cet endroit ?" + step6_2: "Eh bien, vous ne venez pas de rejoindre Firefish. Vous avez rejoint un + portail vers le Fediverse, un réseau interconnecté de milliers de serveurs, appelés + \"instances\"." + step6_3: "Chaque serveur fonctionne différemment, et tous les serveurs n'utilisent + pas Firefish. Cependant, celui-ci le fait ! C'est un peu délicat, mais vous aurez + le coup de main en un rien de temps." + step6_4: "Maintenant, allez-y, explorez et amusez-vous !" _2fa: alreadyRegistered: "Configuration déjà achevée." - registerDevice: "Ajouter un nouvel appareil" - registerKey: "Enregistrer une clef" - step1: "Tout d'abord, installez une application d'authentification, telle que {a} ou {b}, sur votre appareil." + registerTOTP: "Ajouter un nouvel appareil" + registerSecurityKey: "Enregistrer une clef" + step1: "Tout d'abord, installez une application d'authentification, telle que {a} + ou {b}, sur votre appareil." step2: "Ensuite, scannez le code QR affiché sur l’écran." - step2Url: "Vous pouvez également saisir cette URL si vous utilisez un programme de bureau :" + step2Url: "Vous pouvez également saisir cette URL si vous utilisez un programme + de bureau :" step3: "Entrez le jeton affiché sur votre application pour compléter la configuration." - step4: "À partir de maintenant, ce même jeton vous sera demandé à chacune de vos connexions." - securityKeyInfo: "Vous pouvez configurer l'authentification WebAuthN pour sécuriser davantage le processus de connexion grâce à une clé de sécurité matérielle qui prend en charge FIDO2, ou bien en configurant l'authentification par empreinte digitale ou par code PIN sur votre appareil." + step4: "À partir de maintenant, ce même jeton vous sera demandé à chacune de vos + connexions." + securityKeyInfo: "Vous pouvez configurer l'authentification WebAuthN pour sécuriser + davantage le processus de connexion grâce à une clé de sécurité matérielle qui + prend en charge FIDO2, ou bien en configurant l'authentification par empreinte + digitale ou par code PIN sur votre appareil." _permissions: "read:account": "Afficher les informations du compte" "write:account": "Mettre à jour les informations de votre compte" @@ -1167,11 +1311,13 @@ _permissions: "write:gallery-likes": "Gérer les mentions « J'aime » dans la galerie" _auth: shareAccess: "Autoriser \"{name}\" à accéder à votre compte ?" - shareAccessAsk: "Voulez-vous vraiment autoriser cette application à accéder à votre compte?" + shareAccessAsk: "Voulez-vous vraiment autoriser cette application à accéder à votre + compte?" permissionAsk: "Cette application nécessite les autorisations suivantes :" pleaseGoBack: "Veuillez retourner à l’application" callback: "Retour vers l’application" denied: "Accès refusé" + copyAsk: Veuillez coller le code d’autorisation à l'application _antennaSources: all: "Toutes les notes" homeTimeline: "Notes venant des utilisateur·rice·s auxquel·les je suis abonné" @@ -1206,6 +1352,10 @@ _widgets: serverMetric: "Statistiques du serveur" aiscript: "Console AiScript" aichan: "Ai" + userList: Liste d'utilisateurs + _userList: + chooseList: Sélectionner une liste + unixClock: Horloge UNIX _cw: hide: "Masquer" show: "Afficher plus …" @@ -1262,11 +1412,14 @@ _profile: youCanIncludeHashtags: "Vous pouvez également inclure des hashtags." metadata: "Informations supplémentaires" metadataEdit: "Éditer les informations supplémentaires" - metadataDescription: "Vous pouvez afficher jusqu'à quatre informations supplémentaires dans votre profil." + metadataDescription: "Vous pouvez afficher jusqu'à quatre informations supplémentaires + 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" changeBanner: "Changer de bannière" + locationDescription: Si vous entrez votre ville en premier, votre heure locale sera + affiché aux autres utilisateurs. _exportOrImport: allNotes: "Toutes les notes" followingList: "Abonnements" @@ -1306,6 +1459,7 @@ _timelines: local: "Local" social: "Social" global: "Global" + recommended: Recommandée _pages: newPage: "Créer une page" editPage: "Modifier une page" @@ -1333,7 +1487,8 @@ _pages: url: "URL de la page" summary: "Résumé de page" alignCenter: "Centrée" - hideTitleWhenPinned: "Masquer le titre de la page lorsque celle-ci est épinglée au profil" + hideTitleWhenPinned: "Masquer le titre de la page lorsque celle-ci est épinglée + au profil" font: "Police de caractères" fontSerif: "Serif" fontSansSerif: "Sans Serif" @@ -1383,7 +1538,8 @@ _pages: note: "Note intégrée" _note: id: "Identifiant de la note" - idDescription: "Pour configurer la note, vous pouvez aussi coller ici l'URL correspondante." + idDescription: "Pour configurer la note, vous pouvez aussi coller ici l'URL + correspondante." detailed: "Afficher les détails" switch: "Interrupteur" _switch: @@ -1536,7 +1692,8 @@ _pages: _dailyRannum: arg1: "Minimum" arg2: "Maximum" - dailyRandomPick: "Sélectionné au hasard dans la liste (Quotidien pour chaque utilisateur)" + dailyRandomPick: "Sélectionné au hasard dans la liste (Quotidien pour chaque + utilisateur)" _dailyRandomPick: arg1: "Listes" seedRandom: "Aléatoire (graine)" @@ -1552,7 +1709,8 @@ _pages: _seedRandomPick: arg1: "Graine" arg2: "Listes" - DRPWPM: "Sélectionné au hasard dans une liste de probabilités (Quotidien pour chaque utilisateur)" + DRPWPM: "Sélectionné au hasard dans une liste de probabilités (Quotidien pour + chaque utilisateur)" _DRPWPM: arg1: "Liste de texte" pick: "Sélectionner dans la liste" @@ -1627,6 +1785,7 @@ _notification: followRequestAccepted: "Demande d'abonnement acceptée" groupInvited: "Invitation à un groupe" app: "Notifications provenant des apps" + pollEnded: Fin du sondage _actions: followBack: "Suivre" reply: "Répondre" @@ -1651,3 +1810,230 @@ _deck: list: "Listes" mentions: "Mentions" direct: "Direct" + introduction: Créer l'interface parfaite pour vous en arrangeant les colonnes librement + ! + introduction2: Cliquer sur le + sur la droite de l'écran pour ajouter de nouvelles + colonnes à tout moment. +keepOriginalUploadingDescription: Enregistrer l'image originale telle quelle. Si désactivé, + une version à afficher sur le web sera générée au chargement. +manageGroups: Gérer les groupes +moderation: Modération +disableDrawer: Ne pas utiliser des menus à tiroir +preferencesBackups: Sauvegarde des préférences +confirmToUnclipAlreadyClippedNote: Ce message fait déjà partie du clip "{name}". Voudriez-vous + plutôt le supprimer du clip ? +instanceSecurity: Sécurité de l'instance +recommended: Recommandé +recentNDays: Les derniers {n} jours +recentNHours: Les dernières {n} heures +check: Vérifier +thereIsUnresolvedAbuseReportWarning: Il y a des signalements non résolus. +numberOfPageCacheDescription: Augmenter ce nombre augmentera le confort des utilisateur⋅rice⋅s + mais augmentera la charge de travail du serveur, plus de mémoire sera utilisée. +logoutConfirm: Confirmer la déconnexion ? +lastActiveDate: Dernière utilisation le +cannotUploadBecauseNoFreeSpace: Mise en ligne échouée faute de place sur le Drive. +remoteOnly: Distant seulement +showUpdates: Afficher une fenêtre en sur-impression quand Firefish se met à jour +recommendedInstances: Instances recommandées +caption: Description automatique +migration: Migration +showAdminUpdates: Indiquer qu'une nouvelle version de Firefish est disponible (admin + seulement) +replayTutorial: Relancer le tutoriel +moveTo: Migrer le compte courant vers un nouveau compte +moveFromDescription: Ceci va configurer un alias pour votre ancien compte afin que + vous puissiez migrer de cet ancien compte à l'actuel. Faites ceci AVANT de migrer + de votre ancien compte. Merci d'entrer la mention de l'ancien compte sous ce format + @personne@instance.com +_sensitiveMediaDetection: + sensitivityDescription: Réduire la sensibilité conduira à moins de mauvaises détections + (faux positifs) alors que l'augmenter mènera à moins de détection manquées (faux + négatifs). + analyzeVideosDescription: Analyser les vidéos en plus des images. Cela augmentera + légèrement la charge du serveur. + setSensitiveFlagAutomatically: Marquer comme sensible (NSFW) + sensitivity: Sensibilité de la détection + analyzeVideos: Activer l'analyse des vidéos + setSensitiveFlagAutomaticallyDescription: Les résultats de la détection interne + seront conservés même si cette option est désactivée. + description: Réduit potentiellement l'effort de la modération du serveur en reconnaissant + automatiquement les médias sensibles (NSFW) via de l'intelligence artificielle. + Cela va augmenter légèrement la charge du serveur. +_messaging: + dms: Privé + groups: Groupes +cannotUploadBecauseExceedsFileSizeLimit: Le fichier n'a pas pu être chargé car il + dépasse la taille maximum autorisée. +moveAccountDescription: Ce processus est irréversible. Soyez sûr⋅e que vous avez préparé + un alias pour ce compte sur votre nouveau compte avant de migrer. Merci d'entrer + la mention du compte formaté comme ceci @personne@instance.com +moveAccount: Déplacer le compte ! +seperateRenoteQuote: Séparer les renotes et les boutons de citation +failedToFetchAccountInformation: Impossible de récupérer les informations de compte +noEmailServerWarning: Serveur mail non configuré. +deleteAccount: Supprimer le compte +document: Documentation +numberOfPageCache: Nombre de pages mise en cache +fast: Rapide +failedToUpload: Mise en ligne échouée +enableAutoSensitiveDescription: Permet la détection automatique des médias sensibles + (NSFW) via une intelligence artificielle, quand c'est possible. Même si cette option + est désactivée, elle peut être enclenchée au niveau de l'instance. +activeEmailValidationDescription: Active une vérification plus poussée des adresses + e-mail, ce qui inclut de vérifier la présence d’e-mail jetables et s'il est possible + de communiquer avec ces adresses. Si désactivé, seul le format de l’e-mail est vérifié. +adminCustomCssWarn: Ce paramètre ne devrait être utilisé que si vous savez ce qu'il + fait. Entrer des valeurs impropres pourraient empêcher les clients de TOUT LE MONDE + de fonctionner. Assurez-vous que votre CSS fonctionne correctement en l'essayant + dans vos paramètres utilisateur. +swipeOnDesktop: Permettre le style de glissement de fenêtre de mobile sur PC +moveFromLabel: 'Compte depuis lequel vous migrez :' +migrationConfirm: "Êtes-vous absolument certain⋅e que vous voulez migrer votre compte + vers {account} ? Une fois fait, vous ne pourrez pas revenir en arrière, et vous + ne pourrez plus utiliser le compte actuel normalement à nouveau.\nAussi, assurez-vous + d'avoir configuré le compte actuel comme le compte depuis lequel vous migrez." +_preferencesBackups: + updatedAt: 'Mis à jour le : {date} {time}' + cannotLoad: Le chargement a échoué + invalidFile: Format de fichier invalide + saveConfirm: Enregistrer la sauvegarde sous le nom {name} ? + deleteConfirm: Supprimer la sauvegarde {name} ? + nameAlreadyExists: Une sauvegarde nommée "{name}" existe déjà. Merci d'entrer un + autre nom. + applyConfirm: Voulez-vous vraiment appliquer la sauvegarde "{name} à cet appareil + ? Les réglages existants de cet appareil seront écrasés. + noBackups: Aucune sauvegarde n'existe. Vous pouvez sauvegarder les paramètres de + votre client sur ce serveur en utilisant "Créer une nouvelle sauvegarde". + createdAt: 'Crée le : {date} {time}' + renameConfirm: Renommer la sauvegarde "{old}" en "{new}" ? + list: Sauvegardes créées + saveNew: Faire une nouvelle sauvegarde + loadFile: Charger depuis le fichier + apply: Appliquer à l'appareil + save: Enregistrer les changements + inputName: Merci d'entrer un nom pour cette sauvegarde + cannotSave: La sauvegarde a échoué +privateMode: Mode privé +privateModeInfo: Si activé, seules les instances autorisées peuvent fédérer avec votre + instance. Toutes les publications seront masquées de la visibilité publique. +allowedInstances: Instances autorisées +driveCapOverrideLabel: Changer la capacité du drive pour cet utilisateur +driveCapOverrideCaption: Réinitialiser la capacité à la valeur par défaut en entrant + 0 ou moins. +pleaseSelect: Sélectionner une option +customMOTD: Message du jour personnalisé (Message d'écran de démarrage) +refreshInterval: 'Intervalle de mise à jour ' +type: Type +speed: Vitesse +slow: Lent +move: Déplacer +showAds: Afficher les annonces +enterSendsMessage: Appuyer sur Entrée pendant la rédaction pour envoyer le message + (sinon Ctrl+Entrée) +allowedInstancesDescription: Hôtes des instances autorisées pour la fédération, chacun + séparé par une nouvelle ligne (s'applique uniquement en mode privé). +enableAutoSensitive: Marquage automatique du contenu sensible (NSFW) +regexpErrorDescription: "Il y a eu une erreur dans l'expression régulière à la ligne + {line} de votre {tab} des mots masqués :" +forwardReportIsAnonymous: À la place de votre compte, un compte système anonyme sera + affiché comme rapporteur à l'instance distante. +noThankYou: Non merci +addInstance: Ajouter une instance +renoteMute: Mettre en silence les renotes +flagSpeakAsCat: Parler comme un chat +flagSpeakAsCatDescription: Vos messages seront nyanifiés en mode chat +hiddenTags: Hashtags cachés +hiddenTagsDescription: "Lister les hashtags (sans le #) que vous souhaitez cacher + de tendances et explorer. Les hashtags cachés sont toujours découvrables par d'autres + moyens. Les instances bloqués ne sont pas ne sont pas affectés, même si ils sont + présent dans cette liste." +antennaInstancesDescription: Lister un hôte d'instance par ligne +userSaysSomethingReason: '{name} a dit {reason}' +breakFollowConfirm: Êtes vous sur de vouloir retirer l'abonné ? +recommendedInstancesDescription: Instances recommandées séparées par une nouvelle + ligne pour apparaître dans la timeline recommandée. +sendPushNotificationReadMessage: Supprimer les notifications push une fois que les + notifications ou messages concernés ont été lus +sendPushNotificationReadMessageCaption: Une notification contenant le texte "{emptyPushNotificationMessage}" + sera affichée pendant un court instant. Cela peut augmenter la consommation de batterie + de votre appareil. +splash: Écran d’Accueil +pushNotificationNotSupported: Votre navigateur ou instance ne supporte pas les notifications + push +customMOTDDescription: Messages personnalisé pour le message du jour (sur l'écran + d’accueil), séparés par des retours à la ligne, affichés au hasard à chaque (re)chargement + de page. +customSplashIcons: Icônes de l'écran d’accueil personnalisées (urls) +customSplashIconsDescription: URLs pour les icônes personnalisées de l'écran d’accueil, + séparés par des retours à la ligne, qui seront affichées aléatoirement à chaque + (re)chargement de page. Assurez-vous que les images sont sur des URL statiques, + de préférence toutes de taille 192x192. +updateAvailable: Une mise à jour est peut-être disponible ! +accountMoved: "L'utilisateur a migré vers un nouveau compte :" +enableEmojiReactions: Activer les réactions par émojis +showEmojisInReactionNotifications: Montrer les émojis dans les notifications de réactions +renoteUnmute: Notifier les renotes +selectInstance: Choisir une instance +noInstances: Il n'y a aucune instance +showLocalPosts: 'Montrer les notes locales dans :' +homeTimeline: Timeline d'Accueil +socialTimeline: Timeline Sociale +requireAdminForView: Vous avez besoin d'un compte d'administration pour voir cela. +isSystemAccount: Un compte créé et géré automatiquement par le système. +typeToConfirm: Entrer {x} pour confirmer +statusbar: Barre d'état +sensitiveMediaDetection: Détection des médias sensibles (NSFW) +cannotUploadBecauseInappropriate: Ce fichier n'a pas pu être mis en ligne, car il + a été détecté comme potentiellement sensible (NSFW). +beta: Beta +navbar: Barre de navigation +shuffle: Mélanger +pushNotification: Notifications push +subscribePushNotification: Activer les notifications push +unsubscribePushNotification: Désactiver les notifications push +pushNotificationAlreadySubscribed: Notifications push déjà activées +logoImageUrl: URL de l'image du logo +moveToLabel: 'Compte vers lequel vous migrez :' +moveFrom: Migrer vers ce compte depuis un ancien compte +defaultReaction: Émoji de réaction par défaut pour les notes entrantes et sortantes +license: Licence +indexPosts: Indexer les Notes +indexNotice: Indexation en cours. Cela prendra certainement du temps, veuillez ne + pas redémarrer votre serveur pour au moins une heure. +customKaTeXMacro: Macros KaTeX personnalisées +enableCustomKaTeXMacro: Activer les macros KaTeX personnalisées +noteId: ID de note +customKaTeXMacroDescription: "Définissez des macros pour écrire des expressions mathématiques + simplement ! La notation se conforme aux définitions de commandes LaTeX et s'écrit + \\newcommand{\\name}{content} ou \\newcommand{\\name}[number of arguments]{content}. + Par exemple, \\newcommand{\\add}[2]{#1 + #2} étendra \\add{3}{foo} en 3 + foo. Les + accolades entourant le nom de la macro peuvent être changés pour des parenthèses + ou des crochets. Cela affectera les types de parenthèses utilisées pour les arguments. + Une (et une seule) macro peut être définie par ligne, et vous ne pouvez pas couper + la ligne au milieu d'une définition. Les lignes invalides sont simplement ignorées. + Seulement de simples fonctions de substitution de chaines sont supportées; la syntaxe + avancée, telle que la ramification conditionnelle, ne peut pas être utilisée ici." +enableRecommendedTimeline: Activer la chronologie recommandée +silenceThisInstance: Ne plus montrer cet instance +silencedInstances: Instances silencieuses +silenced: Silencieux +deleted: Effacé +editNote: Modifier note +edited: 'Modifié à {date} {time}' +flagShowTimelineRepliesDescription: Si activé, affiche dans le fil les réponses des + personnes aux publications des autres. +_experiments: + alpha: Alpha + beta: Beta + enablePostImports: Autoriser l'importation de messages + title: Expérimentations +findOtherInstance: Trouver un autre serveur +userSaysSomethingReasonQuote: '{name} a cité une note contenant {reason}' +signupsDisabled: Les inscriptions sur ce serveur sont actuellement désactivés, mais + vous pouvez toujours vous inscrire sur un autre serveur ! Si vous avez un code d'invitation + pour ce serveur, entrez-le ci-dessous s'il vous plait. +apps: Applications +userSaysSomethingReasonReply: '{noms} a répondu à une note contenant {raison}' +defaultValueIs: 'défaut : {valeur}' +searchPlaceholder: Recherchez sur Firefish diff --git a/locales/gl.yml b/locales/gl.yml new file mode 100644 index 0000000000..5aaae34f6b --- /dev/null +++ b/locales/gl.yml @@ -0,0 +1,17 @@ +_lang_: Inglés +introMisskey: Benvida! Firefish é unha plataforma de medios sociais de código aberto, + descentralizada e gratuíta para sempre!🚀 +monthAndDay: '{day}/{month}' +notifications: Notificacións +password: Contrasinal +forgotPassword: Esquecín o contrasinal +gotIt: Vale! +cancel: Cancelar +noThankYou: Non, grazas +headlineMisskey: Plataforma de medios sociais de código aberto e descentralizada, + gratuíta para sempre!🚀 +search: Buscar +searchPlaceholder: Buscar en Firefish +username: Identificador +fetchingAsApObject: Descargando desde o Fediverso +ok: OK diff --git a/locales/id-ID.yml b/locales/id-ID.yml index bb3904e2e8..c0de97a76e 100644 --- a/locales/id-ID.yml +++ b/locales/id-ID.yml @@ -1,7 +1,9 @@ ---- _lang_: "Bahasa Indonesia" headlineMisskey: "Jaringan terhubung melalui catatan" -introMisskey: "Selamat datang! Misskey adalah perangkat mikroblog tercatu bersifat sumber terbuka.\nMulailah menuliskan catatan, bagikan peristiwa terkini, serta ceritakan segala tentangmu.📡\nTunjukkan juga reaksimu pada catatan pengguna lain.👍\nMari jelajahi dunia baru🚀" +introMisskey: "Selamat datang! Firefish adalah perangkat mikroblog tercatu bersifat\ + \ sumber terbuka.\nMulailah menuliskan catatan, bagikan peristiwa terkini, serta\ + \ ceritakan segala tentangmu.\U0001F4E1\nTunjukkan juga reaksimu pada catatan pengguna\ + \ lain.\U0001F44D\nMari jelajahi dunia baru\U0001F680" monthAndDay: "{day} {month}" search: "Penelusuran" notifications: "Pemberitahuan" @@ -44,7 +46,8 @@ copyContent: "Salin konten" copyLink: "Salin tautan" delete: "Hapus" deleteAndEdit: "Hapus dan sunting" -deleteAndEditConfirm: "Apakah kamu yakin ingin menghapus note ini dan menyuntingnya? Kamu akan kehilangan semua reaksi, renote dan balasan di note ini." +deleteAndEditConfirm: "Apakah kamu yakin ingin menghapus note ini dan menyuntingnya?\ + \ Kamu akan kehilangan semua reaksi, renote dan balasan di note ini." addToList: "Tambahkan ke daftar" sendMessage: "Kirim pesan" copyUsername: "Salin nama pengguna" @@ -66,7 +69,8 @@ files: "Berkas" download: "Unduh" driveFileDeleteConfirm: "Hapus {name}? Catatan dengan berkas terkait juga akan terhapus." unfollowConfirm: "Berhenti mengikuti {name}?" -exportRequested: "Kamu telah meminta ekspor. Ini akan memakan waktu sesaat. Setelah ekspor selesai, berkas yang dihasilkan akan ditambahkan ke Drive" +exportRequested: "Kamu telah meminta ekspor. Ini akan memakan waktu sesaat. Setelah\ + \ ekspor selesai, berkas yang dihasilkan akan ditambahkan ke Drive" importRequested: "Kamu telah meminta impor. Ini akan memakan waktu sesaat." lists: "Daftar" noLists: "Kamu tidak memiliki daftar apapun" @@ -81,9 +85,12 @@ error: "Galat" somethingHappened: "Terjadi kesalahan" retry: "Coba lagi" pageLoadError: "Gagal memuat halaman." -pageLoadErrorDescription: "Umumnya disebabkan jaringan atau tembolok perambah. Cobalah bersihkan tembolok peramban lalu tunggu sesaat sebelum mencoba kembali." -serverIsDead: "Tidak ada respon dari peladen. Mohon tunggu dan coba beberapa saat lagi." -youShouldUpgradeClient: "Untuk melihat halaman ini, mohon muat ulang untuk memutakhirkan klienmu." +pageLoadErrorDescription: "Umumnya disebabkan jaringan atau tembolok perambah. Cobalah\ + \ bersihkan tembolok peramban lalu tunggu sesaat sebelum mencoba kembali." +serverIsDead: "Tidak ada respon dari peladen. Mohon tunggu dan coba beberapa saat\ + \ lagi." +youShouldUpgradeClient: "Untuk melihat halaman ini, mohon muat ulang untuk memutakhirkan\ + \ klienmu." enterListName: "Masukkan nama daftar" privacy: "Privasi" makeFollowManuallyApprove: "Permintaan mengikuti membutuhkan persetujuan" @@ -108,7 +115,8 @@ sensitive: "Konten sensitif" add: "Tambahkan" reaction: "Reaksi" reactionSetting: "Reaksi untuk dimunculkan di bilah reaksi" -reactionSettingDescription2: "Geser untuk memindah urutkan, klik untuk menghapus, tekan \"+\" untuk menambahkan" +reactionSettingDescription2: "Geser untuk memindah urutkan, klik untuk menghapus,\ + \ tekan \"+\" untuk menambahkan" rememberNoteVisibility: "Ingat pengaturan visibilitas catatan" attachCancel: "Hapus lampiran" markAsSensitive: "Tandai sebagai konten sensitif" @@ -137,14 +145,22 @@ emojiUrl: "URL Emoji" addEmoji: "Tambahkan emoji" settingGuide: "Pengaturan rekomendasi" cacheRemoteFiles: "Tembolokkan berkas remote" -cacheRemoteFilesDescription: "Ketika pengaturan ini dinonaktifkan, berkas luar akan dimuat langsung dari instansi luar. Menonaktifkan ini akan mengurangi penggunaan penyimpanan, namun dapat menyebabkan meningkatkan lalu lintas bandwidth, karena thumbnail tidak dihasilkan." +cacheRemoteFilesDescription: "Ketika pengaturan ini dinonaktifkan, berkas luar akan\ + \ dimuat langsung dari instansi luar. Menonaktifkan ini akan mengurangi penggunaan\ + \ penyimpanan, namun dapat menyebabkan meningkatkan lalu lintas bandwidth, karena\ + \ thumbnail tidak dihasilkan." flagAsBot: "Atur akun ini sebagai Bot" -flagAsBotDescription: "Jika akun ini dikendalikan oleh program, tetapkanlah opsi ini. Jika diaktifkan, ini akan berfungsi sebagai tanda bagi pengembang lain untuk mencegah interaksi berantai dengan bot lain dan menyesuaikan sistem internal Misskey untuk memperlakukan akun ini sebagai bot." +flagAsBotDescription: "Jika akun ini dikendalikan oleh program, tetapkanlah opsi ini.\ + \ Jika diaktifkan, ini akan berfungsi sebagai tanda bagi pengembang lain untuk mencegah\ + \ interaksi berantai dengan bot lain dan menyesuaikan sistem internal Firefish untuk\ + \ memperlakukan akun ini sebagai bot." flagAsCat: "Atur akun ini sebagai kucing" flagAsCatDescription: "Nyalakan tanda ini untuk menandai akun ini sebagai kucing." flagShowTimelineReplies: "Tampilkan balasan di linimasa" -flagShowTimelineRepliesDescription: "Menampilkan balasan pengguna dari note pengguna lain di linimasa apabila dinyalakan." -autoAcceptFollowed: "Setujui otomatis permintaan mengikuti dari pengguna yang kamu ikuti" +flagShowTimelineRepliesDescription: "Menampilkan balasan pengguna dari note pengguna\ + \ lain di linimasa apabila dinyalakan." +autoAcceptFollowed: "Setujui otomatis permintaan mengikuti dari pengguna yang kamu\ + \ ikuti" addAccount: "Tambahkan akun" loginFailed: "Gagal untuk masuk" showOnRemote: "Lihat profil asli" @@ -156,7 +172,11 @@ searchWith: "Cari: {q}" youHaveNoLists: "Kamu tidak memiliki daftar apapun" followConfirm: "Apakah kamu yakin ingin mengikuti {name}?" proxyAccount: "Akun proksi" -proxyAccountDescription: "Akun proksi merupakan sebuah akun yang bertindak sebagai pengikut luar untuk pengguna dalam kondisi tertentu. Sebagai contoh, ketika pengguna menambahkan seorang pengguna luar ke dalam daftar, aktivitas dari pengguna luar tidak akan disampaikan ke instansi apabila tidak ada pengguna lokal yang mengikuti pengguna tersebut, dengan begitu akun proksilah yang akan mengikutinya." +proxyAccountDescription: "Akun proksi merupakan sebuah akun yang bertindak sebagai\ + \ pengikut luar untuk pengguna dalam kondisi tertentu. Sebagai contoh, ketika pengguna\ + \ menambahkan seorang pengguna luar ke dalam daftar, aktivitas dari pengguna luar\ + \ tidak akan disampaikan ke instansi apabila tidak ada pengguna lokal yang mengikuti\ + \ pengguna tersebut, dengan begitu akun proksilah yang akan mengikutinya." host: "Host" selectUser: "Pilih pengguna" recipient: "Penerima" @@ -177,7 +197,6 @@ operations: "Tindakan" software: "Perangkat lunak" version: "Versi" metadata: "Metadata" -withNFiles: "{n} berkas" monitor: "Pantau" jobQueue: "Antrian kerja" cpuAndMemory: "CPU dan Memori" @@ -187,11 +206,15 @@ instanceInfo: "Informasi Instansi" statistics: "Statistik" clearQueue: "Bersihkan antrian" clearQueueConfirmTitle: "Apakah kamu yakin ingin membersihkan antrian?" -clearQueueConfirmText: "Seluruh sisa catatan yang tidak tersampaikan di dalam antrian tidak akan difederasi. Biasanya operasi ini TIDAK dibutuhkan." +clearQueueConfirmText: "Seluruh sisa catatan yang tidak tersampaikan di dalam antrian\ + \ tidak akan difederasi. Biasanya operasi ini TIDAK dibutuhkan." clearCachedFiles: "Hapus tembolok" -clearCachedFilesConfirm: "Apakah kamu yakin ingin menghapus seluruh tembolok berkas remote?" +clearCachedFilesConfirm: "Apakah kamu yakin ingin menghapus seluruh tembolok berkas\ + \ remote?" blockedInstances: "Instansi terblokir" -blockedInstancesDescription: "Daftar nama host dari instansi yang diperlukan untuk diblokir. Instansi yang didaftarkan tidak akan dapat berkomunikasi dengan instansi ini." +blockedInstancesDescription: "Daftar nama host dari instansi yang diperlukan untuk\ + \ diblokir. Instansi yang didaftarkan tidak akan dapat berkomunikasi dengan instansi\ + \ ini." muteAndBlock: "Bisukan / Blokir" mutedUsers: "Pengguna yang dibisukan" blockedUsers: "Pengguna yang diblokir" @@ -199,7 +222,7 @@ noUsers: "Tidak ada pengguna" editProfile: "Sunting profil" noteDeleteConfirm: "Apakah kamu yakin ingin menghapus catatan ini?" pinLimitExceeded: "Kamu tidak dapat menyematkan catatan lagi" -intro: "Instalasi Misskey telah selesai! Mohon untuk membuat pengguna admin." +intro: "Instalasi Firefish telah selesai! Mohon untuk membuat pengguna admin." done: "Selesai" processing: "Memproses" preview: "Pratinjau" @@ -239,7 +262,8 @@ saved: "Telah disimpan" messaging: "Pesan" upload: "Unggah" keepOriginalUploading: "Simpan gambar asli" -keepOriginalUploadingDescription: "Simpan gambar yang diunggah sebagaimana gambar aslinya. Bila dimatikan, versi tampilan web akan dihasilkan pada saat diunggah." +keepOriginalUploadingDescription: "Simpan gambar yang diunggah sebagaimana gambar\ + \ aslinya. Bila dimatikan, versi tampilan web akan dihasilkan pada saat diunggah." fromDrive: "Dari Drive" fromUrl: "Dari URL" uploadFromUrl: "Unggah dari URL" @@ -255,7 +279,8 @@ 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 ini mungkin tidak mutakhir, karena pengguna ini berasal\ + \ dari instansi luar." activity: "Aktivitas" images: "Gambar" birthday: "Tanggal lahir" @@ -288,7 +313,8 @@ unableToDelete: "Tidak dapat menghapus" inputNewFileName: "Masukkan nama berkas yang baru" inputNewDescription: "Masukkan keterangan disini" inputNewFolderName: "Masukkan nama folder yang baru" -circularReferenceFolder: "Folder tujuan adalah subfolder dari folder yang ingin kamu pindahkan." +circularReferenceFolder: "Folder tujuan adalah subfolder dari folder yang ingin kamu\ + \ pindahkan." hasChildFilesOrFolders: "Karena folder ini tidak kosong, maka tidak dapat dihapus." copyUrl: "Salin tautan" rename: "Ubah nama" @@ -322,7 +348,8 @@ connectService: "Sambungkan" disconnectService: "Putuskan" enableLocalTimeline: "Nyalakan linimasa lokal" enableGlobalTimeline: "Nyalakan linimasa global" -disablingTimelinesInfo: "Admin dan Moderator akan selalu memiliki akses ke semua linimasa meskipun linimasa tersebut tidak diaktifkan." +disablingTimelinesInfo: "Admin dan Moderator akan selalu memiliki akses ke semua linimasa\ + \ meskipun linimasa tersebut tidak diaktifkan." registration: "Pendaftaran" enableRegistration: "Nyalakan pendaftaran pengguna baru" invite: "Undang" @@ -334,9 +361,11 @@ bannerUrl: "URL Banner" backgroundImageUrl: "URL Gambar latar" basicInfo: "Informasi Umum" pinnedUsers: "Pengguna yang disematkan" -pinnedUsersDescription: "Tuliskan satu nama pengguna dalam satu baris. Pengguna yang dituliskan disini akan disematkan dalam bilah \"Jelajahi\"." +pinnedUsersDescription: "Tuliskan satu nama pengguna dalam satu baris. Pengguna yang\ + \ dituliskan disini akan disematkan dalam bilah \"Jelajahi\"." pinnedPages: "Halaman yang disematkan" -pinnedPagesDescription: "Masukkan tautan dari halaman yang kamu ingin sematkan ke halaman utama dari instansi ini, dipisah dengan membuat baris baru." +pinnedPagesDescription: "Masukkan tautan dari halaman yang kamu ingin sematkan ke\ + \ halaman utama dari instansi ini, dipisah dengan membuat baris baru." pinnedClipId: "ID dari klip yang disematkan" pinnedNotes: "Catatan yang disematkan" hcaptcha: "hCaptcha" @@ -347,14 +376,17 @@ recaptcha: "reCAPTCHA" enableRecaptcha: "Nyalakan reCAPTCHA" recaptchaSiteKey: "Site key" recaptchaSecretKey: "Secret Key" -avoidMultiCaptchaConfirm: "Menggunakan banyak Captcha dapat menyebabkan gangguan. Apakah kamu ingin untuk menonaktifkan Captcha yang lain? Kamu dapat membiarkan fitur ini tetap aktif dengan menekan tombol batal." +avoidMultiCaptchaConfirm: "Menggunakan banyak Captcha dapat menyebabkan gangguan.\ + \ Apakah kamu ingin untuk menonaktifkan Captcha yang lain? Kamu dapat membiarkan\ + \ fitur ini tetap aktif dengan menekan tombol batal." antennas: "Antena" manageAntennas: "Pengelola Antena" name: "Nama" antennaSource: "Sumber Antenna" antennaKeywords: "Kata kunci yang diterima" antennaExcludeKeywords: "Kata kunci yang dikecualikan" -antennaKeywordsDescription: "Pisahkan dengan spasi untuk kondisi AND. Pisahkan dengan baris baru untuk kondisi OR." +antennaKeywordsDescription: "Pisahkan dengan spasi untuk kondisi AND. Pisahkan dengan\ + \ baris baru untuk kondisi OR." notifyAntenna: "Beritahu untuk catatan baru" withFileAntenna: "Hanya tampilkan catatan dengan berkas yang dilampirkan" enableServiceworker: "Aktifkan ServiceWorker" @@ -377,7 +409,7 @@ exploreFediverse: "Jelajahi Fediverse" popularTags: "Tag populer" userList: "Daftar" about: "Informasi" -aboutMisskey: "Tentang Misskey" +aboutFirefish: "Tentang Firefish" administrator: "Admin" token: "Token" twoStepAuthentication: "Otentikasi dua faktor" @@ -441,7 +473,8 @@ strongPassword: "Kata sandi kuat" passwordMatched: "Kata sandi sama" passwordNotMatched: "Kata sandi tidak sama" signinWith: "Masuk dengan {x}" -signinFailed: "Tidak dapat masuk. Nama pengguna atau kata sandi yang kamu masukkan salah." +signinFailed: "Tidak dapat masuk. Nama pengguna atau kata sandi yang kamu masukkan\ + \ salah." tapSecurityKey: "Ketuk kunci keamanan kamu" or: "atau" language: "Bahasa" @@ -482,19 +515,29 @@ showFeaturedNotesInTimeline: "Tampilkan catatan yang diunggulkan di linimasa" objectStorage: "Object Storage" useObjectStorage: "Gunakan object storage" objectStorageBaseUrl: "Base URL" -objectStorageBaseUrlDesc: "Prefix URL digunakan untuk mengkonstruksi URL ke object (media) referencing. Tentukan URL jika kamu menggunakan CDN atau Proxy, jika tidak tentukan alamat yang dapat diakses secara publik sesuai dengan panduan dari layanan yang akan kamu gunakan, contohnya. 'https://.s3.amazonaws.com' untuk AWS S3, dan 'https://storage.googleapis.com/' untuk GCS." +objectStorageBaseUrlDesc: "Prefix URL digunakan untuk mengkonstruksi URL ke object\ + \ (media) referencing. Tentukan URL jika kamu menggunakan CDN atau Proxy, jika tidak\ + \ tentukan alamat yang dapat diakses secara publik sesuai dengan panduan dari layanan\ + \ yang akan kamu gunakan, contohnya. 'https://.s3.amazonaws.com' untuk AWS\ + \ S3, dan 'https://storage.googleapis.com/' untuk GCS." objectStorageBucket: "Bucket" -objectStorageBucketDesc: "Mohon tentukan nama bucket yang digunakan pada layanan yang telah dikonfigurasi." +objectStorageBucketDesc: "Mohon tentukan nama bucket yang digunakan pada layanan yang\ + \ telah dikonfigurasi." objectStoragePrefix: "Prefix" objectStoragePrefixDesc: "Berkas tidak akan disimpan dalam direktori dari prefix ini." objectStorageEndpoint: "Endpoint" -objectStorageEndpointDesc: "Kosongkan bagian ini jika kamu menggunakan AWS S3, jika tidak tentukan endpoint sebagai '' atau ':' sesuai dengan panduan dari layanan yang akan kamu gunakan." +objectStorageEndpointDesc: "Kosongkan bagian ini jika kamu menggunakan AWS S3, jika\ + \ tidak tentukan endpoint sebagai '' atau ':' sesuai dengan panduan\ + \ dari layanan yang akan kamu gunakan." objectStorageRegion: "Region" -objectStorageRegionDesc: "Tentukan region seperti 'xx-east-1'. Jika layanan kamu tidak memiliki perbedaan mengenai region, kosongkan saja atau isi dengan 'us-east-1'." +objectStorageRegionDesc: "Tentukan region seperti 'xx-east-1'. Jika layanan kamu tidak\ + \ memiliki perbedaan mengenai region, kosongkan saja atau isi dengan 'us-east-1'." objectStorageUseSSL: "Gunakan SSL" -objectStorageUseSSLDesc: "Matikan ini jika kamu tidak akan menggunakan HTTPS untuk koneksi API" +objectStorageUseSSLDesc: "Matikan ini jika kamu tidak akan menggunakan HTTPS untuk\ + \ koneksi API" objectStorageUseProxy: "Hubungkan melalui Proxy" -objectStorageUseProxyDesc: "Matikan ini jika kamu tidak akan menggunakan Proxy untuk koneksi ObjectStorage" +objectStorageUseProxyDesc: "Matikan ini jika kamu tidak akan menggunakan Proxy untuk\ + \ koneksi ObjectStorage" objectStorageSetPublicRead: "Setel \"public-read\" disaat mengunggah" serverLogs: "Log Peladen" deleteAll: "Hapus semua" @@ -522,7 +565,9 @@ sort: "Urutkan" ascendingOrder: "Urutkan naik" descendingOrder: "Urutkan menurun" scratchpad: "Scratchpad" -scratchpadDescription: "Scratchpad menyediakan lingkungan eksperimen untuk AiScript. Kamu bisa menulis, mengeksuksi, serta mengecek hasil yang berinteraksi dengan Misskey." +scratchpadDescription: "Scratchpad menyediakan lingkungan eksperimen untuk AiScript.\ + \ Kamu bisa menulis, mengeksuksi, serta mengecek hasil yang berinteraksi dengan\ + \ Firefish." output: "Keluaran" script: "Script" disablePagesScript: "Nonaktifkan script pada halaman" @@ -530,11 +575,14 @@ updateRemoteUser: "Perbaharui informasi pengguna luar" deleteAllFiles: "Hapus semua berkas" deleteAllFilesConfirm: "Apakah kamu yakin ingin menghapus semua berkas?" removeAllFollowing: "Tahan semua mengikuti" -removeAllFollowingDescription: "Batal mengikuti semua akun dari {host}. Mohon jalankan ini ketika instansi sudah tidak ada lagi." +removeAllFollowingDescription: "Batal mengikuti semua akun dari {host}. Mohon jalankan\ + \ ini ketika instansi sudah tidak ada lagi." userSuspended: "Pengguna ini telah dibekukan." userSilenced: "Pengguna ini telah dibungkam." yourAccountSuspendedTitle: "Akun ini dibekukan" -yourAccountSuspendedDescription: "Akun ini dibekukan karena melanggar ketentuan penggunaan layanan peladen atau semacamnya. Hubungi admin apabila ingin tahu alasan lebih lanjut. Mohon untuk tidak membuat akun baru." +yourAccountSuspendedDescription: "Akun ini dibekukan karena melanggar ketentuan penggunaan\ + \ layanan peladen atau semacamnya. Hubungi admin apabila ingin tahu alasan lebih\ + \ lanjut. Mohon untuk tidak membuat akun baru." menu: "Menu" divider: "Pembagi" addItem: "Tambahkan item" @@ -579,7 +627,8 @@ notificationType: "Jenis pemberitahuan" edit: "Sunting" emailServer: "Peladen surel" enableEmail: "Nyalakan distribusi surel" -emailConfigInfo: "Digunakan untuk mengonfirmasi surel kamu disaat mendaftar dan lupa kata sandi" +emailConfigInfo: "Digunakan untuk mengonfirmasi surel kamu disaat mendaftar dan lupa\ + \ kata sandi" email: "Surel" emailAddress: "Alamat surel" smtpConfig: "Konfigurasi peladen SMTP" @@ -587,13 +636,15 @@ smtpHost: "Host" smtpPort: "Port" smtpUser: "Nama Pengguna" smtpPass: "Kata sandi" -emptyToDisableSmtpAuth: "Kosongkan nama pengguna dan kata sandi untuk menonaktifkan verifikasi SMTP" +emptyToDisableSmtpAuth: "Kosongkan nama pengguna dan kata sandi untuk menonaktifkan\ + \ verifikasi SMTP" smtpSecure: "Gunakan SSL/TLS implisit untuk koneksi SMTP" smtpSecureInfo: "Matikan ini ketika menggunakan STARTTLS" testEmail: "Tes pengiriman surel" wordMute: "Bisukan kata" regexpError: "Kesalahan ekspresi reguler" -regexpErrorDescription: "Galat terjadi pada baris {line} ekspresi reguler dari {tab} kata yang dibisukan:" +regexpErrorDescription: "Galat terjadi pada baris {line} ekspresi reguler dari {tab}\ + \ kata yang dibisukan:" instanceMute: "Bisuka instansi" userSaysSomething: "{name} mengatakan sesuatu" makeActive: "Aktifkan" @@ -609,30 +660,37 @@ create: "Buat" notificationSetting: "Pengaturan Pemberitahuan" notificationSettingDesc: "Pilih tipe pemberitahuan untuk ditampilkan" useGlobalSetting: "Gunakan setelan global" -useGlobalSettingDesc: "Jika dinyalakan, setelan pemberitahuan akun kamu akan digunakan. Jika dimatikan, konfigurasi secara individu dapat dibuat." +useGlobalSettingDesc: "Jika dinyalakan, setelan pemberitahuan akun kamu akan digunakan.\ + \ Jika dimatikan, konfigurasi secara individu dapat dibuat." other: "Lainnya" regenerateLoginToken: "Perbarui token login" -regenerateLoginTokenDescription: "Perbarui token yang digunakan secara internal saat login. Normalnya aksi ini tidak diperlukan. Jika diperbarui, semua perangkat akan dilogout." -setMultipleBySeparatingWithSpace: "Kamu dapat menyetel banyak dengan memisahkannya menggunakan spasi." +regenerateLoginTokenDescription: "Perbarui token yang digunakan secara internal saat\ + \ login. Normalnya aksi ini tidak diperlukan. Jika diperbarui, semua perangkat akan\ + \ dilogout." +setMultipleBySeparatingWithSpace: "Kamu dapat menyetel banyak dengan memisahkannya\ + \ menggunakan spasi." fileIdOrUrl: "File-ID atau URL" behavior: "Perilaku" sample: "Contoh" abuseReports: "Laporkan" reportAbuse: "Laporkan" reportAbuseOf: "Laporkan {name}" -fillAbuseReportDescription: "Mohon isi rincian laporan. Jika laporan ini mengenai catatan yang spesifik, mohon lampirkan serta URL catatan tersebut." +fillAbuseReportDescription: "Mohon isi rincian laporan. Jika laporan ini mengenai\ + \ catatan yang spesifik, mohon lampirkan serta URL catatan tersebut." abuseReported: "Laporan kamu telah dikirimkan. Terima kasih." reporter: "Pelapor" reporteeOrigin: "Yang dilaporkan" reporterOrigin: "Pelapor" forwardReport: "Teruskan laporan ke instansi luar" -forwardReportIsAnonymous: "Untuk melindungi privasi akun kamu, akun anonim dari sistem akan digunakan sebagai pelapor pada instansi luar." +forwardReportIsAnonymous: "Untuk melindungi privasi akun kamu, akun anonim dari sistem\ + \ akan digunakan sebagai pelapor pada instansi luar." send: "Kirim" abuseMarkAsResolved: "Tandai laporan sebagai selesai" openInNewTab: "Buka di tab baru" openInSideView: "Buka di tampilan samping" defaultNavigationBehaviour: "Navigasi bawaan" -editTheseSettingsMayBreakAccount: "Menyunting pengaturan ini memiliki kemungkinan untuk merusak akun kamu." +editTheseSettingsMayBreakAccount: "Menyunting pengaturan ini memiliki kemungkinan\ + \ untuk merusak akun kamu." instanceTicker: "Informasi pengguna pada instansi" waitingFor: "Menunggu untuk {x}" random: "Acak" @@ -644,9 +702,11 @@ createNew: "Buat baru" optional: "Opsional" createNewClip: "Buat klip baru" unclip: "Batalkan klip" -confirmToUnclipAlreadyClippedNote: "Catatan ini sudah disertakan di klip \"{name}\". Yakin ingin membatalkan catatan dari klip ini?" +confirmToUnclipAlreadyClippedNote: "Catatan ini sudah disertakan di klip \"{name}\"\ + . Yakin ingin membatalkan catatan dari klip ini?" public: "Publik" -i18nInfo: "Calckey diterjemahkan ke dalam banyak bahasa oleh sukarelawan. Kamu dapat ikut membantu di {link}." +i18nInfo: "Firefish diterjemahkan ke dalam banyak bahasa oleh sukarelawan. Kamu dapat\ + \ ikut membantu di {link}." manageAccessTokens: "Kelola access token" accountInfo: "Informasi akun" notesCount: "Jumlah catatan" @@ -665,12 +725,16 @@ no: "Tidak" driveFilesCount: "Jumlah berkas drive" driveUsage: "Penggunaan ruang penyimpanan drive" noCrawle: "Tolak pengindeksan crawler" -noCrawleDescription: "Meminta mesin pencari untuk tidak mengindeks halaman profil kamu, catatan, Halaman, dll." -lockedAccountInfo: "Kecuali kamu menyetel visibilitas catatan milikmu ke \"Hanya pengikut\", catatan milikmu akan dapat dilihat oleh siapa saja, bahkan jika kamu memerlukan pengikut untuk disetujui secara manual." +noCrawleDescription: "Meminta mesin pencari untuk tidak mengindeks halaman profil\ + \ kamu, catatan, Halaman, dll." +lockedAccountInfo: "Kecuali kamu menyetel visibilitas catatan milikmu ke \"Hanya pengikut\"\ + , catatan milikmu akan dapat dilihat oleh siapa saja, bahkan jika kamu memerlukan\ + \ pengikut untuk disetujui secara manual." alwaysMarkSensitive: "Tandai media dalam catatan sebagai media sensitif" loadRawImages: "Tampilkan lampiran gambar secara penuh daripada thumbnail" disableShowingAnimatedImages: "Jangan mainkan gambar bergerak" -verificationEmailSent: "Surel verifikasi telah dikirimkan. Mohon akses tautan yang telah disertakan untuk menyelesaikan verifikasi." +verificationEmailSent: "Surel verifikasi telah dikirimkan. Mohon akses tautan yang\ + \ telah disertakan untuk menyelesaikan verifikasi." notSet: "Tidak disetel" emailVerified: "Surel telah diverifikasi" noteFavoritesCount: "Jumlah catatan yang difavoritkan" @@ -682,14 +746,16 @@ clips: "Klip" experimentalFeatures: "Fitur eksperimental" developer: "Pengembang" makeExplorable: "Buat akun tampil di \"Jelajahi\"" -makeExplorableDescription: "Jika kamu mematikan ini, akun kamu tidak akan muncul di bagian \"Jelajahi:" +makeExplorableDescription: "Jika kamu mematikan ini, akun kamu tidak akan muncul di\ + \ bagian \"Jelajahi:" showGapBetweenNotesInTimeline: "Tampilkan jarak diantara catatan pada linimasa" duplicate: "Duplikat" left: "Kiri" center: "Tengah" wide: "Lebar" narrow: "Sempit" -reloadToApplySetting: "Pengaturan ini akan diterapkan saat memuat halaman kembali. Apakah kamu ingin memuat halaman kembali sekarang?" +reloadToApplySetting: "Pengaturan ini akan diterapkan saat memuat halaman kembali.\ + \ Apakah kamu ingin memuat halaman kembali sekarang?" needReloadToApply: "Pengaturan ini hanya akan diterapkan setelah memuat ulang halaman." showTitlebar: "Tampilkan bilah judul" clearCache: "Hapus tembolok" @@ -697,7 +763,10 @@ onlineUsersCount: "{n} orang sedang daring" nUsers: "{n} Pengguna" nNotes: "{n} Catatan" sendErrorReports: "Kirim laporan kesalahan" -sendErrorReportsDescription: "Ketika dinyalakan, informasi kesalahan rinci akan dibagikan dengan Misskey ketika masalah terjadi, hal ini untuk membantu kualitas Misskey. Fitur ini memungkinkan memuat informasi seperti sistem operasi yang kamu gunakan dan versinya, aplikasi peramban yang kamu gunakan, riwayat aktivitas kamu, dll." +sendErrorReportsDescription: "Ketika dinyalakan, informasi kesalahan rinci akan dibagikan\ + \ dengan Firefish ketika masalah terjadi, hal ini untuk membantu kualitas Firefish.\ + \ Fitur ini memungkinkan memuat informasi seperti sistem operasi yang kamu gunakan\ + \ dan versinya, aplikasi peramban yang kamu gunakan, riwayat aktivitas kamu, dll." myTheme: "Tema saya" backgroundColor: "Latar Belakang" accentColor: "Aksen" @@ -736,14 +805,17 @@ unlikeConfirm: "Yakin ingin hapus sukamu?" fullView: "Tampilan penuh" quitFullView: "Keluar tampilan penuh" addDescription: "Tambahkan deskripsi" -userPagePinTip: "Kamu dapat membuat catatan untuk ditampilkan disini dengan memilih \"Sematkan ke profil\" dari menu pada catatan individu." -notSpecifiedMentionWarning: "Catatan ini mengandung sebutan dari pengguna yang tidak dimuat sebagai penerima" +userPagePinTip: "Kamu dapat membuat catatan untuk ditampilkan disini dengan memilih\ + \ \"Sematkan ke profil\" dari menu pada catatan individu." +notSpecifiedMentionWarning: "Catatan ini mengandung sebutan dari pengguna yang tidak\ + \ dimuat sebagai penerima" info: "Informasi" userInfo: "Informasi pengguna" unknown: "Tidak diketahui" onlineStatus: "Status daring" hideOnlineStatus: "Sembunyikan status daring" -hideOnlineStatusDescription: "Menyembunyikan status daring kamu umengurangi kenyamanan untuk beberapa fungsi seperti contohnya pencarian." +hideOnlineStatusDescription: "Menyembunyikan status daring kamu umengurangi kenyamanan\ + \ untuk beberapa fungsi seperti contohnya pencarian." online: "Daring" active: "Aktif" offline: "Luring" @@ -778,7 +850,8 @@ emailNotConfiguredWarning: "Alamat surel tidak disetel." ratio: "Rasio" previewNoteText: "Tampilkan pratinjau" customCss: "Custom CSS" -customCssWarn: "Pengaturan ini seharusnya digunakan jika kamu tahu cara kerjanya. Memasukkan nilai yang tidak tepat dapat menyebabkan klien tidak berfungsi semestinya." +customCssWarn: "Pengaturan ini seharusnya digunakan jika kamu tahu cara kerjanya.\ + \ Memasukkan nilai yang tidak tepat dapat menyebabkan klien tidak berfungsi semestinya." global: "Global" squareAvatars: "Tampilkan avatar sebagai persegi" sent: "Kirim" @@ -788,12 +861,14 @@ hashtags: "Tagar" troubleshooting: "Penyelesaian Masalah" useBlurEffect: "Gunakan efek blur pada antarmuka" learnMore: "Pelajari lebih lanjut" -misskeyUpdated: "Misskey telah dimutakhirkan!" +misskeyUpdated: "Firefish telah dimutakhirkan!" whatIsNew: "Lihat perubahan pemutakhiran" translate: "Terjemahkan" translatedFrom: "Terjemahkan dari {x}" accountDeletionInProgress: "Penghapusan akun sedang dalam proses" -usernameInfo: "Nama yang mengidentifikasikan akun kamu dari yang lain pada peladen ini. Kamu dapat menggunakan alfabet (a~z, A~Z), digit (0~9) atau garis bawah (_). Username tidak dapat diubah setelahnya." +usernameInfo: "Nama yang mengidentifikasikan akun kamu dari yang lain pada peladen\ + \ ini. Kamu dapat menggunakan alfabet (a~z, A~Z), digit (0~9) atau garis bawah (_).\ + \ Username tidak dapat diubah setelahnya." aiChanMode: "Mode Ai" keepCw: "Biarkan Peringatan Konten" pubSub: "Akun Pub/Sub" @@ -809,12 +884,14 @@ filter: "Saring" controlPanel: "Panel kendali" manageAccounts: "Kelola Akun" makeReactionsPublic: "Tampilkan riwayat reaksi ke publik" -makeReactionsPublicDescription: "Pengaturan ini akan membuat daftar dari semua reaksi masa lalu kamu ditampilkan secara publik." +makeReactionsPublicDescription: "Pengaturan ini akan membuat daftar dari semua reaksi\ + \ masa lalu kamu ditampilkan secara publik." classic: "Klasik" muteThread: "Bisukan thread" unmuteThread: "Suarakan thread" ffVisibility: "Visibilitas Mengikuti/Pengikut" -ffVisibilityDescription: "Mengatur siapa yang dapat melihat pengikutmu dan yang kamu ikuti." +ffVisibilityDescription: "Mengatur siapa yang dapat melihat pengikutmu dan yang kamu\ + \ ikuti." continueThread: "Lihat lanjutan thread" deleteAccountConfirm: "Akun akan dihapus. Apakah kamu yakin?" incorrectPassword: "Kata sandi salah." @@ -824,7 +901,8 @@ leaveGroup: "Keluar grup" leaveGroupConfirm: "Apakah kamu yakin untuk keluar dari \"{name}\"?" useDrawerReactionPickerForMobile: "Tampilkan bilah reaksi sebagai laci di ponsel" welcomeBackWithName: "Selamat datang kembali, {name}." -clickToFinishEmailVerification: "Mohon klik [{ok}] untuk menyelesaikan verifikasi email." +clickToFinishEmailVerification: "Mohon klik [{ok}] untuk menyelesaikan verifikasi\ + \ email." overridedDeviceKind: "Tipe perangkat" smartphone: "Ponsel" tablet: "Tablet" @@ -866,11 +944,16 @@ _ffVisibility: _signup: almostThere: "Hampir selesai" emailAddressInfo: "Mohon masukkan alamat surel kamu." - emailSent: "Konfirmasi surel telah dikirimkan ke alamat surel kamu ({email}). Mohon klik tautan yang tercantum di dalamnya untuk menyelesaikan pembuatan akun." + emailSent: "Konfirmasi surel telah dikirimkan ke alamat surel kamu ({email}). Mohon\ + \ klik tautan yang tercantum di dalamnya untuk menyelesaikan pembuatan akun." _accountDelete: accountDelete: "Hapus akun" - mayTakeTime: "Karena penghapusan akun merupakan proses yang berat dan intensif, kemungkinan dapat membutuhkan waktu untuk menyelesaikan tergantung daripada berapa banyak konten yang kamu buat dan berapa banyak berkas yang telah kamu unggah." - sendEmail: "Setelah penghapusan akun selesai, pemberitahuan akan dikirimkan ke alamat surel yang terdaftarkan pada akun ini." + mayTakeTime: "Karena penghapusan akun merupakan proses yang berat dan intensif,\ + \ kemungkinan dapat membutuhkan waktu untuk menyelesaikan tergantung daripada\ + \ berapa banyak konten yang kamu buat dan berapa banyak berkas yang telah kamu\ + \ unggah." + sendEmail: "Setelah penghapusan akun selesai, pemberitahuan akan dikirimkan ke alamat\ + \ surel yang terdaftarkan pada akun ini." requestAccountDelete: "Minta penghapusan akun" started: "Penghapusan telah dimulai" inProgress: "Penghapusan sedang dalam proses" @@ -878,9 +961,13 @@ _ad: back: "Kembali" reduceFrequencyOfThisAd: "Tampilkan iklan ini lebih sedikit" _forgotPassword: - enterEmail: "Masukkan alamat surel yang kamu gunakan pada saat mendaftar. Sebuah tautan untuk mengatur ulang kata sandi kamu akan dikirimkan ke alamat surel tersebut." - ifNoEmail: "Apabila kamu tidak menggunakan surel pada saat pendaftaran, mohon hubungi admin segera." - contactAdmin: "Instansi ini tidak mendukung menggunakan alamat surel, mohon kontak admin untuk mengatur ulang password kamu." + enterEmail: "Masukkan alamat surel yang kamu gunakan pada saat mendaftar. Sebuah\ + \ tautan untuk mengatur ulang kata sandi kamu akan dikirimkan ke alamat surel\ + \ tersebut." + ifNoEmail: "Apabila kamu tidak menggunakan surel pada saat pendaftaran, mohon hubungi\ + \ admin segera." + contactAdmin: "Instansi ini tidak mendukung menggunakan alamat surel, mohon kontak\ + \ admin untuk mengatur ulang password kamu." _gallery: my: "Postingan saya" liked: "Postingan yang disukai" @@ -901,14 +988,16 @@ _registry: keys: "Kunci" domain: "Domain" createKey: "Buat kunci" -_aboutMisskey: - about: "Misskey adalah perangkat lunak sumber terbuka yang sedang dikembangkan oleh syuilo sejak 2014." +_aboutFirefish: + about: "Firefish adalah perangkat lunak sumber terbuka yang sedang dikembangkan oleh\ + \ syuilo sejak 2014." contributors: "Kontributor utama" allContributors: "Seluruh kontributor" source: "Sumber kode" - translation: "Terjemahkan Misskey" - donate: "Donasi ke Misskey" - morePatrons: "Kami sangat mengapresiasi dukungan dari banyak penolong lain yang tidak tercantum disini. Terima kasih! 🥰" + translation: "Terjemahkan Firefish" + donate: "Donasi ke Firefish" + morePatrons: "Kami sangat mengapresiasi dukungan dari banyak penolong lain yang\ + \ tidak tercantum disini. Terima kasih! \U0001F970" patrons: "Pendukung" _nsfw: respect: "Sembunyikan media NSFW" @@ -916,10 +1005,12 @@ _nsfw: force: "Sembunyikan semua media" _mfm: cheatSheet: "Contekan MFM" - intro: "MFM adalah Misskey-exclusive Markup Language yang dapat digunakan di banyak tempat. Berikut kamu bisa melihat daftar dari syntax MFM yang ada." - dummy: "Misskey membentangkan dunia Fediverse" + intro: "MFM adalah Firefish-exclusive Markup Language yang dapat digunakan di banyak\ + \ tempat. Berikut kamu bisa melihat daftar dari syntax MFM yang ada." + dummy: "Firefish membentangkan dunia Fediverse" mention: "Sebut" - mentionDescription: "Kamu dapat menentukan pengguna tertentu dengan menggunakan simbol-At dan nama engguna mereka." + mentionDescription: "Kamu dapat menentukan pengguna tertentu dengan menggunakan\ + \ simbol-At dan nama engguna mereka." hashtag: "Tagar" hashtagDescription: "Kamu dapat menentukan tagar dengan menggunakan angka dan teks." url: "URL" @@ -935,15 +1026,18 @@ _mfm: inlineCode: "Kode (Dalam baris)" inlineCodeDescription: "Menampilkan sorotan sintaks dalam baris untuk kode(program-)." blockCode: "Kode (Blok)" - blockCodeDescription: "Menampilkan sorotan sintaks untuk kode(program-) multi baris dalam sebuah blok." + blockCodeDescription: "Menampilkan sorotan sintaks untuk kode(program-) multi baris\ + \ dalam sebuah blok." inlineMath: "Matematika (Dalam baris)" inlineMathDescription: "Menampilkan formula matematika (KaTeX) dalam baris." blockMath: "Matematika (Blok)" - blockMathDescription: "Menampilkan formula matematika (KaTeX) multibaris dalam sebuah blok." + blockMathDescription: "Menampilkan formula matematika (KaTeX) multibaris dalam sebuah\ + \ blok." quote: "Kutip" quoteDescription: "Menampilkan konten sebagai kutipan." emoji: "Emoji kustom" - emojiDescription: "Emoji kustom dapat ditampilkan dengan mengurung nama emoji kustom menggunakan tanda titik dua." + emojiDescription: "Emoji kustom dapat ditampilkan dengan mengurung nama emoji kustom\ + \ menggunakan tanda titik dua." search: "Penelusuran" searchDescription: "Menampilkan kotak pencarian dengan teks yang sudah dimasukkan." flip: "Balik" @@ -969,7 +1063,8 @@ _mfm: x4: "Sangat besar" x4Description: "Tampilka konten menjadi sangat besar." blur: "Buram" - blurDescription: "Konten dapat diburamkan dengan efek ini. Konten dapat ditampilkan dengan jelas dengan melayangkan kursor tetikus di atasnya." + blurDescription: "Konten dapat diburamkan dengan efek ini. Konten dapat ditampilkan\ + \ dengan jelas dengan melayangkan kursor tetikus di atasnya." font: "Font" fontDescription: "Setel font yang ditampilkan untuk konten." rainbow: "Pelangi" @@ -1003,15 +1098,21 @@ _menuDisplay: hide: "Sembunyikan" _wordMute: muteWords: "Kata yang dibisukan" - muteWordsDescription: "Pisahkan dengan spasi untuk kondisi AND. Pisahkan dengan baris baru untuk kondisi OR." - muteWordsDescription2: "Kurung kata kunci dengan garis miring untuk menggunakan regular expressions." + muteWordsDescription: "Pisahkan dengan spasi untuk kondisi AND. Pisahkan dengan\ + \ baris baru untuk kondisi OR." + muteWordsDescription2: "Kurung kata kunci dengan garis miring untuk menggunakan\ + \ regular expressions." softDescription: "Sembunyikan catatan yang memenuhi aturan kondisi dari linimasa." - hardDescription: "Cegah catatan memenuhi aturan kondisi dari ditambahkan ke linimasa. Dengan tambahan, catatan berikut tidak akan ditambahkan ke linimasa meskipun jika kondisi tersebut diubah." + hardDescription: "Cegah catatan memenuhi aturan kondisi dari ditambahkan ke linimasa.\ + \ Dengan tambahan, catatan berikut tidak akan ditambahkan ke linimasa meskipun\ + \ jika kondisi tersebut diubah." soft: "Lembut" hard: "Keras" mutedNotes: "Catatan yang dibisukan" _instanceMute: - instanceMuteDescription: "Pengaturan ini akan membisukan note/renote apa saja dari instansi yang terdaftar, termasuk pengguna yang membalas pengguna lain dalam instansi yang dibisukan." + instanceMuteDescription: "Pengaturan ini akan membisukan note/renote apa saja dari\ + \ instansi yang terdaftar, termasuk pengguna yang membalas pengguna lain dalam\ + \ instansi yang dibisukan." instanceMuteDescription2: "Pisah dengan baris baru" title: "Sembunyikan note dari instansi terdaftar." heading: "Daftar instansi yang akan dibisukan" @@ -1043,7 +1144,8 @@ _theme: darken: "Mengelamkan" lighten: "Menerangkan" inputConstantName: "Masukkan nama untuk konstanta" - importInfo: "Jika kamu memasukkan kode tema disini, kamu dapat mengimpornya ke penyunting tema" + importInfo: "Jika kamu memasukkan kode tema disini, kamu dapat mengimpornya ke penyunting\ + \ tema" deleteConstantConfirm: "apakah kamu ingin menghapus konstanta {const}?" keys: accent: "Aksen" @@ -1113,38 +1215,58 @@ _time: hour: "jam" day: "hari" _tutorial: - title: "Cara menggunakan Misskey" + title: "Cara menggunakan Firefish" step1_1: "Selamat datang!" - step1_2: "Halaman ini disebut \"linimasa\". Halaman ini menampilkan \"catatan\" yang diurutkan secara kronologis dari orang-orang yang kamu \"ikuti\"." - step1_3: "Linimasa kamu kosong, karena kamu belum mencatat catatan apapun atau mengikuti siapapun." - step2_1: "Selesaikan menyetel profilmu sebelum menulis sebuah catatan atau mengikuti seseorang." - step2_2: "Menyediakan beberapa informasi tentang siapa kamu akan membuat orang lain mudah untuk mengikutimu kembali." - step3_1: "Selesai menyetel profil kamu?" - step3_2: "Langkah selanjutnya adalah membuat catatan. Kamu bisa lakukan ini dengan mengklik ikon pensil pada layar kamu." - step3_3: "Isilah di dalam modal dan tekan tombol pada atas kanan untuk memcatat catatan kamu." - step3_4: "Bingung tidak berpikiran untuk mengatakan sesuatu? Coba saja \"baru aja ikutan bikin akun misskey punyaku\"!" + step1_2: "Halaman ini disebut \"linimasa\". Halaman ini menampilkan \"catatan\"\ + \ yang diurutkan secara kronologis dari orang-orang yang kamu \"ikuti\"." + step1_3: "Linimasa kamu kosong, karena kamu belum mencatat catatan apapun atau mengikuti\ + \ siapapun." + step2_1: "Selesaikan menyetel profilmu sebelum menulis sebuah catatan atau mengikuti\ + \ seseorang." + step2_2: "Menyediakan beberapa informasi tentang siapa kamu akan membuat orang lain\ + \ mudah untuk mengikutimu kembali." + step3_1: "Sekarang saatnya mengikuti beberapa orang!" + step3_2: "Langkah selanjutnya adalah membuat catatan. Kamu bisa lakukan ini dengan\ + \ mengklik ikon pensil pada layar kamu." + step3_3: "Isilah di dalam modal dan tekan tombol pada atas kanan untuk memcatat\ + \ catatan kamu." + step3_4: "Bingung tidak berpikiran untuk mengatakan sesuatu? Coba saja \"baru aja\ + \ ikutan bikin akun misskey punyaku\"!" step4_1: "Selesai mencatat catatan pertamamu?" step4_2: "Horee! Sekarang catatan pertamamu sudah ditampilkan di linimasa milikmu." - step5_1: "Sekarang, mari mencoba untuk membuat linimasamu lebih hidup dengan mengikuti orang lain." - step5_2: "{featured} akan memperlihatkan catatan yang sedang tren saat ini untuk kamu. {explore} akan membantumu untuk mencari pengguna yang sedang tren juga saat ini. Coba ikuti seseorang yang kamu suka!" - step5_3: "Untuk mengikuti pengguna lain, klik pada ikon mereka dan tekan tombol follow pada profil mereka." - step5_4: "Jika pengguna lain memiliki ikon gembok di sebelah nama mereka, maka pengguna rersebut harus menyetujui permintaan mengikuti dari kamu secara manual." + step5_1: "Sekarang, mari mencoba untuk membuat linimasamu lebih hidup dengan mengikuti\ + \ orang lain." + step5_2: "{featured} akan memperlihatkan catatan yang sedang tren saat ini untuk\ + \ kamu. {explore} akan membantumu untuk mencari pengguna yang sedang tren juga\ + \ saat ini. Coba ikuti seseorang yang kamu suka!" + step5_3: "Untuk mengikuti pengguna lain, klik pada ikon mereka dan tekan tombol\ + \ follow pada profil mereka." + step5_4: "Jika pengguna lain memiliki ikon gembok di sebelah nama mereka, maka pengguna\ + \ rersebut harus menyetujui permintaan mengikuti dari kamu secara manual." step6_1: "Sekarang kamu dapat melihat catatan pengguna lain pada linimasamu." - step6_2: "Kamu juga bisa memberikan \"reaksi\" ke catatan orang lain untuk merespon dengan cepat." - step6_3: "Untuk memberikan \"reaksi\", tekan tanda \"+\" pada catatan pengguna lain dan pilih emoji yang kamu suka untuk memberikan reaksimu kepada mereka." - step7_1: "Yay, Selamat! Kamu sudah menyelesaikan tutorial dasar Misskey." - step7_2: "Jika kamu ingin mempelajari lebih lanjut tentang Misskey, cobalah berkunjung ke bagian {help}." - step7_3: "Semoga berhasil dan bersenang-senanglah! 🚀" + step6_2: "Kamu juga bisa memberikan \"reaksi\" ke catatan orang lain untuk merespon\ + \ dengan cepat." + step6_3: "Untuk memberikan \"reaksi\", tekan tanda \"+\" pada catatan pengguna lain\ + \ dan pilih emoji yang kamu suka untuk memberikan reaksimu kepada mereka." + step7_1: "Yay, Selamat! Kamu sudah menyelesaikan tutorial dasar Firefish." + step7_2: "Jika kamu ingin mempelajari lebih lanjut tentang Firefish, cobalah berkunjung\ + \ ke bagian {help}." + step7_3: "Semoga berhasil dan bersenang-senanglah! \U0001F680" _2fa: alreadyRegistered: "Kamu telah mendaftarkan perangkat otentikasi dua faktor." - registerDevice: "Daftarkan perangkat baru" - registerKey: "Daftarkan kunci keamanan baru" - step1: "Pertama, pasang aplikasi otentikasi (seperti {a} atau {b}) di perangkat kamu." + registerTOTP: "Daftarkan perangkat baru" + registerSecurityKey: "Daftarkan kunci keamanan baru" + step1: "Pertama, pasang aplikasi otentikasi (seperti {a} atau {b}) di perangkat\ + \ kamu." step2: "Lalu, pindai kode QR yang ada di layar." step2Url: "Di aplikasi desktop, masukkan URL berikut:" - step3: "Masukkan token yang telah disediakan oleh aplikasimu untuk menyelesaikan pemasangan." - step4: "Mulai sekarang, upaya login apapun akan meminta token login dari aplikasi otentikasi kamu." - securityKeyInfo: "Kamu dapat memasang otentikasi WebAuthN untuk mengamankan proses login lebih lanjut dengan tidak hanya perangkat keras kunci keamanan yang mendukung FIDO2, namun juga sidik jari atau otentikasi PIN pada perangkatmu." + step3: "Masukkan token yang telah disediakan oleh aplikasimu untuk menyelesaikan\ + \ pemasangan." + step4: "Mulai sekarang, upaya login apapun akan meminta token login dari aplikasi\ + \ otentikasi kamu." + securityKeyInfo: "Kamu dapat memasang otentikasi WebAuthN untuk mengamankan proses\ + \ login lebih lanjut dengan tidak hanya perangkat keras kunci keamanan yang mendukung\ + \ FIDO2, namun juga sidik jari atau otentikasi PIN pada perangkatmu." _permissions: "read:account": "Lihat informasi akun" "write:account": "Sunting informasi akun" @@ -1180,7 +1302,8 @@ _permissions: "write:gallery-likes": "Sunting daftar postingan galeri yang disukai" _auth: shareAccess: "Apakah kamu ingin mengijinkan \"{name}\" untuk mengakses akun ini?" - shareAccessAsk: "Apakah kamu ingin mengijinkan aplikasi ini untuk mengakses akun kamu?" + shareAccessAsk: "Apakah kamu ingin mengijinkan aplikasi ini untuk mengakses akun\ + \ kamu?" permissionAsk: "Aplikasi ini membutuhkan beberapa ijin, yaitu:" pleaseGoBack: "Mohon kembali ke aplikasi kamu" callback: "Mengembalikan kamu ke aplikasi" @@ -1275,7 +1398,8 @@ _profile: youCanIncludeHashtags: "Kamu juga dapat menambahkan tagar ke dalam bio." metadata: "Informasi tambahan" metadataEdit: "Sunting informasi tambahan" - metadataDescription: "Kamu dapat menampilkan hingga 4 bagian informasi tambahan ke dalam profilmu." + metadataDescription: "Kamu dapat menampilkan hingga 4 bagian informasi tambahan\ + \ 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" @@ -1596,7 +1720,8 @@ _pages: _for: arg1: "Jumlah angka untuk diulangi" arg2: "Aksi" - typeError: "Slot {slot} menerima tipe \"{expect}\", sayangnya nilai yang disediakan adalah \"{actual}\"!" + typeError: "Slot {slot} menerima tipe \"{expect}\", sayangnya nilai yang disediakan\ + \ adalah \"{actual}\"!" thereIsEmptySlot: "Slot {slot} kosong!" types: string: "Teks" diff --git a/locales/index.js b/locales/index.js index 7399bb5a18..62e55e7e5c 100644 --- a/locales/index.js +++ b/locales/index.js @@ -2,59 +2,90 @@ * Languages Loader */ -const fs = require('fs'); -const yaml = require('js-yaml'); -let languages = [] -let languages_custom = [] - -const merge = (...args) => args.reduce((a, c) => ({ - ...a, - ...c, - ...Object.entries(a) - .filter(([k]) => c && typeof c[k] === 'object') - .reduce((a, [k, v]) => (a[k] = merge(v, c[k]), a), {}) -}), {}); +const fs = require("fs"); +const yaml = require("js-yaml"); +const languages = []; +const languages_custom = []; +const merge = (...args) => + args.reduce( + (a, c) => ({ + ...a, + ...c, + ...Object.entries(a) + .filter(([k]) => c && typeof c[k] === "object") + .reduce((a, [k, v]) => ((a[k] = merge(v, c[k])), a), {}), + }), + {}, + ); fs.readdirSync(__dirname).forEach((file) => { - if (file.includes('.yml')){ - file = file.slice(0, file.indexOf('.')) + if (file.includes(".yml")) { + file = file.slice(0, file.indexOf(".")); languages.push(file); } -}) +}); -fs.readdirSync(__dirname + '/../custom/locales').forEach((file) => { - if (file.includes('.yml')){ - file = file.slice(0, file.indexOf('.')) +fs.readdirSync(__dirname + "/../custom/locales").forEach((file) => { + if (file.includes(".yml")) { + file = file.slice(0, file.indexOf(".")); languages_custom.push(file); } -}) +}); const primaries = { - 'en': 'US', - 'ja': 'JP', - 'zh': 'CN', + en: "US", + ja: "JP", + zh: "CN", }; // 何故か文字列にバックスペース文字が混入することがあり、YAMLが壊れるので取り除く -const clean = (text) => text.replace(new RegExp(String.fromCodePoint(0x08), 'g'), ''); +const clean = (text) => + text.replace(new RegExp(String.fromCodePoint(0x08), "g"), ""); -const locales = languages.reduce((a, c) => (a[c] = yaml.load(clean(fs.readFileSync(`${__dirname}/${c}.yml`, 'utf-8'))) || {}, a), {}); -const locales_custom = languages_custom.reduce((a, c) => (a[c] = yaml.load(clean(fs.readFileSync(`${__dirname}/../custom/locales/${c}.yml`, 'utf-8'))) || {}, a), {}); -Object.assign(locales, locales_custom) +const locales = languages.reduce( + (a, c) => ( + (a[c] = + yaml.load(clean(fs.readFileSync(`${__dirname}/${c}.yml`, "utf-8"))) || + {}), + a + ), + {}, +); +const locales_custom = languages_custom.reduce( + (a, c) => ( + (a[c] = + yaml.load( + clean( + fs.readFileSync(`${__dirname}/../custom/locales/${c}.yml`, "utf-8"), + ), + ) || {}), + a + ), + {}, +); +Object.assign(locales, locales_custom); -module.exports = Object.entries(locales) - .reduce((a, [k ,v]) => (a[k] = (() => { - const [lang] = k.split('-'); - switch (k) { - case 'ja-JP': return v; - case 'ja-KS': - case 'en-US': return merge(locales['ja-JP'], v); - default: return merge( - locales['ja-JP'], - locales['en-US'], - locales[`${lang}-${primaries[lang]}`] || {}, - v - ); - } - })(), a), {}); +module.exports = Object.entries(locales).reduce( + (a, [k, v]) => ( + (a[k] = (() => { + const [lang] = k.split("-"); + switch (k) { + case "ja-JP": + return v; + case "ja-KS": + case "en-US": + return merge(locales["ja-JP"], v); + default: + return merge( + locales["ja-JP"], + locales["en-US"], + locales[`${lang}-${primaries[lang]}`] || {}, + v, + ); + } + })()), + a + ), + {}, +); diff --git a/locales/it-IT.yml b/locales/it-IT.yml index 185d12d5ac..77bc1cf846 100644 --- a/locales/it-IT.yml +++ b/locales/it-IT.yml @@ -1,7 +1,10 @@ ---- _lang_: "Italiano" headlineMisskey: "Rete collegata tramite note" -introMisskey: "Benvenut@! Misskey è un servizio di microblogging decentralizzato, libero e aperto. \nScrivi \"note\" per condividere ciò che sta succedendo adesso o per dire a tutti qualcosa di te. 📡\nGrazie alla funzione \"reazioni\" puoi anche mandare reazioni rapide alle note delle altre persone del Fediverso. 👍\nEsplora un nuovo mondo! 🚀" +introMisskey: "Benvenut@! Firefish è un servizio di microblogging decentralizzato, + libero e aperto. \nScrivi \"note\" per condividere ciò che sta succedendo adesso + o per dire a tutti qualcosa di te. 📡\nGrazie alla funzione \"reazioni\" puoi anche + mandare reazioni rapide alle note delle altre persone del Fediverso. 👍\nEsplora + un nuovo mondo! 🚀" monthAndDay: "{day}/{month}" search: "Cerca" notifications: "Notifiche" @@ -10,7 +13,7 @@ password: "Password" forgotPassword: "Hai dimenticato la tua password?" fetchingAsApObject: "Recuperando dal Fediverso" ok: "OK" -gotIt: "Ho capito" +gotIt: "Ho capito!" cancel: "Annulla" enterUsername: "Inserisci un nome utente" renotedBy: "Rinotato da {user}" @@ -30,9 +33,9 @@ logout: "Esci" signup: "Iscriviti" uploading: "Caricamento..." save: "Salva" -users: "Utente" +users: "Utenti" addUser: "Aggiungi utente" -favorite: "Preferiti" +favorite: "Aggiungi ai preferiti" favorites: "Preferiti" unfavorite: "Rimuovi nota dai preferiti" favorited: "Aggiunta ai tuoi preferiti." @@ -44,7 +47,8 @@ copyContent: "Copia il contenuto" copyLink: "Copia il link" delete: "Elimina" deleteAndEdit: "Elimina e modifica" -deleteAndEditConfirm: "Vuoi davvero cancellare questa nota e scriverla di nuovo? Verrano eliminate anche tutte le reazioni, Rinote e risposte collegate." +deleteAndEditConfirm: "Vuoi davvero cancellare questa nota e scriverla di nuovo? Verrano + eliminate anche tutte le reazioni, Rinote e risposte collegate." addToList: "Aggiungi alla lista" sendMessage: "Invia messaggio" copyUsername: "Copia nome utente" @@ -54,7 +58,7 @@ loadMore: "Mostra di più" showMore: "Mostra di più" showLess: "Chiudi" youGotNewFollower: "Ha iniziato a seguirti" -receiveFollowRequest: "Hai ricevuto una richiesta di follow." +receiveFollowRequest: "Hai ricevuto una richiesta di follow" followRequestAccepted: "Richiesta di follow accettata" mention: "Menzioni" mentions: "Menzioni" @@ -64,9 +68,11 @@ import: "Importa" export: "Esporta" files: "Allegati" download: "Scarica" -driveFileDeleteConfirm: "Vuoi davvero eliminare il file「{name}? Anche gli allegati verranno eliminati." +driveFileDeleteConfirm: "Vuoi davvero eliminare il file「{name}? Anche gli allegati + verranno eliminati." unfollowConfirm: "Vuoi davvero smettere di seguire {name}?" -exportRequested: "Hai richiesto un'esportazione, e potrebbe volerci tempo. Quando sarà compiuta, il file verrà aggiunto direttamente al Drive." +exportRequested: "Hai richiesto un'esportazione, e potrebbe volerci tempo. Quando + sarà compiuta, il file verrà aggiunto direttamente al Drive." importRequested: "Hai richiesto un'importazione. Può volerci tempo. " lists: "Liste" noLists: "Nessuna lista" @@ -81,9 +87,11 @@ error: "Errore" somethingHappened: "Si è verificato un problema" retry: "Riprova" pageLoadError: "Caricamento pagina non riuscito. " -pageLoadErrorDescription: "Questo viene normalmente causato dalla rete o dalla cache del browser. Si prega di pulire la cache, o di attendere e riprovare più tardi." +pageLoadErrorDescription: "Questo viene normalmente causato dalla rete o dalla cache + del browser. Si prega di pulire la cache, o di attendere e riprovare più tardi." serverIsDead: "Il server non risponde. Si prega di attendere e riprovare più tardi." -youShouldUpgradeClient: "Per visualizzare la pagina è necessario aggiornare il client alla nuova versione e ricaricare." +youShouldUpgradeClient: "Per visualizzare la pagina è necessario aggiornare il client + alla nuova versione e ricaricare." enterListName: "Nome della lista" privacy: "Privacy" makeFollowManuallyApprove: "Richiedi di approvare i follower manualmente" @@ -108,7 +116,8 @@ sensitive: "Contenuto sensibile" add: "Aggiungi" reaction: "Reazione" reactionSetting: "Reazioni visualizzate sul pannello" -reactionSettingDescription2: "Trascina per riorganizzare, clicca per cancellare, usa il pulsante \"+\" per aggiungere." +reactionSettingDescription2: "Trascina per riorganizzare, clicca per cancellare, usa + il pulsante \"+\" per aggiungere." rememberNoteVisibility: "Ricordare le impostazioni di visibilità delle note" attachCancel: "Rimuovi allegato" markAsSensitive: "Segna come sensibile" @@ -137,12 +146,19 @@ emojiUrl: "URL dell'emoji" addEmoji: "Aggiungi un emoji" settingGuide: "Configurazione suggerita" cacheRemoteFiles: "Memorizzazione nella cache dei file remoti" -cacheRemoteFilesDescription: "Disabilitando questa opzione, i file remoti verranno linkati direttamente senza essere memorizzati nella cache. Sarà possibile risparmiare spazio di archiviazione sul server, ma il traffico aumenterà in quanto non verranno generate anteprime." +cacheRemoteFilesDescription: "Disabilitando questa opzione, i file remoti verranno + linkati direttamente senza essere memorizzati nella cache. Sarà possibile risparmiare + spazio di archiviazione sul server, ma il traffico aumenterà in quanto non verranno + generate anteprime." flagAsBot: "Io sono un robot" -flagAsBotDescription: "Se l'account esegue principalmente operazioni automatiche, attiva quest'opzione. Quando attivata, opera come un segnalatore per gli altri sviluppatori allo scopo di prevenire catene d’interazione senza fine con altri bot, e di adeguare i sistemi interni di Misskey perché trattino questo account come un bot." +flagAsBotDescription: "Se l'account esegue principalmente operazioni automatiche, + attiva quest'opzione. Quando attivata, opera come un segnalatore per gli altri sviluppatori + allo scopo di prevenire catene d’interazione senza fine con altri bot, e di adeguare + i sistemi interni di Firefish perché trattino questo account come un bot." flagAsCat: "Io sono un gatto" flagAsCatDescription: "Abilita l'opzione \"Io sono un gatto\" per l'account." -autoAcceptFollowed: "Accetta automaticamente le richieste di follow da utenti che già segui" +autoAcceptFollowed: "Accetta automaticamente le richieste di follow da utenti che + già segui" addAccount: "Aggiungi account" loginFailed: "Accesso non riuscito" showOnRemote: "Sfoglia sull'istanza remota" @@ -154,7 +170,10 @@ searchWith: "Cerca: {q}" youHaveNoLists: "Non hai ancora creato nessuna lista" followConfirm: "Sei sicur@ di voler seguire {name}?" proxyAccount: "Account proxy" -proxyAccountDescription: "Un account proxy è un account che funziona da follower remoto per gli utenti sotto certe condizioni. Ad esempio, quando un utente aggiunge un utente remoto alla lista, dato che se nessun utente locale segue quell'utente le sue attività non verranno distribuite, al suo posto lo seguirà un account proxy." +proxyAccountDescription: "Un account proxy è un account che funziona da follower remoto + per gli utenti sotto certe condizioni. Ad esempio, quando un utente aggiunge un + utente remoto alla lista, dato che se nessun utente locale segue quell'utente le + sue attività non verranno distribuite, al suo posto lo seguirà un account proxy." host: "Server remoto" selectUser: "Seleziona utente" recipient: "Destinatario" @@ -175,7 +194,6 @@ operations: "Operazioni" software: "Software" version: "Versione" metadata: "Metadato" -withNFiles: "{n} file in allegato" monitor: "Monitorare" jobQueue: "Coda di lavoro" cpuAndMemory: "CPU e Memoria" @@ -185,11 +203,13 @@ instanceInfo: "Informazioni sull'istanza" statistics: "Statistiche" clearQueue: "Svuota coda" clearQueueConfirmTitle: "Vuoi davvero svuotare la coda?" -clearQueueConfirmText: "Le note ancora non distribuite non verranno rilasciate. Solitamente, non è necessario eseguire questa operazione." +clearQueueConfirmText: "Le note ancora non distribuite non verranno rilasciate. Solitamente, + non è necessario eseguire questa operazione." clearCachedFiles: "Svuota cache" clearCachedFilesConfirm: "Vuoi davvero svuotare la cache da tutti i file remoti?" blockedInstances: "Istanze bloccate" -blockedInstancesDescription: "Elenca le istanze che vuoi bloccare, una per riga. Esse non potranno più interagire con la tua istanza." +blockedInstancesDescription: "Elenca le istanze che vuoi bloccare, una per riga. Esse + non potranno più interagire con la tua istanza." muteAndBlock: "Silenziati / Bloccati" mutedUsers: "Account silenziati" blockedUsers: "Account bloccati" @@ -197,7 +217,7 @@ noUsers: "Nessun utente trovato" editProfile: "Modifica profilo" noteDeleteConfirm: "Eliminare questo Nota?" pinLimitExceeded: "Non puoi fissare altre note " -intro: "L'installazione di Misskey è finita! Si prega di creare un account amministratore." +intro: "L'installazione di Firefish è finita! Si prega di creare un account amministratore." done: "Fine" processing: "In elaborazione" preview: "Anteprima" @@ -251,7 +271,8 @@ agreeTo: "Sono d'accordo con {0}" tos: "Termini di servizio" start: "Inizia!" home: "Home" -remoteUserCaution: "Può darsi che le informazioni siano incomplete perché questo è un utente remoto." +remoteUserCaution: "Può darsi che le informazioni siano incomplete perché questo è + un utente remoto." activity: "Attività" images: "Immagini" birthday: "Compleanno" @@ -284,7 +305,8 @@ unableToDelete: "Eliminazione impossibile" inputNewFileName: "Inserisci nome del nuovo file" inputNewDescription: "Inserisci una nuova descrizione" inputNewFolderName: "Inserisci nome della nuova cartella" -circularReferenceFolder: "La cartella di destinazione è una sottocartella della cartella che vuoi spostare." +circularReferenceFolder: "La cartella di destinazione è una sottocartella della cartella + che vuoi spostare." hasChildFilesOrFolders: "Impossibile eliminare la cartella perché non è vuota" copyUrl: "Copia URL" rename: "Modifica nome" @@ -318,7 +340,8 @@ connectService: "Connessione" disconnectService: "Disconnessione " enableLocalTimeline: "Abilita Timeline locale" enableGlobalTimeline: "Abilita Timeline federata" -disablingTimelinesInfo: "Anche se disabiliti queste timeline, gli amministratori e i moderatori potranno sempre accederci." +disablingTimelinesInfo: "Anche se disabiliti queste timeline, gli amministratori e + i moderatori potranno sempre accederci." registration: "Iscriviti" enableRegistration: "Permettere nuove registrazioni" invite: "Invita" @@ -330,9 +353,11 @@ bannerUrl: "URL dell'immagine d'intestazione" backgroundImageUrl: "URL dello sfondo" basicInfo: "Informazioni fondamentali" pinnedUsers: "Utenti in evidenza" -pinnedUsersDescription: "Elenca gli/le utenti che vuoi fissare in cima alla pagina \"Esplora\", un@ per riga." +pinnedUsersDescription: "Elenca gli/le utenti che vuoi fissare in cima alla pagina + \"Esplora\", un@ per riga." pinnedPages: "Pagine in evidenza" -pinnedPagesDescription: "Specifica il percorso delle pagine che vuoi fissare in cima alla pagina dell'istanza. Una pagina per riga." +pinnedPagesDescription: "Specifica il percorso delle pagine che vuoi fissare in cima + alla pagina dell'istanza. Una pagina per riga." pinnedClipId: "ID della clip in evidenza" pinnedNotes: "Nota fissata" hcaptcha: "hCaptcha" @@ -343,14 +368,17 @@ recaptcha: "reCAPTCHA" enableRecaptcha: "Abilita reCAPTCHA" recaptchaSiteKey: "Chiave del sito" recaptchaSecretKey: "Chiave segreta" -avoidMultiCaptchaConfirm: "Utilizzare diversi Captcha può causare interferenze. Vuoi disattivare l'altro Captcha? Puoi lasciare diversi Captcha attivi premendo \"Cancella\"." +avoidMultiCaptchaConfirm: "Utilizzare diversi Captcha può causare interferenze. Vuoi + disattivare l'altro Captcha? Puoi lasciare diversi Captcha attivi premendo \"Cancella\"\ + ." antennas: "Antenne" manageAntennas: "Gestore delle antenne" name: "Nome" antennaSource: "Fonte dell'antenna" antennaKeywords: "Parole chiavi da ricevere" antennaExcludeKeywords: "Parole chiavi da escludere" -antennaKeywordsDescription: "Separare con uno spazio indica la condizione \"E\". Separare con un'interruzzione riga indica la condizione \"O\"." +antennaKeywordsDescription: "Separare con uno spazio indica la condizione \"E\". Separare + con un'interruzzione riga indica la condizione \"O\"." notifyAntenna: "Invia notifiche delle nuove note" withFileAntenna: "Solo note con file in allegato" enableServiceworker: "Abilita ServiceWorker" @@ -373,7 +401,7 @@ exploreFediverse: "Esplora il Fediverso" popularTags: "Tag di tendenza" userList: "Liste" about: "Informazioni" -aboutMisskey: "Informazioni di Misskey" +aboutFirefish: "Informazioni di Firefish" administrator: "Amministratore" token: "Token" twoStepAuthentication: "Autenticazione a due fattori" @@ -478,19 +506,26 @@ showFeaturedNotesInTimeline: "Mostrare le note di tendenza nella tua timeline" objectStorage: "Stoccaggio oggetti" useObjectStorage: "Utilizza stoccaggio oggetti" objectStorageBaseUrl: "Base URL" -objectStorageBaseUrlDesc: "URL di riferimento. In caso di utilizzo di proxy o CDN l'URL è 'https://.s3.amazonaws.com' per S3, 'https://storage.googleapis.com/' per GCS eccetera. " +objectStorageBaseUrlDesc: "URL di riferimento. In caso di utilizzo di proxy o CDN + l'URL è 'https://.s3.amazonaws.com' per S3, 'https://storage.googleapis.com/' + per GCS eccetera. " objectStorageBucket: "Bucket" objectStorageBucketDesc: "Specificare il nome del bucket utilizzato dal provider." objectStoragePrefix: "Prefix" objectStoragePrefixDesc: "I file saranno conservati sotto la directory di questo prefisso." objectStorageEndpoint: "Endpoint" -objectStorageEndpointDesc: "Lasciare vuoto se si sta utilizzando S3. In caso contrario si prega di specificare l'endpoint come '' oppure ':' a seconda del servizio utilizzato." +objectStorageEndpointDesc: "Lasciare vuoto se si sta utilizzando S3. In caso contrario + si prega di specificare l'endpoint come '' oppure ':' a seconda + del servizio utilizzato." objectStorageRegion: "Region" -objectStorageRegionDesc: "Specificate una regione, quale 'xx-east-1'. Se il servizio in utilizzo non distingue tra regioni, lasciate vuoto o inserite 'us-east-1'." +objectStorageRegionDesc: "Specificate una regione, quale 'xx-east-1'. Se il servizio + in utilizzo non distingue tra regioni, lasciate vuoto o inserite 'us-east-1'." objectStorageUseSSL: "Usare SSL" -objectStorageUseSSLDesc: "Disabilita quest'opzione se non utilizzi HTTPS per le connessioni API." +objectStorageUseSSLDesc: "Disabilita quest'opzione se non utilizzi HTTPS per le connessioni + API." objectStorageUseProxy: "Usa proxy" -objectStorageUseProxyDesc: "Disabilita quest'opzione se non usi proxy per la connessione API." +objectStorageUseProxyDesc: "Disabilita quest'opzione se non usi proxy per la connessione + API." objectStorageSetPublicRead: "Imposta \"visibilità pubblica\" al momento di caricare" serverLogs: "Log del server" deleteAll: "Cancella cronologia" @@ -518,7 +553,9 @@ sort: "Ordina per" ascendingOrder: "Ascendente" descendingOrder: "Discendente" scratchpad: "ScratchPad" -scratchpadDescription: "Lo Scratchpad offre un ambiente per esperimenti di AiScript. È possibile scrivere, eseguire e confermare i risultati dell'interazione del codice con Misskey." +scratchpadDescription: "Lo Scratchpad offre un ambiente per esperimenti di AiScript. + È possibile scrivere, eseguire e confermare i risultati dell'interazione del codice + con Firefish." output: "Uscita" script: "Script" disablePagesScript: "Disabilita AiScript nelle pagine" @@ -526,11 +563,14 @@ updateRemoteUser: "Aggiornare le informazioni di utente remot@" deleteAllFiles: "Elimina tutti i file" deleteAllFilesConfirm: "Vuoi davvero eliminare tutti i file?" removeAllFollowing: "Cancella tutti i follows" -removeAllFollowingDescription: "Cancella tutti i follows del server {host}. Per favore, esegui se, ad esempio, l'istanza non esiste più." +removeAllFollowingDescription: "Cancella tutti i follows del server {host}. Per favore, + esegui se, ad esempio, l'istanza non esiste più." userSuspended: "L'utente è sospes@." userSilenced: "L'utente è silenziat@." yourAccountSuspendedTitle: "Questo account è sospeso." -yourAccountSuspendedDescription: "Questo account è stato sospeso a causa di una violazione dei termini di servizio del server. Contattare l'amministrazione per i dettagli. Si prega di non creare un nuovo account." +yourAccountSuspendedDescription: "Questo account è stato sospeso a causa di una violazione + dei termini di servizio del server. Contattare l'amministrazione per i dettagli. + Si prega di non creare un nuovo account." menu: "Menù" divider: "Linea di separazione" addItem: "Aggiungi elemento" @@ -570,12 +610,14 @@ permission: "Autorizzazioni " enableAll: "Abilita tutto" disableAll: "Disabilita tutto" tokenRequested: "Autorizza accesso all'account" -pluginTokenRequestedDescription: "Il plugin potrà utilizzare le autorizzazioni impostate qui." +pluginTokenRequestedDescription: "Il plugin potrà utilizzare le autorizzazioni impostate + qui." notificationType: "Tipo di notifiche" edit: "Modifica" emailServer: "Server email" enableEmail: "Abilita consegna email" -emailConfigInfo: "Utilizzato per verificare il tuo indirizzo di posta elettronica e per reimpostare la tua password" +emailConfigInfo: "Utilizzato per verificare il tuo indirizzo di posta elettronica + e per reimpostare la tua password" email: "Email" emailAddress: "Indirizzo di posta elettronica" smtpConfig: "Impostazioni del server SMTP" @@ -583,7 +625,8 @@ smtpHost: "Server remoto" smtpPort: "Porta" smtpUser: "Nome utente" smtpPass: "Password" -emptyToDisableSmtpAuth: "Lasciare il nome utente e la password vuoti per disabilitare la verifica SMTP" +emptyToDisableSmtpAuth: "Lasciare il nome utente e la password vuoti per disabilitare + la verifica SMTP" smtpSecure: "Usare la porta SSL/TLS implicito per le connessioni SMTP" smtpSecureInfo: "Disabilitare quando è attivo STARTTLS." testEmail: "Testare la consegna di posta elettronica" @@ -603,10 +646,13 @@ create: "Crea" notificationSetting: "Impostazioni notifiche" notificationSettingDesc: "Seleziona il tipo di notifiche da visualizzare." useGlobalSetting: "Usa impostazioni generali" -useGlobalSettingDesc: "Se abilitato, le impostazioni notifiche dell'account verranno utilizzate. Se disabilitato, si possono definire diverse singole impostazioni." +useGlobalSettingDesc: "Se abilitato, le impostazioni notifiche dell'account verranno + utilizzate. Se disabilitato, si possono definire diverse singole impostazioni." other: "Avanzate" regenerateLoginToken: "Genera di nuovo un token di connessione" -regenerateLoginTokenDescription: "Genera un nuovo token di autenticazione. Solitamente questa operazione non è necessaria: quando si genera un nuovo token, tutti i dispositivi vanno disconnessi." +regenerateLoginTokenDescription: "Genera un nuovo token di autenticazione. Solitamente + questa operazione non è necessaria: quando si genera un nuovo token, tutti i dispositivi + vanno disconnessi." setMultipleBySeparatingWithSpace: "È possibile creare multiple voci separate da spazi." fileIdOrUrl: "ID o URL del file" behavior: "Comportamento" @@ -614,7 +660,8 @@ sample: "Esempio" abuseReports: "Segnalazioni" reportAbuse: "Segnalazioni" reportAbuseOf: "Segnala {name}" -fillAbuseReportDescription: "Si prega di spiegare il motivo della segnalazione. Se riguarda una nota precisa, si prega di collegare anche l'URL della nota." +fillAbuseReportDescription: "Si prega di spiegare il motivo della segnalazione. Se + riguarda una nota precisa, si prega di collegare anche l'URL della nota." abuseReported: "La segnalazione è stata inviata. Grazie." reporter: "il corrispondente" reporteeOrigin: "Origine del segnalato" @@ -624,7 +671,8 @@ abuseMarkAsResolved: "Contrassegna la segnalazione come risolta" openInNewTab: "Apri in una nuova scheda" openInSideView: "Apri in vista laterale" defaultNavigationBehaviour: "Navigazione preimpostata" -editTheseSettingsMayBreakAccount: "Modificare queste impostazioni può danneggiare l'account." +editTheseSettingsMayBreakAccount: "Modificare queste impostazioni può danneggiare + l'account." instanceTicker: "Informazioni sull'istanza da cui vengono le note" waitingFor: "Aspettando {x}" random: "Casuale" @@ -636,7 +684,8 @@ createNew: "Crea nuov@" optional: "Opzionale" createNewClip: "Nuova clip" public: "Pubblica" -i18nInfo: "Calckey è tradotto in diverse lingue da volontari. Anche tu puoi contribuire su {link}." +i18nInfo: "Firefish è tradotto in diverse lingue da volontari. Anche tu puoi contribuire + su {link}." manageAccessTokens: "Gestisci token di accesso" accountInfo: "Informazioni account" notesCount: "Conteggio note" @@ -655,12 +704,16 @@ no: "No" driveFilesCount: "Numero di file nel Drive" driveUsage: "Utilizzazione del Drive" noCrawle: "Rifiuta l'indicizzazione dai robot." -noCrawleDescription: "Richiedi che i motori di ricerca non indicizzino la tua pagina di profilo, le tue note, pagine, ecc." -lockedAccountInfo: "A meno che non imposti la visibilità delle tue note su \"Solo ai follower\", le tue note sono visibili da tutti, anche se hai configurato l'account per confermare manualmente le richieste di follow." +noCrawleDescription: "Richiedi che i motori di ricerca non indicizzino la tua pagina + di profilo, le tue note, pagine, ecc." +lockedAccountInfo: "A meno che non imposti la visibilità delle tue note su \"Solo + ai follower\", le tue note sono visibili da tutti, anche se hai configurato l'account + per confermare manualmente le richieste di follow." alwaysMarkSensitive: "Segnare i media come sensibili per impostazione predefinita" loadRawImages: "Visualizza le intere immagini allegate invece delle miniature." disableShowingAnimatedImages: "Disabilita le immagini animate" -verificationEmailSent: "Una mail di verifica è stata inviata. Si prega di accedere al collegamento per compiere la verifica." +verificationEmailSent: "Una mail di verifica è stata inviata. Si prega di accedere + al collegamento per compiere la verifica." notSet: "Non impostato" emailVerified: "Il tuo indirizzo email è stato verificato" noteFavoritesCount: "Conteggio note tra i preferiti" @@ -672,13 +725,15 @@ clips: "Clip" experimentalFeatures: "Funzioni sperimentali" developer: "Sviluppatore" makeExplorable: "Account visibile sulla pagina \"Esplora\"" -makeExplorableDescription: "Se disabiliti l'opzione, il tuo account non verrà visualizzato sulla pagina \"Esplora\"." +makeExplorableDescription: "Se disabiliti l'opzione, il tuo account non verrà visualizzato + sulla pagina \"Esplora\"." showGapBetweenNotesInTimeline: "Mostrare un intervallo tra le note sulla timeline" duplicate: "Duplica" left: "Sinistra" center: "Centro" wide: "Largo" -reloadToApplySetting: "Le tue preferenze verranno impostate dopo il ricaricamento della pagina. Vuoi ricaricare adesso?" +reloadToApplySetting: "Le tue preferenze verranno impostate dopo il ricaricamento + della pagina. Vuoi ricaricare adesso?" needReloadToApply: "È necessario riavviare per rendere effettive le modifiche." showTitlebar: "Visualizza la barra del titolo" clearCache: "Svuota cache" @@ -686,7 +741,10 @@ onlineUsersCount: "{n} utenti online" nUsers: "{n} utenti" nNotes: "{n}Note" sendErrorReports: "Invia segnalazioni di errori" -sendErrorReportsDescription: "Quando abilitato, se si verifica un problema, informazioni dettagliate sugli errori verranno condivise con Misskey in modo da aiutare a migliorare la qualità del software.\nCiò include informazioni come la versione del sistema operativo, il tipo di navigatore web che usi, la cronologia delle attività, ecc." +sendErrorReportsDescription: "Quando abilitato, se si verifica un problema, informazioni + dettagliate sugli errori verranno condivise con Firefish in modo da aiutare a migliorare + la qualità del software.\nCiò include informazioni come la versione del sistema + operativo, il tipo di navigatore web che usi, la cronologia delle attività, ecc." myTheme: "I miei temi" backgroundColor: "Sfondo" textColor: "Testo" @@ -712,7 +770,8 @@ receiveAnnouncementFromInstance: "Ricevi i messaggi informativi dall'istanza" emailNotification: "Eventi per notifiche via mail" publish: "Pubblico" inChannelSearch: "Cerca in canale" -useReactionPickerForContextMenu: "Cliccare sul tasto destro per aprire il pannello di reazioni" +useReactionPickerForContextMenu: "Cliccare sul tasto destro per aprire il pannello + di reazioni" typingUsers: "{users} sta(nno) scrivendo" jumpToSpecifiedDate: "Vai alla data " showingPastTimeline: "Stai visualizzando una vecchia timeline" @@ -723,14 +782,17 @@ unlikeConfirm: "Non ti piace più?" fullView: "Schermo intero" quitFullView: "Esci dalla modalità a schermo intero" addDescription: "Aggiungi descrizione" -userPagePinTip: "Qui puoi appuntare note, premendo \"Fissa sul profilo\" nel menù delle singole note." -notSpecifiedMentionWarning: "Sono menzionati account che non vengono inclusi fra i destinatari" +userPagePinTip: "Qui puoi appuntare note, premendo \"Fissa sul profilo\" nel menù + delle singole note." +notSpecifiedMentionWarning: "Sono menzionati account che non vengono inclusi fra i + destinatari" info: "Informazioni" userInfo: "Informazioni utente" unknown: "Sconosciuto" onlineStatus: "Stato di connessione" hideOnlineStatus: "Stato invisibile" -hideOnlineStatusDescription: "Abilitare l'opzione di stato invisibile può guastare la praticità di singole funzioni, come la ricerca." +hideOnlineStatusDescription: "Abilitare l'opzione di stato invisibile può guastare + la praticità di singole funzioni, come la ricerca." online: "Online" active: "Attiv@" offline: "Offline" @@ -773,12 +835,14 @@ hashtags: "Hashtag" troubleshooting: "Risoluzione problemi" useBlurEffect: "Utilizza effetto sfocatura per l'interfaccia utente" learnMore: "Più dettagli" -misskeyUpdated: "Misskey è stato aggiornato!" +misskeyUpdated: "Firefish è stato aggiornato!" whatIsNew: "Visualizza le informazioni sull'aggiornamento" translate: "Traduzione" translatedFrom: "Tradotto da {x}" accountDeletionInProgress: "La cancellazione dell'account è in corso" -usernameInfo: "Un nome per identificare univocamente il tuo account sul server. È possibile utilizzare caratteri alfanumerici (a~z, A~Z, 0~9) e il trattino basso (_). Non sarà possibile cambiare il nome utente in seguito." +usernameInfo: "Un nome per identificare univocamente il tuo account sul server. È + possibile utilizzare caratteri alfanumerici (a~z, A~Z, 0~9) e il trattino basso + (_). Non sarà possibile cambiare il nome utente in seguito." aiChanMode: "Modalità Ai" keepCw: "Mantieni il CW" resolved: "Risolto" @@ -802,7 +866,8 @@ leaveGroup: "Esci dal gruppo" leaveGroupConfirm: "Uscire da「{name}」?" useDrawerReactionPickerForMobile: "Mostra sul drawer da dispositivo mobile" welcomeBackWithName: "Bentornato/a, {name}" -clickToFinishEmailVerification: "Fai click su [{ok}] per completare la verifica dell'indirizzo email." +clickToFinishEmailVerification: "Fai click su [{ok}] per completare la verifica dell'indirizzo + email." searchByGoogle: "Cerca" indefinitely: "Non scade" tenMinutes: "10 minuti" @@ -830,7 +895,8 @@ _signup: emailAddressInfo: "Inserisci il tuo indirizzo email. Non verrà reso pubblico." _accountDelete: accountDelete: "Cancellazione account" - sendEmail: "Al termine della cancellazione dell'account, verrà inviata una mail all'indirizzo a cui era registrato." + sendEmail: "Al termine della cancellazione dell'account, verrà inviata una mail + all'indirizzo a cui era registrato." requestAccountDelete: "Richiesta di cancellazione account" started: "Il processo di cancellazione è iniziato." inProgress: "Cancellazione in corso" @@ -838,9 +904,13 @@ _ad: back: "Indietro" reduceFrequencyOfThisAd: "Visualizza questa pubblicità meno spesso" _forgotPassword: - enterEmail: "Inserisci l'indirizzo di posta elettronica che hai registrato nel tuo profilo. Il collegamento necessario per ripristinare la password verrà inviato a questo indirizzo." - ifNoEmail: "Se nessun indirizzo e-mail è stato registrato, si prega di contattare l'amministratore·trice dell'istanza." - contactAdmin: "Poiché questa istanza non permette l'utilizzo di una mail, si prega di contattare l'amministratore·trice dell'istanza per poter ripristinare la password." + enterEmail: "Inserisci l'indirizzo di posta elettronica che hai registrato nel tuo + profilo. Il collegamento necessario per ripristinare la password verrà inviato + a questo indirizzo." + ifNoEmail: "Se nessun indirizzo e-mail è stato registrato, si prega di contattare + l'amministratore·trice dell'istanza." + contactAdmin: "Poiché questa istanza non permette l'utilizzo di una mail, si prega + di contattare l'amministratore·trice dell'istanza per poter ripristinare la password." _gallery: my: "Le mie pubblicazioni" liked: "Pubblicazioni che mi piacciono" @@ -853,21 +923,23 @@ _email: title: "Hai ricevuto una richiesta di follow" _plugin: install: "Installa estensioni" - installWarn: "Si prega di installare soltanto estensioni che provengono da fonti affidabili." + installWarn: "Si prega di installare soltanto estensioni che provengono da fonti + affidabili." manage: "Gestisci estensioni" _registry: key: "Dati" keys: "Dati" domain: "Dominio" createKey: "Crea chiave" -_aboutMisskey: +_aboutFirefish: about: "Misskey è un software libero e open source, sviluppato da syuilo dal 2014." contributors: "Principali sostenitori" allContributors: "Tutti i sostenitori" source: "Codice sorgente" - translation: "Tradurre Misskey" - donate: "Sostieni Misskey" - morePatrons: "Apprezziamo sinceramente il supporto di tante altre persone. Grazie mille! 🥰" + translation: "Tradurre Firefish" + donate: "Sostieni Firefish" + morePatrons: "Apprezziamo sinceramente il supporto di tante altre persone. Grazie + mille! 🥰" patrons: "Sostenitori" _nsfw: respect: "Nascondere i media segnati come sensibli" @@ -875,10 +947,12 @@ _nsfw: force: "Nascondere tutti i media" _mfm: cheatSheet: "Bigliettino MFM" - intro: "MFM è un linguaggio Markdown particolare che si può usare in diverse parti di Misskey. Qui puoi visualizzare a colpo d'occhio tutta la sintassi MFM utile." - dummy: "Il Fediverso si espande con Misskey" + intro: "MFM è un linguaggio Markdown particolare che si può usare in diverse parti + di Firefish. Qui puoi visualizzare a colpo d'occhio tutta la sintassi MFM utile." + dummy: "Il Fediverso si espande con Firefish" mention: "Menzioni" - mentionDescription: "Si può menzionare un utente specifico digitando il suo nome utente subito dopo il segno @." + mentionDescription: "Si può menzionare un utente specifico digitando il suo nome + utente subito dopo il segno @." hashtag: "Hashtag" url: "URL" link: "Link" @@ -905,11 +979,14 @@ _mfm: x4: "Estremamente più grande" x4Description: "Mostra il contenuto estremamente più ingrandito." blur: "Sfocatura" - blurDescription: "È possibile rendere sfocato il contenuto. Spostando il cursore su di esso tornerà visibile chiaramente." + blurDescription: "È possibile rendere sfocato il contenuto. Spostando il cursore + su di esso tornerà visibile chiaramente." font: "Tipo di carattere" fontDescription: "Puoi scegliere il tipo di carattere per il contenuto." rainbow: "Arcobaleno" rotate: "Ruota" + fade: "Dissolvenza" + fadeDescription: "Dissolvenza in entrata e in uscita del contenuto." _instanceTicker: none: "Nascondi" remote: "Mostra solo per gli/le utenti remotə" @@ -932,10 +1009,15 @@ _menuDisplay: hide: "Nascondere" _wordMute: muteWords: "Parole da filtrare" - muteWordsDescription: "Separare con uno spazio indica la condizione \"E\". Separare con un'interruzzione riga indica la condizione \"O\"." - muteWordsDescription2: "Metti le parole chiavi tra slash per usare espressioni regolari (regexp)." - softDescription: "Nascondi della timeline note che rispondono alle condizioni impostate qui." - hardDescription: "Impedisci alla timeline di caricare le note che rispondono alle condizioni impostate qui. Inoltre, le note scompariranno in modo irreversibile, anche se le condizioni verranno successivamente rimosse." + muteWordsDescription: "Separare con uno spazio indica la condizione \"E\". Separare + con un'interruzzione riga indica la condizione \"O\"." + muteWordsDescription2: "Metti le parole chiavi tra slash per usare espressioni regolari + (regexp)." + softDescription: "Nascondi della timeline note che rispondono alle condizioni impostate + qui." + hardDescription: "Impedisci alla timeline di caricare le note che rispondono alle + condizioni impostate qui. Inoltre, le note scompariranno in modo irreversibile, + anche se le condizioni verranno successivamente rimosse." soft: "Moderato" hard: "Severo" mutedNotes: "Note silenziate" @@ -1025,28 +1107,39 @@ _time: hour: "ore" day: "giorni" _tutorial: - titolo: "Come usare Calckey" + titolo: "Come usare Firefish" step1_1: "Benvenuto!" step1_2: "Vediamo di configurarla. Sarete operativi in men che non si dica!" step2_1: "Per prima cosa, compila il tuo profilo" - step2_2: "Fornendo alcune informazioni su chi siete, sarà più facile per gli altri capire se vogliono vedere le vostre note o seguirvi" + step2_2: "Fornendo alcune informazioni su chi siete, sarà più facile per gli altri + capire se vogliono vedere le vostre note o seguirvi" step3_1: "Ora è il momento di seguire alcune persone!" - step3_2: "La vostra home e le vostre timeline social si basano su chi seguite, quindi provate a seguire un paio di account per iniziare.\nCliccate sul cerchio più in alto a destra di un profilo per seguirlo" + step3_2: "La vostra home e le vostre timeline social si basano su chi seguite, quindi + provate a seguire un paio di account per iniziare.\nCliccate sul cerchio più in + alto a destra di un profilo per seguirlo" step4_1: "Fatevi conoscere" - step4_2: "Per il vostro primo post, alcuni preferiscono fare un post di {introduction} o un semplice \"Ciao mondo!\"" + step4_2: "Per il vostro primo post, alcuni preferiscono fare un post di {introduction} + o un semplice \"Ciao mondo!\"" step5_1: "Linee temporali, linee temporali dappertutto!" step5_2: "La tua istanza ha attivato {timelines} diverse timelines" - step5_3: "La timeline Home {icon} è quella in cui si possono vedere i post dei propri follower" - step5_4: "La timeline Locale {icon} è quella in cui si possono vedere i post di tutti gli altri utenti di questa istanza" - step5_5: "La timeline Raccomandati {icon} è quella in cui si possono vedere i post delle istanze raccomandate dagli amministratori" - step5_6: "La timeline Social {icon} è quella in cui si possono vedere i post degli amici dei propri follower" - step5_7: "La timeline Globale {icon} è quella in cui si possono vedere i post di ogni altra istanza collegata" + step5_3: "La timeline Home {icon} è quella in cui si possono vedere i post dei propri + follower" + step5_4: "La timeline Locale {icon} è quella in cui si possono vedere i post di + tutti gli altri utenti di questa istanza" + step5_5: "La timeline Raccomandati {icon} è quella in cui si possono vedere i post + delle istanze raccomandate dagli amministratori" + step5_6: "La timeline Social {icon} è quella in cui si possono vedere i post degli + amici dei propri follower" + step5_7: "La timeline Globale {icon} è quella in cui si possono vedere i post di + ogni altra istanza collegata" step6_1: "Allora, cos'è questo posto?" - step6_2: "Beh, non ti sei semplicemente unito a Calckey. Sei entrato in un portale del Fediverse, una rete interconnessa di migliaia di server, chiamata \"istanze\"" - step6_3: "Ogni server funziona in modo diverso, e non tutti i server eseguono Calckey. Questo però lo fa! È un po' complicato, ma ci riuscirete in poco tempo" + step6_2: "Beh, non ti sei semplicemente unito a Firefish. Sei entrato in un portale + del Fediverse, una rete interconnessa di migliaia di server, chiamata \"istanze\"" + step6_3: "Ogni server funziona in modo diverso, e non tutti i server eseguono Firefish. + Questo però lo fa! È un po' complicato, ma ci riuscirete in poco tempo" step6_4: "Ora andate, esplorate e divertitevi!" _2fa: - registerDevice: "Aggiungi dispositivo" + registerTOTP: "Aggiungi dispositivo" _permissions: "read:account": "Visualizzare le informazioni dell'account" "write:account": "Modificare le informazioni dell'account" @@ -1172,7 +1265,8 @@ _profile: youCanIncludeHashtags: "Puoi anche includere hashtag." metadata: "Informazioni aggiuntive" metadataEdit: "Modifica informazioni aggiuntive" - metadataDescription: "Puoi pubblicare fino a quattro informazioni aggiuntive sul profilo." + metadataDescription: "Puoi pubblicare fino a quattro informazioni aggiuntive sul + 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" @@ -1464,3 +1558,6 @@ _deck: list: "Liste" mentions: "Menzioni" direct: "Diretta" +noThankYou: No grazie +addInstance: Aggiungi un'istanza +deleted: Eliminato diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 5372c8e3d0..46a486d916 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -1,22 +1,23 @@ _lang_: "日本語" - -headlineMisskey: "ノートでつながるネットワーク" -introMisskey: "ようこそ!Misskeyは、オープンソースの分散型マイクロブログサービスです。\n「ノート」を作成して、いま起こっていることを共有したり、あなたについて皆に発信しよう📡\n「リアクション」機能で、皆のノートに素早く反応を追加することもできます👍\n新しい世界を探検しよう🚀" +headlineMisskey: "ずっと無料でオープンソースの非中央集権型ソーシャルメディアプラットフォーム🚀" +introMisskey: "ようこそ!Firefishは、オープンソースの非中央集権型ソーシャルメディアプラットフォームです。\nいま起こっていることを共有したり、あなたについて皆に発信しましょう📡\n\ + 「リアクション」機能で、皆の投稿に素早く反応を追加できます👍\n新しい世界を探検しよう🚀" monthAndDay: "{month}月 {day}日" search: "検索" notifications: "通知" username: "ユーザー名" password: "パスワード" -forgotPassword: "パスワードを忘れた" -fetchingAsApObject: "連合に照会中" +forgotPassword: "パスワードを忘れました" +fetchingAsApObject: "連合宇宙から取得中" ok: "OK" -gotIt: "わかった" +gotIt: "わかった!" cancel: "キャンセル" +noThankYou: "やめておく" enterUsername: "ユーザー名を入力" -renotedBy: "{user}がRenote" -noNotes: "ノートはありません" +renotedBy: "{user}がブースト" +noNotes: "投稿はありません" noNotifications: "通知はありません" -instance: "インスタンス" +instance: "サーバー" settings: "設定" basicSettings: "基本設定" otherSettings: "その他の設定" @@ -32,6 +33,7 @@ uploading: "アップロード中" save: "保存" users: "ユーザー" addUser: "ユーザーを追加" +addInstance: "サーバーを追加" favorite: "お気に入り" favorites: "お気に入り" unfavorite: "お気に入り解除" @@ -44,13 +46,13 @@ copyContent: "内容をコピー" copyLink: "リンクをコピー" delete: "削除" deleteAndEdit: "削除して編集" -deleteAndEditConfirm: "このノートを削除してもう一度編集しますか?このノートへのリアクション、Renote、返信も全て削除されます。" +deleteAndEditConfirm: "この投稿を削除してもう一度編集しますか?この投稿へのリアクション、ブースト、返信は全て失われます。" addToList: "リストに追加" sendMessage: "メッセージを送信" copyUsername: "ユーザー名をコピー" searchUser: "ユーザーを検索" reply: "返信" -loadMore: "もっと見る" +loadMore: "もっと読み込む" showMore: "もっと見る" showLess: "閉じる" youGotNewFollower: "フォローされました" @@ -64,14 +66,14 @@ import: "インポート" export: "エクスポート" files: "ファイル" download: "ダウンロード" -driveFileDeleteConfirm: "ファイル「{name}」を削除しますか?このファイルを添付したノートも消えます。" -unfollowConfirm: "{name}のフォローを解除しますか?" +driveFileDeleteConfirm: "ファイル「{name}」を削除しますか?これにより、このファイルが添付されている投稿も削除されます。" +unfollowConfirm: "{name}さんのフォローを解除しますか?" exportRequested: "エクスポートをリクエストしました。これには時間がかかる場合があります。エクスポートが終わると、「ドライブ」に追加されます。" importRequested: "インポートをリクエストしました。これには時間がかかる場合があります。" lists: "リスト" noLists: "リストはありません" -note: "ノート" -notes: "ノート" +note: "投稿" +notes: "投稿" following: "フォロー" followers: "フォロワー" followsYou: "フォローされています" @@ -94,19 +96,21 @@ followRequests: "フォロー申請" unfollow: "フォロー解除" followRequestPending: "フォロー許可待ち" enterEmoji: "絵文字を入力" -renote: "Renote" -unrenote: "Renote解除" -renoted: "Renoteしました。" -cantRenote: "この投稿はRenoteできません。" -cantReRenote: "RenoteをRenoteすることはできません。" +renote: "ブースト" +unrenote: "ブースト解除" +renoted: "ブーストしました。" +cantRenote: "この投稿はブーストできません。" +cantReRenote: "ブーストをブーストすることはできません。" quote: "引用" -pinnedNote: "ピン留めされたノート" +pinnedNote: "ピン留めされた投稿" pinned: "ピン留め" you: "あなた" clickToShow: "クリックして表示" sensitive: "閲覧注意" add: "追加" reaction: "リアクション" +enableEmojiReactions: "絵文字リアクションを有効にする" +showEmojisInReactionNotifications: "自分の投稿に対するリアクションの通知で絵文字を表示する" reactionSetting: "ピッカーに表示するリアクション" reactionSettingDescription2: "ドラッグして並び替え、クリックして削除、+を押して追加します。" rememberNoteVisibility: "公開範囲を記憶する" @@ -116,17 +120,20 @@ unmarkAsSensitive: "閲覧注意を解除する" enterFileName: "ファイル名を入力" mute: "ミュート" unmute: "ミュート解除" +renoteMute: "ブーストをミュート" +renoteUnmute: "ブーストのミュートを解除" block: "ブロック" unblock: "ブロック解除" suspend: "凍結" unsuspend: "解凍" blockConfirm: "ブロックしますか?" -unblockConfirm: "ブロック解除しますか?" +unblockConfirm: "ブロックを解除しますか?" suspendConfirm: "凍結しますか?" unsuspendConfirm: "解凍しますか?" selectList: "リストを選択" selectAntenna: "アンテナを選択" selectWidget: "ウィジェットを選択" +selectChannel: "チャンネルを選択" editWidgets: "ウィジェットを編集" editWidgetsExit: "編集を終了" customEmojis: "カスタム絵文字" @@ -137,14 +144,16 @@ emojiUrl: "絵文字画像URL" addEmoji: "絵文字を追加" settingGuide: "おすすめ設定" cacheRemoteFiles: "リモートのファイルをキャッシュする" -cacheRemoteFilesDescription: "この設定を無効にすると、リモートファイルをキャッシュせず直リンクするようになります。サーバーのストレージを節約できますが、サムネイルが生成されないので通信量が増加します。" +cacheRemoteFilesDescription: "この設定を無効にすると、リモートファイルをキャッシュせず直リンクします。サーバーのストレージを節約できますが、サムネイルが生成されないので通信量が増加します。" flagAsBot: "Botとして設定" -flagAsBotDescription: "このアカウントがプログラムによって運用される場合は、このフラグをオンにします。オンにすると、反応の連鎖を防ぐためのフラグとして他の開発者に役立ったり、Misskeyのシステム上での扱いがBotに合ったものになります。" -flagAsCat: "Catとして設定" -flagAsCatDescription: "このアカウントが猫であることを示す場合は、このフラグをオンにします。" -flagShowTimelineReplies: "タイムラインにノートへの返信を表示する" -flagShowTimelineRepliesDescription: "オンにすると、タイムラインにユーザーのノート以外にもそのユーザーの他のノートへの返信を表示します。" -autoAcceptFollowed: "フォロー中ユーザーからのフォロリクを自動承認" +flagAsBotDescription: "このアカウントがBotである場合は、この設定をオンにします。オンにすると、反応の連鎖を防ぐためのフラグとして他の開発者に役立ったり、Firefishのシステム上での扱いがBotに合ったものになります。" +flagAsCat: "あなたは…猫?😺" +flagAsCatDescription: "このアカウントが猫であることを示す猫モードを有効にするには、このフラグをオンにします。" +flagSpeakAsCat: "猫語で話す" +flagSpeakAsCatDescription: "猫モードが有効の場合にオンにすると、あなたの投稿の「な」を「にゃ」に変換します。" +flagShowTimelineReplies: "タイムラインに投稿の返信を表示する" +flagShowTimelineRepliesDescription: "オンにすると、タイムラインにユーザーの他の投稿への返信も表示されます。" +autoAcceptFollowed: "フォローしているユーザーからのフォロー申請を自動承認" addAccount: "アカウントを追加" loginFailed: "ログインに失敗しました" showOnRemote: "リモートで表示" @@ -157,13 +166,14 @@ searchWith: "検索: {q}" youHaveNoLists: "リストがありません" followConfirm: "{name}をフォローしますか?" proxyAccount: "プロキシアカウント" -proxyAccountDescription: "プロキシアカウントは、特定の条件下でユーザーのリモートフォローを代行するアカウントです。例えば、ユーザーがリモートユーザーをリストに入れたとき、リストに入れられたユーザーを誰もフォローしていないとアクティビティがインスタンスに配達されないため、代わりにプロキシアカウントがフォローするようにします。" +proxyAccountDescription: "プロキシアカウントは、特定の条件下でユーザーのリモートフォローを代行するアカウントです。例えば、ユーザーがリモートユーザーをリストに入れたとき、リストに入れられたユーザーを誰もフォローしていないとアクティビティがサーバーに配達されないため、代わりにプロキシアカウントがフォローするようにします。" host: "ホスト" selectUser: "ユーザーを選択" +selectInstance: "サーバーを選択" recipient: "宛先" annotation: "注釈" federation: "連合" -instances: "インスタンス" +instances: "サーバー" registeredAt: "初観測" latestRequestSentAt: "直近のリクエスト送信" latestRequestReceivedAt: "直近のリクエスト受信" @@ -173,34 +183,37 @@ charts: "チャート" perHour: "1時間ごと" perDay: "1日ごと" stopActivityDelivery: "アクティビティの配送を停止" -blockThisInstance: "このインスタンスをブロック" +blockThisInstance: "このサーバーをブロック" +silenceThisInstance: "このサーバーをサイレンス" operations: "操作" software: "ソフトウェア" version: "バージョン" metadata: "メタデータ" -withNFiles: "{n}つのファイル" monitor: "モニター" jobQueue: "ジョブキュー" cpuAndMemory: "CPUとメモリ" network: "ネットワーク" disk: "ディスク" -instanceInfo: "インスタンス情報" +instanceInfo: "サーバー情報" statistics: "統計" clearQueue: "キューをクリア" clearQueueConfirmTitle: "キューをクリアしますか?" clearQueueConfirmText: "未配達の投稿は配送されなくなります。通常この操作を行う必要はありません。" clearCachedFiles: "キャッシュをクリア" clearCachedFilesConfirm: "キャッシュされたリモートファイルをすべて削除しますか?" -blockedInstances: "ブロックしたインスタンス" -blockedInstancesDescription: "ブロックしたいインスタンスのホストを改行で区切って設定します。ブロックされたインスタンスは、このインスタンスとやり取りできなくなります。" +blockedInstances: "ブロックしたサーバー" +blockedInstancesDescription: "ブロックしたいサーバーのホストを改行で区切って設定します。ブロックされたサーバーは、このサーバーとやり取りできなくなります。" +silencedInstances: "サイレンスしたサーバー" +silencedInstancesDescription: "サイレンスしたいサーバーのホストを改行で区切って設定します。サイレンスされたサーバーに所属するアカウントはすべて「サイレンス」として扱われ、フォローがすべてリクエストになり、フォロワーでないローカルアカウントにはメンションできなくなります。ブロックしたサーバーには影響しません。" muteAndBlock: "ミュートとブロック" mutedUsers: "ミュートしたユーザー" blockedUsers: "ブロックしたユーザー" noUsers: "ユーザーはいません" +noInstances: "サーバーがありません" editProfile: "プロフィールを編集" -noteDeleteConfirm: "このノートを削除しますか?" +noteDeleteConfirm: "この投稿を削除しますか?" pinLimitExceeded: "これ以上ピン留めできません" -intro: "Misskeyのインストールが完了しました!管理者アカウントを作成しましょう。" +intro: "Firefishのインストールが完了しました!管理者アカウントを作成しましょう。" done: "完了" processing: "処理中" preview: "プレビュー" @@ -210,14 +223,15 @@ noCustomEmojis: "絵文字はありません" noJobs: "ジョブはありません" federating: "連合中" blocked: "ブロック中" +silenced: "サイレンス中" suspended: "配信停止" all: "全て" subscribing: "購読中" publishing: "配信中" notResponding: "応答なし" -instanceFollowing: "インスタンスのフォロー" -instanceFollowers: "インスタンスのフォロワー" -instanceUsers: "インスタンスのユーザー" +instanceFollowing: "サーバーのフォロー" +instanceFollowers: "サーバーのフォロワー" +instanceUsers: "このサーバーの利用者" changePassword: "パスワードを変更" security: "セキュリティ" retypedNotMatch: "入力が一致しません。" @@ -289,7 +303,7 @@ emptyDrive: "ドライブは空です" emptyFolder: "フォルダーは空です" unableToDelete: "削除できません" inputNewFileName: "新しいファイル名を入力してください" -inputNewDescription: "新しいキャプションを入力してください" +inputNewDescription: "新しい説明を入力してください" inputNewFolderName: "新しいフォルダ名を入力してください" circularReferenceFolder: "移動先のフォルダーは、移動するフォルダーのサブフォルダーです。" hasChildFilesOrFolders: "このフォルダは空でないため、削除できません。" @@ -308,8 +322,8 @@ unwatch: "ウォッチ解除" accept: "許可" reject: "拒否" normal: "正常" -instanceName: "インスタンス名" -instanceDescription: "インスタンスの紹介" +instanceName: "サーバー名" +instanceDescription: "サーバーの紹介文" maintainerName: "管理者の名前" maintainerEmail: "管理者のメールアドレス" tosUrl: "利用規約URL" @@ -325,8 +339,8 @@ connectService: "接続する" disconnectService: "切断する" enableLocalTimeline: "ローカルタイムラインを有効にする" enableGlobalTimeline: "グローバルタイムラインを有効にする" -enableRecommendedTimeline: "推奨されるタイムラインを有効にする" -disablingTimelinesInfo: "これらのタイムラインを無効化しても、利便性のため管理者およびモデレーターは引き続き利用することができます。" +enableRecommendedTimeline: "おすすめタイムラインを有効にする" +disablingTimelinesInfo: "これらのタイムラインを無効化しても、利便性のため管理者およびモデレーターは引き続き利用できます。" registration: "登録" enableRegistration: "誰でも新規登録できるようにする" invite: "招待" @@ -340,9 +354,9 @@ basicInfo: "基本情報" pinnedUsers: "ピン留めユーザー" pinnedUsersDescription: "「みつける」ページなどにピン留めしたいユーザーを改行で区切って記述します。" pinnedPages: "ピン留めページ" -pinnedPagesDescription: "インスタンスのトップページにピン留めしたいページのパスを改行で区切って記述します。" +pinnedPagesDescription: "サーバーのトップページにピン留めしたいページのパスを改行で区切って記述します。" pinnedClipId: "ピン留めするクリップのID" -pinnedNotes: "ピン留めされたノート" +pinnedNotes: "ピン留めされた投稿" hcaptcha: "hCaptcha" enableHcaptcha: "hCaptchaを有効にする" hcaptchaSiteKey: "サイトキー" @@ -359,10 +373,11 @@ antennaSource: "受信ソース" antennaKeywords: "受信キーワード" antennaExcludeKeywords: "除外キーワード" antennaKeywordsDescription: "スペースで区切るとAND指定になり、改行で区切るとOR指定になります" -notifyAntenna: "新しいノートを通知する" -withFileAntenna: "ファイルが添付されたノートのみ" +notifyAntenna: "新しい投稿を通知する" +withFileAntenna: "ファイルが添付された投稿のみ" enableServiceworker: "ブラウザへのプッシュ通知を有効にする" antennaUsersDescription: "ユーザー名を改行で区切って指定します" +antennaInstancesDescription: "サーバーを改行で区切って指定します" caseSensitive: "大文字小文字を区別する" withReplies: "返信を含む" connectedTo: "次のアカウントに接続されています" @@ -371,7 +386,7 @@ withFiles: "ファイル付き" silence: "サイレンス" silenceConfirm: "サイレンスしますか?" unsilence: "サイレンス解除" -unsilenceConfirm: "サイレンス解除しますか?" +unsilenceConfirm: "サイレンスを解除しますか?" popularUsers: "人気のユーザー" recentlyUpdatedUsers: "最近投稿したユーザー" recentlyRegisteredUsers: "最近登録したユーザー" @@ -381,7 +396,7 @@ exploreFediverse: "Fediverseを探索" popularTags: "人気のタグ" userList: "リスト" about: "情報" -aboutMisskey: "Calckeyについて" +aboutFirefish: "Firefishについて" administrator: "管理者" token: "トークン" twoStepAuthentication: "二段階認証" @@ -393,7 +408,7 @@ securityKeyName: "キーの名前" registerSecurityKey: "セキュリティキーを登録する" lastUsed: "最後の使用" unregister: "登録を解除" -passwordLessLogin: "パスワード無しログイン" +passwordLessLogin: "パスワード無しでログイン" resetPassword: "パスワードをリセット" newPasswordIs: "新しいパスワードは「{password}」です" reduceUiAnimation: "UIのアニメーションを減らす" @@ -422,11 +437,11 @@ messagingWithGroup: "グループでチャット" title: "タイトル" text: "テキスト" enable: "有効にする" -next: "次" +next: "次へ" retype: "再入力" -noteOf: "{user}のノート" +noteOf: "{user}の投稿" inviteToGroup: "グループに招待" -quoteAttached: "引用付き" +quoteAttached: "引用" quoteQuestion: "引用として添付しますか?" noMessagesYet: "まだチャットはありません" newMessageExists: "新しいメッセージがあります" @@ -482,12 +497,13 @@ accountSettings: "アカウント設定" promotion: "プロモーション" promote: "プロモート" numberOfDays: "日数" -hideThisNote: "このノートを非表示" -showFeaturedNotesInTimeline: "タイムラインにおすすめのノートを表示する" +hideThisNote: "この投稿を非表示" +showFeaturedNotesInTimeline: "タイムラインにおすすめの投稿を表示する" objectStorage: "オブジェクトストレージ" useObjectStorage: "オブジェクトストレージを使用" objectStorageBaseUrl: "Base URL" -objectStorageBaseUrlDesc: "参照に使用するURL。CDNやProxyを使用している場合はそのURL、S3: 'https://.s3.amazonaws.com'、GCS等: 'https://storage.googleapis.com/'。" +objectStorageBaseUrlDesc: "参照に使用するURL。CDNやProxyを使用している場合はそのURL、S3: 'https://.s3.amazonaws.com'、GCS等: + 'https://storage.googleapis.com/'。" objectStorageBucket: "Bucket" objectStorageBucketDesc: "使用サービスのbucket名を指定してください。" objectStoragePrefix: "Prefix" @@ -504,7 +520,7 @@ objectStorageSetPublicRead: "アップロード時に'public-read'を設定す serverLogs: "サーバーログ" deleteAll: "全て削除" showFixedPostForm: "タイムライン上部に投稿フォームを表示する" -newNoteRecived: "新しいノートがあります" +newNoteRecived: "新しい投稿があります" sounds: "サウンド" listen: "聴く" none: "なし" @@ -519,7 +535,7 @@ recentUsed: "最近使用" install: "インストール" uninstall: "アンインストール" installedApps: "インストールされたアプリ" -nothing: "ありません" +nothing: "まだ何もありません" installedDate: "インストール日時" lastUsedDate: "最終使用日時" state: "状態" @@ -527,15 +543,15 @@ sort: "ソート" ascendingOrder: "昇順" descendingOrder: "降順" scratchpad: "スクラッチパッド" -scratchpadDescription: "スクラッチパッドは、AiScriptの実験環境を提供します。Misskeyと対話するコードの記述、実行、結果の確認ができます。" +scratchpadDescription: "スクラッチパッドは、AiScriptの実験環境を提供します。Firefishと対話するコードの記述、実行、結果の確認ができます。" output: "出力" script: "スクリプト" -disablePagesScript: "Pagesのスクリプトを無効にする" +disablePagesScript: "ページのスクリプトを無効にする" updateRemoteUser: "リモートユーザー情報の更新" deleteAllFiles: "すべてのファイルを削除" deleteAllFilesConfirm: "すべてのファイルを削除しますか?" removeAllFollowing: "フォローを全解除" -removeAllFollowingDescription: "{host}からのフォローをすべて解除します。そのインスタンスがもう存在しなくなった場合などに実行してください。" +removeAllFollowingDescription: "{host}からのフォローをすべて解除します。そのサーバーがもう存在しなくなった場合などに実行してください。" userSuspended: "このユーザーは凍結されています。" userSilenced: "このユーザーはサイレンスされています。" yourAccountSuspendedTitle: "アカウントが凍結されています" @@ -559,8 +575,8 @@ disablePlayer: "プレイヤーを閉じる" expandTweet: "ツイートを展開する" themeEditor: "テーマエディター" description: "説明" -describeFile: "キャプションを付ける" -enterFileDescription: "キャプションを入力" +describeFile: "説明を付ける" +enterFileDescription: "説明を入力" author: "作者" leaveConfirm: "未保存の変更があります。破棄しますか?" manage: "管理" @@ -600,8 +616,12 @@ testEmail: "配信テスト" wordMute: "ワードミュート" regexpError: "正規表現エラー" regexpErrorDescription: "{tab}ワードミュートの{line}行目の正規表現にエラーが発生しました:" -instanceMute: "インスタンスミュート" +instanceMute: "サーバーミュート" userSaysSomething: "{name}が何かを言いました" +userSaysSomethingReason: "{name}が{reason}と言いました" +userSaysSomethingReasonReply: "{name}が{reason}を含む投稿に返信しました" +userSaysSomethingReasonRenote: "{name}が{reason}を含む投稿をブーストしました" +userSaysSomethingReasonQuote: "{name}が{reason}を含む投稿を引用しました" makeActive: "アクティブにする" display: "表示" copy: "コピー" @@ -626,20 +646,20 @@ sample: "サンプル" abuseReports: "通報" reportAbuse: "通報" reportAbuseOf: "{name}を通報する" -fillAbuseReportDescription: "通報理由の詳細を記入してください。対象のノートがある場合はそのURLも記入してください。" +fillAbuseReportDescription: "通報理由の詳細を記入してください。対象の投稿がある場合はそのURLも記入してください。" abuseReported: "内容が送信されました。ご報告ありがとうございました。" reporter: "通報者" reporteeOrigin: "通報先" reporterOrigin: "通報元" -forwardReport: "リモートインスタンスに通報を転送する" -forwardReportIsAnonymous: "リモートインスタンスからはあなたの情報は見れず、匿名のシステムアカウントとして表示されます。" +forwardReport: "リモートサーバーに通報を転送する" +forwardReportIsAnonymous: "リモートサーバーからはあなたの情報は見られず、匿名のシステムアカウントとして表示されます。" send: "送信" abuseMarkAsResolved: "対応済みにする" openInNewTab: "新しいタブで開く" openInSideView: "サイドビューで開く" defaultNavigationBehaviour: "デフォルトのナビゲーション" editTheseSettingsMayBreakAccount: "これらの設定を編集するとアカウントが破損する可能性があります。" -instanceTicker: "ノートのインスタンス情報" +instanceTicker: "投稿のサーバー情報" waitingFor: "{x}を待っています" random: "ランダム" system: "システム" @@ -650,16 +670,16 @@ createNew: "新規作成" optional: "任意" createNewClip: "新しいクリップを作成" unclip: "クリップ解除" -confirmToUnclipAlreadyClippedNote: "このノートはすでにクリップ「{name}」に含まれています。ノートをこのクリップから除外しますか?" -public: "パブリック" -i18nInfo: "Calckeyは有志によって様々な言語に翻訳されています。{link}で翻訳に協力できます。" +confirmToUnclipAlreadyClippedNote: "この投稿はすでにクリップ「{name}」に含まれています。投稿をこのクリップから除外しますか?" +public: "公開" +i18nInfo: "Firefishは有志によって様々な言語に翻訳されています。{link}で翻訳に協力できます。" manageAccessTokens: "アクセストークンの管理" accountInfo: "アカウント情報" -notesCount: "ノートの数" +notesCount: "投稿の数" repliesCount: "返信した数" -renotesCount: "Renoteした数" +renotesCount: "ブーストした数" repliedCount: "返信された数" -renotedCount: "Renoteされた数" +renotedCount: "ブーストされた数" followingCount: "フォロー数" followersCount: "フォロワー数" sentReactionsCount: "リアクションした数" @@ -671,17 +691,17 @@ no: "いいえ" driveFilesCount: "ドライブのファイル数" driveUsage: "ドライブ使用量" noCrawle: "クローラーによるインデックスを拒否" -noCrawleDescription: "検索エンジンにあなたのユーザーページ、ノート、Pagesなどのコンテンツを登録(インデックス)しないよう要請します。" -lockedAccountInfo: "フォローを承認制にしても、ノートの公開範囲を「フォロワー」にしない限り、誰でもあなたのノートを見ることができます。" +noCrawleDescription: "検索エンジンにあなたのプロフィールや投稿、ページなどのコンテンツを登録(インデックス)しないよう要請します。" +lockedAccountInfo: "フォローを承認制にしても、投稿の公開範囲を「フォロワー」にしない限り、誰でもあなたの投稿を見られます。" alwaysMarkSensitive: "デフォルトでメディアを閲覧注意にする" loadRawImages: "添付画像のサムネイルをオリジナル画質にする" disableShowingAnimatedImages: "アニメーション画像を再生しない" verificationEmailSent: "確認のメールを送信しました。メールに記載されたリンクにアクセスして、設定を完了してください。" notSet: "未設定" emailVerified: "メールアドレスが確認されました" -noteFavoritesCount: "お気に入りノートの数" -pageLikesCount: "Pageにいいねした数" -pageLikedCount: "Pageにいいねされた数" +noteFavoritesCount: "お気に入りの投稿の数" +pageLikesCount: "ページにいいねした数" +pageLikedCount: "ページにいいねされた数" contact: "連絡先" useSystemFont: "システムのデフォルトのフォントを使う" clips: "クリップ" @@ -689,7 +709,7 @@ experimentalFeatures: "実験的機能" developer: "開発者" makeExplorable: "アカウントを見つけやすくする" makeExplorableDescription: "オフにすると、「みつける」にアカウントが載らなくなります。" -showGapBetweenNotesInTimeline: "タイムラインのノートを離して表示" +showGapBetweenNotesInTimeline: "タイムラインの投稿を離して表示する" duplicate: "複製" left: "左" center: "中央" @@ -701,9 +721,10 @@ showTitlebar: "タイトルバーを表示する" clearCache: "キャッシュをクリア" onlineUsersCount: "{n}人がオンライン" nUsers: "{n}ユーザー" -nNotes: "{n}ノート" +nNotes: "{n}投稿" sendErrorReports: "エラーリポートを送信" -sendErrorReportsDescription: "オンにすると、問題が発生したときにエラーの詳細情報がMisskeyに共有され、ソフトウェアの品質向上に役立てることができます。エラー情報には、OSのバージョン、ブラウザの種類、行動履歴などが含まれます。" +sendErrorReportsDescription: "オンにすると、問題が発生したときにエラーの詳細情報がFirefishに共有され、ソフトウェアの品質向上に役立てられます。\n\ + エラー情報には、OSのバージョン、ブラウザの種類、行動履歴などが含まれます。" myTheme: "マイテーマ" backgroundColor: "背景" accentColor: "アクセント" @@ -727,7 +748,7 @@ capacity: "容量" inUse: "使用中" editCode: "コードを編集" apply: "適用" -receiveAnnouncementFromInstance: "インスタンスからのお知らせを受け取る" +receiveAnnouncementFromInstance: "サーバーからのお知らせを受け取る" emailNotification: "メール通知" publish: "公開" inChannelSearch: "チャンネル内検索" @@ -738,11 +759,11 @@ showingPastTimeline: "過去のタイムラインを表示しています" clear: "クリア" markAllAsRead: "全て既読にする" goBack: "戻る" -unlikeConfirm: "いいね解除しますか?" +unlikeConfirm: "いいねを解除しますか?" fullView: "フルビュー" quitFullView: "フルビュー解除" addDescription: "説明を追加" -userPagePinTip: "個々のノートのメニューから「ピン留め」を選択することで、ここにノートを表示しておくことができます。" +userPagePinTip: "個々の投稿のメニューから「ピン留め」を選択することで、ここに投稿を表示できます。" notSpecifiedMentionWarning: "宛先に含まれていないメンションがあります" info: "情報" userInfo: "ユーザー情報" @@ -755,7 +776,7 @@ active: "アクティブ" offline: "オフライン" notRecommended: "非推奨" botProtection: "Botプロテクション" -instanceBlocking: "インスタンスブロック" +instanceBlocking: "連合の管理" selectAccount: "アカウントを選択" switchAccount: "アカウントを切り替え" enabled: "有効" @@ -772,7 +793,7 @@ postToGallery: "ギャラリーへ投稿" gallery: "ギャラリー" recentPosts: "最近の投稿" popularPosts: "人気の投稿" -shareWithNote: "ノートで共有" +shareWithNote: "投稿で共有" ads: "広告" expiration: "期限" memo: "メモ" @@ -783,18 +804,19 @@ low: "低" emailNotConfiguredWarning: "メールアドレスの設定がされていません。" ratio: "比率" secureMode: "セキュアモード (Authorized Fetch)" -instanceSecurity: "インスタンスのセキュリティー" -secureModeInfo: "他のインスタンスからリクエストするときに、証明を付けなければ返送しません。他のインスタンスの設定ファイルでsignToActivityPubGetはtrueにしてください。" +instanceSecurity: "サーバーのセキュリティー" +secureModeInfo: "認証情報の無いリモートサーバーからのリクエストに応えません。" privateMode: "非公開モード" -privateModeInfo: "有効にして、許可されているインスタンスのみがリクエストできます。すべてのノートが公開に非表示にします。" -allowedInstances: "許可されたインスタンス" -allowedInstancesDescription: "許可したいインスタンスのホストを改行で区切って設定します。非公開モードだけで有効です。" +privateModeInfo: "有効にすると、許可したサーバーのみからリクエストを受け付けます。" +allowedInstances: "許可されたサーバー" +allowedInstancesDescription: "許可したいサーバーのホストを改行で区切って設定します。非公開モードだけで有効です。" previewNoteText: "本文をプレビュー" customCss: "カスタムCSS" customCssWarn: "この設定は必ず知識のある方が行ってください。不適切な設定を行うとクライアントが正常に使用できなくなる恐れがあります。" global: "グローバル" +recommended: "推奨" squareAvatars: "アイコンを四角形で表示" -seperateRenoteQuote: "リノートと引用ボタンを分ける" +seperateRenoteQuote: "ブーストと引用のボタンを分ける" sent: "送信" received: "受信" searchResult: "検索結果" @@ -802,13 +824,13 @@ hashtags: "ハッシュタグ" troubleshooting: "トラブルシューティング" useBlurEffect: "UIにぼかし効果を使用" learnMore: "詳しく" -misskeyUpdated: "Misskeyが更新されました!" +misskeyUpdated: "Firefishが更新されました!" whatIsNew: "更新情報を見る" translate: "翻訳" translatedFrom: "{x}から翻訳" accountDeletionInProgress: "アカウントの削除が進行中です" -usernameInfo: "サーバー上であなたのアカウントを一意に識別するための名前。アルファベット(a~z, A~Z)、数字(0~9)、およびアンダーバー(_)が使用できます。ユーザー名は後から変更することは出来ません。" -aiChanMode: "藍モード" +usernameInfo: "サーバー上であなたのアカウントを一意に識別するための名前です。アルファベット(a~z, A~Z)、数字(0~9)、およびアンダーバー(_)が使用できます。ユーザー名は後から変更できません。" +aiChanMode: "藍モード(クラシックUI)" enterSendsMessage: "メッセージングでReturnキーを押すと、メッセージが送信されます(デフォルトはCtrl + Returnです)" keepCw: "CWを維持する" pubSub: "Pub/Subのアカウント" @@ -816,7 +838,7 @@ lastCommunication: "直近の通信" resolved: "解決済み" unresolved: "未解決" breakFollow: "フォロワーを解除" -breakFollowConfirm: "フォロワー解除しますか?" +breakFollowConfirm: "フォロワーから削除しますか?" itsOn: "オンになっています" itsOff: "オフになっています" emailRequiredForSignup: "アカウント登録にメールアドレスを必須にする" @@ -826,7 +848,7 @@ controlPanel: "コントロールパネル" manageAccounts: "アカウントを管理" makeReactionsPublic: "リアクション一覧を公開する" makeReactionsPublicDescription: "あなたがしたリアクション一覧を誰でも見れるようにします。" -classic: "クラシック" +classic: "中央寄せ" muteThread: "スレッドをミュート" unmuteThread: "スレッドのミュートを解除" ffVisibility: "つながりの公開範囲" @@ -845,12 +867,15 @@ overridedDeviceKind: "デバイスタイプ" smartphone: "スマートフォン" tablet: "タブレット" auto: "自動" +showLocalPosts: "ローカルの投稿を表示する場所" +homeTimeline: "ホームタイムライン" +socialTimeline: "ソーシャルタイムライン" themeColor: "テーマカラー" size: "サイズ" numberOfColumn: "列の数" searchByGoogle: "検索" -instanceDefaultLightTheme: "インスタンスデフォルトのライトテーマ" -instanceDefaultDarkTheme: "インスタンスデフォルトのダークテーマ" +instanceDefaultLightTheme: "サーバーの標準ライトテーマ" +instanceDefaultDarkTheme: "サーバーの標準ダークテーマ" instanceDefaultThemeDescription: "オブジェクト形式のテーマコードを記入します。" mutePeriod: "ミュートする期限" indefinitely: "無期限" @@ -868,12 +893,11 @@ recentNHours: "直近{n}時間" recentNDays: "直近{n}日" noEmailServerWarning: "メールサーバーの設定がされていません。" thereIsUnresolvedAbuseReportWarning: "未対応の通報があります。" -recommended: "推奨" check: "チェック" driveCapOverrideLabel: "このユーザーのドライブ容量上限を変更" driveCapOverrideCaption: "0以下を指定すると解除されます。" requireAdminForView: "閲覧するには管理者アカウントでログインしている必要があります。" -isSystemAccount: "システムにより自動で作成・管理されているアカウントです。" +isSystemAccount: "システムにより自動で作成・管理されているアカウントです。モデレーション・編集・削除を行うとサーバーの動作が不正になる可能性があるため、操作しないでください。" typeToConfirm: "この操作を行うには {x} と入力してください" deleteAccount: "アカウント削除" document: "ドキュメント" @@ -897,91 +921,121 @@ remoteOnly: "リモートのみ" failedToUpload: "アップロード失敗" cannotUploadBecauseInappropriate: "不適切な内容を含む可能性があると判定されたためアップロードできません。" cannotUploadBecauseNoFreeSpace: "ドライブの空き容量が無いためアップロードできません。" +cannotUploadBecauseExceedsFileSizeLimit: "ファイルサイズの制限を超えているためアップロードできません。" beta: "ベータ" enableAutoSensitive: "自動NSFW判定" -enableAutoSensitiveDescription: "利用可能な場合は、機械学習を利用して自動でメディアにNSFWフラグを設定します。この機能をオフにしても、インスタンスによっては自動で設定されることがあります。" +enableAutoSensitiveDescription: "利用可能な場合は、機械学習を利用して自動でメディアにNSFWフラグを設定します。この機能をオフにしても、サーバーによっては自動で設定されることがあります。" activeEmailValidationDescription: "ユーザーのメールアドレスのバリデーションを、捨てアドかどうかや実際に通信可能かどうかなどを判定しより積極的に行います。オフにすると単に文字列として正しいかどうかのみチェックされます。" showAds: "広告を表示する" navbar: "ナビゲーションバー" shuffle: "シャッフル" account: "アカウント" move: "移動" +pushNotification: "プッシュ通知" +subscribePushNotification: "プッシュ通知を有効化" +unsubscribePushNotification: "プッシュ通知を停止する" +pushNotificationAlreadySubscribed: "プッシュ通知は有効です" +pushNotificationNotSupported: "ブラウザまたはサーバーがプッシュ通知に非対応です" +sendPushNotificationReadMessage: "通知やメッセージが既読になったらプッシュ通知を削除する" +sendPushNotificationReadMessageCaption: "「{emptyPushNotificationMessage}」という通知が一瞬表示されるようになります。端末の電池消費量が増加する可能性があります。" adminCustomCssWarn: "この設定は、それが何をするものであるかを知っている場合のみ使用してください。不適切な値を入力すると、クライアントが正常に動作しなくなる可能性があります。ユーザー設定でCSSをテストし、正しく動作することを確認してください。" customMOTD: "カスタムMOTD(スプラッシュスクリーンメッセージ)" customMOTDDescription: "ユーザがページをロード/リロードするたびにランダムに表示される、改行で区切られたMOTD(スプラッシュスクリーン)用のカスタムメッセージ" customSplashIcons: "カスタムスプラッシュスクリーンアイコン" -customSplashIconsDescription: "ユーザがページをロード/リロードするたびにランダムに表示される、改行で区切られたカスタムスプラッシュスクリーンアイコンの URL。画像は静的なURLで、できればすべて192x192にリサイズしてください。" -showUpdates: "Calckeyの更新時にポップアップを表示する" -recommendedInstances: "推奨インスタンス" -recommendedInstancesDescription: "推奨タイムラインに表示するために改行で区切られた推奨インスタンス。`https://`を追加しないでください。ドメインのみを追加してください。" +customSplashIconsDescription: "ユーザがページをロード/リロードするたびにランダムに表示される、改行で区切られたカスタムスプラッシュスクリーンアイコンの + URL。画像は静的なURLで、できればすべて192x192にリサイズしてください。" +showUpdates: "Firefishの更新時にポップアップを表示する" +recommendedInstances: "おすすめサーバー" +recommendedInstancesDescription: "おすすめタイムラインに表示するサーバーを改行区切りで入力してください。" caption: "自動キャプション" splash: "スプラッシュスクリーン" -updateAvailable: "アップデートがありますよ" +updateAvailable: "アップデートがありますよ!" swipeOnDesktop: "デスクトップでモバイルスタイルのスワイプを可能にする" logoImageUrl: "ロゴのURL" -showAdminUpdates: "新しいCalckeyのバージョンが利用可能であることを示す(管理者のみ)" -replayTutorial: "リプレイチュートリアル" +showAdminUpdates: "新しいFirefishのバージョンが利用可能なときに通知する(管理者のみ)" +replayTutorial: "もう一度チュートリアルを見る" +migration: "アカウントの引っ越し" +moveTo: "このアカウントを新しいアカウントに引っ越す" +moveToLabel: "引っ越し先のアカウント:" +moveAccount: "引っ越し実行!" +moveAccountDescription: "この操作は取り消せません。まずは引っ越し先のアカウントでこのアカウントに対しエイリアスを作成したことを確認してください。エイリアス作成後、引っ越し先のアカウントをこのように入力してください:@person@server.com" +moveFrom: "別のアカウントからこのアカウントに引っ越す" +moveFromLabel: "引っ越し元のアカウント:" +moveFromDescription: "別のアカウントからこのアカウントにフォロワーを引き継いで引っ越したい場合、ここでエイリアスを作成しておく必要があります。必ず引っ越しを実行する前に作成してください!引っ越し元のアカウントをこのように入力してください:@person@server.com" +migrationConfirm: "本当にこのアカウントを {account} に引っ越しますか?一度引っ越しを行うと取り消せず、二度とこのアカウントを元の状態で使用できなくなります。\n\ + この操作を行う前に引っ越し先のアカウントでエイリアスを作成する必要があります。エイリアスが作成されているか、必ず確認してください。" +defaultReaction: "リモートとローカルの投稿に対するデフォルトの絵文字リアクション" +license: "ライセンス" +indexPosts: "投稿をインデックス" +indexFrom: "この投稿ID以降をインデックスする" +indexFromDescription: "空白で全ての投稿を指定します" +indexNotice: "インデックスを開始しました。完了まで時間がかかる場合があるため、少なくとも1時間はサーバーを再起動しないでください。" +customKaTeXMacro: "カスタムKaTeXマクロ" +customKaTeXMacroDescription: "数式入力を楽にするためのマクロを設定しましょう!記法はLaTeXにおけるコマンドの定義と同様に \\newcommand{\\ + name}{content} または \\newcommand{\\add}[2]{#1 + #2} のように記述します。後者の例では \\add{3}{foo} + が 3 + foo に展開されます。また、マクロの名前を囲む波括弧を丸括弧 () および角括弧 [] に変更した場合、マクロの引数に使用する括弧が変更されます。マクロの定義は一行に一つのみで、途中で改行はできません。マクロの定義が無効な行は無視されます。文字列を単純に置換する機能のみに対応していて、条件分岐などの高度な構文は使用できません。" +enableCustomKaTeXMacro: "カスタムKaTeXマクロを有効にする" +preventAiLearning: "AIによる学習を防止" +preventAiLearningDescription: "投稿したノート、添付した画像などのコンテンツを学習の対象にしないようAIに要求します。これはnoaiフラグをHTMLレスポンスに含めることによって実現されます。" +noGraze: "ブラウザの拡張機能「Graze for Mastodon」は、Firefishの動作を妨げるため、無効にしてください。" +enableServerMachineStats: "サーバーのマシン情報を公開する" +enableIdenticonGeneration: "ユーザーごとのIdenticon生成を有効にする" +showPopup: "ポップアップを表示してユーザーに知らせる" +showWithSparkles: "タイトルをキラキラさせる" +youHaveUnreadAnnouncements: "未読のお知らせがあります" +neverShow: "今後表示しない" +remindMeLater: "また後で" _sensitiveMediaDetection: - description: "機械学習を使って自動でセンシティブなメディアを検出し、モデレーションに役立てることができます。サーバーの負荷が少し増えます。" + description: "機械学習を使って自動でセンシティブなメディアを検出し、モデレーションに役立てられます。サーバーの負荷が少し増えます。" sensitivity: "検出感度" sensitivityDescription: "感度を低くすると、誤検知(偽陽性)が減ります。感度を高くすると、検知漏れ(偽陰性)が減ります。" setSensitiveFlagAutomatically: "NSFWフラグを設定する" setSensitiveFlagAutomaticallyDescription: "この設定をオフにしても内部的に判定結果は保持されます。" analyzeVideos: "動画の解析を有効化" analyzeVideosDescription: "静止画に加えて動画も解析するようにします。サーバーの負荷が少し増えます。" - _emailUnavailable: used: "既に使用されています" format: "形式が正しくありません" disposable: "恒久的に使用可能なアドレスではありません" mx: "正しいメールサーバーではありません" smtp: "メールサーバーが応答しません" - _ffVisibility: public: "公開" followers: "フォロワーだけに公開" private: "非公開" - _signup: almostThere: "ほとんど完了です" emailAddressInfo: "あなたが使っているメールアドレスを入力してください。メールアドレスが公開されることはありません。" emailSent: "入力されたメールアドレス({email})宛に確認のメールが送信されました。メールに記載されたリンクにアクセスすると、アカウントの作成が完了します。" - _accountDelete: accountDelete: "アカウントの削除" mayTakeTime: "アカウントの削除は負荷のかかる処理であるため、作成したコンテンツの数やアップロードしたファイルの数が多いと完了までに時間がかかることがあります。" - sendEmail: "アカウントの削除が完了する際は、登録してあったメールアドレス宛に通知を送信します。" + sendEmail: "アカウントの削除が完了した際に、登録されていたメールアドレス宛に通知を送信します。" requestAccountDelete: "アカウント削除をリクエスト" - started: "削除処理が開始されました。" + started: "削除処理を開始しました。" inProgress: "削除が進行中" - _ad: back: "戻る" reduceFrequencyOfThisAd: "この広告の表示頻度を下げる" - _forgotPassword: enterEmail: "アカウントに登録したメールアドレスを入力してください。そのアドレス宛てに、パスワードリセット用のリンクが送信されます。" ifNoEmail: "メールアドレスを登録していない場合は、管理者までお問い合わせください。" - contactAdmin: "このインスタンスではメールがサポートされていないため、パスワードリセットを行う場合は管理者までお問い合わせください。" - + contactAdmin: "このインスタンスではメールアドレスの登録がサポートされていないため、パスワードリセットを行う場合は管理者までお問い合わせください。" _gallery: my: "自分の投稿" liked: "いいねした投稿" like: "いいね!" unlike: "いいね解除" - _email: _follow: title: "フォローされました" _receiveFollowRequest: title: "フォローリクエストを受け取りました" - _plugin: install: "プラグインのインストール" installWarn: "信頼できないプラグインはインストールしないでください。" manage: "プラグインの管理" - _preferencesBackups: list: "作成したバックアップ" saveNew: "新規保存" @@ -998,49 +1052,50 @@ _preferencesBackups: noBackups: "バックアップはありません。「新規保存」で現在のクライアント設定をサーバーに保存できます。" createdAt: "作成日時: {date} {time}" updatedAt: "更新日時: {date} {time}" - cannotLoad: "読み込みできません" + cannotLoad: "読み込めません。" invalidFile: "ファイル形式が違います。" - _registry: scope: "スコープ" key: "キー" keys: "キー" domain: "ドメイン" createKey: "キーを作成" - -_aboutMisskey: - about: "Calckeyは、2022年から開発されているThatOneCalculator社製のMisskeyのforkです。" +_aboutFirefish: + about: "Firefishは、2022年に生まれたThatOneCalculatorによるMisskeyのforkです。" contributors: "主なコントリビューター" allContributors: "全てのコントリビューター" source: "ソースコード" - translation: "Misskeyを翻訳" - donate: "Misskeyに寄付" - morePatrons: "他にも多くの方が支援してくれています。ありがとうございます🥰" + translation: "Firefishを翻訳" + donate: "Firefishに寄付" + morePatrons: "他にも多くの方が支援してくれています。ありがとうございます! 🥰" patrons: "支援者" - + patronsList: 寄付額ではなく時系列順に並んでいます。上記のリンクから寄付を行ってここにあなたのIDを載せましょう! + pleaseDonateToFirefish: Firefish開発への寄付をご検討ください。 + pleaseDonateToHost: また、このサーバー {host} の運営者への寄付もご検討ください。 + donateHost: '{host} に寄付する' + donateTitle: Firefishを気に入りましたか? _nsfw: respect: "閲覧注意のメディアは隠す" ignore: "閲覧注意のメディアを隠さない" force: "常にメディアを隠す" - _mfm: cheatSheet: "MFMチートシート" - intro: "MFMは、Misskey内の様々な場所で使用できる専用のマークアップ言語です。ここでは、MFMで使用可能な構文一覧が確認できます。" - dummy: "MisskeyでFediverseの世界が広がります" + intro: "MFMは、MisskeyやFirefish、Akkomaなどの様々な場所で使用できるマークアップ言語です。ここでは、MFMで使用可能な構文一覧が確認できます。" + dummy: "FirefishでFediverseの世界が広がります" mention: "メンション" - mentionDescription: "アットマーク + ユーザー名で、特定のユーザーを示すことができます。" + mentionDescription: "アットマーク + ユーザー名で、特定のユーザーを示せます。" hashtag: "ハッシュタグ" - hashtagDescription: "ナンバーサイン + タグで、ハッシュタグを示すことができます。" + hashtagDescription: "ナンバーサイン + タグで、ハッシュタグを示せます。" url: "URL" - urlDescription: "URLを示すことができます。" + urlDescription: "URLを表示できます。" link: "リンク" - linkDescription: "文章の特定の範囲を、URLに紐づけることができます。" + linkDescription: "文章の特定の範囲を、URLに紐づけられます。" bold: "太字" - boldDescription: "文字を太く表示して強調することができます。" + boldDescription: "文字を太く表示して強調できます。" small: "目立たなく" - smallDescription: "内容を小さく・薄く表示させることができます。" + smallDescription: "内容を小さく・薄く表示させられます。" center: "中央寄せ" - centerDescription: "内容を中央寄せで表示させることができます。" + centerDescription: "内容を中央寄せで表示させられます。" inlineCode: "コード(インライン)" inlineCodeDescription: "プログラムなどのコードをインラインでシンタックスハイライトします。" blockCode: "コード(ブロック)" @@ -1048,13 +1103,13 @@ _mfm: inlineMath: "数式(インライン)" inlineMathDescription: "数式(KaTeX)をインラインで表示します。" blockMath: "数式(ブロック)" - blockMathDescription: "複数行の数式(KaTeX)をブロックで表示します。" + blockMathDescription: "数式(KaTeX)をブロックで表示します。" quote: "引用" - quoteDescription: "内容が引用であることを示すことができます。" + quoteDescription: "内容が引用であることを示せます。" emoji: "カスタム絵文字" - emojiDescription: "コロンでカスタム絵文字名を囲むと、カスタム絵文字を表示させることができます。" + emojiDescription: "コロンでカスタム絵文字名を囲むと、カスタム絵文字を表示させられます。" search: "検索" - searchDescription: "入力済み検索ボックスを表示させることができます。" + searchDescription: "検索ボックスを表示させられます。" flip: "反転" flipDescription: "内容を上下または左右に反転させます。" jelly: "アニメーション(びよんびよん)" @@ -1080,7 +1135,7 @@ _mfm: blur: "ぼかし" blurDescription: "内容をぼかすことができます。ポインターを上に乗せるとはっきり見えるようになります。" font: "フォント" - fontDescription: "内容のフォントを指定することができます。" + fontDescription: "内容のフォントを指定できます。" rainbow: "レインボー" rainbowDescription: "内容をレインボーにします。" sparkle: "キラキラ" @@ -1089,18 +1144,33 @@ _mfm: rotateDescription: "指定した角度で回転させます。" plain: "プレーン" plainDescription: "内側の構文を全て無効にします。" - + position: 位置 + stop: MFMを停止 + alwaysPlay: MFMアニメーションを自動再生する + play: MFMを再生 + warn: MFMアニメーションは激しい動きを含む可能性があります。 + positionDescription: 位置を指定した値だけずらします。 + foreground: 文字色 + backgroundDescription: 背景の色を変更します。 + background: 背景色 + scale: 拡大・縮小 + scaleDescription: 大きさを指定した値に拡大・縮小します。 + foregroundDescription: 文字の色を変更します。 + fade: フェード + fadeDescription: フェードインとフェードアウトする。 + crop: 切り抜き + cropDescription: 内容を切り抜く。 + advancedDescription: オフにすると、アニメーション再生中を除いて基本的なMFMだけ表示します。 + advanced: 高度なMFM _instanceTicker: none: "表示しない" remote: "リモートユーザーに表示" always: "常に表示" - _serverDisconnectedBehavior: reload: "自動でリロード" dialog: "ダイアログで警告" quiet: "控えめに警告" - nothing: "何も起こらない" - + nothing: "何もしない" _channel: create: "チャンネルを作成" edit: "チャンネルを編集" @@ -1111,33 +1181,30 @@ _channel: following: "フォロー中" usersCount: "{n}人が参加中" notesCount: "{n}投稿があります" - + nameAndDescription: "名前と説明" + nameOnly: "名前のみ" _messaging: - dms: "ディーエム" + dms: "プライベート" groups: "グループ" - _menuDisplay: sideFull: "横" sideIcon: "横(アイコン)" top: "上部" hide: "隠す" - _wordMute: muteWords: "ミュートするワード" muteWordsDescription: "スペースで区切るとAND指定になり、改行で区切るとOR指定になります。" muteWordsDescription2: "キーワードをスラッシュで囲むと正規表現になります。" - softDescription: "指定した条件のノートをタイムラインから隠します。" - hardDescription: "指定した条件のノートをタイムラインに追加しないようにします。追加されなかったノートは、条件を変更しても除外されたままになります。" + softDescription: "指定した条件の投稿をタイムラインから隠します。" + hardDescription: "指定した条件の投稿をタイムラインに追加しないようにします。追加されなかった投稿は、条件を変更しても除外されたままになります。" soft: "ソフト" hard: "ハード" - mutedNotes: "ミュートされたノート" - + mutedNotes: "ミュートされた投稿" _instanceMute: - instanceMuteDescription: "ミュートしたインスタンスのユーザーへの返信を含めて、設定したインスタンスの全てのノートとRenoteをミュートします。" + instanceMuteDescription: "ミュートしたサーバーのユーザーへの返信を含めて、設定したサーバーの全ての投稿とブーストをミュートします。" instanceMuteDescription2: "改行で区切って設定します" - title: "設定したインスタンスのノートを隠します。" - heading: "ミュートするインスタンス" - + title: "設定したサーバーの投稿を隠します。" + heading: "ミュートするサーバー" _theme: explore: "テーマを探す" install: "テーマのインストール" @@ -1168,7 +1235,6 @@ _theme: inputConstantName: "定数名を入力してください" importInfo: "ここにテーマコードを貼り付けて、エディターにインポートできます" deleteConstantConfirm: "定数 {const} を削除しても良いですか?" - keys: accent: "アクセント" bg: "背景" @@ -1187,7 +1253,7 @@ _theme: hashtag: "ハッシュタグ" mention: "メンション" mentionMe: "あなた宛てメンション" - renote: "Renote" + renote: "ブースト" modalBg: "モーダルの背景" divider: "分割線" scrollbarHandle: "スクロールバーの取っ手" @@ -1213,16 +1279,14 @@ _theme: accentDarken: "アクセント (暗め)" accentLighten: "アクセント (明るめ)" fgHighlighted: "強調された文字" - _sfx: - note: "ノート" - noteMy: "ノート(自分)" + note: "投稿" + noteMy: "投稿(自分)" notification: "通知" chat: "チャット" chatBg: "チャット(バックグラウンド)" antenna: "アンテナ受信" channel: "チャンネル通知" - _ago: future: "未来" justNow: "たった今" @@ -1233,46 +1297,58 @@ _ago: weeksAgo: "{n}週間前" monthsAgo: "{n}ヶ月前" yearsAgo: "{n}年前" - _time: second: "秒" minute: "分" hour: "時間" day: "日" - _tutorial: - title: "Calckeyの使い方" - step1_1: "ようこそ!" - step1_2: "設定をしてみましょう" - step2_1: "メモを書いたり、誰かをフォローする前に、プロフィールの設定を済ませましょう。" - step2_2: "あなたが誰なのか、いくつかの情報を提供することで、他の人があなたのメモを見たり、フォローしたりしたいのかがわかりやすくなります。" - step3_1: "さあ、何人かの人をフォローする時間です!" - step3_2: "あなたのホームとソーシャルタイムラインは、あなたが誰をフォローしているかで決まります。 まずは、いくつかのアカウントをフォローしてみましょう。" - step4_1: "さあ、外に出てみましょう。" - step4_2: "最初の投稿は、{introduction}の投稿や、シンプルに「こんにちは、世界よ!」的な投稿をするのが好きな人もいます。" + title: "Firefishの使い方" + step1_1: "ようこそ!" + step1_2: "使い始める前に、いくつか設定を済ませましょう。すぐできますよ!" + step2_1: "最初に、あなたのプロフィールを作りましょう。" + step2_2: "プロフィールを設定することで、他の人があなたの投稿を見たり、フォローしたりするときの助けになります。" + step3_1: "それでは、何人かフォローしてみましょう!" + step3_2: "あなたのホームとソーシャルタイムラインは、あなたが誰をフォローしているかで決まります。まずは、いくつかのアカウントをフォローしてみましょう。\n\ + プロフィールの右上にある丸い+ボタンをクリックするとフォローできます。" + step4_1: "投稿してみましょう!" + step4_2: "最初は{introduction}に投稿したり、シンプルに「こんにちは、アカウント作ってみました!」などの投稿をする人もいます。" step5_1: "タイムライン、タイムラインだらけ!" - step5_2: "あなたのインスタンスは{timelines}異なるタイムラインを有効にしています。" - step5_3: "ホーム{icon}のタイムラインは、あなたのフォロワーからの投稿を見ることができます。" - step5_4: "ローカル{icon}タイムラインは、このインスタンスのみんなの投稿を見ることができる場所です。" - step5_5: "おすすめ{icon}のタイムラインは、管理人がおすすめするインスタンスの投稿を見ることができます。" - step5_6: "ソーシャル{icon}のタイムラインは、あなたのフォロワーの友達の投稿を見ることができる場所です。" - step5_7: "グローバル{icon}タイムラインは、接続している他のすべてのインスタンスからの投稿を見ることができます。" - step6_1: "それで、ここは何なの?" - step6_2: "まあ、あなたはCalckeyに参加しただけではありません。何千ものサーバーが相互接続されたネットワークで インスタンスと呼ばれる。" - step6_3: "各サーバーは異なる方法で動作し、すべてのサーバーがCalckeyを実行するわけではありません。でも、このサーバーは動くんです" - step6_4: "さあ、探検して、楽しんでください!" - + step5_2: "あなたのサーバーでは{timelines}種類のタイムラインが有効になっています。" + step5_3: "ホーム{icon}タイムラインでは、あなたがフォローしているアカウントの投稿を見られます。" + step5_4: "ローカル{icon}タイムラインでは、このサーバーにいるみんなの投稿を見られます。" + step5_5: "ソーシャル{icon}タイムラインでは、ホームタイムラインとローカルタイムラインの投稿が両方表示されます。" + step5_6: "おすすめ{icon}タイムラインでは、管理人がおすすめするサーバーの投稿を見られます。" + step5_7: "グローバル{icon}タイムラインでは、接続している他のすべてのサーバーからの投稿を見られます。" + step6_1: "じゃあ、ここはどんな場所なの?" + step6_2: "実は、あなたはただFirefishに参加しただけではありません。ここは、何千もの相互接続されたサーバーが構成する Fediverse への入口です。" + step6_3: "それぞれのサーバーでは必ずしもFirefishが使われているわけではなく、異なる動作をするサーバーもあります。しかし、あなたは他のサーバーのアカウントもフォローしたり、返信・ブーストができます。一見難しそうですが大丈夫!すぐ慣れます。" + step6_4: "これで完了です。お楽しみください!" _2fa: alreadyRegistered: "既に設定は完了しています。" - registerDevice: "デバイスを登録" - registerKey: "キーを登録" + registerTOTP: "認証アプリの設定を開始" step1: "まず、{a}や{b}などの認証アプリをお使いのデバイスにインストールします。" step2: "次に、表示されているQRコードをアプリでスキャンします。" - step2Url: "デスクトップアプリでは次のURLを入力します:" - step3: "アプリに表示されているトークンを入力して完了です。" - step4: "これからログインするときも、同じようにトークンを入力します。" - securityKeyInfo: "FIDO2をサポートするハードウェアセキュリティキーもしくは端末の指紋認証やPINを使用してログインするように設定できます。" - + step2Click: "QRコードをクリックすると、お使いの端末にインストールされている認証アプリやキーリングに登録できます。" + step2Url: "デスクトップアプリでは次のURIを入力します:" + step3Title: "確認コードを入力" + step3: "アプリに表示されている確認コード(トークン)を入力して完了です。" + step4: "これからログインするときも、同じように確認コードを入力します。" + securityKeyNotSupported: "お使いのブラウザはセキュリティキーに対応していません。" + registerTOTPBeforeKey: "セキュリティキー・パスキーを登録するには、まず認証アプリの設定を行なってください。" + securityKeyInfo: "FIDO2をサポートするハードウェアセキュリティキー、端末の生体認証やPINロック、パスキーといった、WebAuthn由来の鍵を登録します。" + chromePasskeyNotSupported: "Chromeのパスキーは現在サポートしていません。" + registerSecurityKey: "セキュリティキー・パスキーを登録する" + securityKeyName: "キーの名前を入力" + tapSecurityKey: "ブラウザの指示に従い、セキュリティキーやパスキーを登録してください" + removeKey: "セキュリティキーを削除" + removeKeyConfirm: "{name}を削除しますか?" + whyTOTPOnlyRenew: "セキュリティキーが登録されている場合、認証アプリの設定は解除できません。" + renewTOTP: "認証アプリを再設定" + renewTOTPConfirm: "今までの認証アプリの確認コードは使用できなくなります" + renewTOTPOk: "再設定する" + renewTOTPCancel: "やめておく" + token: "多要素認証トークン" _permissions: "read:account": "アカウントの情報を見る" "write:account": "アカウントの情報を変更する" @@ -1288,7 +1364,7 @@ _permissions: "write:messaging": "チャットを操作する" "read:mutes": "ミュートを見る" "write:mutes": "ミュートを操作する" - "write:notes": "ノートを作成・削除する" + "write:notes": "投稿を作成・削除する" "read:notifications": "通知を見る" "write:notifications": "通知を操作する" "read:reactions": "リアクションを見る" @@ -1306,22 +1382,22 @@ _permissions: "write:gallery": "ギャラリーを操作する" "read:gallery-likes": "ギャラリーのいいねを見る" "write:gallery-likes": "ギャラリーのいいねを操作する" - _auth: shareAccess: "「{name}」がアカウントにアクセスすることを許可しますか?" shareAccessAsk: "アカウントへのアクセスを許可しますか?" - permissionAsk: "このアプリは次の権限を要求しています" - pleaseGoBack: "アプリケーションに戻ってやっていってください" + permissionAsk: "このアプリケーションは次の権限を要求しています:" + pleaseGoBack: "アプリケーションに戻り続行してください" callback: "アプリケーションに戻っています" denied: "アクセスを拒否しました" - + copyAsk: "以下の認証コードをアプリケーションにコピーしてください:" + allPermissions: 全てのアクセス権 _antennaSources: - all: "全てのノート" - homeTimeline: "フォローしているユーザーのノート" - users: "指定した一人または複数のユーザーのノート" - userList: "指定したリストのユーザーのノート" - userGroup: "指定したグループのユーザーのノート" - + all: "全ての投稿" + homeTimeline: "フォローしているユーザーの投稿" + users: "指定した一人または複数のユーザーの投稿" + userList: "指定したリストのユーザーの投稿" + userGroup: "指定したグループのユーザーの投稿" + instances: "指定したサーバーの全ユーザーの投稿" _weekday: sunday: "日曜日" monday: "月曜日" @@ -1330,7 +1406,6 @@ _weekday: thursday: "木曜日" friday: "金曜日" saturday: "土曜日" - _widgets: memo: "付箋" notifications: "通知" @@ -1345,7 +1420,7 @@ _widgets: digitalClock: "デジタル時計" unixClock: "UNIX時計" federation: "連合" - instanceCloud: "インスタンスクラウド" + instanceCloud: "サーバークラウド" postForm: "投稿フォーム" slideshow: "スライドショー" button: "ボタン" @@ -1353,14 +1428,18 @@ _widgets: jobQueue: "ジョブキュー" serverMetric: "サーバーメトリクス" aiscript: "AiScriptコンソール" - aichan: "藍" - + userList: "ユーザーリスト" + _userList: + chooseList: "リストを選択" + meiliStatus: サーバーステータス + serverInfo: サーバー情報 + meiliSize: インデックスサイズ + meiliIndexCount: インデックス済みの投稿 _cw: hide: "隠す" show: "もっと見る" chars: "{count}文字" files: "{count}ファイル" - _poll: noOnlyOneChoice: "選択肢は最低2つ必要です" choiceN: "選択肢{n}" @@ -1383,22 +1462,20 @@ _poll: remainingHours: "終了まであと{h}時間{m}分" remainingMinutes: "終了まであと{m}分{s}秒" remainingSeconds: "終了まであと{s}秒" - _visibility: - public: "パブリック" - publicDescription: "全てのユーザーに公開" - home: "ホーム" + public: "公開" + publicDescription: "全ての公開タイムラインに配信されます" + home: "未収載" homeDescription: "ホームタイムラインのみに公開" followers: "フォロワー" - followersDescription: "自分のフォロワーのみに公開" + followersDescription: "フォロワーと会話相手のみに公開" specified: "ダイレクト" specifiedDescription: "指定したユーザーのみに公開" localOnly: "ローカルのみ" localOnlyDescription: "リモートユーザーには非公開" - _postForm: - replyPlaceholder: "このノートに返信..." - quotePlaceholder: "このノートを引用..." + replyPlaceholder: "この投稿に返信..." + quotePlaceholder: "この投稿を引用..." channelPlaceholder: "チャンネルに投稿..." _placeholders: a: "いまどうしてる?" @@ -1407,64 +1484,59 @@ _postForm: d: "言いたいことは?" e: "ここに書いてください" f: "あなたが書くのを待っています..." - _profile: name: "名前" username: "ユーザー名" description: "自己紹介" - youCanIncludeHashtags: "ハッシュタグを含めることができます。" + youCanIncludeHashtags: "ハッシュタグを含められます。" metadata: "追加情報" metadataEdit: "追加情報を編集" - metadataDescription: "プロフィールに表として追加情報を表示することができます。" + metadataDescription: "プロフィールに表として追加情報を表示できます。{a}タグまたは{l}タグを{rel}とともに追加すると、プロフィールのリンクを確認できます。" metadataLabel: "ラベル" metadataContent: "内容" changeAvatar: "アバター画像を変更" changeBanner: "バナー画像を変更" - + locationDescription: "英語表記の都市名から始まる内容を入力すると、現地時間がユーザーページに表示されます。" _exportOrImport: - allNotes: "全てのノート" + allNotes: "全ての投稿" followingList: "フォロー" muteList: "ミュート" blockingList: "ブロック" userLists: "リスト" excludeMutingUsers: "ミュートしているユーザーを除外" excludeInactiveUsers: "使われていないアカウントを除外" - _charts: federation: "連合" apRequest: "リクエスト" usersIncDec: "ユーザーの増減" usersTotal: "ユーザーの合計" activeUsers: "アクティブユーザー数" - notesIncDec: "ノートの増減" - localNotesIncDec: "ローカルのノートの増減" - remoteNotesIncDec: "リモートのノートの増減" - notesTotal: "ノートの合計" + notesIncDec: "投稿の増減" + localNotesIncDec: "ローカルの投稿の増減" + remoteNotesIncDec: "リモートの投稿の増減" + notesTotal: "投稿の合計" filesIncDec: "ファイルの増減" filesTotal: "ファイルの合計" storageUsageIncDec: "ストレージ使用量の増減" storageUsageTotal: "ストレージ使用量の合計" - _instanceCharts: requests: "リクエスト" users: "ユーザーの増減" usersTotal: "ユーザーの累積" - notes: "ノートの増減" - notesTotal: "ノートの累積" + notes: "投稿の増減" + notesTotal: "投稿の累積" ff: "フォロー/フォロワーの増減" ffTotal: "フォロー/フォロワーの累積" cacheSize: "キャッシュサイズの増減" cacheSizeTotal: "キャッシュサイズの累積" files: "ファイル数の増減" filesTotal: "ファイル数の累積" - _timelines: home: "ホーム" local: "ローカル" - recommended: "一押し" + recommended: "おすすめ" social: "ソーシャル" global: "グローバル" - _pages: newPage: "ページの作成" editPage: "ページの編集" @@ -1511,59 +1583,49 @@ _pages: section: "セクション" image: "画像" button: "ボタン" - if: "もし" _if: variable: "変数" - post: "投稿フォーム" _post: text: "内容" attachCanvasImage: "キャンバスの画像を添付する" canvasId: "キャンバスID" - textInput: "テキスト入力" _textInput: name: "変数名" text: "タイトル" default: "デフォルト値" - textareaInput: "複数行テキスト入力" _textareaInput: name: "変数名" text: "タイトル" default: "デフォルト値" - numberInput: "数値入力" _numberInput: name: "変数名" text: "タイトル" default: "デフォルト値" - canvas: "キャンバス" _canvas: id: "キャンバスID" width: "幅" height: "高さ" - - note: "ノート埋め込み" + note: "投稿の埋め込み" _note: - id: "ノートID" - idDescription: "ノートURLをペーストして設定することもできます。" + id: "投稿のID" + idDescription: "投稿のURLをペーストして設定することもできます。" detailed: "詳細な表示" - switch: "スイッチ" _switch: name: "変数名" text: "タイトル" default: "デフォルト値" - counter: "カウンター" _counter: name: "変数名" text: "タイトル" inc: "増加値" - _button: text: "タイトル" colored: "色付き" @@ -1582,14 +1644,12 @@ _pages: callAiScript: "AiScript呼び出し" _callAiScript: functionName: "関数名" - radioButton: "選択肢" _radioButton: name: "変数名" title: "タイトル" values: "改行で区切った選択肢" default: "デフォルト値" - script: categories: flow: "制御" @@ -1766,18 +1826,16 @@ _pages: enviromentVariables: "環境変数" pageVariables: "ページ要素" argVariables: "入力スロット" - _relayStatus: requesting: "承認待ち" accepted: "承認済み" rejected: "拒否済み" - _notification: fileUploaded: "ファイルがアップロードされました" youGotMention: "{name}からのメンション" youGotReply: "{name}からのリプライ" youGotQuote: "{name}による引用" - youRenoted: "{name}がRenoteしました" + youRenoted: "{name}がブーストしました" youGotPoll: "{name}が投票しました" youGotMessagingMessageFromUser: "{name}からのチャットがあります" youGotMessagingMessageFromGroup: "{name}のチャットがあります" @@ -1787,13 +1845,12 @@ _notification: youWereInvitedToGroup: "{userName}があなたをグループに招待しました" pollEnded: "アンケートの結果が出ました" emptyPushNotificationMessage: "プッシュ通知の更新をしました" - _types: all: "すべて" follow: "フォロー" mention: "メンション" reply: "リプライ" - renote: "Renote" + renote: "ブースト" quote: "引用" reaction: "リアクション" pollVote: "アンケートに投票された" @@ -1802,12 +1859,13 @@ _notification: followRequestAccepted: "フォローが受理された" groupInvited: "グループに招待された" app: "連携アプリからの通知" - _actions: followBack: "フォローバック" reply: "返信" - renote: "Renote" - + renote: "ブースト" + reacted: がリアクションしました + renoted: がブーストしました + voted: が投票しました _deck: alwaysShowMainColumn: "常にメインカラムを表示" columnAlign: "カラムの寄せ" @@ -1819,13 +1877,14 @@ _deck: swapDown: "下に移動" stackLeft: "左に重ねる" popRight: "右に出す" - profile: "プロファイル" - newProfile: "新規プロファイル" - deleteProfile: "プロファイルを削除" + profile: "ワークスペース" + newProfile: "新規ワークスペース" + renameProfile: "ワークスペース名を変更" + deleteProfile: "ワークスペースを削除" + nameAlreadyExists: "この名前のワークスペースは既に存在します。" introduction: "カラムを組み合わせて自分だけのインターフェイスを作りましょう!" introduction2: "画面の右にある + を押して、いつでもカラムを追加できます。" widgetsIntroduction: "カラムのメニューから、「ウィジェットの編集」を選択してウィジェットを追加してください" - _columns: main: "メイン" widgets: "ウィジェット" @@ -1833,5 +1892,70 @@ _deck: tl: "タイムライン" antenna: "アンテナ" list: "リスト" + channel: "チャンネル" mentions: "あなた宛て" direct: "ダイレクト" +noteId: 投稿のID +hiddenTagsDescription: 'トレンドと「みつける」から除外したいハッシュタグを(先頭の # を除いて)改行区切りで入力してください。この設定はトレンドと「みつける」以外には影響しません。' +hiddenTags: 非表示にするハッシュタグ +apps: "アプリ" +_experiments: + title: 試験的な機能 + postImportsCaption: + ユーザーが過去の投稿をFirefish・Misskey・Mastodon・Akkoma・Pleromaからインポートすることを許可します。キューが溜まっているときにインポートするとサーバーに負荷がかかる可能性があります。 + enablePostImports: 投稿のインポートを有効にする +sendModMail: モデレーション通知を送る +deleted: 削除済み +editNote: 投稿を編集 +edited: '編集済み: {date} {time}' +signupsDisabled: + 現在、このサーバーでは新規登録が一般開放されていません。招待コードをお持ちの場合には、以下の欄に入力してください。招待コードをお持ちでない場合にも、新規登録を開放している他のサーバーには入れますよ! +findOtherInstance: 他のサーバーを探す +newer: 新しい投稿 +older: 古い投稿 +accessibility: アクセシビリティ +jumpToPrevious: 前に戻る +cw: 閲覧注意 +silencedWarning: スパムの可能性があるため、これらのユーザーが所属するサーバーは管理者によりサイレンスされています。 +searchPlaceholder: Firefishを検索 +channelFederationWarn: 現時点では、チャンネルは他のサーバーへ連合しません +listsDesc: リストでは指定したユーザーだけのタイムラインを作れます。リストには「タイムライン」のページからアクセスできます。 +antennasDesc: "アンテナでは指定した条件に合致する投稿が表示されます。\nアンテナには「タイムライン」のページからアクセスできます。" +expandOnNoteClickDesc: オフの場合、右クリックメニューか日付をクリックすることで開けます。 +expandOnNoteClick: クリックで投稿の詳細を開く +clipsDesc: クリップは分類と共有ができるブックマークです。各投稿のメニューからクリップを作成できます。 +_dialog: + charactersExceeded: "最大文字数を超えています! 現在 {current} / 制限 {max}" + charactersBelow: "最小文字数を下回っています! 現在 {current} / 制限 {min}" +_filters: + followersOnly: フォロワーのみ + fromUser: ユーザーを指定 + withFile: 添付ファイルあり + fromDomain: ドメインを指定 + notesBefore: 指定の日付以前 + notesAfter: 指定の日付以降 + followingOnly: フォロー中のみ +isModerator: モデレーター +audio: 音声 +image: 画像 +video: 動画 +isBot: このアカウントはBotです +isLocked: このアカウントのフォローは承認制です +isAdmin: 管理者 +isPatron: Firefish 後援者 +_skinTones: + light: ペールオレンジ + mediumLight: ミディアムライト + medium: ミディアム + mediumDark: ミディアムダーク + yellow: 黄色 + dark: 茶色 +removeReaction: リアクションを取り消す +alt: 代替テキスト +swipeOnMobile: ページ間のスワイプを有効にする +reactionPickerSkinTone: 優先する絵文字のスキン色 +xl: 特大 +donationLink: 寄付ページへのリンク +removeMember: メンバーを削除 +removeQuote: 引用を削除 +removeRecipient: 宛先を削除 diff --git a/locales/ja-KS.yml b/locales/ja-KS.yml index d5c48276f4..40b32b4c01 100644 --- a/locales/ja-KS.yml +++ b/locales/ja-KS.yml @@ -177,7 +177,6 @@ operations: "操作" software: "ソフトウェア" version: "バージョン" metadata: "メタデータ" -withNFiles: "{n}個のファイル" monitor: "モニター" jobQueue: "ジョブキュー" cpuAndMemory: "CPUとメモリ" @@ -378,7 +377,7 @@ exploreFediverse: "Fediverseを探ってみる" popularTags: "人気のタグ" userList: "リスト" about: "情報" -aboutMisskey: "Misskeyってなんや?" +aboutFirefish: "Misskeyってなんや?" administrator: "管理者" token: "トークン" twoStepAuthentication: "二段階認証" @@ -648,7 +647,7 @@ createNewClip: "新しいクリップを作るで" unclip: "クリップ解除するで" confirmToUnclipAlreadyClippedNote: "このノートはすでにクリップ「{name}」に含まれとるで。ノートをこのクリップから除外したる?" public: "パブリック" -i18nInfo: "Calckeyは有志によっていろんな言語に翻訳されとるで。{link}で翻訳に協力したってやー。" +i18nInfo: "Firefishは有志によっていろんな言語に翻訳されとるで。{link}で翻訳に協力したってやー。" manageAccessTokens: "アクセストークンの管理" accountInfo: "アカウント情報" notesCount: "ノートの数やで" @@ -868,7 +867,7 @@ _registry: keys: "キー" domain: "ドメイン" createKey: "キーを作る" -_aboutMisskey: +_aboutFirefish: about: "Misskeyはsyuiloが2014年からずっと作ってはる、オープンソースなソフトウェアや。" contributors: "主な貢献者" allContributors: "全ての貢献者" diff --git a/locales/kn-IN.yml b/locales/kn-IN.yml index 39b3cbe915..04b4ef6e2e 100644 --- a/locales/kn-IN.yml +++ b/locales/kn-IN.yml @@ -1,6 +1,6 @@ --- _lang_: "ಕನ್ನಡ" -introMisskey: "ಸ್ವಾಗತ! Misskey ಓಪನ್ ಸೋರ್ಸ್ ಒಕ್ಕೂಟ ಮೈಕ್ರೋಬ್ಲಾಗಿಂಗ್ ಸೇವೆಯಾಗಿದೆ.\n ಏನಾಗುತ್ತಿದೆ ಎಂಬುದನ್ನು ಹಂಚಿಕೊಳ್ಳಲು ಅಥವಾ ನಿಮ್ಮ ಬಗ್ಗೆ ಎಲ್ಲರಿಗೂ ಹೇಳಲು \"ಟಿಪ್ಪಣಿ\"ಗಳನ್ನು ರಚಿಸಿ📡\n \"ಸ್ಪಂದನೆ\" ಕ್ರಿಯೆಯೊಂದಿಗೆ, ನೀವು ಎಲ್ಲರ ಟಿಪ್ಪಣಿಗಳಿಗೆ ತ್ವರಿತವಾಗಿ ಸ್ಪಂದನೆಗಳನ್ನು ಕೂಡ ಸೇರಿಸಬಹುದು.👍\n ಹೊಸ ಜಗತ್ತನ್ನು ಅನ್ವೇಷಿಸಿ🚀" +introMisskey: "ಸ್ವಾಗತ! Firefish ಓಪನ್ ಸೋರ್ಸ್ ಒಕ್ಕೂಟ ಮೈಕ್ರೋಬ್ಲಾಗಿಂಗ್ ಸೇವೆಯಾಗಿದೆ.\n ಏನಾಗುತ್ತಿದೆ ಎಂಬುದನ್ನು ಹಂಚಿಕೊಳ್ಳಲು ಅಥವಾ ನಿಮ್ಮ ಬಗ್ಗೆ ಎಲ್ಲರಿಗೂ ಹೇಳಲು \"ಟಿಪ್ಪಣಿ\"ಗಳನ್ನು ರಚಿಸಿ📡\n \"ಸ್ಪಂದನೆ\" ಕ್ರಿಯೆಯೊಂದಿಗೆ, ನೀವು ಎಲ್ಲರ ಟಿಪ್ಪಣಿಗಳಿಗೆ ತ್ವರಿತವಾಗಿ ಸ್ಪಂದನೆಗಳನ್ನು ಕೂಡ ಸೇರಿಸಬಹುದು.👍\n ಹೊಸ ಜಗತ್ತನ್ನು ಅನ್ವೇಷಿಸಿ🚀" monthAndDay: "{month}ನೇ ತಿಂಗಳ {day}ನೇ ದಿನ" search: "ಹುಡುಕು" notifications: "ಅಧಿಸೂಚನೆಗಳು" diff --git a/locales/ko-KR.yml b/locales/ko-KR.yml index b87a4b06ce..b869fb0858 100644 --- a/locales/ko-KR.yml +++ b/locales/ko-KR.yml @@ -1,7 +1,7 @@ --- _lang_: "한국어" headlineMisskey: "노트로 연결되는 네트워크" -introMisskey: "환영합니다! Misskey 는 오픈 소스 분산형 마이크로 블로그 서비스입니다.\n\"노트\" 를 작성해서, 지금 일어나고 있는 일을 공유하거나, 당신만의 이야기를 모두에게 발신하세요📡\n\"리액션\" 기능으로, 친구의 노트에 총알같이 반응을 추가할 수도 있습니다👍\n새로운 세계를 탐험해 보세요🚀" +introMisskey: "환영합니다! Firefish 는 오픈 소스 분산형 마이크로 블로그 서비스입니다.\n\"노트\" 를 작성해서, 지금 일어나고 있는 일을 공유하거나, 당신만의 이야기를 모두에게 발신하세요📡\n\"리액션\" 기능으로, 친구의 노트에 총알같이 반응을 추가할 수도 있습니다👍\n새로운 세계를 탐험해 보세요🚀" monthAndDay: "{month}월 {day}일" search: "검색" notifications: "알림" @@ -177,7 +177,6 @@ operations: "작업" software: "소프트웨어" version: "버전" metadata: "메타데이터" -withNFiles: "{n}개의 파일" monitor: "모니터" jobQueue: "작업 대기열" cpuAndMemory: "CPU와 메모리" @@ -378,7 +377,7 @@ exploreFediverse: "연합우주를 탐색" popularTags: "인기 태그" userList: "리스트" about: "정보" -aboutMisskey: "Misskey에 대하여" +aboutFirefish: "Misskey에 대하여" administrator: "관리자" token: "토큰" twoStepAuthentication: "2단계 인증" @@ -524,7 +523,7 @@ sort: "정렬" ascendingOrder: "오름차순" descendingOrder: "내림차순" scratchpad: "스크래치 패드" -scratchpadDescription: "스크래치 패드는 AiScript 의 테스트 환경을 제공합니다. Misskey 와 상호 작용하는 코드를 작성, 실행 및 결과를 확인할 수 있습니다." +scratchpadDescription: "스크래치 패드는 AiScript 의 테스트 환경을 제공합니다. Firefish 와 상호 작용하는 코드를 작성, 실행 및 결과를 확인할 수 있습니다." output: "출력" script: "스크립트" disablePagesScript: "Pages 에서 AiScript 를 사용하지 않음" @@ -649,7 +648,7 @@ createNewClip: "새 클립 만들기" unclip: "클립 해제" confirmToUnclipAlreadyClippedNote: "이 노트는 이미 \"{name}\" 클립에 포함되어 있습니다. 클립을 해제하시겠습니까?" public: "공개" -i18nInfo: "Calckey는 자원봉사자들에 의해 다양한 언어로 번역되고 있습니다. {link}에서 번역에 참가할 수 있습니다." +i18nInfo: "Firefish는 자원봉사자들에 의해 다양한 언어로 번역되고 있습니다. {link}에서 번역에 참가할 수 있습니다." manageAccessTokens: "액세스 토큰 관리" accountInfo: "계정 정보" notesCount: "노트 수" @@ -965,7 +964,7 @@ _registry: keys: "키" domain: "도메인" createKey: "키 생성" -_aboutMisskey: +_aboutFirefish: about: "Misskey는 syuilo에 의해서 2014년부터 개발되어 온 오픈소스 소프트웨어 입니다." contributors: "주요 기여자" allContributors: "모든 기여자" @@ -1180,8 +1179,8 @@ _time: day: "일" _2fa: alreadyRegistered: "이미 설정이 완료되었습니다." - registerDevice: "디바이스 등록" - registerKey: "키를 등록" + registerTOTP: "디바이스 등록" + registerSecurityKey: "키를 등록" step1: "먼저, {a}나 {b}등의 인증 앱을 사용 중인 디바이스에 설치합니다." step2: "그 후, 표시되어 있는 QR코드를 앱으로 스캔합니다." step2Url: "데스크톱 앱에서는 다음 URL을 입력하세요:" @@ -1320,7 +1319,7 @@ _profile: youCanIncludeHashtags: "해시 태그를 포함할 수 있습니다." metadata: "추가 정보" metadataEdit: "추가 정보 편집" - metadataDescription: "프로필에 추가 정보를 표시할 수 있어요" + metadataDescription: "프로필에 추가 정보를 표시할 수 있어요. {rel}과 함께 {a} 태그 또는 {l} 태그를 추가하여 프로필의 링크를 확인할 수 있습니다!" metadataLabel: "라벨" metadataContent: "내용" changeAvatar: "아바타 이미지 변경" diff --git a/locales/nl-NL.yml b/locales/nl-NL.yml index 7a0580f2d0..4eeb7ec720 100644 --- a/locales/nl-NL.yml +++ b/locales/nl-NL.yml @@ -1,7 +1,10 @@ ---- _lang_: "Nederlands" -headlineMisskey: "Netwerk verbonden door notities" -introMisskey: "Welkom! Misskey is een open source, gedecentraliseerde microblogdienst.\nMaak \"notities\" om je gedachten te delen met iedereen om je heen. 📡\nMet \"reacties\" kun je ook snel je mening geven over berichten van anderen. 👍\nLaten we een nieuwe wereld verkennen! 🚀" +headlineMisskey: "Een open source, gedecentraliseerd, social media platform dat voor + altijd gratis is! 🚀" +introMisskey: "Welkom! Firefish is een open source, gedecentraliseerde microblogdienst.\n + Maak \"notities\" om je gedachten te delen met iedereen om je heen. 📡\nMet \"reacties\"\ + \ kun je ook snel je mening geven over berichten van anderen. 👍\nLaten we een nieuwe + wereld verkennen! 🚀" monthAndDay: "{day} {month}" search: "Zoeken" notifications: "Meldingen" @@ -10,7 +13,7 @@ password: "Wachtwoord" forgotPassword: "Wachtwoord vergeten" fetchingAsApObject: "Ophalen vanuit de Fediverse" ok: "Ok" -gotIt: "Begrepen" +gotIt: "Begrepen!" cancel: "Annuleren" enterUsername: "Voer een gebruikersnaam in" renotedBy: "Hergedeeld door {user}" @@ -44,15 +47,16 @@ copyContent: "Kopiëren inhoud" copyLink: "Kopiëren link" delete: "Verwijderen" deleteAndEdit: "Verwijderen en bewerken" -deleteAndEditConfirm: "Weet je zeker dat je deze notitie wilt verwijderen en dan bewerken? Je verliest alle reacties, herdelingen en antwoorden erop." +deleteAndEditConfirm: "Weet je zeker dat je deze post wilt verwijderen en dan bewerken? + Je verliest alle reacties, boosts en antwoorden erop." addToList: "Aan lijst toevoegen" sendMessage: "Verstuur bericht" -copyUsername: "Kopiëren gebruikersnaam " -searchUser: "Zoeken een gebruiker" +copyUsername: "Gebruikersnaam kopiëren" +searchUser: "Zoek een gebruiker" reply: "Antwoord" loadMore: "Laad meer" showMore: "Toon meer" -youGotNewFollower: "volgde jou" +youGotNewFollower: "volgt jou" receiveFollowRequest: "Volgverzoek ontvangen" followRequestAccepted: "Volgverzoek geaccepteerd" mention: "Vermelding" @@ -63,9 +67,11 @@ import: "Import" export: "Export" files: "Bestanden" download: "Downloaden" -driveFileDeleteConfirm: "Weet je zeker dat je het bestand \"{name}\" wilt verwijderen? Notities met dit bestand als bijlage worden ook verwijderd." +driveFileDeleteConfirm: "Weet je zeker dat je het bestand \"{name}\" wilt verwijderen? + Posts met dit bestand als bijlage worden ook verwijderd." unfollowConfirm: "Weet je zeker dat je {name} wilt ontvolgen?" -exportRequested: "Je hebt een export aangevraagd. Dit kan een tijdje duren. Het wordt toegevoegd aan je Drive zodra het is voltooid." +exportRequested: "Je hebt een export aangevraagd. Dit kan een tijdje duren. Het wordt + toegevoegd aan je Drive zodra het is voltooid." importRequested: "Je hebt een import aangevraagd. Dit kan even duren." lists: "Lijsten" noLists: "Je hebt geen lijsten" @@ -75,12 +81,14 @@ following: "Volgend" followers: "Volgers" followsYou: "Volgt jou" createList: "Creëer lijst" -manageLists: "Beheren lijsten" +manageLists: "Lijsten beheren" error: "Fout" somethingHappened: "Er is iets misgegaan." retry: "Probeer opnieuw" pageLoadError: "Pagina laden mislukt" -pageLoadErrorDescription: "Dit wordt normaal gesproken veroorzaakt door netwerkfouten of door de cache van de browser. Probeer de cache te wissen en probeer het na een tijdje wachten opnieuw." +pageLoadErrorDescription: "Dit wordt normaal gesproken veroorzaakt door netwerkfouten + of door de cache van de browser. Probeer de cache te wissen en probeer het na een + tijdje wachten opnieuw." serverIsDead: "De server reageert niet. Wacht even en probeer het opnieuw." youShouldUpgradeClient: "Werk je client bij om deze pagina te zien." enterListName: "Voer de naam van de lijst in" @@ -93,25 +101,26 @@ followRequests: "Volgverzoeken" unfollow: "Ontvolgen" followRequestPending: "Wachten op goedkeuring volgverzoek" enterEmoji: "Voer een emoji in" -renote: "Herdelen" -unrenote: "Stop herdelen" -renoted: "Herdeeld" -cantRenote: "Dit bericht kan niet worden herdeeld" -cantReRenote: "Een herdeling kan niet worden herdeeld" +renote: "Boost" +unrenote: "Boost intrekken" +renoted: "Boosted." +cantRenote: "Dit bericht kan niet worden geboost." +cantReRenote: "Een boost kan niet worden geboost." quote: "Quote" -pinnedNote: "Vastgemaakte notitie" +pinnedNote: "Vastgemaakte post" pinned: "Vastmaken aan profielpagina" you: "Jij" clickToShow: "Klik om te bekijken" sensitive: "NSFW" add: "Toevoegen" reaction: "Reacties" -reactionSettingDescription2: "Sleep om opnieuw te ordenen, Klik om te verwijderen, Druk op \"+\" om toe te voegen" -rememberNoteVisibility: "Vergeet niet de notitie zichtbaarheidsinstellingen" +reactionSettingDescription2: "Sleep om opnieuw te ordenen, Klik om te verwijderen, + Druk op \"+\" om toe te voegen" +rememberNoteVisibility: "Onthoud post zichtbaarheidsinstellingen" attachCancel: "Verwijder bijlage" markAsSensitive: "Markeren als NSFW" unmarkAsSensitive: "Geen NSFW" -enterFileName: "Invoeren bestandsnaam" +enterFileName: "Bestandsnaam invoeren" mute: "Dempen" unmute: "Stop dempen" block: "Blokkeren" @@ -122,16 +131,21 @@ blockConfirm: "Weet je zeker dat je dit account wil blokkeren?" unblockConfirm: "Ben je zeker dat je deze account wil blokkeren?" suspendConfirm: "Ben je zeker dat je deze account wil suspenderen?" unsuspendConfirm: "Ben je zeker dat je deze account wil opnieuw aanstellen?" -flagAsBot: "Markeer dit account als een robot." -flagAsBotDescription: "Als dit account van een programma wordt beheerd, zet deze vlag aan. Het aanzetten helpt andere ontwikkelaars om bijvoorbeeld onbedoelde feedback loops te doorbreken of om Misskey meer geschikt te maken." +flagAsBot: "Markeer dit account als een robot" +flagAsBotDescription: "Als dit account van een programma wordt beheerd, zet deze vlag + aan. Het aanzetten helpt andere ontwikkelaars om bijvoorbeeld onbedoelde feedback + loops te doorbreken of om Firefish meer geschikt te maken." flagAsCat: "Markeer dit account als een kat." -flagAsCatDescription: "Zet deze vlag aan als je wilt aangeven dat dit account een kat is." -flagShowTimelineReplies: "Toon antwoorden op de tijdlijn." -flagShowTimelineRepliesDescription: "Als je dit vlag aanzet, toont de tijdlijn ook antwoorden op andere en niet alleen jouw eigen notities." -autoAcceptFollowed: "Accepteer verzoeken om jezelf te volgen vanzelf als je de verzoeker al volgt." +flagAsCatDescription: "Zet deze vlag aan als je wilt aangeven dat dit account een + kat is." +flagShowTimelineReplies: "Toon antwoorden op de tijdlijn" +flagShowTimelineRepliesDescription: "Als je deze vlag aanzet, toont de tijdlijn ook + antwoorden op andere en niet alleen jouw eigen post." +autoAcceptFollowed: "Accepteer verzoeken om jezelf te volgen vanzelf als je de verzoeker + al volgt" addAccount: "Account toevoegen" loginFailed: "Aanmelding mislukt." -showOnRemote: "Toon op de externe instantie." +showOnRemote: "Bekijk op de externe server" general: "Algemeen" wallpaper: "Achtergrond" setWallpaper: "Achtergrond instellen" @@ -140,13 +154,17 @@ searchWith: "Zoeken: {q}" youHaveNoLists: "Je hebt geen lijsten" followConfirm: "Weet je zeker dat je {name} wilt volgen?" proxyAccount: "Proxy account" -proxyAccountDescription: "Een proxy-account is een account dat onder bepaalde voorwaarden fungeert als externe volger voor gebruikers. Als een gebruiker bijvoorbeeld een externe gebruiker aan de lijst toevoegt, wordt de activiteit van de externe gebruiker niet aan de server geleverd als geen lokale gebruiker die gebruiker volgt, dus het proxy-account volgt in plaats daarvan." +proxyAccountDescription: "Een proxy-account is een account dat onder bepaalde voorwaarden + fungeert als externe volger voor gebruikers. Als een gebruiker bijvoorbeeld een + externe gebruiker aan de lijst toevoegt, wordt de activiteit van de externe gebruiker + niet aan de server geleverd als geen lokale gebruiker die gebruiker volgt, dus het + proxy-account volgt in plaats daarvan." host: "Server" selectUser: "Kies een gebruiker" -recipient: "Ontvanger" +recipient: "Ontvanger(s)" annotation: "Reacties" federation: "Federatie" -instances: "Server" +instances: "Servers" registeredAt: "Geregistreerd op" latestRequestSentAt: "Laatste aanvraag verstuurd" latestRequestReceivedAt: "Laatste aanvraag ontvangen" @@ -161,7 +179,6 @@ operations: "Verwerkingen" software: "Software" version: "Versie" metadata: "Metadata" -withNFiles: "{n} bestand(en)" monitor: "Monitor" jobQueue: "Job Queue" cpuAndMemory: "CPU en geheugen" @@ -171,19 +188,23 @@ instanceInfo: "Serverinformatie" statistics: "Statistieken" clearQueue: "Wachtrij wissen" clearQueueConfirmTitle: "Weet je zeker dat je de wachtrji leeg wil maken?" -clearQueueConfirmText: "Niet-bezorgde biljetten die nog in de wachtrij staan, worden niet gefedereerd. Meestal is deze operatie niet nodig." +clearQueueConfirmText: "Niet-bezorgde posts die nog in de wachtrij staan, worden niet + gefedereerd. Meestal is deze operatie niet nodig." clearCachedFiles: "Cache opschonen" -clearCachedFilesConfirm: "Weet je zeker dat je alle externe bestanden in de cache wilt verwijderen?" +clearCachedFilesConfirm: "Weet je zeker dat je alle externe bestanden in de cache + wilt verwijderen?" blockedInstances: "Geblokkeerde servers" -blockedInstancesDescription: "Maak een lijst van de servers die moeten worden geblokkeerd, gescheiden door regeleinden. Geblokkeerde servers kunnen niet meer communiceren met deze server." +blockedInstancesDescription: "Maak een lijst van de servers die moeten worden geblokkeerd, + gescheiden door regeleinden. Geblokkeerde servers kunnen niet meer communiceren + met deze server." muteAndBlock: "Gedempt en geblokkeerd" mutedUsers: "Gedempte gebruikers" blockedUsers: "Geblokkeerde gebruikers" noUsers: "Er zijn geen gebruikers." editProfile: "Bewerk Profiel" -noteDeleteConfirm: "Ben je zeker dat je dit bericht wil verwijderen?" -pinLimitExceeded: "Je kunt geen berichten meer vastprikken" -intro: "Installatie van Misskey geëindigd! Maak nu een beheerder aan." +noteDeleteConfirm: "Ben je zeker dat je deze post wil verwijderen?" +pinLimitExceeded: "Je kunt geen posts meer vastprikken" +intro: "Installatie van Firefish geëindigd! Maak nu een beheerder aan." done: "Klaar" processing: "Bezig met verwerken" preview: "Voorbeeld" @@ -223,9 +244,11 @@ saved: "Opgeslagen" messaging: "Chat" upload: "Uploaden" keepOriginalUploading: "Origineel beeld behouden." -keepOriginalUploadingDescription: "Bewaar de originele versie bij het uploaden van afbeeldingen. Indien uitgeschakeld, wordt bij het uploaden een alternatieve versie voor webpublicatie genereert." +keepOriginalUploadingDescription: "Bewaar de originele versie bij het uploaden van + afbeeldingen. Indien uitgeschakeld, wordt bij het uploaden een alternatieve versie + voor webpublicatie genereert." fromDrive: "Van schijf" -fromUrl: "Van URL" +fromUrl: "Van URL" uploadFromUrl: "Uploaden vanaf een URL" uploadFromUrlDescription: "URL van het bestand dat je wil uploaden" uploadFromUrlRequested: "Uploadverzoek" @@ -239,7 +262,8 @@ agreeTo: "Ik stem in met {0}" tos: "Gebruiksvoorwaarden" start: "Aan de slag" home: "Startpagina" -remoteUserCaution: "Aangezien deze gebruiker van een externe server afkomstig is, kan de weergegeven informatie onvolledig zijn." +remoteUserCaution: "Aangezien deze gebruiker van een externe server afkomstig is, + kan de weergegeven informatie onvolledig zijn." activity: "Activiteit" images: "Afbeeldingen" birthday: "Geboortedatum" @@ -280,7 +304,7 @@ disconnectedFromServer: "Verbinding met de server onderbroken." inMb: "in megabytes" pinnedNotes: "Vastgemaakte notitie" userList: "Lijsten" -aboutMisskey: "Over Misskey" +aboutFirefish: "Over Firefish" administrator: "Beheerder" token: "Token" securityKeyName: "Sleutelnaam" @@ -308,7 +332,7 @@ cropImageAsk: "Bijsnijdengevraagd" file: "Bestanden" _email: _follow: - title: "volgde jou" + title: "Je hebt een nieuwe volger" _mfm: mention: "Vermelding" quote: "Quote" @@ -367,7 +391,7 @@ _pages: types: array: "Lijsten" _notification: - youWereFollowed: "volgde jou" + youWereFollowed: "volgt jou" _types: follow: "Volgend" mention: "Vermelding" @@ -383,3 +407,278 @@ _deck: tl: "Tijdlijn" list: "Lijsten" mentions: "Vermeldingen" +showLess: Sluiten +emoji: Emoji +selectList: Selecteer een lijst +selectAntenna: Selecteer een antenne +deleted: Verwijderd +editNote: Bewerk notitie +edited: 'Bewerkt om {date} {time}' +emojis: Emojis +emojiName: Emoji naam +emojiUrl: Emoji URL +addEmoji: Voeg toe +settingGuide: Aanbevolen instellingen +flagSpeakAsCat: Praat als een kat +accountMoved: 'Gebruiker is naar een nieuw account verhuisd:' +showEmojisInReactionNotifications: Toon emojis in reactie notificaties +selectWidget: Selecteer een widget +editWidgetsExit: Klaar +noThankYou: Nee bedankt +addInstance: Voeg een server toe +enableEmojiReactions: Schakel emoji reacties in +editWidgets: Bewerk widgets +thisYear: Jaar +thisMonth: Maand +registration: Registreren +_ffVisibility: + public: Openbaar + private: Privé + followers: Alleen zichtbaar voor volgers +noInstances: Er zijn geen servers +_signup: + almostThere: Bijna klaar + emailAddressInfo: Voer je emailadres in. Deze zal niet openbaar gemaakt worden. +_ad: + back: Terug + reduceFrequencyOfThisAd: Toon deze advertentie minder +pushNotificationNotSupported: Je browser of server ondersteunt geen pushmeldingen +sendPushNotificationReadMessage: Verwijder pushmeldingen wanneer de relevante meldingen + of berichten zijn gelezen +customEmojis: Custom emoji +cacheRemoteFiles: Cache externe bestanden +hiddenTags: Verborgen hashtags +enableRecommendedTimeline: Schakel aanbevolen tijdlijn in +_forgotPassword: + enterEmail: Voer het emailadres in dat je gebruikte om te registreren. Een link + waarmee je je wachtwoord opnieuw kunt instellen zal daar naartoe gestuurd worden. +jumpToReply: Spring naar Antwoord +newer: nieuwer +older: ouder +selectInstance: Kies een server +defaultValueIs: 'Standaard: {value}' +reload: Hernieuwen +doNothing: Negeren +today: Vandaag +inputNewDescription: Voer een nieuw onderschrift in +inputNewFolderName: Voer een nieuwe mapnaam in +circularReferenceFolder: De bestemmingsmap is een submap van de map die je wil verplaatsen. +hasChildFilesOrFolders: Omdat deze map niet leeg is, kan deze niet verwijderd worden. +enableLocalTimeline: Schakel lokale tijdlijn in +enableGlobalTimeline: Schakel globale tijdlijn in +enableRegistration: Nieuwe gebruikersregistratie inschakelen +invite: Uitnodigen +move: Verplaatsen +showAds: Toon advertenties +pushNotification: Pushmeldingen +_gallery: + my: Mijn Gallerij +reactionSetting: Reacties om te tonen in het reactie selectie menu +dayX: '{day}' +renoteMute: Demp boosts +reloadConfirm: Wil je de tijdlijn hernieuwen? +watch: Volgen +unwatch: Ontvolgen +accept: Accepteren +reject: Afwijzen +normal: Normaal +pages: Pagina's +integration: Integraties +connectService: Koppelen +monthX: '{month}' +yearX: '{year}' +instanceName: Servernaam +instanceDescription: Server omschrijving +maintainerName: Onderhouder +maintainerEmail: Onderhouder email +tosUrl: Algemene Voorwaarden URL +disconnectService: Ontkoppelen +unread: Ongelezen +manageGroups: Beheer groepen +subscribePushNotification: Pushmeldingen inschakelen +unsubscribePushNotification: Pushmeldingen uitschakelen +pushNotificationAlreadySubscribed: Pushmeldingen zijn al ingeschakeld +antennaSource: Antenne bron +antennaKeywords: Trefwoorden om naar te luisteren +antennaExcludeKeywords: Trefwoorden om te negeren +driveCapacityPerRemoteAccount: Schijfruimte per externe gebruiker +backgroundImageUrl: Achtergrondafbeelding URL +basicInfo: Basis informatie +pinnedUsers: Vastgezette gebruikers +pinnedPages: Vastgezette Pagina's +driveCapacityPerLocalAccount: Schijfruimte per lokale gebruiker +iconUrl: Icoon URL +bannerUrl: Banner afbeelding URL +manageAntennas: Beheer Antennes +name: Naam +notifyAntenna: Meld nieuwe posts +withFileAntenna: Alleen posts met bestanden +enableServiceworker: Schakel pushmeldingen voor je browser in +renoteUnmute: Ontdemp boosts +jumpToPrevious: Spring naar vorige +caseSensitive: Hoofdlettergevoelig +cw: Inhoudswaarschuwing +recaptcha: reCAPTCHA +enableRecaptcha: reCAPTCHA inschakelen +recaptchaSiteKey: Site sleutel +notFoundDescription: Een pagina met deze URL kon niet worden gevonden. +uploadFolder: Standaard map voor uploads +markAsReadAllNotifications: Markeer alle notificaties als gelezen +text: Tekst +enable: Inschakelen +or: Of +language: Taal +securityKey: Veiligheidssleutel +groupInvited: Je bent voor een groep uitgenodigd +docSource: Bron van dit document +createAccount: Maak account aan +groupName: Groepsnaam +members: Leden +messagingWithUser: Privé chat +messagingWithGroup: Groepschat +title: Titel +createGroup: Maak een groep +ownedGroups: Beheerde groepen +invites: Uitnodigingen +useOsNativeEmojis: Gebruik je standaard besturingssysteem Emojis +disableDrawer: Gebruik niet de lade-stijl menus +joinOrCreateGroup: Krijg een uitnodiging voor een groep of maak er zelf eentje aan. +noHistory: Geen geschiedenis beschikbaar +signinHistory: Inloggeschiedenis +available: Beschikbaar +unavailable: Niet beschikbaar +tooShort: Te kort +signinFailed: Niet gelukt om in te loggen. Gebruikersnaam of wachtwoord is incorrect. +tapSecurityKey: Tik je veiligheidssleutel aan +recaptchaSecretKey: Geheime sleutel +antennas: Antennes +antennaUsersDescription: Zet één gebruikersnaam per regel neer +notesAndReplies: Posts en antwoorden +withFiles: Met bestanden +popularUsers: Populaire gebruikers +recentlyUpdatedUsers: Recente actieve gebruikers +recentlyRegisteredUsers: Nieuwe gebruikers +recentlyDiscoveredUsers: Nieuwe ontdekte gebruikers +exploreUsersCount: Er zijn {count} gebruikers +about: Over +exploreFediverse: Ontdek de Fediverse +popularTags: Populaire labels +moderation: Moderatie +nUsersMentioned: Genoemd door {n} gebruikers +markAsReadAllUnreadNotes: Markeer alle posts als gelezen +markAsReadAllTalkMessages: Markeer alle berichten als gelezen +help: Help +inputMessageHere: Schrijf hier je bericht +close: Sluiten +group: Groep +groups: Groepen +newMessageExists: Er zijn nieuwe berichten +next: Volgende +noteOf: Post door {user} +inviteToGroup: Nodig uit voor de groep +quoteAttached: Quote +noMessagesYet: Nog geen berichten +weakPassword: Zwak wachtwoord +normalPassword: Middelmatig wachtwoord +strongPassword: Sterk wachtwoord +onlyOneFileCanBeAttached: Je kan maar één bestand toevoegen aan je bericht +invitationCode: Uitnodigingscode +checking: Controleren... +uiLanguage: Gebruikersinterface taal +aboutX: Over {x} +youHaveNoGroups: Je hebt geen groepen +disableAnimatedMfm: Schakel MFM met animaties uit +passwordMatched: Komt overeen +passwordNotMatched: Komt niet overeen +signinWith: Log in met {x} +fontSize: Tekstgrootte +openImageInNewTab: Open afbeeldingen in een nieuwe tab +category: Categorie +tags: Labels +existingAccount: Bestaand account +regenerate: Hernieuwen +dayOverDayChanges: Verschillen met gisteren +appearance: Uiterlijk +local: Lokaal +remote: Extern +total: Totaal +weekOverWeekChanges: Verschillen met vorige week +hcaptcha: hCaptcha +enableHcaptcha: hCaptcha inschakelen +hcaptchaSiteKey: Site sleutel +hcaptchaSecretKey: Geheime sleutel +withReplies: Met antwoorden +twoStepAuthentication: Tweefactorauthenticatie +moderator: Moderator +invitations: Uitnodigingen +tooLong: Te lang +doing: Verwerken... +silencedInstances: Gedempte Servers +cacheRemoteFilesDescription: Als deze instelling is uitgeschakeld, worden externe + bestanden direct van de externe server geladen. Het uitschakelen zal opslagruimte + verminderen, maar verkeer zal toenemen, omdat er geen thumbnails gemaakt zullen + worden. +flagSpeakAsCatDescription: Je posts zullen worden ge-'nyanified' als je in kat-modus + zit +avoidMultiCaptchaConfirm: Het gebruik van meerdere Captcha systemen kan voor storing + zorgen tussen ze. Wil je de andere actieve Captcha systemen uitschakelen? Als je + ze ingeschakeld wilt houden, klik op annuleren. +silence: Dempen +silenceConfirm: Weet je zeker dat je deze gebruiker wilt dempen? +unsilence: Ontdempen +unsilenceConfirm: Weet je zeker dat je het dempen van deze gebruiker ongedaan wilt + maken? +silenceThisInstance: Demp deze server +silenced: Gedempt +disablingTimelinesInfo: Beheerders en moderators zullen altijd toegang hebben tot + alle tijdlijnen, zelfs als deze uitgeschakeld zijn. +accountSettings: Account Instellingen +numberOfDays: Aantal dagen +hideThisNote: Verberg deze post +dashboard: Dashboard +accessibility: Toegankelijkheid +promotion: Gepromoot +promote: Promoten +objectStorage: Objectopslag +useObjectStorage: Gebruik objectopslag +objectStorageBaseUrl: Basis -URL +objectStorageUseSSLDesc: Schakel dit uit als je geen HTTPS voor je API connecties + gebruikt +objectStorageUseProxy: Verbind over Proxy +objectStorageUseProxyDesc: Schakel dit uit als je geen Proxy voor je API connecties + gebruikt +sounds: Geluiden +lastUsedDate: Laatst gebruikt op +installedDate: Geautoriseerd op +sort: Sorteren +output: Uitvoer +script: Script +popout: Pop-out +descendingOrder: Aflopend +showInPage: Toon in de pagina +chooseEmoji: Kies een emoji +ascendingOrder: Oplopend +volume: Volume +masterVolume: Master volume +details: Details +unableToProcess: Deze operatie kon niet worden voltooid +nothing: Niks te zien hier +scratchpad: Kladblok +recentUsed: Recentelijk gebruikt +install: Installeer +uninstall: Verwijderen +installedApps: Geautoriseerde Applicaties +state: Status +updateRemoteUser: Update externe gebruikersinformatie +listen: Luister +none: Geen +scratchpadDescription: Het kladblok is een omgeving voor AiScript experimenten. Je + kan hier schrijven, uitvoeren, en de resultaten bekijken van de interactie met Firefish. +disablePagesScript: Zet AiScript op Pages uit +deleteAllFiles: Verwijder alle bestanden +deleteAllFilesConfirm: Weet je zeker dat je alle bestanden wil verwijderen? +removeAllFollowing: Ontvolg alle gevolgde gebruikers +serverLogs: Server logboek +deleteAll: Verwijder alles +showFixedPostForm: Toon het post formulier bovenaan de tijdlijn +newNoteRecived: Er zijn nieuwe posts diff --git a/locales/no-NO.yml b/locales/no-NO.yml index 83e189b9cf..a7f779c44e 100644 --- a/locales/no-NO.yml +++ b/locales/no-NO.yml @@ -1,2 +1,83 @@ ---- _lang_: "Norsk Bokmål" +search: Søk +monthAndDay: '{day}/{month}' +fetchingAsApObject: Henter fra fediverset +ok: OK +gotIt: Jeg forstår! +profile: Profil +timeline: Tidslinje +save: Lagre +addToList: Legg til liste +searchPlaceholder: Søk Firefish +username: Brukernavn +password: Passord +notifications: Meldinger +forgotPassword: Glemt passord +cancel: Avbryt +noNotes: Ingen poster +instance: Server +settings: Innstillinger +noAccountDescription: Denne brukeren har ikke fylt ut bio'en sin ennå. +login: Logg inn +loggingIn: Logger inn +signup: Oppretter bruker +uploading: Laster opp.. +enterUsername: Skriv inn brukernavn +noNotifications: Ingen meldinger +users: Brukere +addUser: Legg til en bruker +favorite: Legg til i bokmerker +cantFavorite: Kunne ikke legges til i bokmerker. +pin: Fest til profilen +copyContent: Kopier innhold +deleteAndEdit: Slett og rediger +sendMessage: Send en melding +copyUsername: Kopier brukernavn +reply: Svar +loadMore: Last mer +showLess: Lukk +receiveFollowRequest: Følgeforespørsel mottatt +directNotes: Direktemelding +importAndExport: Importer/eksporter data +importRequested: Du har bedt om en importering. Dette vil ta litt tid. +lists: Lister +listsDesc: Lister lar deg lage tidslinjer med utvalgte brukere. De kan hentes frem + fra tidslinje-siden. +deleted: Slettet +editNote: Rediger notat +followsYou: Følger deg +createList: Lag liste +newer: nyere +older: eldre +download: Last ned +unfollowConfirm: Er du sikker på at du ikke lenger vil følge {name}? +noLists: Du har ingen lister +following: Følger +files: Filer +note: Post +notes: Poster +followers: Følgere +otherSettings: Andre innstillinger +addInstance: Legg til en server +alreadyFavorited: Allerede lagt til i bokmerker. +delete: Slett +openInWindow: Åpne i vindu +basicSettings: Grunnleggende innstillinger +headlineMisskey: En desentralisert sosialt media-plattform, basert på åpen kildekode, + som alltid vil være gratis! 🚀 +introMisskey: Velkommen! Firefish er en desentralisert sosialt media-plattform, basert + på åpen kildekode, som alltid vil være gratis! 🚀 +exportRequested: Du har bedt om en eksportering. Dette vil ta litt tid. Den vil bli + lagt til på disken din når den er ferdig. +noThankYou: Nei takk +favorites: Bokmerker +unfavorite: Fjern fra bokmerker +favorited: Lagt til i bokmerker. +copyLink: Kopier lenke +searchUser: Søk etter en bruker +jumpToPrevious: Gå til foregående +showMore: Vis mer +followRequestAccepted: Følgeforespørsel godtatt +import: Importer +export: Eksporter +logout: Logger ut diff --git a/locales/pl-PL.yml b/locales/pl-PL.yml index a7cbec5b64..edb5513773 100644 --- a/locales/pl-PL.yml +++ b/locales/pl-PL.yml @@ -1,7 +1,8 @@ ---- _lang_: "Polski" -headlineMisskey: "Sieć połączona wpisami" -introMisskey: "Misskey jest serwisem mikroblogowym typu open source.\nMisskey to opensource'owy serwis mikroblogowy, w którym możesz tworzyć \"notatki\", aby dzielić się tym, co się dzieje i opowiadać wszystkim o sobie.\nMożesz również użyć funkcji \"Reakcje\", aby szybko dodać własne reakcje do notatek innych użytkowników👍.\nOdkrywaj nowy świat🚀!" +headlineMisskey: "Otwartoźródłowa, zdecentralizowana sieć społecznościowa, która zawsze + będzie darmowa! 🚀" +introMisskey: "Hej! Firefish to otwartoźródłowa oraz zdecentralizowana sieć społecznościowa, + która zawsze będzie darmowa! 🚀" monthAndDay: "{month}-{day}" search: "Szukaj" notifications: "Powiadomienia" @@ -13,60 +14,63 @@ ok: "OK" gotIt: "Rozumiem!" cancel: "Anuluj" enterUsername: "Wprowadź nazwę użytkownika" -renotedBy: "Udostępniono przez {user}" +renotedBy: "Podbito przez {user}" noNotes: "Brak wpisów" noNotifications: "Brak powiadomień" -instance: "Instancja" +instance: "Serwer" settings: "Ustawienia" basicSettings: "Podstawowe ustawienia" otherSettings: "Pozostałe ustawienia" openInWindow: "Otwórz w oknie" profile: "Profil" timeline: "Oś czasu" -noAccountDescription: "Ten użytkownik nie napisał jeszcze swojej biografii." +noAccountDescription: "Ten użytkownik nie napisał jeszcze swojego opisu." login: "Zaloguj się" loggingIn: "Logowanie" logout: "Wyloguj się" signup: "Zarejestruj się" -uploading: "Wysyłanie" +uploading: "Wysyłanie..." save: "Zapisz" users: "Użytkownicy" addUser: "Dodaj użytkownika" favorite: "Dodaj do ulubionych" -favorites: "Ulubione" -unfavorite: "Usuń z ulubionych" -favorited: "Dodano do ulubionych." -alreadyFavorited: "Już jest w ulubionych." -cantFavorite: "Nie można dodać do ulubionych." +favorites: "Zakładki" +unfavorite: "Usuń zakładkę" +favorited: "Dodano do zakładek." +alreadyFavorited: "Już jest w zakładkach." +cantFavorite: "Nie można dodać do zakładek." pin: "Przypnij do profilu" unpin: "Odepnij z profilu" copyContent: "Skopiuj zawartość" copyLink: "Skopiuj odnośnik" delete: "Usuń" deleteAndEdit: "Usuń i edytuj" -deleteAndEditConfirm: "Czy na pewno chcesz usunąć ten wpis i zedytować go? Utracisz wszystkie reakcje, udostępnienia i odpowiedzi do tego wpisu." +deleteAndEditConfirm: "Czy na pewno chcesz usunąć ten wpis i zedytować go? Utracisz + wszystkie reakcje, podbicia i odpowiedzi do tego wpisu." addToList: "Dodaj do listy" sendMessage: "Wyślij wiadomość" copyUsername: "Kopiuj nazwę użytkownika" searchUser: "Wyszukiwanie użytkowników" reply: "Odpowiedz" loadMore: "Załaduj więcej" -showMore: "Załaduj więcej" +showMore: "Pokaż więcej" showLess: "Zamknij" -youGotNewFollower: "Zaobserwował(a) Cię" +youGotNewFollower: "Zaobserwował* Cię" receiveFollowRequest: "Otrzymano prośbę o możliwość obserwacji" followRequestAccepted: "Zaakceptowano prośbę o możliwość obserwacji" mention: "Wspomnij" mentions: "Wspomnienia" -directNotes: "Bezpośrednie wpisy" -importAndExport: "Import i eksport" +directNotes: "Bezpośrednie wiadomości" +importAndExport: "Import i eksport danych" import: "Importuj" export: "Eksportuj" files: "Pliki" download: "Pobierz" -driveFileDeleteConfirm: "Czy chcesz usunąć plik \"{name}\"? Zniknie również notatka, do której dołączony jest ten plik." +driveFileDeleteConfirm: "Czy chcesz usunąć plik \"{name}\"? Wszystkie wpisy zawierające + ten plik również zostaną usunięte." unfollowConfirm: "Czy na pewno chcesz przestać obserwować {name}?" -exportRequested: "Zażądałeś eksportu. Może to zająć trochę czasu. Po zakończeniu eksportu zostanie on dodany do Twoich \"dysków\"." +exportRequested: "Zażądałeś eksportu. Może to zająć chwilę. Po zakończeniu eksportu + zostanie on dodany do Twojego dysku." importRequested: "Zażądano importu. Może to zająć chwilę." lists: "Listy" noLists: "Nie masz żadnych list" @@ -80,11 +84,12 @@ manageLists: "Zarządzaj listami" error: "Błąd" somethingHappened: "Coś poszło nie tak" retry: "Spróbuj ponownie" -pageLoadError: "Nie udało się załadować strony" -pageLoadErrorDescription: "Zwykle jest to spowodowane problemem z siecią lub cache przeglądarki. Spróbuj wyczyścić cache i sprawdź jeszcze raz za chwilę." +pageLoadError: "Nie udało się załadować strony." +pageLoadErrorDescription: "Zwykle jest to spowodowane problemem z siecią lub cache + przeglądarki. Spróbuj wyczyścić cache i sprawdź jeszcze raz za chwilę." serverIsDead: "Serwer nie odpowiada. Zaczekaj chwilę i spróbuj ponownie." -youShouldUpgradeClient: "Odśwież stronę, by zaaktualizować klienta." -enterListName: "Nazwa listy" +youShouldUpgradeClient: "Aby zobaczyć tą stronę, odśwież ją, by zaaktualizować klienta." +enterListName: "Wpisz nazwę listy" privacy: "Prywatność" makeFollowManuallyApprove: "Prośby o możliwość obserwacji wymagają zatwierdzenia" defaultNoteVisibility: "Domyślna widoczność" @@ -94,11 +99,11 @@ followRequests: "Prośby o możliwość obserwacji" unfollow: "Przestań obserwować" followRequestPending: "Oczekująca prośba o możliwość obserwacji" enterEmoji: "Wprowadź emoji" -renote: "Udostępnij" -unrenote: "Cofnij udostępnienie" -renoted: "Udostępniono." -cantRenote: "Ten wpis nie może zostać udostępniony." -cantReRenote: "Udostępnienie nie może zostać udostępnione." +renote: "Podbij" +unrenote: "Cofnij podbicie" +renoted: "Podbito." +cantRenote: "Ten wpis nie może zostać podbity." +cantReRenote: "Podbicie nie może zostać podbite." quote: "Cytuj" pinnedNote: "Przypięty wpis" pinned: "Przypnij do profilu" @@ -108,7 +113,8 @@ sensitive: "NSFW" add: "Dodaj" reaction: "Reakcja" reactionSetting: "Reakcje do pokazania w wyborniku reakcji" -reactionSettingDescription2: "Przeciągnij aby zmienić kolejność, naciśnij aby usunąć, naciśnij „+” aby dodać" +reactionSettingDescription2: "Przeciągnij aby zmienić kolejność, naciśnij aby usunąć, + naciśnij „+” aby dodać." rememberNoteVisibility: "Zapamiętuj ustawienia widoczności wpisu" attachCancel: "Usuń załącznik" markAsSensitive: "Oznacz jako NSFW" @@ -125,7 +131,7 @@ unblockConfirm: "Czy na pewno chcesz odblokować to konto?" suspendConfirm: "Czy na pewno chcesz zawiesić to konto?" unsuspendConfirm: "Czy na pewno chcesz cofnąć zawieszenie tego konta?" selectList: "Wybierz listę" -selectAntenna: "Wybierz Antennę" +selectAntenna: "Wybierz antenę" selectWidget: "Wybierz widżet" editWidgets: "Edytuj widżety" editWidgetsExit: "Gotowe" @@ -137,16 +143,22 @@ emojiUrl: "Adres URL emoji" addEmoji: "Dodaj emoji" settingGuide: "Proponowana konfiguracja" cacheRemoteFiles: "Przechowuj zdalne pliki w pamięci podręcznej" -cacheRemoteFilesDescription: "Gdy ta opcja jest wyłączona, zdalne pliki są ładowane bezpośrednio ze zdalnych instancji. Wyłączenie the opcji zmniejszy użycie powierzchni dyskowej, ale zwiększy transfer, ponieważ miniaturki nie będą generowane." +cacheRemoteFilesDescription: "Gdy ta opcja jest wyłączona, zdalne pliki są ładowane + bezpośrednio ze zdalnego serwera. Wyłączenie tej opcji zmniejszy użycie powierzchni + dyskowej, ale zwiększy transfer, ponieważ miniaturki nie będą generowane." flagAsBot: "To konto jest botem" -flagAsBotDescription: "Jeżeli ten kanał jest kontrolowany przez jakiś program, ustaw tę opcję. Jeżeli włączona, będzie działać jako flaga informująca innych programistów, aby zapobiegać nieskończonej interakcji z różnymi botami i dostosowywać wewnętrzne systemy Misskey, traktując konto jako bota." -flagAsCat: "To konto jest kotem" -flagAsCatDescription: "Przełącz tę opcję, aby konto było oznaczone jako kot." +flagAsBotDescription: "Jeżeli ten kanał jest kontrolowany przez jakiś program, ustaw + tę opcję. Jeżeli włączona, będzie działać jako flaga informująca innych programistów, + aby zapobiegać nieskończonej interakcji z różnymi botami i dostosowywać wewnętrzne + systemy Firefish, traktując konto jako bota." +flagAsCat: "Czy jesteś kotem? 😺" +flagAsCatDescription: "Dostaniesz kocie uszka, oraz będziesz mówić jak kot!" flagShowTimelineReplies: "Pokazuj odpowiedzi na osi czasu" -autoAcceptFollowed: "Automatycznie przyjmuj prośby o możliwość obserwacji od użytkowników, których obserwujesz" +autoAcceptFollowed: "Automatycznie przyjmuj prośby o możliwość obserwacji od użytkowników, + których obserwujesz" addAccount: "Dodaj konto" loginFailed: "Nie udało się zalogować" -showOnRemote: "Zobacz na zdalnej instancji" +showOnRemote: "Zobacz na zdalnym serwerze" general: "Ogólne" wallpaper: "Tapeta" setWallpaper: "Ustaw tapetę" @@ -157,10 +169,10 @@ followConfirm: "Czy na pewno chcesz zaobserwować {name}?" proxyAccount: "Konto proxy" host: "Host" selectUser: "Wybierz użytkownika" -recipient: "Odbiorca" +recipient: "Odbiorca(-y)" annotation: "Komentarze" federation: "Federacja" -instances: "Instancja" +instances: "Serwery" registeredAt: "Zarejestrowano" latestRequestSentAt: "Ostatnie żądanie wysłano o" latestRequestReceivedAt: "Ostatnie żądanie otrzymano o" @@ -170,34 +182,36 @@ charts: "Wykresy" perHour: "co godzinę" perDay: "co dzień" stopActivityDelivery: "Przestań przesyłać aktywności" -blockThisInstance: "Zablokuj tę instancję" +blockThisInstance: "Zablokuj ten serwer" operations: "Działania" software: "Oprogramowanie" version: "Wersja" metadata: "Metadane" -withNFiles: "{n} plik(i)" monitor: "Monitor" jobQueue: "Kolejka zadań" cpuAndMemory: "CPU i pamięć" network: "Sieć" disk: "Dysk" -instanceInfo: "Informacje o instancji" +instanceInfo: "Informacje o serwerze" statistics: "Statystyki" clearQueue: "Wyczyść kolejkę" clearQueueConfirmTitle: "Czy na pewno chcesz wyczyścić kolejkę?" -clearQueueConfirmText: "Wszystkie niewysłane wpisy z kolejki nie zostaną wysłane. Zwykle to nie jest konieczne." +clearQueueConfirmText: "Wszystkie niewysłane wpisy z kolejki nie zostaną wysłane. + Zwykle to nie jest konieczne." clearCachedFiles: "Wyczyść pamięć podręczną" -clearCachedFilesConfirm: "Czy na pewno chcesz usunąć wszystkie zdalne pliki z pamięci podręcznej?" -blockedInstances: "Zablokowane instancje" -blockedInstancesDescription: "Wypisz nazwy hostów instancji, które powinny zostać zablokowane. Wypisane instancje nie będą mogły dłużej komunikować się z tą instancją." -muteAndBlock: "Wycisz / Zablokuj" +clearCachedFilesConfirm: "Czy na pewno chcesz usunąć wszystkie zdalne pliki z pamięci + podręcznej?" +blockedInstances: "Zablokowane serwery" +blockedInstancesDescription: "Wypisz nazwy hostów serwerów, które chcesz zablokować. + Wymienione serwery nie będą mogły dłużej komunikować się z tym serwerem." +muteAndBlock: "Wyciszenia i blokady" mutedUsers: "Wyciszeni użytkownicy" blockedUsers: "Zablokowani użytkownicy" noUsers: "Brak użytkowników" editProfile: "Edytuj profil" noteDeleteConfirm: "Czy na pewno chcesz usunąć ten wpis?" -pinLimitExceeded: "Nie możesz przypiąć więcej wpisów." -intro: "Zakończono instalację Misskey! Utwórz konto administratora." +pinLimitExceeded: "Nie możesz przypiąć więcej wpisów" +intro: "Zakończono instalację Firefish! Utwórz konto administratora." done: "Gotowe" processing: "Przetwarzanie" preview: "Podgląd" @@ -212,9 +226,9 @@ all: "Wszystkie" subscribing: "Subskrybowanie" publishing: "Publikowanie" notResponding: "Nie odpowiada" -instanceFollowing: "Obserwowani na instancji" -instanceFollowers: "Obserwujący na instancji" -instanceUsers: "Użytkownicy tej instancji" +instanceFollowing: "Obserwowani na serwerze" +instanceFollowers: "Obserwujący na serwerze" +instanceUsers: "Użytkownicy tego serwera" changePassword: "Zmień hasło" security: "Bezpieczeństwo" retypedNotMatch: "Wejście nie zgadza się." @@ -253,7 +267,8 @@ agreeTo: "Wyrażam zgodę na {0}" tos: "Regulamin" start: "Rozpocznij" home: "Strona główna" -remoteUserCaution: "Te informacje mogą nie być aktualne, ponieważ użytkownik pochodzi ze zdalnej instancji." +remoteUserCaution: "Te informacje mogą nie być aktualne, ponieważ użytkownik pochodzi + ze zdalnej instancji." activity: "Aktywność" images: "Zdjęcia" birthday: "Data urodzenia" @@ -286,7 +301,8 @@ unableToDelete: "Nie można usunąć" inputNewFileName: "Wprowadź nową nazwę pliku" inputNewDescription: "Proszę wpisać nowy napis" inputNewFolderName: "Wprowadź nową nazwę katalogu" -circularReferenceFolder: "Katalog docelowy jest podkatalogiem katalogu, który chcesz przenieść." +circularReferenceFolder: "Katalog docelowy jest podkatalogiem katalogu, który chcesz + przenieść." hasChildFilesOrFolders: "Ponieważ ten katalog nie jest pusty, nie może być usunięty." copyUrl: "Skopiuj adres URL" rename: "Zmień nazwę" @@ -294,7 +310,7 @@ avatar: "Awatar" banner: "Baner" nsfw: "NSFW" whenServerDisconnected: "Po utracie połączenia z serwerem" -disconnectedFromServer: "Utracono połączenie z serwerem." +disconnectedFromServer: "Utracono połączenie z serwerem" reload: "Odśwież" doNothing: "Ignoruj" reloadConfirm: "Czy chcesz odświeżyć oś czasu?" @@ -303,8 +319,8 @@ unwatch: "Przestań śledzić" accept: "Akceptuj" reject: "Odrzuć" normal: "Normalny" -instanceName: "Nazwa instancji" -instanceDescription: "Opis instancji" +instanceName: "Nazwa serwera" +instanceDescription: "Opis serwera" maintainerName: "Administrator" maintainerEmail: "E-mail administratora" tosUrl: "Adres URL regulaminu" @@ -315,12 +331,13 @@ dayX: "{day}" monthX: "{month}" yearX: "{year}" pages: "Strony" -integration: "Integracja" +integration: "Integracje" connectService: "Połącz" disconnectService: "Rozłącz" enableLocalTimeline: "Włącz lokalną oś czasu" enableGlobalTimeline: "Włącz globalną oś czasu" -disablingTimelinesInfo: "Administratorzy i moderatorzy będą zawsze mieć dostęp do wszystkich osi czasu, nawet gdy są one wyłączone." +disablingTimelinesInfo: "Administratorzy i moderatorzy będą zawsze mieć dostęp do + wszystkich osi czasu, nawet gdy są one wyłączone." registration: "Zarejestruj się" enableRegistration: "Włącz rejestrację nowych użytkowników" invite: "Zaproś" @@ -332,9 +349,11 @@ bannerUrl: "Adres URL banera" backgroundImageUrl: "Adres URL tła" basicInfo: "Podstawowe informacje" pinnedUsers: "Przypięty użytkownik" -pinnedUsersDescription: "Wypisz po jednej nazwie użytkownika w wierszu. Podani użytkownicy zostaną przypięci pod kartą „Eksploruj”." +pinnedUsersDescription: "Wypisz po jednej nazwie użytkownika w wierszu. Podani użytkownicy + zostaną przypięci pod kartą „Eksploruj”." pinnedPages: "Przypięte strony" -pinnedPagesDescription: "Wprowadź ścieżki stron które chcesz przypiąć na głównej stronie instancji, oddzielone znakiem nowego wiersza." +pinnedPagesDescription: "Wprowadź ścieżki stron, które chcesz przypiąć do górnej strony + tego serwera, oddzielając je znakami końca wiersza." pinnedClipId: "ID przypiętego klipu" pinnedNotes: "Przypięty wpis" hcaptcha: "hCaptcha" @@ -345,17 +364,19 @@ recaptcha: "reCAPTCHA" enableRecaptcha: "Włącz reCAPTCHA" recaptchaSiteKey: "Klucz strony" recaptchaSecretKey: "Tajny klucz" -avoidMultiCaptchaConfirm: "Używanie wielu Captchy może spowodować zakłócenia. Czy chcesz wyłączyć inną Captchę? Możesz zostawić wiele jednocześnie, klikając Anuluj." +avoidMultiCaptchaConfirm: "Używanie wielu Captchy może spowodować zakłócenia. Czy + chcesz wyłączyć inną Captchę? Możesz zostawić wiele jednocześnie, klikając Anuluj." antennas: "Anteny" -manageAntennas: "Zarządzaj Antenami" +manageAntennas: "Zarządzaj antenami" name: "Nazwa" -antennaSource: "Źródło Anteny" +antennaSource: "Źródło anteny" antennaKeywords: "Słowa kluczowe do obserwacji" antennaExcludeKeywords: "Wykluczone słowa kluczowe" -antennaKeywordsDescription: "Oddziel spacjami dla warunku AND, albo wymuś koniec linii dla warunku OR" +antennaKeywordsDescription: "Oddziel spacjami dla warunku AND, albo wymuś koniec linii + dla warunku OR." notifyAntenna: "Powiadamiaj o nowych wpisach" withFileAntenna: "Filtruj tylko wpisy z załączonym plikiem" -enableServiceworker: "Włącz ServiceWorker" +enableServiceworker: "Włącz powiadomienia push dla twojej przeglądarki" antennaUsersDescription: "Wypisz po jednej nazwie użytkownika w linii" caseSensitive: "Wielkość liter ma znaczenie" withReplies: "Uwzględnij odpowiedzi" @@ -375,7 +396,7 @@ exploreFediverse: "Eksploruj Fediwersum" popularTags: "Tagi na czasie" userList: "Listy" about: "Informacje" -aboutMisskey: "O Misskey" +aboutFirefish: "O Firefish" administrator: "Admin" token: "Token" twoStepAuthentication: "Uwierzytelnianie dwuskładnikowe" @@ -402,7 +423,7 @@ markAsReadAllTalkMessages: "Oznacz wszystkie wiadomości jako przeczytane" help: "Pomoc" inputMessageHere: "Wprowadź wiadomość tutaj" close: "Zamknij" -group: "Grupy" +group: "Grupa" groups: "Grupy" createGroup: "Utwórz grupę" ownedGroups: "Posiadane grupy" @@ -428,10 +449,10 @@ onlyOneFileCanBeAttached: "Możesz załączyć tylko jeden plik do wiadomości" signinRequired: "Proszę się zalogować" invitations: "Zaproś" invitationCode: "Kod zaproszenia" -checking: "Sprawdzam" +checking: "Sprawdzam..." available: "Dostępna" unavailable: "Niedostępna" -usernameInvalidFormat: "może zawierać litery, cyfry i podkreślniki." +usernameInvalidFormat: "Nazwa użytkownika może zawierać litery, cyfry i podkreślniki." tooShort: "Zbyt krótka" tooLong: "Zbyt długa" weakPassword: "Słabe hasło" @@ -440,7 +461,8 @@ strongPassword: "Silne hasło" passwordMatched: "Pasuje" passwordNotMatched: "Hasła nie pasują do siebie" signinWith: "Zaloguj się z {x}" -signinFailed: "Nie udało się zalogować. Wprowadzona nazwa użytkownika lub hasło są nieprawidłowe." +signinFailed: "Nie udało się zalogować. Wprowadzona nazwa użytkownika lub hasło są + nieprawidłowe." tapSecurityKey: "Wybierz swój klucz bezpieczeństwa" or: "Lub" language: "Język" @@ -486,13 +508,18 @@ objectStorageBucketDesc: "Podaj nazwę „wiadra” używaną przez konfigurowan objectStoragePrefix: "Prefiks" objectStoragePrefixDesc: "Pliki będą przechowywane w katalogu z tym prefiksem." objectStorageEndpoint: "Punkt końcowy" -objectStorageEndpointDesc: "Pozostaw puste jeżeli używasz AWS S3, w innym wypadku określ punkt końcowy jako '' lub ':' zgodnie z instrukcjami usługi, której używasz." +objectStorageEndpointDesc: "Pozostaw puste jeżeli używasz AWS S3, w innym wypadku + określ punkt końcowy jako '' lub ':' zgodnie z instrukcjami usługi, + której używasz." objectStorageRegion: "Region" -objectStorageRegionDesc: "Określ region, np. 'xx-east-1'. Jeżeli usługa której używasz nie zawiera rozróżnienia regionów, pozostaw to pustym lub wprowadź 'us-east-1'." +objectStorageRegionDesc: "Określ region, np. 'xx-east-1'. Jeżeli usługa której używasz + nie zawiera rozróżnienia regionów, pozostaw to pustym lub wprowadź 'us-east-1'." objectStorageUseSSL: "Użyj SSL" -objectStorageUseSSLDesc: "Wyłącz, jeżeli nie zamierzasz używać HTTPS dla połączenia z API" +objectStorageUseSSLDesc: "Wyłącz, jeżeli nie zamierzasz używać HTTPS dla połączenia + z API" objectStorageUseProxy: "Połącz przez proxy" -objectStorageUseProxyDesc: "Wyłącz, jeżeli nie zamierzasz używać proxy dla połączenia z pamięcią blokową" +objectStorageUseProxyDesc: "Wyłącz, jeżeli nie zamierzasz używać proxy dla połączenia + z pamięcią blokową" serverLogs: "Dziennik zdarzeń" deleteAll: "Usuń wszystkie" showFixedPostForm: "Wyświetlaj formularz tworzenia wpisu w górnej części osi czasu" @@ -506,7 +533,7 @@ volume: "Głośność" masterVolume: "Głośność główna" details: "Szczegóły" chooseEmoji: "Wybierz emoji" -unableToProcess: "Nie udało się dokończyć działania." +unableToProcess: "Nie udało się dokończyć działania" recentUsed: "Ostatnio używane" install: "Zainstaluj" uninstall: "Odinstaluj" @@ -519,18 +546,22 @@ sort: "Sortuj" ascendingOrder: "Rosnąco" descendingOrder: "Malejąco" scratchpad: "Brudnopis" -scratchpadDescription: "Brudnopis zawiera eksperymentalne środowisko dla AiScript. Możesz pisać, wykonywać i sprawdzać wyniki w interakcji z Misskey." +scratchpadDescription: "Brudnopis to środowisko dla eksperymentów z AiScript. Możesz + pisać, wykonywać i sprawdzać wyniki interakcji skryptu z Firefish." output: "Wyjście" script: "Skrypt" disablePagesScript: "Wyłącz AiScript na Stronach" updateRemoteUser: "Aktualizuj zdalne dane o użytkowniku" deleteAllFiles: "Usuń wszystkie pliki" deleteAllFilesConfirm: "Czy na pewno chcesz usunąć wszystkie pliki?" -removeAllFollowingDescription: "Przestań obserwować wszystkie konta z {host}. Wykonaj to, jeżeli instancja już nie istnieje." +removeAllFollowingDescription: "Wykonanie tego polecenia spowoduje usunięcie wszystkich + kont z {host}. Zrób to, jeśli serwer np. już nie istnieje." userSuspended: "To konto zostało zawieszone." userSilenced: "Ten użytkownik został wyciszony." yourAccountSuspendedTitle: "To konto jest zawieszone" -yourAccountSuspendedDescription: "To konto zostało zawieszone z powodu złamania regulaminu serwera lub innych podobnych. Skontaktuj się z administratorem, jeśli chciałbyś poznać bardziej szczegółowy powód. Proszę nie zakładać nowego konta." +yourAccountSuspendedDescription: "To konto zostało zawieszone z powodu złamania regulaminu + serwera lub innych podobnych. Skontaktuj się z administratorem, jeśli chciałbyś + poznać bardziej szczegółowy powód. Proszę nie zakładać nowego konta." menu: "Menu" divider: "Rozdzielacz" addItem: "Dodaj element" @@ -569,12 +600,14 @@ permission: "Uprawnienia" enableAll: "Włącz wszystko" disableAll: "Wyłącz wszystko" tokenRequested: "Przydziel dostęp do konta" -pluginTokenRequestedDescription: "Ta wtyczka będzie mogła korzystać z ustawionych tu uprawnień." +pluginTokenRequestedDescription: "Ta wtyczka będzie mogła korzystać z ustawionych + tu uprawnień." notificationType: "Rodzaj powiadomień" edit: "Edytuj" emailServer: "Serwer poczty e-mail" enableEmail: "Włącz dostarczanie wiadomości e-mail" -emailConfigInfo: "Wykorzystywany do potwierdzenia adresu e-mail w trakcie rejestracji, lub gdy zapomnisz hasła" +emailConfigInfo: "Wykorzystywany do potwierdzenia adresu e-mail w trakcie rejestracji, + lub gdy zapomnisz hasła" email: "Adres e-mail" emailAddress: "Adres e-mail" smtpConfig: "Konfiguracja serwera SMTP" @@ -582,12 +615,13 @@ smtpHost: "Host" smtpPort: "Port" smtpUser: "Nazwa użytkownika" smtpPass: "Hasło" -emptyToDisableSmtpAuth: "Pozostaw adres e-mail i hasło puste, aby wyłączyć weryfikację SMTP" +emptyToDisableSmtpAuth: "Pozostaw adres e-mail i hasło puste, aby wyłączyć weryfikację + SMTP" smtpSecureInfo: "Wyłącz, jeżeli używasz STARTTLS" testEmail: "Przetestuj dostarczanie wiadomości e-mail" wordMute: "Wyciszenie słowa" -instanceMute: "Wyciszone instancje" -userSaysSomething: "{name} powiedział(-a) coś" +instanceMute: "Wyciszenie serwera" +userSaysSomething: "{name} powiedział* coś" makeActive: "Aktywuj" display: "Wyświetlanie" copy: "Kopiuj" @@ -599,12 +633,14 @@ database: "Baza danych" channel: "Kanały" create: "Utwórz" notificationSetting: "Ustawienia powiadomień" -notificationSettingDesc: "Wybierz rodzaj powiadomień do wyświetlania" +notificationSettingDesc: "Wybierz rodzaj powiadomień do wyświetlania." useGlobalSetting: "Użyj globalnych ustawień" -useGlobalSettingDesc: "Jeżeli włączone, zostaną wykorzystane ustawienia powiadomień Twojego konta. Jeżeli wyłączone, mogą zostać wykonane oddzielne konfiguracje." +useGlobalSettingDesc: "Jeżeli włączone, zostaną wykorzystane ustawienia powiadomień + Twojego konta. Jeżeli wyłączone, mogą zostać wykonane oddzielne konfiguracje." other: "Inne" regenerateLoginToken: "Generuj token logowania ponownie" -regenerateLoginTokenDescription: "Regeneruje token używany wewnętrznie podczas logowania. Zazwyczaj nie jest to konieczne. Po regeneracji wszystkie urządzenia zostaną wylogowane." +regenerateLoginTokenDescription: "Regeneruje token używany wewnętrznie podczas logowania. + Zazwyczaj nie jest to konieczne. Po regeneracji wszystkie urządzenia zostaną wylogowane." setMultipleBySeparatingWithSpace: "Możesz ustawić wiele, oddzielając je spacjami." fileIdOrUrl: "ID pliku albo URL" behavior: "Zachowanie" @@ -612,38 +648,41 @@ sample: "Przykład" abuseReports: "Zgłoszenia" reportAbuse: "Zgłoś" reportAbuseOf: "Zgłoś {name}" -fillAbuseReportDescription: "Wypełnij szczegóły zgłoszenia. Jeżeli dotyczy ono określonego wpisu, uwzględnij jego adres URL." +fillAbuseReportDescription: "Wypełnij szczegóły zgłoszenia. Jeżeli dotyczy ono określonego + wpisu, uwzględnij jego adres URL." abuseReported: "Twoje zgłoszenie zostało wysłane. Dziękujemy." -reporteeOrigin: "Pochodzenie zgłoszonego" -reporterOrigin: "Pochodzenie zgłaszającego" -forwardReport: "Przekaż zgłoszenie do innej instancji" +reporteeOrigin: "Pochodzenie osoby zgłoszonej" +reporterOrigin: "Pochodzenie osoby zgłaszającej" +forwardReport: "Przekaż zgłoszenie do zdalnego serwera" send: "Wyślij" abuseMarkAsResolved: "Oznacz zgłoszenie jako rozwiązane" openInNewTab: "Otwórz w nowej karcie" openInSideView: "Otwórz w bocznym widoku" defaultNavigationBehaviour: "Domyślne zachowanie nawigacji" editTheseSettingsMayBreakAccount: "Edycja tych ustawień może uszkodzić Twoje konto." -instanceTicker: "Informacje o wpisach instancji" +instanceTicker: "Informacje o wpisach serwera" waitingFor: "Oczekiwanie na {x}" random: "Losowe" system: "System" -switchUi: "Przełącz interfejs użytkownika" +switchUi: "Layout" desktop: "Pulpit" clip: "Klip" createNew: "Utwórz nowy" optional: "Nieobowiązkowe" createNewClip: "Utwórz nowy klip" unclip: "Odczep" -confirmToUnclipAlreadyClippedNote: "Ten wpis jest już częścią klipu \"{name}\". Czy chcesz ją usunąć z tego klipu?" +confirmToUnclipAlreadyClippedNote: "Ten wpis jest już częścią klipu \"{name}\". Czy + chcesz ją usunąć z tego klipu?" public: "Publiczny" -i18nInfo: "Calckey jest tłumaczone na wiele języków przez wolontariuszy. Możesz pomóc na {link}." +i18nInfo: "Firefish jest tłumaczone na wiele języków przez wolontariuszy. Możesz pomóc + na {link}." manageAccessTokens: "Zarządzaj tokenami dostępu" accountInfo: "Informacje o koncie" notesCount: "Liczba wpisów" repliesCount: "Liczba wysłanych odpowiedzi" -renotesCount: "Liczba wysłanych udostępnień" +renotesCount: "Liczba wysłanych podbić" repliedCount: "Liczba otrzymanych odpowiedzi" -renotedCount: "Liczba otrzymanych udostępnień" +renotedCount: "Liczba otrzymanych podbić" followingCount: "Liczba obserwowanych kont" followersCount: "Liczba obserwujących" sentReactionsCount: "Liczba wysłanych reakcji" @@ -655,15 +694,18 @@ no: "Nie" driveFilesCount: "Liczba plików na dysku" driveUsage: "Użycie przestrzeni dyskowej" noCrawle: "Odrzuć indeksowanie przez crawlery" -noCrawleDescription: "Proś wyszukiwarki internetowe, aby nie indeksowały Twojego profilu, wpisów, stron itd." -lockedAccountInfo: "Dopóki nie ustawisz widoczności wpisu na \"Obserwujący\", twoje wpisy będą mogli widzieć wszyscy, nawet jeśli ustawisz manualne zatwierdzanie obserwujących." +noCrawleDescription: "Proś wyszukiwarki internetowe, aby nie indeksowały Twojego profilu, + wpisów, stron itd." +lockedAccountInfo: "Dopóki nie ustawisz widoczności wpisu na \"Obserwujący\", twoje + wpisy będą mogli widzieć wszyscy, nawet jeśli ustawisz manualne zatwierdzanie obserwujących." alwaysMarkSensitive: "Oznacz domyślnie jako NSFW" loadRawImages: "Wyświetlaj zdjęcia w załącznikach w całości zamiast miniatur" disableShowingAnimatedImages: "Nie odtwarzaj animowanych obrazów" -verificationEmailSent: "Wiadomość weryfikacyjna została wysłana. Odwiedź uwzględniony odnośnik, aby ukończyć weryfikację." +verificationEmailSent: "Wiadomość weryfikacyjna została wysłana. Odwiedź uwzględniony + odnośnik, aby ukończyć weryfikację." notSet: "Nie ustawiono" emailVerified: "Adres e-mail został potwierdzony" -noteFavoritesCount: "Liczba polubionych wpisów" +noteFavoritesCount: "Liczba zakładek" pageLikesCount: "Liczba otrzymanych polubień stron" pageLikedCount: "Liczba polubionych stron" contact: "Kontakt" @@ -672,15 +714,17 @@ clips: "Klipy" experimentalFeatures: "Eksperymentalne funkcje" developer: "Programista" makeExplorable: "Pokazuj konto na stronie „Eksploruj”" -makeExplorableDescription: "Jeżeli wyłączysz tę opcję, Twoje konto nie będzie wyświetlać się w sekcji „Eksploruj”." -showGapBetweenNotesInTimeline: "Pokazuj odstęp między wpisami na osi czasu." +makeExplorableDescription: "Jeżeli wyłączysz tę opcję, Twoje konto nie będzie wyświetlać + się w sekcji „Eksploruj”." +showGapBetweenNotesInTimeline: "Pokazuj odstęp między wpisami na osi czasu" duplicate: "Duplikuj" left: "Lewo" -center: "Wyśsrodkuj" +center: "Wyśrodkuj" wide: "Szerokie" narrow: "Wąskie" -reloadToApplySetting: "To ustawienie zostanie zastosowane po odświeżeniu strony. Chcesz odświeżyć?" -needReloadToApply: "To ustawienie zostanie zastosowane po odświeżeniu strony" +reloadToApplySetting: "To ustawienie zostanie zastosowane po odświeżeniu strony. Chcesz + odświeżyć?" +needReloadToApply: "To ustawienie zostanie zastosowane po odświeżeniu strony." showTitlebar: "Pokazuj pasek tytułowy" clearCache: "Wyczyść pamięć podręczną" onlineUsersCount: "{n} osób jest online" @@ -710,12 +754,12 @@ capacity: "Pojemność" inUse: "Użyto" editCode: "Edytuj kod" apply: "Zastosuj" -receiveAnnouncementFromInstance: "Otrzymuj powiadomienia e-mail z tej instancji" +receiveAnnouncementFromInstance: "Otrzymuj powiadomienia e-mail z tego serwera" emailNotification: "Powiadomienia e-mail" publish: "Publikuj" inChannelSearch: "Szukaj na kanale" useReactionPickerForContextMenu: "Otwórz wybornik reakcji prawym kliknięciem" -typingUsers: "{users} pisze" +typingUsers: "{users} pisze/ą" jumpToSpecifiedDate: "Przejdź do określonej daty" showingPastTimeline: "Obecnie wyświetla starą oś czasu" clear: "Wróć" @@ -725,20 +769,23 @@ unlikeConfirm: "Na pewno chcesz usunąć polubienie?" fullView: "Pełny widok" quitFullView: "Opuść pełny widok" addDescription: "Dodaj opis" -userPagePinTip: "Możesz wyświetlać wpisy w tym miejscu po wybraniu \"Przypnij do profilu\" z menu pojedyńczego wpisu" -notSpecifiedMentionWarning: "Ten wpis zawiera wzmianki o użytkownikach niezawartych jako odbiorcy" +userPagePinTip: "Możesz wyświetlać wpisy w tym miejscu po wybraniu \"Przypnij do profilu\"\ + \ z menu pojedynczego wpisu." +notSpecifiedMentionWarning: "Ten wpis zawiera wzmianki o użytkownikach niezawartych + jako odbiorcy" info: "Informacje" userInfo: "Informacje o użykowniku" unknown: "Nieznane" onlineStatus: "Status online" hideOnlineStatus: "Ukryj status online" -hideOnlineStatusDescription: "Ukrywanie statusu online ogranicza wygody niektórych funkcji, tj. wyszukiwanie" +hideOnlineStatusDescription: "Ukrywanie statusu online ogranicza wygody niektórych + funkcji, takich jak wyszukiwanie." online: "Online" active: "Aktywny" offline: "Offline" notRecommended: "Nie zalecane" botProtection: "Zabezpieczenie przed botami" -instanceBlocking: "Zablokowane instancje" +instanceBlocking: "Zarządzanie federacją" selectAccount: "Wybierz konto" switchAccount: "Przełącz konto" enabled: "Właczono" @@ -763,27 +810,30 @@ priority: "Priorytet" high: "Wysoki" middle: "Średnie" low: "Niski" -emailNotConfiguredWarning: "Nie podano adresu e-mail" +emailNotConfiguredWarning: "Nie podano adresu e-mail." ratio: "Stosunek" previewNoteText: "Pokaż podgląd" customCss: "Własny CSS" -customCssWarn: "Używaj tego ustawienia tylko wtedy, gdy wiesz co ono robi. Nieprawidłowe wpisy mogą spowodować, że klient przestanie działać poprawnie." +customCssWarn: "Używaj tego ustawienia tylko wtedy, gdy wiesz co ono robi. Nieprawidłowe + wpisy mogą spowodować, że klient przestanie działać poprawnie." global: "Globalna" squareAvatars: "Wyświetlaj kwadratowe awatary" -sent: "Wyślij" +sent: "Wysłane" received: "Otrzymane" searchResult: "Wyniki wyszukiwania" hashtags: "Hashtag" troubleshooting: "Rozwiązywanie problemów" useBlurEffect: "Użyj efektów rozmycia w UI" learnMore: "Dowiedz się więcej" -misskeyUpdated: "Misskey zostało zaktualizowane!" +misskeyUpdated: "Firefish zostało zaktualizowane!" whatIsNew: "Pokaż zmiany" translate: "Przetłumacz" translatedFrom: "Przetłumaczone z {x}" accountDeletionInProgress: "Trwa usuwanie konta" -usernameInfo: "Nazwa, która identyfikuje Twoje konto spośród innych na tym serwerze. Możesz użyć alfabetu (a~z, A~Z), cyfr (0~9) lub podkreślników (_). Nazwy użytkownika nie mogą być później zmieniane." -aiChanMode: "Tryb Ai" +usernameInfo: "Nazwa, która identyfikuje Twoje konto spośród innych na tym serwerze.\ + \ Możesz użyć alfabetu (a~z, A~Z), cyfr (0~9) lub podkreślników (_). Nazwy użytkownika + nie mogą być później zmieniane." +aiChanMode: "Ai-chan w klasycznym interfejsie" keepCw: "Zostaw ostrzeżenia o zawartości" pubSub: "Konta Pub/Sub" resolved: "Rozwiązane" @@ -795,21 +845,24 @@ unread: "Nieodczytane" filter: "Filtr" controlPanel: "Panel sterowania" manageAccounts: "Zarządzaj kontami" -makeReactionsPublic: "Ustawić historię reakcji jako publiczną" -makeReactionsPublicDescription: "To spowoduje, że lista wszystkich Twoich dotychczasowych reakcji będzie publicznie widoczna." -classic: "Klasyczny" +makeReactionsPublic: "Ustaw historię reakcji jako publiczną" +makeReactionsPublicDescription: "To spowoduje, że lista wszystkich Twoich dotychczasowych + reakcji będzie publicznie widoczna." +classic: "Wyśrodkowany" muteThread: "Wycisz wątek" unmuteThread: "Wyłącz wyciszenie wątku" ffVisibility: "Widoczność obserwowanych/obserwujących" -ffVisibilityDescription: "Pozwala skonfigurować, kto może zobaczyć, kogo obserwujesz i kto Cię obserwuje." -continueThread: "Pokaż kontynuację wątku" +ffVisibilityDescription: "Pozwala skonfigurować, kto może zobaczyć, kogo obserwujesz + i kto Cię obserwuje." +continueThread: "Kontynuuj wątek" deleteAccountConfirm: "Spowoduje to nieodwracalne usunięcie Twojego konta. Kontynuować?" incorrectPassword: "Nieprawidłowe hasło." voteConfirm: "Potwierdzić swój głos na \"{choice}\"?" hide: "Ukryj" leaveGroup: "Opuść grupę" leaveGroupConfirm: "Czy na pewno chcesz opuścić \"{name}\"?" -useDrawerReactionPickerForMobile: "Wyświetlaj wybornik reakcji jako szufladę na urządzeniach mobilnych" +useDrawerReactionPickerForMobile: "Wyświetlaj wybornik reakcji jako szufladę na urządzeniach + mobilnych" welcomeBackWithName: "Witaj z powrotem, {name}" clickToFinishEmailVerification: "Kliknij [{ok}], aby zakończyć weryfikację e-mail." overridedDeviceKind: "Typ urządzenia" @@ -819,7 +872,7 @@ auto: "Automatycznie" size: "Rozmiar" numberOfColumn: "Liczba kolumn" searchByGoogle: "Szukaj" -indefinitely: "Nigdy" +indefinitely: "Dożywotnio" file: "Pliki" logoutConfirm: "Czy na pewno chcesz się wylogować?" lastActiveDate: "Ostatnio użyte w" @@ -830,19 +883,34 @@ colored: "Kolorowe" label: "Etykieta" type: "Typ" speed: "Prędkość" -localOnly: "Lokalne tylko" +localOnly: "Tylko lokalne" failedToUpload: "Przesyłanie nie powiodło się" -cannotUploadBecauseInappropriate: "Nie można przesłać tego pliku, ponieważ jego części zostały wykryte jako potencjalnie nieodpowiednie." -cannotUploadBecauseNoFreeSpace: "Przesyłanie nie powiodło się z powodu braku miejsca na dysku." +cannotUploadBecauseInappropriate: "Nie można przesłać tego pliku, ponieważ jego części + zostały wykryte jako potencjalnie nieodpowiednie." +cannotUploadBecauseNoFreeSpace: "Przesyłanie nie powiodło się z powodu braku miejsca + na dysku." beta: "Beta" enableAutoSensitive: "Automatyczne oznaczanie NSFW" -enableAutoSensitiveDescription: "Umożliwia automatyczne wykrywanie i oznaczanie zawartości NSFW za pomocą uczenia maszynowego. Nawet jeśli ta opcja jest wyłączona, może być włączona w całej instancji." +enableAutoSensitiveDescription: "Umożliwia automatyczne wykrywanie i oznaczanie zawartości + NSFW za pomocą uczenia maszynowego tam, gdzie to możliwe. Nawet jeśli ta opcja jest + wyłączona, może być włączona na całym serwerze." navbar: "Pasek nawigacyjny" account: "Konta" move: "Przenieś" _sensitiveMediaDetection: - description: "Zmniejsza wysiłek związany z moderacją serwera dzięki automatycznemu rozpoznawaniu zawartości NSFW za pomocą uczenia maszynowego. To nieznacznie zwiększy obciążenie serwera." + description: "Zmniejsza wysiłek związany z moderacją serwera dzięki automatycznemu + rozpoznawaniu zawartości NSFW za pomocą uczenia maszynowego. To nieznacznie zwiększy + obciążenie serwera." setSensitiveFlagAutomatically: "Oznacz jako NSFW" + sensitivity: Czułość wykrywania + analyzeVideosDescription: Analizuje filmy, w dodatku do zdjęć. Zwiększy to nieznacznie + zużycie serwera. + sensitivityDescription: Zmniejszenie czułości doprowadzi do mniejszej liczby błędnych + wykryć (fałszywie pozytywnych), podczas gdy zwiększenie czułości doprowadzi do + mniejszej liczby brakujących wykryć (fałszywie negatywnych). + setSensitiveFlagAutomaticallyDescription: Wyniki wykrywania wewnętrznego zostaną + zachowane, nawet jeśli ta opcja jest wyłączona. + analyzeVideos: Włącz analizę filmów _emailUnavailable: used: "Ten adres e-mail jest już używany" format: "Format tego adresu e-mail jest nieprawidłowy" @@ -856,11 +924,15 @@ _ffVisibility: _signup: almostThere: "Prawie na miejscu" emailAddressInfo: "Podaj swój adres e-mail. Nie zostanie on upubliczniony." - emailSent: "E-mail z potwierdzeniem został wysłany na Twój adres e-mail ({email}). Kliknij dołączony link, aby dokończyć tworzenie konta." + emailSent: "E-mail z potwierdzeniem został wysłany na Twój adres e-mail ({email}). + Kliknij dołączony link, aby dokończyć tworzenie konta." _accountDelete: accountDelete: "Usuń konto" - mayTakeTime: "Ponieważ usuwanie konta jest procesem wymagającym dużej ilości zasobów, jego ukończenie może zająć trochę czasu, w zależności od ilości utworzonej zawartości i liczby przesłanych plików." - sendEmail: "Po zakończeniu usuwania konta na adres e-mail zarejestrowany na tym koncie zostanie wysłana wiadomość e-mail." + mayTakeTime: "Ponieważ usuwanie konta jest procesem wymagającym dużej ilości zasobów, + jego ukończenie może zająć trochę czasu, w zależności od ilości utworzonej zawartości + i liczby przesłanych plików." + sendEmail: "Po zakończeniu usuwania konta na adres e-mail zarejestrowany na tym + koncie zostanie wysłana wiadomość e-mail." requestAccountDelete: "Poproś o usunięcie konta" started: "Usuwanie się rozpoczęło." inProgress: "Usuwanie jest obecnie w toku" @@ -868,9 +940,12 @@ _ad: back: "Wróć" reduceFrequencyOfThisAd: "Pokazuj tę reklamę rzadziej" _forgotPassword: - enterEmail: "Wpisz adres e-mail użyty do rejestracji. Zostanie do niego wysłany link, za pomocą którego możesz zresetować hasło." - ifNoEmail: "Jeżeli nie podano adresu e-mail podczas rejestracji, skontaktuj się z administratorem zamiast tego." - contactAdmin: "Jeżeli Twoja instancja nie obsługuje adresów e-mail, skontaktuj się zamiast tego z administratorem, aby zresetować hasło." + enterEmail: "Wpisz adres e-mail użyty do rejestracji. Zostanie do niego wysłany + link, za pomocą którego możesz zresetować hasło." + ifNoEmail: "Jeśli nie użyłeś adresu e-mail podczas rejestracji, skontaktuj się z + administratorem serwera." + contactAdmin: "Ten serwer nie obsługuje adresów e-mail, zamiast tego skontaktuj + się z administratorem serwera, aby zresetować hasło." _gallery: my: "Moja galeria" liked: "Polubione wpisy" @@ -878,7 +953,7 @@ _gallery: unlike: "Cofnij polubienie" _email: _follow: - title: "Zaobserwował(a) Cię" + title: "Zaobserwował* Cię" _receiveFollowRequest: title: "Otrzymano prośbę o możliwość obserwacji" _plugin: @@ -893,29 +968,35 @@ _preferencesBackups: save: "Zapisz zmiany" inputName: "Proszę podać nazwę dla tej kopii zapasowej" cannotSave: "Zapisanie nie powiodło się" - nameAlreadyExists: "Kopia zapasowa o nazwie \"{name}\" już istnieje. Proszę podać inną nazwę." - applyConfirm: "Czy na pewno chcesz zastosować kopię zapasową \"{name}\" na tym urządzeniu? Istniejące ustawienia tego urządzenia zostaną nadpisane." + nameAlreadyExists: "Kopia zapasowa o nazwie \"{name}\" już istnieje. Proszę podać + inną nazwę." + applyConfirm: "Czy na pewno chcesz zastosować kopię zapasową \"{name}\" na tym urządzeniu? + Istniejące ustawienia tego urządzenia zostaną nadpisane." saveConfirm: "Zapisać kopię zapasową jako {name}?" deleteConfirm: "Usunąć kopię zapasową {name}?" renameConfirm: "Zmienić nazwę kopii zapasowej z \"{old}\" na \"{new}\"?" - createdAt: "Utworzony w: {date} {time}" + createdAt: "Utworzono w: {date} {time}" updatedAt: "Zaktualizowano w: {date} {time}" cannotLoad: "Ładowanie nie powiodło się" invalidFile: "Nieprawidłowy format pliku" + noBackups: Nie znaleziono kopii zapasowych. Możesz utworzyć kopię zapasową twoich + ustawień klienta na tym serwerze poprzez użycie “Utwórz nową kopię zapasową”. _registry: scope: "Zakres" key: "Klucz" keys: "Klucz" domain: "Domena" createKey: "Utwórz klucz" -_aboutMisskey: - about: "Misskey jest oprogramowanie open source rozwijanym przez syuilo od 2014." +_aboutFirefish: + about: "Firefish jest forkiem Misskey utworzonym przez ThatOneCalculator, rozwijanym + od 2022." contributors: "Główni twórcy" allContributors: "Wszyscy twórcy" source: "Kod źródłowy" - translation: "Tłumacz Misskey" - donate: "Przekaż darowiznę na Misskey" - morePatrons: "Naprawdę doceniam wsparcie ze strony wielu niewymienionych tu osób. Dziękuję! 🥰" + translation: "Tłumacz Firefish" + donate: "Przekaż darowiznę na Firefish" + morePatrons: "Naprawdę doceniam wsparcie ze strony wielu niewymienionych tu osób. + Dziękuję! 🥰" patrons: "Wspierający" _nsfw: respect: "Ukrywaj media NSFW" @@ -923,14 +1004,17 @@ _nsfw: force: "Ukrywaj wszystkie media" _mfm: cheatSheet: "Ściąga MFM" - intro: "MFM to język składniowy wyjątkowy dla Misskey, który może być użyty w wielu miejscach. Tu znajdziesz listę wszystkich możliwych elementów składni MFM." - dummy: "Misskey rozszerza świat Fediwersum" + intro: "MFM jest językiem składniowym używanym przez m.in. Firefish, forki *key (w + tym Firefish), oraz Akkomę, który może być użyty w wielu miejscach. Tu znajdziesz + listę wszystkich możliwych elementów składni MFM." + dummy: "Firefish rozszerza świat Fediwersum" mention: "Wspomnij" - mentionDescription: "Używając znaku @ i nazwy użytkownika, możesz określić danego użytkownika." + mentionDescription: "Używając znaku @ i nazwy użytkownika, możesz określić danego + użytkownika." hashtag: "Hashtag" hashtagDescription: "Używając kratki i tekstu, możesz określić hashtag." url: "Adres URL" - urlDescription: "Adresy URL mogą być wyświetlane" + urlDescription: "Adresy URL mogą być wyświetlane." link: "Odnośnik" linkDescription: "Określone części tekstu mogą być wyświetlane jako adres URL." bold: "Pogrubienie" @@ -941,19 +1025,21 @@ _mfm: centerDescription: "Wyśrodkowuje zawartość." inlineCode: "Kod (w wierszu)" blockCode: "Kod (blok)" - blockCodeDescription: "Wyświetla kod z podświetlaną składnią składający się z wielu linii." + blockCodeDescription: "Wyświetla kod z podświetlaną składnią składający się z wielu + linii." blockMath: "Matematyka (Blok)" quote: "Cytuj" quoteDescription: "Wyświetla treść jako cytat." emoji: "Niestandardowe emoji" - emojiDescription: "Otaczając nazwę niestandardowego emoji dwukropkami, możesz użyć niestandardowego emoji." + emojiDescription: "Otaczając nazwę niestandardowego emoji dwukropkami, możesz użyć + niestandardowego emoji." search: "Szukaj" searchDescription: "Wyświetla pole wyszukiwania z wcześniej wpisanym tekstem." flip: "Odwróć" flipDescription: "Przerzuca treść poziomo lub pionowo." jelly: "Animacja (Galaretka)" jellyDescription: "Nadaje treści galaretowatą animację." - tada: "Animation (Tada)" + tada: "Animacja (Tada)" tadaDescription: "Nadaje treści animację podobną do \"Tada!\"." jump: "Animacja (Skok)" jumpDescription: "Nadaje treści animację skakania." @@ -970,7 +1056,7 @@ _mfm: x3: "Bardzo duże" x3Description: "Czyni treść jeszcze większą." x4: "Ogromne" - x4Description: "Czyni treść jeszcze większą niż jeszcze większa." + x4Description: "Czyni treść nawet większą niż jeszcze większa." blur: "Rozmycie" blurDescription: "Rozmywa treść. Zostanie wyraźnie wyświetlona po najechaniu." font: "Czcionka" @@ -979,10 +1065,22 @@ _mfm: rainbowDescription: "Sprawia, że zawartość pojawia się w kolorach tęczy." sparkle: "Blask" sparkleDescription: "Nadaje zawartości efekt lśniącego brokatu." - rotate: "Obróć" + rotate: "Obrót" rotateDescription: "Obraca zawartość o określony kąt." plain: "Zwyczajny" plainDescription: "Wyłącza efekty wszystkich MFM zawartych w tym efekcie MFM." + inlineCodeDescription: Wyświetla podświetlanie składni dla kodu (programu) w linii. + inlineMath: Matematyka (Inline) + inlineMathDescription: Pokaż formuły matematyczne (KaTeX) w linii + blockMathDescription: Pokaż wieloliniowe formuły matematyczne (KaTeX) w bloku + background: Kolor tła + backgroundDescription: Zmień kolor tła tekstu. + foregroundDescription: Zmień kolor pierwszoplanowy tekstu. + positionDescription: Przesuń zawartość o określoną wartość. + position: Pozycjonuj + foreground: Kolor pierwszoplanowy + scaleDescription: Skaluj treść o określoną wielkość. + scale: Skaluj _instanceTicker: none: "Nigdy nie pokazuj" remote: "Pokaż dla zdalnych użytkowników" @@ -991,6 +1089,7 @@ _serverDisconnectedBehavior: reload: "Automatycznie odśwież" dialog: "Pokazuj okno ostrzeżenia" quiet: "Pokazuj nieirytujące ostrzeżenia" + nothing: Nic nie rób _channel: create: "Utwórz kanał" edit: "Edytuj kanał" @@ -1001,18 +1100,31 @@ _channel: following: "Śledzeni" usersCount: "{n} uczestnicy" notesCount: "{n} wpisy" + nameAndDescription: Nazwa i opis + nameOnly: Tylko nazwa _menuDisplay: top: "Góra" hide: "Ukryj" + sideFull: Z boku + sideIcon: Z boku (tylko ikony) _wordMute: muteWords: "Słowo do wyciszenia" muteWordsDescription2: "Otocz słowa kluczowe ukośnikami, aby używać wyrażeń regularnych." soft: "Łagodny" hard: "Twardy" mutedNotes: "Wyciszone wpisy" + muteWordsDescription: Rozdzielaj spacją dla kondycji AND, lub przerwaniem wiersza + dla kondycji OR. + softDescription: Ukryj z osi czasu wpisy, które spełniają podane warunki. + hardDescription: Zapobiega dodawania do osi czasu wpisów, które spełniają podane + warunki. Dodatkowo, te wpisy nie zostaną dodane do osi czasu, jeśli warunki się + zmienią. _instanceMute: title: "Ukrywa wpisy z wymienionych instancji." heading: "Lista instancji do wyciszenia" + instanceMuteDescription2: Oddzielaj nowymi liniami + instanceMuteDescription: Spowoduje to wyciszenie wszystkich wpisów/podbić z podanych + instancji, w tym tych od użytkowników odpowiadających na wpisy z wyciszonych instancji. _theme: explore: "Przeglądaj motywy" install: "Zainstaluj motyw" @@ -1023,7 +1135,7 @@ _theme: installedThemes: "Zainstalowane motywy" builtinThemes: "Wbudowane motywy" alreadyInstalled: "Motyw jest już zainstalowany" - invalid: "Format motywu jest nieprawidłowy." + invalid: "Format motywu jest nieprawidłowy" make: "Utwórz motyw" base: "Podstawowy" addConstant: "Dodaj stałą" @@ -1041,8 +1153,9 @@ _theme: darken: "Ściemnij" lighten: "Rozjaśnij" inputConstantName: "Wprowadź nazwę stałej" - importInfo: "Jeżeli wprowadzisz tu kod motywu, możesz zaimportować go w edytorze motywu" - deleteConstantConfirm: "Czy na pewno chcesz usunąć stała {const}?" + importInfo: "Jeżeli wprowadzisz tu kod motywu, możesz zaimportować go w edytorze + motywu" + deleteConstantConfirm: "Czy na pewno chcesz usunąć stałą {const}?" keys: accent: "Akcent" bg: "Tło" @@ -1061,7 +1174,7 @@ _theme: hashtag: "Hashtag" mention: "Wspomnij" mentionMe: "Wspomnienia (ja)" - renote: "Udostępnij" + renote: "Podbij" modalBg: "Tło modalu" divider: "Rozdzielacz" scrollbarHandle: "Uchwyt paska przewijania" @@ -1111,66 +1224,95 @@ _time: hour: "godz." day: "dzień" _tutorial: - title: "Jak korzystać z Calckey" + title: "Jak korzystać z Firefish" step1_1: "Witamy!" - step1_2: "Pozwól, że cię skonfigurujemy. Będziesz działać w mgnieniu oka!" - step2_1: "Po pierwsze, proszę wypełnić swój profil" - step2_2: "Podanie kilku informacji o tym, kim jesteś, ułatwi innym stwierdzenie, czy chcą zobaczyć Twoje notatki lub śledzić Cię." - step3_1: "Teraz czas na śledzenie niektórych osób!" - step3_2: "Twoje domowe i społeczne linie czasu opierają się na tym, kogo śledzisz, więc spróbuj śledzić kilka kont, aby zacząć.\nKliknij kółko z plusem w prawym górnym rogu profilu, aby go śledzić." - step4_1: "Pozwól, że się tam dostaniesz." - step4_2: "Dla twojego pierwszego postu, niektórzy ludzie lubią zrobić {introduction} post lub prosty \"Hello world!\"" - step5_1: "Timelines, timelines everywhere!" - step5_2: "Twoja instancja ma włączone {timelines} różne timelines" - step5_3: "Oś czasu Home {icon} to miejsce, w którym możesz zobaczyć posty od swoich zwolenników" - step5_4: "The Local {icon} timeline to miejsce, w którym możesz zobaczyć posty od wszystkich innych osób na tej instancji." - step5_5: "Oś czasu Recommended {icon} to miejsce, gdzie możesz zobaczyć posty z instancji, które admini polecają." - step5_6: "Oś czasu Social {icon} to miejsce, w którym możesz zobaczyć posty od znajomych swoich followersów." - step5_7: "The Global {icon} timeline to miejsce, gdzie możesz zobaczyć posty z każdej innej połączonej instancji." - step6_1: "Więc, co to jest to miejsce?" - step6_2: "Cóż, nie dołączyłeś po prostu do Calckey. Dołączyłeś do portalu do Fediverse, połączonej sieci tysięcy serwerów, zwanych instancjami." - step6_3: "Każdy serwer działa w inny sposób, i nie wszystkie serwery działają z Calckey. Ten jednak działa! Jest to trochę skomplikowane, ale w krótkim czasie załapiesz o co chodzi." + step1_2: "Pozwól, że Cię skonfigurujemy. Będziesz działać w mgnieniu oka!" + step2_1: "Najpierw, proszę wypełnij swój profil." + step2_2: "Podanie kilku informacji o tym, kim jesteś, ułatwi innym stwierdzenie, + czy chcą zobaczyć Twoje wpisy lub śledzić Cię." + step3_1: "Pora znaleźć osoby do śledzenia!" + step3_2: "Twoje domowe i społeczne linie czasu opierają się na tym, kogo śledzisz, + więc spróbuj śledzić kilka kont, aby zacząć.\nKliknij kółko z plusem w prawym + górnym rogu profilu, aby go śledzić." + step4_1: "Pozwól, że zabierzemy Cię tam." + step4_2: "W pierwszym wpisie możesz się przedstawić lub wysłać powitanie - \"Witaj, + świecie!\"" + step5_1: "Osie czasu, wszędzie widzę osie czasu!" + step5_2: "Twoja instancja ma włączone {timelines} różne osie czasu." + step5_3: "Główna {icon} oś czasu to miejsce, w którym możesz zobaczyć posty od użytkowników + których obserwujesz, oraz innych użytkowników z tej instancji. Jeśli wolisz, by + główna oś czasu pokazywała tylko posty od użytkowników których obserwujesz, możesz + łatwo to zmienić w ustawieniach!" + step5_4: "Lokalna {icon} oś czasu to miejsce, w którym możesz zobaczyć posty od + wszystkich innych osób na tej instancji." + step5_5: "Społeczna {icon} oś czasu to miejsce, gdzie możesz zobaczyć posty z instancji, + które admini polecają." + step5_6: "Polecana {icon} oś czasu to miejsce, gdzie możesz zobaczyć posty z instancji, + które admini polecają." + step5_7: "Globalna {icon} oś czasu to miejsce, gdzie możesz zobaczyć posty z każdej + innej połączonej instancji." + step6_1: "Więc, czym to jest to miejsce?" + step6_2: "Cóż, nie dołączył*ś po prostu do Firefish. Dołączył*ś do portalu do Fediverse, + połączonej sieci tysięcy serwerów, zwanych instancjami." + step6_3: "Każdy serwer działa w inny sposób, i nie wszystkie serwery używają Firefish. + Ten jednak używa! Jest to trochę skomplikowane, ale w krótkim czasie załapiesz + o co chodzi." step6_4: "A teraz idź, odkrywaj i baw się dobrze!" _2fa: alreadyRegistered: "Zarejestrowałeś już urządzenie do uwierzytelniania dwuskładnikowego." - registerDevice: "Zarejestruj nowe urządzenie" - registerKey: "Zarejestruj klucz bezpieczeństwa" - step1: "Najpierw, zainstaluj aplikację uwierzytelniającą (taką jak {a} lub {b}) na swoim urządzeniu." + registerTOTP: "Zarejestruj nowe urządzenie" + registerSecurityKey: "Zarejestruj klucz bezpieczeństwa" + step1: "Najpierw, zainstaluj aplikację uwierzytelniającą (taką jak {a} lub {b}) + na swoim urządzeniu." step2: "Następnie, zeskanuje kod QR z ekranu." step3: "Wprowadź token podany w aplikacji, aby ukończyć konfigurację." step4: "Od teraz, przy każdej próbie logowania otrzymasz prośbę o token logowania." + step2Url: 'Możesz też wpisać ten URL jeśli używasz programu komputerowego:' + securityKeyInfo: Oprócz uwierzytelnienia odciskiem palców lub PIN, możesz również + skonfigurować uwierzytelnienie za pomocą kluczy sprzętowych obsługujących FIDO2, + w celu dalszego zabezpieczenia Twojego konta. _permissions: - "read:account": "Wyświetl informacje o swoim koncie" - "write:account": "Edytuj swoje informacje o koncie" - "read:blocks": "Zobacz listę osób, które zablokowałeś(-aś)" - "write:blocks": "Edytuj listę osób, które zablokowałeś(-aś)" - "read:drive": "Dostęp do plików i katalogów ze Twojego dysku" - "write:drive": "Edycja i usuwanie plików i katalogów z Twojego dysku." - "read:favorites": "Wyświetlanie Twojej listy ulubionych." - "write:favorites": "Edycja Twojej listy ulubionych." + "read:account": "Wyświetlanie informacji o twoim koncie" + "write:account": "Edycja informacji o twoim koncie" + "read:blocks": "Wyświetlanie listy zablokowanych użytkowników" + "write:blocks": "Blokowanie i odblokowywanie użytkowników" + "read:drive": "Wyświetlanie plików i folderów z twojego Dysku" + "write:drive": "Edycja i usuwanie plików i katalogów z Twojego dysku" + "read:favorites": "Wyświetlanie Twoich zakładek" + "write:favorites": "Edycja Twoich zakładek" "read:following": "Wyświetlanie informacji o obserwowanych" "write:following": "Obserwowanie lub cofanie obserwacji innych kont" - "read:messaging": "Zobacz swoje czaty" - "read:mutes": "Wyświetlanie listy osób, które wyciszyłeś(-aś)" - "write:mutes": "Edycja listy osób, które wyciszyłeś(-aś)" + "read:messaging": "Wyświetlanie twoich czatów" + "read:mutes": "Wyświetlanie listy wyciszonych osób" + "write:mutes": "Edycja listy wyciszonych osób" "read:notifications": "Wyświetlanie powiadomień" "write:notifications": "Działanie na powiadomieniach" "read:reactions": "Wyświetlanie reakcji" "write:reactions": "Edycja reakcji" "write:votes": "Głosowanie w ankiecie" "read:pages": "Wyświetlanie Twoich stron" - "write:pages": "Edycja lub usuwanie Twoich stron" + "write:pages": "Edycja i usuwanie Twoich stron" "read:page-likes": "Wyświetlanie polubień na stronach" "write:page-likes": "Edycja polubień na stronach" "read:user-groups": "Wyświetlanie grup użytkownika" - "write:user-groups": "Edycja lub usuwanie grup użytkownika" - "read:channels": "Zobacz swoje kanały" - "write:channels": "Edytuj swoje kanały" - "read:gallery": "Zobacz swoją galerię" - "write:gallery": "Edytuj swoją galerię" + "write:user-groups": "Edycja i usuwanie grup użytkownika" + "read:channels": "Wyświetlenie Twoich kanałów" + "write:channels": "Edycja Twoich kanałów" + "read:gallery": "Wyświetlenie Twojej galerii" + "write:gallery": "Edycja Twojej galerii" + "write:messaging": Tworzenie i usuwanie wiadomości czatu + "write:notes": Tworzenie i usuwanie wpisów + "read:gallery-likes": Wyświetlenie Twojej listy z polubionymi postami galerii + "write:gallery-likes": Edycja Twojej listy z polubionymi postami galerii _auth: shareAccess: "Czy chcesz autoryzować „{name}” do dostępu do tego konta?" permissionAsk: "Ta aplikacja wymaga następujących uprawnień:" + denied: Odmowa dostępu + copyAsk: Proszę wpisz następujący kod autoryzacyjny w aplikacji + shareAccessAsk: Czy na pewno chcesz upoważnić tą aplikację do dostępu do Twojego + konta? + pleaseGoBack: Wróć do aplikacji + callback: Wracam do aplikacji _weekday: sunday: "Niedziela" monday: "Poniedziałek" @@ -1201,11 +1343,15 @@ _widgets: serverMetric: "Metryka serwera" aiscript: "Konsola AiScript" aichan: "Ai" + rssTicker: Ticker RSS + userList: Lista użytkowników + _userList: + chooseList: Wybierz listę _cw: hide: "Ukryj" show: "Załaduj więcej" - chars: "{count} znaków" - files: "{count} plików" + chars: "{count} znak(-i/-ów)" + files: "{count} plik(-i/-ów)" _poll: noOnlyOneChoice: "Wymagane są przynajmniej dwie opcje" choiceN: "Opcja {n}" @@ -1230,11 +1376,15 @@ _poll: remainingSeconds: "Pozostało {s} sekund" _visibility: public: "Publiczny" - publicDescription: "Twój wpis pojawi się w publicznych osiach czasu" - home: "Strona główna" + publicDescription: "Wpis pojawi się u wszystkich" + home: "Niewidoczny" followers: "Obserwujący" specified: "Bezpośredni" specifiedDescription: "Napisz tylko określonym użytkownikom" + homeDescription: Wpis będzie publiczny ale nie pojawi się na osi czasu instancji + followersDescription: Wpis pojawi się tylko na osiach czasu Twoich obserwujących + localOnly: Lokalnie + localOnlyDescription: Wpis będzie widoczny tylko dla użytkowników tej instancji _postForm: _placeholders: a: "Co się dzieje?" @@ -1243,6 +1393,9 @@ _postForm: d: "Czy masz coś do powiedzenia?" e: "Zacznij coś pisać…" f: "Czekamy, aż coś napiszesz." + quotePlaceholder: Cytuj ten wpis... + channelPlaceholder: Wyślij na kanał... + replyPlaceholder: Odpowiedz na ten wpis... _profile: name: "Nazwa" username: "Nazwa użytkownika" @@ -1250,47 +1403,65 @@ _profile: youCanIncludeHashtags: "Możesz umieścić hashtagi w swoim opisie." metadata: "Dodatkowe informacje" metadataEdit: "Edytuj dodatkowe informacje" - metadataDescription: "Możesz wyświetlać do czterech sekcji dodatkowych informacji na swoim profilu." + metadataDescription: "Możesz wyświetlać do czterech sekcji dodatkowych informacji + 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" changeBanner: "Zmień baner" + locationDescription: Jeśli wpiszesz z początku swoje miasto, twój czas lokalny będzie + się pokazywać innym użytkownikom. _exportOrImport: allNotes: "Wszystkie wpisy" followingList: "Obserwowani" muteList: "Wycisz" blockingList: "Zablokuj" userLists: "Listy" + excludeMutingUsers: Wyklucz wyciszonych użytkowników + excludeInactiveUsers: Wyklucz nieaktywnych użytkowników _charts: federation: "Federacja" apRequest: "Żądania" usersTotal: "Łącznie # użytkowników" activeUsers: "Aktywni użytkownicy" + storageUsageTotal: Łączne użycie dysku + filesIncDec: Różnica w liczbie plików + filesTotal: Łączna liczba plików + storageUsageIncDec: Różnica w wykorzystaniu miejsca + localNotesIncDec: Różnica w liczbie lokalnych wpisów + remoteNotesIncDec: Różnica w liczbie zdalnych wpisów + notesTotal: Łączna liczba wpisów + usersIncDec: Różnica w liczbie użytkowników + notesIncDec: Różnica w liczbie wpisów _instanceCharts: requests: "Żądania" notesTotal: "Łącznie # wpisów" - ff: "Różnica w # obserwujących" + ff: "Różnica w # obserwujących " ffTotal: "Łączna liczba # obserwujących" cacheSize: "Różnica w rozmiarze pamięci podręcznej" cacheSizeTotal: "Łączny rozmiar pamięci podręcznej" files: "Różnica # plików" filesTotal: "Łącznie # plików" + usersTotal: Łączna liczba użytkowników + users: Różnica w liczbie użytkowników + notes: Różnica w liczbie wpisów _timelines: home: "Strona główna" local: "Lokalne" - social: "Społeczność" + social: "Społeczna" global: "Globalna" + recommended: Polecana _pages: newPage: "Utwórz stronę" editPage: "Edytuj tę stronę" readPage: "Aktywowano widok źródła" - created: "Pomyślnie utworzono stronę!" - updated: "Pomyślnie zaktualizowano stronę!" - deleted: "Strona została usunięta" + created: "Pomyślnie utworzono stronę" + updated: "Pomyślnie zaktualizowano stronę" + deleted: "Pomyślnie usunięto stronę" pageSetting: "Ustawienia strony" nameAlreadyExists: "Określony adres URL strony już istnieje" invalidNameTitle: "Podany adres URL strony jest nieprawidłowy" - invalidNameText: "Sprawdź, czy nie jest puste" + invalidNameText: "Upewnij się, że pole tytułowe strony nie jest puste" editThisPage: "Edytuj tę stronę" viewSource: "Zobacz źródło" viewPage: "Wyświetlanie Twoich stron" @@ -1298,10 +1469,10 @@ _pages: unlike: "Cofnij polubienie" my: "Moje strony" liked: "Polubione strony" - featured: "Wyróżnione" + featured: "Popularne" inspector: "Inspektor" contents: "Zawartość" - content: "Blokada strony" + content: "Blok strony" variables: "Zmienne" title: "Tytuł" url: "URL strony" @@ -1311,8 +1482,8 @@ _pages: font: "Czcionka" fontSerif: "Szeryfowa" fontSansSerif: "Bezszeryfowa" - eyeCatchingImageSet: "Ustaw przyciągające wzrok zdjęcie" - eyeCatchingImageRemove: "Usuń przyciągające wzrok zdjęcie" + eyeCatchingImageSet: "Ustaw miniaturę" + eyeCatchingImageRemove: "Usuń miniaturę" chooseBlock: "Dodaj blok" selectType: "Wybierz typ" enterVariableName: "Wprowadź nazwę dla swojej zmiennej" @@ -1332,12 +1503,14 @@ _pages: post: "Utwórz wpis" _post: text: "Treść" + attachCanvasImage: Załącz obraz płótna + canvasId: ID płótna textInput: "Pole tekstowe" _textInput: name: "Nazwa zmiennej" text: "Tytuł" default: "Domyślna wartość" - textareaInput: "Pole tekstowe na wiele wierszy" + textareaInput: "Wielowierszowe pole tekstowe" _textareaInput: name: "Nazwa zmiennej" text: "Tytuł" @@ -1350,6 +1523,7 @@ _pages: _canvas: width: "Szerokość" height: "Wysokość" + id: ID płótna note: "Osadzony wpis" _note: id: "ID wpisu" @@ -1389,6 +1563,7 @@ _pages: title: "Tytuł" values: "Lista wyborów (oddzielonych znakiem nowego wiersza)" default: "Domyślna wartość" + canvas: Płótno script: categories: flow: "Kontrola przepływu" @@ -1479,7 +1654,8 @@ _pages: if: "Warunek" _if: arg1: "Jeżeli" - arg2: "Jeżeli prawda" + arg2: "Wtedy" + arg3: Inaczej not: "NIE" _not: arg1: "NIE" @@ -1493,14 +1669,14 @@ _pages: randomPick: "Wybierz losowo z listy" _randomPick: arg1: "Listy" - dailyRandom: "Losowo (zostaje na dzień)" + dailyRandom: "Losowo (zmienia się raz dziennie dla każdego użytkownika)" _dailyRandom: arg1: "Prawdopodobieństwo" - dailyRannum: "Losowa liczba (zostaje na dzień)" + dailyRannum: "Losowa liczba (zmienia się raz dziennie dla każdego użytkownika)" _dailyRannum: arg1: "Minimalna wartość" arg2: "Maksymalna wartość" - dailyRandomPick: "Wybierz losowo z listy (zostaje na dzień)" + dailyRandomPick: "Wybierz losowo z listy (zmienia się raz dziennie dla każdegoużytkownika)" _dailyRandomPick: arg1: "Listy" seedRandom: "Losowo (z ziarnem)" @@ -1516,7 +1692,7 @@ _pages: _seedRandomPick: arg1: "Ziarno" arg2: "Listy" - DRPWPM: "Wybierz losowo z ważonej listy (zostaje na dzień)" + DRPWPM: "Wybierz losowo z ważonej listy (zmienia się raz dziennie dla każdegoużytkownika)" pick: "Wybierz z listy" _pick: arg1: "Listy" @@ -1539,53 +1715,69 @@ _pages: fn: "Funkcje" _fn: arg1: "Wyjście" + slots-info: Oddziel każde gniazdo nową linią + slots: Gniazda for: "Powtórzenie" _for: arg1: "Liczba powtórzeń" arg2: "Działanie" + textList: Lista tekstowa + strPick: Wyciągaczka ciągu znaków + strReverse: Odwróć tekst + join: Łączenie tekstu + round: Zaokrąglanie wartości dziesiętnych + _DRPWPM: + arg1: Lista tekstowa types: string: "Tekst" number: "Liczba" boolean: "Flaguj" array: "Listy" + stringArray: Lista tekstowa enviromentVariables: "Zmienna środowiskowa" pageVariables: "Element strony" + argVariables: Gniazda wejściowe + typeError: Gniazdo {slot} akceptuje wartości typu “{expect}”, lecz wprowadzona + wartość jest typu “{actual}”! + thereIsEmptySlot: Gniazdo {slot} jest puste! + emptySlot: Puste gniazdo _relayStatus: requesting: "Oczekujące" accepted: "Zaakceptowano" rejected: "Odrzucono" _notification: fileUploaded: "Pomyślnie wysłano plik" - youGotMention: "{name} wspomniał(a) o Tobie" - youGotReply: "{name} odpowiedział(a) Tobie" - youGotQuote: "{name} zacytował(a) Ciebie" - youRenoted: "{name} udostępnił(a) Twój wpis" - youGotPoll: "{name} zagłosował(a) w Twojej ankiecie" - youGotMessagingMessageFromUser: "{name} wysłał(a) Ci wiadomość" + youGotMention: "{name} wspomniał* o Tobie" + youGotReply: "{name} odpowiedział* Tobie" + youGotQuote: "{name} zacytował* Ciebie" + youRenoted: "{name} podbił* Twój wpis" + youGotPoll: "{name} zagłosował* w Twojej ankiecie" + youGotMessagingMessageFromUser: "{name} wysłał* Ci wiadomość" youGotMessagingMessageFromGroup: "Została wysłana wiadomość do grupy {name}" - youWereFollowed: "Zaobserwował(a) Cię" - youReceivedFollowRequest: "Otrzymałeś(-aś) prośbę o możliwość obserwacji" + youWereFollowed: "Zaobserwował* Cię" + youReceivedFollowRequest: "Otrzymał*ś prośbę o możliwość obserwacji" yourFollowRequestAccepted: "Twoja prośba o możliwość obserwacji została przyjęta" - youWereInvitedToGroup: "Zaproszony(-a) do grupy" + youWereInvitedToGroup: "{userName} zaprosił* Ciebie do grupy" pollEnded: "Wyniki ankiety stały się dostępne" emptyPushNotificationMessage: "Powiadomienia push zostały zaktualizowane" _types: all: "Wszystkie" follow: "Nowi obserwujący" - mention: "Wspomnij" + mention: "Wspomnienia" reply: "Odpowiedzi" - renote: "Udostępnij" - quote: "Cytuj" - reaction: "Reakcja" + renote: "Podbicia" + quote: "Cytaty" + reaction: "Reakcje" pollVote: "Głosy w ankietach" - receiveFollowRequest: "Otrzymano prośbę o możliwość obserwacji" - followRequestAccepted: "Przyjęto prośbę o możliwość obserwacji" - groupInvited: "Zaproszono do grup" - app: "Powiadomienia z aplikacji" + receiveFollowRequest: "Otrzymane prośby o możliwość obserwacji" + followRequestAccepted: "Przyjęte prośby o możliwość obserwacji" + groupInvited: "Zaproszenia do grup" + app: "Powiadomienia z powiązanych aplikacji" + pollEnded: Zakończone ankiety _actions: - followBack: "zaobserwował cię z powrotem" + followBack: "zaobserwował* cię z powrotem" reply: "Odpowiedz" - renote: "Udostępnij" + renote: "Podbicia" _deck: alwaysShowMainColumn: "Zawsze pokazuj główną kolumnę" columnAlign: "Wyrównaj kolumny" @@ -1597,9 +1789,9 @@ _deck: swapDown: "Zamień z poniższym" stackLeft: "Przypnij do lewej" popRight: "Odepnij w prawo" - profile: "Profil" - newProfile: "Nowy profil" - deleteProfile: "Usuń profil" + profile: "Przestrzeń" + newProfile: "Nowa przestrzeń" + deleteProfile: "Usuń przestrzeń" widgetsIntroduction: "Wybierz \"Edytuj widżety\" w menu kolumny i dodaj widżet." _columns: main: "Główna" @@ -1609,4 +1801,232 @@ _deck: antenna: "Anteny" list: "Listy" mentions: "Wspomnienia" - direct: "Bezpośredni" + direct: "Bezpośrednie wiadomości" + introduction2: Kliknij + z prawej strony ekranu, by dodać nowe kolumny kiedy chcesz. + introduction: Utwórz idealny dla siebie interfejs, poprzez dowolne ustawianie kolumn! + renameProfile: Zmień nazwę przestrzeni + nameAlreadyExists: Ta nazwa przestrzeni już istnieje. +accountMoved: 'Użytkownik przeniósł się na nowe konto:' +flagShowTimelineRepliesDescription: Jeśli włączone, pokazuje odpowiedzi użytkowników + na wpisy innych użytkowników na osi czasu. +manageGroups: Zarządzaj grupami +objectStorageSetPublicRead: Ustaw "public-read" podczas wysyłania +removeAllFollowing: Przestań obserwować wszystkich obserwowanych użytkowników +smtpSecure: Użyj implicit SSL/TLS dla połączeń SMTP +secureMode: Tryb bezpieczny (Authorized Fetch) +instanceSecurity: Bezpieczeństwo serwera +privateMode: Tryb prywatny +allowedInstances: Dopuszczone serwery +recommended: Polecane +allowedInstancesDescription: Hosty serwerów, które mają być dopuszczone do federacji, + każdy oddzielony nowym wierszem (dotyczy tylko trybu prywatnego). +seperateRenoteQuote: Oddziel przyciski podbicia i cytowania +refreshInterval: 'Częstotliwość aktualizacji ' +slow: Wolna +_messaging: + dms: Prywatne + groups: Grupy +_antennaSources: + all: Wszystkie wpisy + users: Wpisy od konkretnych użytkowników + homeTimeline: Wpisy od obserwowanych użytkowników + userList: Wpisy od użytkowników z konkretnej listy + userGroup: Wpisy od użytkowników z konkretnej grupy + instances: Wpisy od wszystkich użytkowników na instancji +enableRecommendedTimeline: Włącz polecaną oś czasu +recentNDays: Ostatnie {n} dni +driveCapOverrideCaption: Zresetuj pojemność do domyślnej poprzed wpisanie wartości + 0 lub mniejszej. +requireAdminForView: Musisz zalogować się jako administrator, by to zobaczyć. +replayTutorial: Powtórz samouczek +migration: Migracja +moveTo: Przenieś obecne konto do nowego +moveToLabel: 'Konto na które się przenosisz:' +moveAccount: Przenieś konto! +moveAccountDescription: Ten proces jest nieodwracalny. Upewnij się, że utworzył*ś + alias dla tego konta na nowym koncie, przed rozpoczęciem. Proszę wpisz tag konta + w formacie @osoba@serwer.com +moveFrom: Przejdź ze starego konta na obecne +moveFromLabel: 'Konto które przenosisz:' +showUpdates: Pokaż pop-up po aktualizacji Firefish +swipeOnDesktop: Zezwól na przeciąganie w stylu mobilnym na desktopie +moveFromDescription: To utworzy alias twojego starego konta, w celu umożliwienia migracji + z tamtego konta na to. Zrób to ZANIM rozpoczniesz przenoszenie się z tamtego konta. + Proszę wpisz tag konta w formacie @osoba@serwer.com +migrationConfirm: "Czy jesteś absolutnie pewn* tego, że chcesz przenieść swoje konto + na {account}? Tego działania nie można odwrócić. Nieodwracalnie stracisz możliwość + normalnego korzystania z konta.\nUpewnij się, że to konto zostało ustawione jako + konto z którego się przenosisz." +noThankYou: Nie, dziękuję +addInstance: Dodaj serwer +renoteMute: Wycisz podbicia +renoteUnmute: Odcisz podbicia +flagSpeakAsCat: Mów jak kot +flagSpeakAsCatDescription: Twoje posty zostaną znya-izowane, gdy w trybie kota +selectInstance: Wybierz serwer +noInstances: Brak serwerów +keepOriginalUploadingDescription: Zapisuje oryginalne zdjęcie. Jeśli wyłączone, wersja + do wyświetlania w sieci zostanie wygenerowana podczas wysłania. +antennaInstancesDescription: Wymień jeden host serwera w każdym wierszu +regexpError: Błąd regularnego wyrażenia +regexpErrorDescription: 'Wystąpił błąd w regularnym wyrażeniu znajdującym się w linijce + {line} Twoich {tab} wyciszeń słownych:' +forwardReportIsAnonymous: Zamiast twojego konta, anonimowe konto systemowe będzie + wyświetlane jako zgłaszający na zdalnym serwerze. +breakFollowConfirm: Czy na pewno chcesz usunąć obserwującego? +instanceDefaultThemeDescription: Wpisz kod motywu w formacie obiektowym. +mutePeriod: Długość wyciszenia +tenMinutes: 10 minut +showLocalPosts: 'Pokaż lokalne wpisy w:' +socialTimeline: Społeczna oś czasu +homeTimeline: Główna oś czasu +reflectMayTakeTime: Może upłynąć trochę czasu, zanim pojawią się zmiany. +failedToFetchAccountInformation: Nie można uzyskać informacji o koncie +pushNotification: Powiadomienia push +subscribePushNotification: Włącz powiadomienia push +unsubscribePushNotification: Wyłącz powiadomienia push +pushNotificationAlreadySubscribed: Powiadomienia push są już włączone +pushNotificationNotSupported: Twoja przeglądarka lub serwer nie obsługuje powiadomień + push +sendPushNotificationReadMessage: Usuń powiadomienia push, gdy odpowiednie powiadomienia + lub wiadomości zostaną odczytane +sendPushNotificationReadMessageCaption: Powiadomienie zawierające tekst "{emptyPushNotificationMessage}" + zostanie wyświetlone przez krótką chwilę. Jeśli dotyczy, może to zwiększyć zużycie + baterii Twojego urządzenia. +defaultReaction: Domyślna reakcja emoji dla wychodzących i przychodzących wpisów +license: Licencja +indexPosts: Indeksuj wpisy +indexFrom: Indeksuj wpisy od ID +indexFromDescription: Zostaw puste dla indeksowania wszystkich wpisów +indexNotice: Indeksuję. Zapewne zajmie to chwilę, nie restartuj serwera przez co najmniej + godzinę. +customKaTeXMacro: Niestandardowe makra KaTeX +enableCustomKaTeXMacro: Włącz niestandardowe makra KaTeX +noteId: ID wpisu +hiddenTagsDescription: 'Wypisz tagi (bez #) hashtagów które masz zamiar ukryć z "Na + czasie" i "Eksploruj". Na ukryte hashtagi można dalej wejść innymi sposobami. Ta + lista nie ma wpływu na zablokowane instancje.' +proxyAccountDescription: Konto proxy jest kontem które w określonych sytuacjach zachowuje + się jak zdalny obserwujący. Na przykład, kiedy użytkownik dodaje zdalnego użytkownika + do listy, oraz żaden lokalny użytkownik nie obserwuje tego konta, aktywność owego + użytkownika nie zostanie dostarczona na oś czasu. W takim razie, użytkownika zaobserwuje + konto proxy. +objectStorageBaseUrlDesc: "URL stosowany jako odniesienie. Podaj URL twojego CDN, + albo proxy, jeśli używasz któregokolwiek.\nDla S3 użyj 'https://.s3.amazonaws.com', + a dla GCS i jego odpowiedników użyj 'https://storage.googleapis.com/', itd." +sendErrorReportsDescription: "Gdy ta opcja jest włączona, szczegółowe informacje o + błędach będą udostępnianie z Firefish gdy wystąpi problem, pomagając w ulepszaniu + Firefish.\nZawrze to informacje takie jak wersja twojego systemu operacyjnego, przeglądarki, + Twoja aktywność na Firefish itd." +privateModeInfo: Gdy ta opcja jest włączona, tylko serwery z białej listy mogą federować + się z twoim serwerem. Wszystkie posty będą ukryte publicznie. +oneHour: Godzina +oneDay: Dzień +oneWeek: Tydzień +recommendedInstances: Polecane serwery +recommendedInstancesDescription: Polecane serwery, mające pojawić się w odpowiedniej + osi czasu, oddzielane nowymi liniami. NIE dodawaj “https://”, TYLKO samą domenę. +rateLimitExceeded: Przekroczono ratelimit +cropImage: Kadruj zdjęcie +cropImageAsk: Czy chcesz skadrować to zdjęcie? +recentNHours: Ostatnie {n} godzin +noEmailServerWarning: Serwer email nie jest skonfigurowany. +thereIsUnresolvedAbuseReportWarning: Istnieją nierozwiązane zgłoszenia. +check: Sprawdź +driveCapOverrideLabel: Zmień pojemność dysku dla tego użytkownika +isSystemAccount: To konto jest tworzone i automatycznie obsługiwane przez system. + Nie moderuj, nie edytuj, nie usuwaj, ani w żaden inny sposób nie ingeruj w to konto, + bowiem może to uszkodzić twój serwer. +typeToConfirm: Wpisz {x} by potwierdzić +deleteAccount: Usuń konto +document: Dokumentacja +numberOfPageCache: Liczba zbuforowanych stron +numberOfPageCacheDescription: Zwiększenie tej liczby poprawi wygodę użytkowników, + ale spowoduje większe zużycie serwera, jak i pamięci. +fast: Szybka +sensitiveMediaDetection: Wykrywanie nieodpowiednich multimediów +remoteOnly: Tylko zdalne +activeEmailValidationDescription: Włącza ściślejszą walidację adresów e-mail, która + obejmuje sprawdzanie adresów jednorazowych oraz tego, czy rzeczywiście można się + z nim komunikować. Jeśli wyłączone, walidowany jest tylko format wiadomości e-mail. +shuffle: Losuj +showAds: Pokazuj reklamy +enterSendsMessage: Wciśnij Enter w komunikatorze, by wysłać wiadomość (domyślnie – + Ctrl + Enter) +adminCustomCssWarn: To ustawienie powinno być używane tylko pod warunkiem, że wiesz + za co ono odpowiada. Wpisanie niepoprawnych wartości może spowodować niepoprawne + działanie klientów KAŻDEGO użytkownika. Proszę upewnij się, że twój CSS działa poprawnie + poprzez przetestowanie go w ustawieniach twojego użytkownika. +customMOTD: Niestandardowe MOTD (wiadomości splash screen) +customMOTDDescription: Niestandardowe wiadomości dla MOTD (splash screen), oddzielane + nowymi liniami, mające pokazywać się za każdym razem gdy użytkownik ładuje/odświeża + stronę. +customSplashIcons: Niestandardowe ikony na splash screenie (URL-e) +customSplashIconsDescription: URL-e dla niestandardowych ikonych na splash screenie, + mające pokazywać się za każdym razem, gdy użytkownik ładuje/odświeża stronę, oddzielane + nowymi liniami. Upewnij się, że zdjęcia są na statycznych URL-ach, najlepiej o rozmiarze + 192x192. +caption: Auto opis +splash: Splash screen +updateAvailable: Może być dostępna aktualizacja! +logoImageUrl: URL grafiki loga +showAdminUpdates: Wskaż, że jest dostępna nowa wersja Firefish (tylko dla adminów) +hiddenTags: Ukryte hashtagi +userSaysSomethingReason: '{name} powiedział* {reason}' +customKaTeXMacroDescription: 'Skonfiguruj makra, aby łatwo pisać wyrażenia matematyczne! + Notacja jest zgodna z definicjami poleceń LaTeXa i zapisywana jest jako \newcommand{\nazwa}{treść} + lub \newcommand{\nazwa}[numer argumentów]{treść}. Na przykład, \newcommand{\add}[2]{#1 + + #2} rozszerzy \add{3}{foo} do 3 + foo. Nawiasy klamrowe otaczające nazwę makra + mogą być zmienione na nawiasy okrągłe lub kwadratowe. Wpłynie to na nawiasy używane + dla argumentów. W każdym wierszu można zdefiniować jedno (i tylko jedno) makro i + nie można przerwać linii w środku definicji. Nieprawidłowe linie są po prostu ignorowane. + Obsługiwane są tylko proste funkcje podstawiania łańcuchów; nie można tu stosować + zaawansowanej składni, takiej jak warunkowe rozgałęzienia.' +secureModeInfo: W przypadku żądań z innych serwerów nie odsyłaj bez dowodu. +preferencesBackups: Kopie zapasowe ustawień +undeck: Opuść tablicę +reporter: Osoba zgłaszająca +instanceDefaultDarkTheme: Domyślny ciemny motyw serwera +lastCommunication: Ostatnie połączenie +emailRequiredForSignup: Wymagaj adresu email przy rejestracji +themeColor: Kolor znacznika serwera +instanceDefaultLightTheme: Domyślny jasny motyw serwera +enableEmojiReactions: Włącz reakcje emoji +showEmojisInReactionNotifications: Pokazuj emoji w powiadomieniach reakcyjnych +apps: Aplikacje +silenceThisInstance: Wycisz ten serwer +silencedInstances: Wyciszone serwery +deleted: Usunięte +editNote: Edytuj wpis +edited: 'Edytowano o {date} {time}' +silenced: Wyciszony +findOtherInstance: Znajdź inny serwer +userSaysSomethingReasonReply: '{name} odpowiedział na wpis zawierający {reason}' +userSaysSomethingReasonRenote: '{name} podbił post zawierający {reason}' +signupsDisabled: Rejestracja na tym serwerze jest obecnie zamknięta, ale zawsze możesz + zarejestrować się na innym serwerze! Jeśli masz kod zaproszenia na ten serwer, wpisz + go poniżej. +userSaysSomethingReasonQuote: '{name} zacytował wpis zawierający {reason}' +silencedInstancesDescription: Wypisz nazwy hostów serwerów, które chcesz wyciszyć. + Konta na wymienionych serwerach są traktowane jako "Wyciszone", mogą jedynie wysyłać + prośby obserwacji i nie mogą oznaczać we wzmiankach profili lokalnych jeśli nie + są obserwowane. To nie będzie miało wpływu na zablokowane serwery. +cannotUploadBecauseExceedsFileSizeLimit: Ten plik nie mógł być przesłany, ponieważ + jego wielkość przekracza dozwolony limit. +sendModMail: Wyślij Powiadomienie Moderacyjne +searchPlaceholder: Szukaj Firefish +jumpToPrevious: Przejdź do poprzedniej sekcji +listsDesc: Listy umożliwiają tworzenie osi czasu z określonymi użytkownikami. Dostęp + do nich można uzyskać na stronie osi czasu. +accessibility: Dostępność +selectChannel: Wybierz kanał +antennasDesc: "Anteny wyświetlają nowe posty spełniające ustawione przez Ciebie kryteria!\n + Dostęp do nich można uzyskać ze strony osi czasu." +expandOnNoteClick: Otwórz post przy kliknięciu +expandOnNoteClickDesc: Jeśli opcja ta jest wyłączona, nadal będzie można otwierać + posty w menu po kliknięciu prawym przyciskiem myszy lub klikając znacznik czasowy. +channelFederationWarn: Kanały nie są jeszcze federowane z innymi serwerami +newer: nowsze +older: starsze +cw: Ostrzeżenie zawartości +removeReaction: Usuń reakcję diff --git a/locales/pt-PT.yml b/locales/pt-PT.yml index 054e845b73..4a39c150a5 100644 --- a/locales/pt-PT.yml +++ b/locales/pt-PT.yml @@ -1,7 +1,7 @@ --- _lang_: "Português" headlineMisskey: "Uma rede ligada por notas" -introMisskey: "Bem-vindo! Misskey é um serviço de microblogue descentralizado de código aberto.\nCria \"notas\" e partilha o que te ocorre com todos à tua volta. 📡\nCom \"reações\" podes também expressar logo o que sentes às notas de todos. 👍\nExploremos um novo mundo! 🚀" +introMisskey: "Bem-vindo! Firefish é um serviço de microblogue descentralizado de código aberto.\nCria \"notas\" e partilha o que te ocorre com todos à tua volta. 📡\nCom \"reações\" podes também expressar logo o que sentes às notas de todos. 👍\nExploremos um novo mundo! 🚀" monthAndDay: "{day}/{month}" search: "Buscar" notifications: "Notificações" @@ -139,7 +139,7 @@ settingGuide: "Guia de configuração" cacheRemoteFiles: "Memória transitória de arquivos remotos" cacheRemoteFilesDescription: "Se você desabilitar essa configuração, os arquivos remotos não serão armazenados em memória transitória e serão vinculados diretamente. Economiza o armazenamento do servidor, mas não gera miniaturas, o que aumenta o tráfego." flagAsBot: "Marcar conta como robô" -flagAsBotDescription: "Se esta conta for operada por um programa, ative este sinalizador. Quando ativado, serve como um sinalizador para evitar o encadeamento de reações para outros programadores, e o manuseio do sistema do Misskey é adequado para ‘bots’." +flagAsBotDescription: "Se esta conta for operada por um programa, ative este sinalizador. Quando ativado, serve como um sinalizador para evitar o encadeamento de reações para outros programadores, e o manuseio do sistema do Firefish é adequado para ‘bots’." flagAsCat: "Marcar conta como gato" flagAsCatDescription: "Ative essa opção para marcar essa conta como gato." flagShowTimelineReplies: "Mostrar respostas na linha de tempo" @@ -177,7 +177,6 @@ operations: "operar" software: "Programas" version: "versão" metadata: "Metadados" -withNFiles: "{n} Um arquivo" monitor: "monitor" jobQueue: "Fila de trabalhos" cpuAndMemory: "CPU e memória" @@ -199,7 +198,7 @@ noUsers: "Sem usuários" editProfile: "Editar Perfil" noteDeleteConfirm: "Deseja excluir esta nota?" pinLimitExceeded: "Não consigo mais fixar" -intro: "A instalação do Misskey está completa! Crie uma conta de administrador." +intro: "A instalação do Firefish está completa! Crie uma conta de administrador." done: "Concluído" processing: "Em Progresso" preview: "Pré-visualizar" @@ -377,7 +376,7 @@ exploreFediverse: "Explorar Fediverse" popularTags: "Tags populares" userList: "Listas" about: "Informações" -aboutMisskey: "Sobre Misskey" +aboutFirefish: "Sobre Firefish" administrator: "Administrador" token: "Símbolo" twoStepAuthentication: "Verificação em duas etapas" diff --git a/locales/pt_BR.yml b/locales/pt_BR.yml new file mode 100644 index 0000000000..c26045e022 --- /dev/null +++ b/locales/pt_BR.yml @@ -0,0 +1,112 @@ +username: Nome de usuário +ok: OK +_lang_: Inglês +headlineMisskey: Uma plataforma de mídia social descentralizada e de código aberto + que é gratuita para sempre! 🚀 +search: Pesquisar +gotIt: Entendi! +introMisskey: Bem vinde! Firefish é uma plataforma de mídia social descentralizada + e de código aberto que é gratuita para sempre! 🚀 +searchPlaceholder: Pesquise no Firefish +notifications: Notificações +password: Senha +forgotPassword: Esqueci a senha +cancel: Cancelar +noThankYou: Não, obrigade +save: Salvar +enterUsername: Insira nome de usuário +cw: Aviso de conteúdo +driveFileDeleteConfirm: Tem a certeza de que pretende apagar o arquivo "{name}"? O + arquivo será removido de todas as mensagens que o contenham como anexo. +deleteAndEdit: Deletar e editar +import: Importar +exportRequested: Você pediu uma exportação. Isso pode demorar um pouco. Será adicionado + ao seu Drive quando for completo. +note: Postar +notes: Postagens +deleteAndEditConfirm: Você tem certeza que quer deletar esse post e edita-lo? Você + vai perder todas as reações, impulsionamentos e respostas dele. +showLess: Fechar +importRequested: Você requisitou uma importação. Isso pode demorar um pouco. +listsDesc: Listas deixam você criar linhas do tempo com usuários específicos. Elas + podem ser acessadas pela página de linhas do tempo. +edited: 'Editado às {date} {time}' +sendMessage: Enviar uma mensagem +older: antigo +createList: Criar lista +loadMore: Carregar mais +mentions: Menções +importAndExport: Importar/Exportar Dados +files: Arquivos +lists: Listas +manageLists: Gerenciar listas +error: Erro +somethingHappened: Ocorreu um erro +retry: Tentar novamente +renotedBy: Impulsionado por {user} +noNotes: Nenhum post +noNotifications: Nenhuma notificação +instance: Servidor +settings: Configurações +basicSettings: Configurações Básicas +otherSettings: Outras Configurações +openInWindow: Abrir em janela +profile: Perfil +noAccountDescription: Esse usuário ainda não escreveu sua bio. +login: Entrar +loggingIn: Entrando +logout: Sair +signup: Criar conta +uploading: Enviando... +users: Usuários +addUser: Adicione um usuário +addInstance: Adicionar um servidor +cantFavorite: Não foi possível adicionar aos marcadores. +pin: Fixar no perfil +unpin: Desfixar do perfil +copyContent: Copiar conteúdos +copyLink: Copiar link +delete: Deletar +deleted: Deletado +editNote: Editar anotação +addToList: Adicionar a lista +copyUsername: Copiar nome de usuário +searchUser: Procurar por um usuário +reply: Responder +jumpToPrevious: Pular para o anterior +showMore: Mostrar mais +newer: novo +youGotNewFollower: seguiu você +mention: Mencionar +directNotes: Mensagens diretas +export: Exportar +unfollowConfirm: Você tem certez que deseja para de seguir {name}? +noLists: Você não possui nenhuma lista +following: Seguindo +followers: Seguidores +followsYou: Segue você +fetchingAsApObject: Buscando do Fediverse +timeline: Linha do tempo +favorite: Adicionar aos marcadores +favorites: Marcadores +unfavorite: Remover dos marcadores +favorited: Adicionado aos marcadores. +alreadyFavorited: Já foi adicionado aos marcadores. +download: Download +pageLoadError: Ocorreu um erro ao carregar a página. +pageLoadErrorDescription: Isso normalmente é causado por erros de rede ou pelo cache + do navegador. Tente limpar o cache e, depois de esperar um pouquinho, tente novamente. +serverIsDead: Esse servidos não está respondendo. Por favor espere um pouco e tente + novamente. +youShouldUpgradeClient: Para visualizar essa página, favor reiniciar para atualizar + seu cliente. +enterListName: Insira um nome para a lista +privacy: Privacidade +defaultNoteVisibility: Visibilidade padrão +makeFollowManuallyApprove: Pedidos de seguimento precisam de aprovação +follow: Seguir +followRequest: Seguir +followRequests: Pedidos de seguimento +unfollow: Parar de seguir +followRequestPending: Pedido de seguimento pendente +enterEmoji: Insira um emoji diff --git a/locales/ro-RO.yml b/locales/ro-RO.yml index 8408d4c778..5a3aa4ef4e 100644 --- a/locales/ro-RO.yml +++ b/locales/ro-RO.yml @@ -1,7 +1,7 @@ --- _lang_: "Română" headlineMisskey: "O rețea conectată prin note" -introMisskey: "Bine ai venit! Misskey este un serviciu de microblogging open source și decentralizat.\nCreează \"note\" cu care să îți poți împărți gândurile cu oricine din jurul tău. 📡\nCu \"reacții\" îți poți expirma rapid părerea despre notele oricui. 👍\nHai să explorăm o lume nouă! 🚀" +introMisskey: "Bine ai venit! Firefish este un serviciu de microblogging open source și decentralizat.\nCreează \"note\" cu care să îți poți împărți gândurile cu oricine din jurul tău. 📡\nCu \"reacții\" îți poți expirma rapid părerea despre notele oricui. 👍\nHai să explorăm o lume nouă! 🚀" monthAndDay: "{day}/{month}" search: "Caută" notifications: "Notificări" @@ -139,7 +139,7 @@ settingGuide: "Setări recomandate" cacheRemoteFiles: "Ține fișierele externe in cache" cacheRemoteFilesDescription: "Când această setare este dezactivată, fișierele externe sunt încărcate direct din instanța externă. Dezactivarea va scădea utilizarea spațiului de stocare, dar va crește traficul, deoarece thumbnail-urile nu vor fi generate." flagAsBot: "Marchează acest cont ca bot" -flagAsBotDescription: "Activează această opțiune dacă acest cont este controlat de un program. Daca e activată, aceasta va juca rolul unui indicator pentru dezvoltatori pentru a preveni interacțiunea în lanțuri infinite cu ceilalți boți și ajustează sistemele interne al Misskey pentru a trata acest cont drept un bot." +flagAsBotDescription: "Activează această opțiune dacă acest cont este controlat de un program. Daca e activată, aceasta va juca rolul unui indicator pentru dezvoltatori pentru a preveni interacțiunea în lanțuri infinite cu ceilalți boți și ajustează sistemele interne al Firefish pentru a trata acest cont drept un bot." flagAsCat: "Marchează acest cont ca pisică" flagAsCatDescription: "Activează această opțiune dacă acest cont este o pisică." flagShowTimelineReplies: "Arată răspunsurile în cronologie" @@ -177,7 +177,6 @@ operations: "Operațiuni" software: "Software" version: "Versiune" metadata: "Metadata" -withNFiles: "{n} fișier(e)" monitor: "Monitor" jobQueue: "coada de job-uri" cpuAndMemory: "CPU și memorie" @@ -199,7 +198,7 @@ noUsers: "Niciun utilizator" editProfile: "Editează profilul" noteDeleteConfirm: "Ești sigur că vrei să ștergi această notă?" pinLimitExceeded: "Nu poți mai fixa mai multe note" -intro: "Misskey s-a instalat! Te rog crează un utilizator admin." +intro: "Firefish s-a instalat! Te rog crează un utilizator admin." done: "Gata" processing: "Se procesează" preview: "Previzualizare" @@ -377,7 +376,7 @@ exploreFediverse: "Explorează Fediverse-ul" popularTags: "Taguri populare" userList: "Liste" about: "Despre" -aboutMisskey: "Despre Misskey" +aboutFirefish: "Despre Firefish" administrator: "Administrator" token: "Token" twoStepAuthentication: "Autentificare în doi pași" @@ -522,7 +521,7 @@ sort: "Sortează" ascendingOrder: "Crescător" descendingOrder: "Descrescător" scratchpad: "Scratchpad" -scratchpadDescription: "Scratchpad-ul oferă un mediu de experimentare în AiScript. Poți scrie, executa și verifica rezultatele acestuia interacționând cu Misskey în el." +scratchpadDescription: "Scratchpad-ul oferă un mediu de experimentare în AiScript. Poți scrie, executa și verifica rezultatele acestuia interacționând cu Firefish în el." output: "Ieșire" script: "Script" disablePagesScript: "Dezactivează AiScript în Pagini" diff --git a/locales/ru-RU.yml b/locales/ru-RU.yml index 9d76dc6239..f6f5a3ca00 100644 --- a/locales/ru-RU.yml +++ b/locales/ru-RU.yml @@ -1,7 +1,7 @@ ---- _lang_: "Русский" headlineMisskey: "Сеть, сплетённая из заметок" -introMisskey: "Добро пожаловать! Misskey — это децентрализованный сервис микроблогов с открытым исходным кодом.\nПишите «заметки» — делитесь со всеми происходящим вокруг или рассказывайте о себе 📡\nСтавьте «реакции» — выражайте свои чувства и эмоции от заметок других 👍\nОткройте для себя новый мир 🚀" +introMisskey: "Firefish - это децентрализованная платформа социальных сетей с открытым + исходным кодом, которая свободна навсегда! 🚀" monthAndDay: "{day}.{month}" search: "Поиск" notifications: "Уведомления" @@ -14,16 +14,16 @@ gotIt: "Ясно!" cancel: "Отмена" enterUsername: "Введите имя пользователя" renotedBy: "{user} делится" -noNotes: "Нет ни одной заметки" +noNotes: "Нет ни одного поста" noNotifications: "Нет ни одного уведомления" -instance: "Инстанс" +instance: "Сервер" settings: "Настройки" basicSettings: "Основные настройки" otherSettings: "Прочие настройки" openInWindow: "Открывать в плавающих окнах" profile: "Профиль" timeline: "Лента" -noAccountDescription: "Пользователь ничего не написал про себя" +noAccountDescription: "Пользователь ничего не написал про себя." login: "Войти" loggingIn: "Выполняется вход" logout: "Выйти" @@ -44,7 +44,8 @@ copyContent: "Скопировать содержимое" copyLink: "Скопировать ссылку" delete: "Удалить" deleteAndEdit: "Удалить и отредактировать" -deleteAndEditConfirm: "Удалить эту заметку и создать отредактированную? Все реакции, ссылки и ответы на существующую будут будут потеряны." +deleteAndEditConfirm: "Удалить этот пост и создать отредактированный? Все реакции, + ссылки и ответы на существующий будут потеряны." addToList: "Добавить в список" sendMessage: "Отправить сообщение" copyUsername: "Скопировать имя пользователя" @@ -64,14 +65,16 @@ import: "Импорт" export: "Экспорт" files: "Файлы" download: "Скачать" -driveFileDeleteConfirm: "Удалить файл «{name}»? Заметки с ним также будут удалены." +driveFileDeleteConfirm: "Удалить файл «{name}»? Он будет удален со всех постов которые + содержат его как вложение." unfollowConfirm: "Удалить из подписок пользователя {name}?" -exportRequested: "Вы запросили экспорт. Это может занять некоторое время. Результат будет добавлен на «Диск»." +exportRequested: "Вы запросили экспорт. Это может занять некоторое время. Результат + будет добавлен на «Диск»." importRequested: "Вы запросили импорт. Это может занять некоторое время." lists: "Списки" noLists: "Нет ни одного списка" -note: "Заметка" -notes: "Заметки" +note: "Пост" +notes: "Посты" following: "Подписки" followers: "Подписчики" followsYou: "Читает вас" @@ -80,14 +83,16 @@ manageLists: "Управление списками" error: "Ошибка" somethingHappened: "Что-то пошло не так" retry: "Повторить попытку" -pageLoadError: "Не удалось загрузить страницу" -pageLoadErrorDescription: "Обычно это случается из-за сбоев в сети или кэша браузера. Попробуйте очистить кэш, или подождать пару минут, а потом попытаться загрузить страницу снова." +pageLoadError: "Не удалось загрузить страницу." +pageLoadErrorDescription: "Обычно это случается из-за сбоев в сети или кэша браузера. + Попробуйте очистить кэш, или подождать пару минут, а потом попытаться загрузить + страницу снова." serverIsDead: "Ответа от сервера нет. Пожалуйста, подождите немного и повторите попытку." youShouldUpgradeClient: "Чтобы просмотреть эту страницу, пожалуйста, обновите ее." enterListName: "Название списка" privacy: "Конфиденциальность" makeFollowManuallyApprove: "Принимать подписчиков вручную" -defaultNoteVisibility: "Видимость заметок по умолчанию" +defaultNoteVisibility: "Видимость постов по умолчанию" follow: "Подписка" followRequest: "Запрос на подписку" followRequests: "Запросы на подписку" @@ -100,7 +105,7 @@ renoted: "Репост совершён." cantRenote: "Это нельзя репостить." cantReRenote: "Невозможно репостить репост." quote: "Цитата" -pinnedNote: "Закреплённая заметка" +pinnedNote: "Закреплённый пост" pinned: "Закрепить в профиле" you: "Вы" clickToShow: "Нажмите для просмотра" @@ -108,8 +113,9 @@ sensitive: "Содержимое не для всех" add: "Добавить" reaction: "Реакции" reactionSetting: "Реакции, отображаемые в палитре" -reactionSettingDescription2: "Расставляйте перетаскиванием, удаляйте нажатием, добавляйте кнопкой «+»." -rememberNoteVisibility: "Запоминать видимость заметок" +reactionSettingDescription2: "Расставляйте перетаскиванием, удаляйте нажатием, добавляйте + кнопкой «+»." +rememberNoteVisibility: "Запоминать видимость постов" attachCancel: "Удалить вложение" markAsSensitive: "Отметить как «не для всех»" unmarkAsSensitive: "Снять отметку «не для всех»" @@ -137,17 +143,22 @@ emojiUrl: "URL эмодзи" addEmoji: "Добавить эмодзи" settingGuide: "Рекомендуемые настройки" cacheRemoteFiles: "Кешировать внешние файлы" -cacheRemoteFilesDescription: "Когда эта настройка отключена, файлы с других сайтов будут загружаться прямо оттуда. Это сэкономит место на сервере, но увеличит трафик, так как не будут создаваться эскизы." +cacheRemoteFilesDescription: "Когда эта настройка отключена, файлы с других сайтов + будут загружаться прямо оттуда. Это сэкономит место на сервере, но увеличит трафик, + так как не будут создаваться эскизы." flagAsBot: "Аккаунт бота" -flagAsBotDescription: "Включите, если этот аккаунт управляется программой. Это позволит системе Misskey учитывать это, а также поможет разработчикам других ботов предотвратить бесконечные циклы взаимодействия." +flagAsBotDescription: "Включите, если этот аккаунт управляется программой. Это позволит + системе Firefish учитывать это, а также поможет разработчикам других ботов предотвратить + бесконечные циклы взаимодействия." flagAsCat: "Аккаунт кота" -flagAsCatDescription: "Включите, и этот аккаунт будет помечен как кошачий." -flagShowTimelineReplies: "Показывать ответы на заметки в ленте" -flagShowTimelineRepliesDescription: "Если этот параметр включен, то в ленте, в дополнение к заметкам пользователя, отображаются ответы на другие заметки пользователя." +flagAsCatDescription: "Вы получите кошачьи ушки и будете говорить как кот!" +flagShowTimelineReplies: "Показывать ответы на посты в ленте" +flagShowTimelineRepliesDescription: "Если этот параметр включен, то в ленте, в дополнение + к постам пользователя, отображаются ответы на другие посты пользователя." autoAcceptFollowed: "Принимать подписчиков автоматически" addAccount: "Добавить учётную запись" loginFailed: "Неудачная попытка входа" -showOnRemote: "Перейти к оригиналу на сайт" +showOnRemote: "Открыть оригинал" general: "Общее" wallpaper: "Обои" setWallpaper: "Установить обои" @@ -156,13 +167,16 @@ searchWith: "Найденное «{q}»" youHaveNoLists: "У вас нет ни одного списка" followConfirm: "Подписаться на {name}?" proxyAccount: "Учётная запись прокси" -proxyAccountDescription: "Учетная запись прокси предназначена служить подписчиком на пользователей с других сайтов. Например, если пользователь добавит кого-то с другого сайта а список, деятельность того не отобразится, пока никто с этого же сайта не подписан на него. Чтобы это стало возможным, на него подписывается прокси." +proxyAccountDescription: "Учетная запись прокси предназначена служить подписчиком + на пользователей с других сайтов. Например, если пользователь добавит кого-то с + другого сайта а список, деятельность того не отобразится, пока никто с этого же + сайта не подписан на него. Чтобы это стало возможным, на него подписывается прокси." host: "Хост" selectUser: "Выберите пользователя" recipient: "Кому" annotation: "Описание" federation: "Федерация" -instances: "Инстанс" +instances: "Серверы" registeredAt: "Первое наблюдение" latestRequestSentAt: "Последний отправленный запрос" latestRequestReceivedAt: "Последний полученный запрос" @@ -172,34 +186,35 @@ charts: "Диаграммы" perHour: "По часам" perDay: "По дням" stopActivityDelivery: "Остановить отправку обновлений активности" -blockThisInstance: "Блокировать этот инстанс" +blockThisInstance: "Блокировать этот сервер" operations: "Операции" software: "Программы" version: "Версия" metadata: "Метаданные" -withNFiles: "Файлы, {n} шт." monitor: "Монитор" jobQueue: "Очередь заданий" cpuAndMemory: "Процессор и память" network: "Сеть" disk: "Диск" -instanceInfo: "Информация об инстансе" +instanceInfo: "Информация о сервере" statistics: "Статистика" clearQueue: "Очистить очередь" clearQueueConfirmTitle: "Очистить очередь?" -clearQueueConfirmText: "Всё, что осталось в очереди, не будет доставлено. Обычно эта операция НЕ нужна." +clearQueueConfirmText: "Всё, что осталось в очереди, не будет доставлено. Обычно эта + операция НЕ нужна." clearCachedFiles: "Очистить кэш" clearCachedFilesConfirm: "Удалить все закэшированные файлы с других сайтов?" -blockedInstances: "Заблокированные инстансы" -blockedInstancesDescription: "Введите список инстансов, которые хотите заблокировать. Они больше не смогут обмениваться с вашим инстансом." +blockedInstances: "Заблокированные серверы" +blockedInstancesDescription: "Введите список серверов, которые хотите заблокировать. + Они больше не смогут обмениваться с вашим сервером." muteAndBlock: "Скрытие и блокировка" mutedUsers: "Скрытые пользователи" blockedUsers: "Заблокированные пользователи" noUsers: "Нет ни одного пользователя" editProfile: "Редактировать профиль" -noteDeleteConfirm: "Вы хотите удалить эту заметку?" -pinLimitExceeded: "Нельзя закрепить ещё больше заметок" -intro: "Установка Misskey завершена! А теперь создайте учетную запись администратора." +noteDeleteConfirm: "Вы хотите удалить этот пост?" +pinLimitExceeded: "Нельзя закрепить ещё больше постов" +intro: "Установка Firefish завершена! А теперь создайте учетную запись администратора." done: "Готово" processing: "Обработка" preview: "Предпросмотр" @@ -214,12 +229,12 @@ all: "Всё" subscribing: "Подписка" publishing: "Публикация" notResponding: "Нет ответа" -instanceFollowing: "Подписанные на инстансе" -instanceFollowers: "Подписчики инстанса" -instanceUsers: "Пользователи инстанса" +instanceFollowing: "Подписанные на сервере" +instanceFollowers: "Подписчики сервера" +instanceUsers: "Пользователи сервера" changePassword: "Изменить пароль" security: "Безопасность" -retypedNotMatch: "Не совпадают" +retypedNotMatch: "Не совпадают." currentPassword: "Текущий пароль" newPassword: "Новый пароль" newPasswordRetype: "Новый пароль (ещё раз)" @@ -232,7 +247,7 @@ lookup: "Запрос" announcements: "Оповещения" imageUrl: "Ссылка на изображение" remove: "Удалить" -removed: "Удалено" +removed: "\uFEFFУдалено" removeAreYouSure: "Хотите удалить «{x}»?" deleteAreYouSure: "Хотите удалить «{x}»?" resetAreYouSure: "На самом деле сбросить?" @@ -240,7 +255,8 @@ saved: "Сохранено" messaging: "Сообщения" upload: "Загрузить" keepOriginalUploading: "Сохранить исходное изображение" -keepOriginalUploadingDescription: "Сохраняет исходную версию при загрузке изображений. Если выключить, то при загрузке браузер генерирует изображение для публикации." +keepOriginalUploadingDescription: "Сохраняет исходную версию при загрузке изображений. + Если выключить, то при загрузке браузер генерирует изображение для публикации." fromDrive: "С «диска»" fromUrl: "По ссылке" uploadFromUrl: "Загрузить по ссылке" @@ -256,7 +272,8 @@ agreeTo: "Я соглашаюсь с {0}" tos: "Пользовательское соглашение" start: "Начать" home: "Главная" -remoteUserCaution: "Это пользователь с другого сайта, поэтому информация может быть неточной." +remoteUserCaution: "Это пользователь с другого сайта, поэтому информация может быть + неточной." activity: "Активность" images: "Изображения" birthday: "День рождения" @@ -288,7 +305,7 @@ emptyFolder: "Папка пуста" unableToDelete: "Удаление невозможно" inputNewFileName: "Введите имя нового файла" inputNewDescription: "Введите новую подпись" -inputNewFolderName: "Пожалуйста, введите новое имя папки!" +inputNewFolderName: "Пожалуйста, введите новое имя папки" circularReferenceFolder: "Вы пытаетесь переместить папку внутрь себя." hasChildFilesOrFolders: "Эта папка не пуста и не может быть удалена." copyUrl: "Копировать ссылку" @@ -306,8 +323,8 @@ unwatch: "Отписаться" accept: "Принять" reject: "Отклонить" normal: "Стабильно" -instanceName: "Название инстанса" -instanceDescription: "Описание инстанса" +instanceName: "Название сервера" +instanceDescription: "Описание сервера" maintainerName: "Имя администратора" maintainerEmail: "Электронная почта администратора" tosUrl: "Ссылка на пользовательское соглашение" @@ -318,12 +335,13 @@ dayX: "{day} день" monthX: "{month} месяц" yearX: "{year} год" pages: "Страницы" -integration: "Интеграция" +integration: "Интеграции" connectService: "Подключиться" disconnectService: "Отключиться" enableLocalTimeline: "Включить локальную ленту" enableGlobalTimeline: "Включить глобальную ленту" -disablingTimelinesInfo: "У администраторов и модераторов есть доступ ко всем лентам, даже если они отключены." +disablingTimelinesInfo: "У администраторов и модераторов есть доступ ко всем лентам, + даже если они отключены." registration: "Регистрация" enableRegistration: "Разрешить регистрацию" invite: "Пригласить" @@ -335,11 +353,13 @@ bannerUrl: "Ссылка на изображение в шапке" backgroundImageUrl: "Ссылка на фоновое изображение" basicInfo: "Общая информация" pinnedUsers: "Прикреплённый пользователь" -pinnedUsersDescription: "Перечислите по одному имени пользователя в строке. Пользователи, перечисленные здесь, будут привязаны к закладке \"Изучение\"." +pinnedUsersDescription: "Перечислите по одному имени пользователя в строке. Пользователи, + перечисленные здесь, будут привязаны к закладке \"Изучение\"." pinnedPages: "Закрепленные страницы" -pinnedPagesDescription: "Если хотите закрепить страницы на главной сайта, сюда можно добавить пути к ним, каждый в отдельной строке." +pinnedPagesDescription: "Если хотите закрепить страницы на главной сайта, сюда можно + добавить пути к ним, каждый в отдельной строке." pinnedClipId: "Идентификатор закреплённой подборки" -pinnedNotes: "Закреплённая заметка" +pinnedNotes: "Закреплённые посты" hcaptcha: "hCaptcha" enableHcaptcha: "Включить hCaptcha" hcaptchaSiteKey: "Ключ сайта" @@ -348,25 +368,28 @@ recaptcha: "reCAPTCHA" enableRecaptcha: "Включить reCAPTCHA" recaptchaSiteKey: "Ключ сайта" recaptchaSecretKey: "Секретный ключ" -avoidMultiCaptchaConfirm: "Несколько способов проверки могут мешать друг другу. Подтвердите, если хотите отключить другие способы. Или нажмите «Отмена», чтобы оставить их включёнными." +avoidMultiCaptchaConfirm: "Несколько способов проверки могут мешать друг другу. Подтвердите, + если хотите отключить другие способы. Или нажмите «Отмена», чтобы оставить их включёнными." antennas: "Антенны" manageAntennas: "Настройки антенн" name: "Название" antennaSource: "Источник антенны" antennaKeywords: "Ключевые слова" antennaExcludeKeywords: "Исключения" -antennaKeywordsDescription: "Пишите слова через пробел в одной строке, чтобы ловить их появление вместе; на отдельных строках располагайте слова, или группы слов, чтобы ловить любые из них." -notifyAntenna: "Уведомлять о новых заметках" -withFileAntenna: "Только заметки с вложениями" +antennaKeywordsDescription: "Пишите слова через пробел в одной строке, чтобы ловить + их появление вместе; на отдельных строках располагайте слова, или группы слов, чтобы + ловить любые из них." +notifyAntenna: "Уведомлять о новых постах" +withFileAntenna: "Только посты с вложениями" enableServiceworker: "Включить ServiceWorker" antennaUsersDescription: "Пишите каждое название аккаута на отдельной строке" caseSensitive: "С учётом регистра" withReplies: "Включая ответы" connectedTo: "Вы подключены к следующим аккаунтам" -notesAndReplies: "Заметки и ответы" -withFiles: "Заметки с файлами" +notesAndReplies: "Посты и ответы" +withFiles: "Посты с файлами" silence: "Заглушить" -silenceConfirm: " Заглушить этого пользователя? Уверены?" +silenceConfirm: "Вы уверены что хотите заглушить этого пользователя?" unsilence: "Снять глушение" unsilenceConfirm: "Снять глушение с этого пользователя? Уверены?" popularUsers: "Популярные пользователи" @@ -378,7 +401,7 @@ exploreFediverse: "Исследуйте Fediverse" popularTags: "Популярные теги" userList: "Списки" about: "Описание" -aboutMisskey: "О Misskey" +aboutFirefish: "О Firefish" administrator: "Администратор" token: "Токен" twoStepAuthentication: "Двухфакторная аутентификация" @@ -391,16 +414,16 @@ registerSecurityKey: "Зарегистрировать защитный ключ lastUsed: "Последнее использование" unregister: "Отписаться" passwordLessLogin: "Настроить вход без пароля" -resetPassword: "Сброс пароля:" -newPasswordIs: "Новый пароль — «{password}»." +resetPassword: "Сброс пароля" +newPasswordIs: "Новый пароль — «{password}»" reduceUiAnimation: "Уменьшить анимацию в пользовательском интерфейсе" share: "Поделиться" notFound: "Не найдено" -notFoundDescription: "Страница по указанной ссылке не найдена" +notFoundDescription: "Страница по указанной ссылке не найдена." uploadFolder: "Место загрузки по умолчанию" cacheClear: "Очистка кэша" markAsReadAllNotifications: "Отметить все уведомления как прочитанные" -markAsReadAllUnreadNotes: "Отметить все заметки как прочитанные" +markAsReadAllUnreadNotes: "Отметить все посты как прочитанные" markAsReadAllTalkMessages: "Отметить все реплики как прочитанные" help: "Помощь" inputMessageHere: "Введите сообщение здесь" @@ -431,10 +454,11 @@ onlyOneFileCanBeAttached: "К сообщению можно прикрепить signinRequired: "Пожалуйста, войдите" invitations: "Приглашения" invitationCode: "Код приглашения" -checking: "Проверка" +checking: "Проверка..." available: "Доступно" unavailable: "Не доступно" -usernameInvalidFormat: "Можно использовать только латинские буквы (A—Z, a—z), цифры (0—9) и знак подчёркивания (_)" +usernameInvalidFormat: "Можно использовать только латинские буквы (A—Z, a—z), цифры + (0—9) и знак подчёркивания (_)." tooShort: "Слишком короткий" tooLong: "Слишком длинный" weakPassword: "Слабый пароль" @@ -443,7 +467,8 @@ strongPassword: "Надёжный пароль" passwordMatched: "Совпали" passwordNotMatched: "Не совпадают" signinWith: "Использовать {x} для входа" -signinFailed: "Невозможно войти в систему. Введенное вами имя пользователя или пароль неверны." +signinFailed: "Невозможно войти в систему. Введенное вами имя пользователя или пароль + неверны." tapSecurityKey: "Нажмите на свой электронный ключ" or: "или" language: "Язык" @@ -453,11 +478,11 @@ aboutX: "Описание {x}" useOsNativeEmojis: "Использовать эмодзи операционной системы" disableDrawer: "Не использовать выдвижные меню" youHaveNoGroups: "У вас нет ни одной группы" -joinOrCreateGroup: "Получайте приглашения в группы или создавайте свои собственные" +joinOrCreateGroup: "Получайте приглашения в группы или создавайте свои собственные." noHistory: "История пока пуста" signinHistory: "Журнал посещений" disableAnimatedMfm: "Отключение анимированной разметки MFM" -doing: "В процессе" +doing: "В процессе..." category: "Категория" tags: "Метки" docSource: "Источник документа" @@ -480,28 +505,38 @@ promotion: "Продвинуто" promote: "Продвинуть" numberOfDays: "Количество дней" hideThisNote: "Спрятать эту запись" -showFeaturedNotesInTimeline: "Показывать в ленте заметки из «Горячего»" +showFeaturedNotesInTimeline: "Показывать в ленте посты из «Горячего»" objectStorage: "Хранилище" -useObjectStorage: "Занято в хранилище" +useObjectStorage: "Использовать объектное хранилище" objectStorageBaseUrl: "Базовый адрес" -objectStorageBaseUrlDesc: "Это начальная часть адреса, используемого CDN или прокси, например для S3: https://.s3.amazonaws.com, или дя GCS: 'https://storage.googleapis.com/'" +objectStorageBaseUrlDesc: "URL используемый для примера. Укажите URL-адрес вашего + CDN или прокси, если вы используете любой из них.\nДля S3 используйте 'https://.s3.amazonaws.com', + а для GCS и подобных сервисов используйте 'https://storage.googleapis.com/', + и т.п." objectStorageBucket: "Bucket" -objectStorageBucketDesc: "Укажите название контейнера (Bucket) который используется на выбранном сервисе." +objectStorageBucketDesc: "Укажите название контейнера (Bucket) который используется + на выбранном сервисе." objectStoragePrefix: "Префикс" -objectStoragePrefixDesc: "Файлы будут храниться в директории, соответствующей указанному здесь префиксу пути" +objectStoragePrefixDesc: "Файлы будут храниться в директории, соответствующей указанному + здесь префиксу пути." objectStorageEndpoint: "Конечная точка" -objectStorageEndpointDesc: "Если используете AWS S3, оставьте пустым. В остальных случаях укажите конечную точку (endpoint) в форме «» или «:», так, как это описано в руководстве той службы, которую собираетесь использовать." +objectStorageEndpointDesc: "Если используете AWS S3, оставьте пустым. В остальных + случаях укажите конечную точку (endpoint) в форме «» или «:», + так, как это описано в руководстве той службы, которую собираетесь использовать." objectStorageRegion: "Регион" -objectStorageRegionDesc: "Укажите регион, например xx-east-1. Если ваша служба не различает регионы, оставьте поле пустым, или впишите us-east-1." +objectStorageRegionDesc: "Укажите регион, например xx-east-1. Если ваша служба не + различает регионы, оставьте поле пустым, или впишите us-east-1." objectStorageUseSSL: "Использовать SSL" -objectStorageUseSSLDesc: "Отключите, если не собираетесь использовать протокол HTTPS для обмена по API." +objectStorageUseSSLDesc: "Отключите, если не собираетесь использовать протокол HTTPS + для обмена по API" objectStorageUseProxy: "Использовать прокси" -objectStorageUseProxyDesc: "Отключите, если не будете испоьзовать прокси для соединений по протоколу ObjectStorage." +objectStorageUseProxyDesc: "Отключите, если не будете испоьзовать прокси для соединений + по протоколу ObjectStorage" objectStorageSetPublicRead: "Устанавливать public-read при загрузке на сервер" serverLogs: "Журнал сервера" deleteAll: "Удалить всё" -showFixedPostForm: "Показывать поле для ввода новой заметки наверху ленты" -newNoteRecived: "Появилась новая заметка" +showFixedPostForm: "Показывать поле для ввода нового поста наверху ленты" +newNoteRecived: "Появился новый пост" sounds: "Звуки" listen: "Слушать" none: "Ничего" @@ -524,7 +559,9 @@ sort: "Сортировать" ascendingOrder: "по возрастанию" descendingOrder: "По убыванию" scratchpad: "Когтеточка" -scratchpadDescription: "«Когтеточка» — это место для опытов с AiScript. Здесь можно писать программы, взаимодействующие с Misskey, запускать и смотреть что из этого получается." +scratchpadDescription: "«Когтеточка» — это место для опытов с AiScript. Здесь можно + писать программы, взаимодействующие с Firefish, запускать и смотреть что из этого + получается." output: "Выходы" script: "Скрипт" disablePagesScript: "Отключить скрипты на «Страницах»" @@ -532,11 +569,14 @@ updateRemoteUser: "Обновить данные пользователя с е deleteAllFiles: "Удалить все файлы" deleteAllFilesConfirm: "Вы хотите удалить все файлы?" removeAllFollowing: "Удалить всех подписчиков" -removeAllFollowingDescription: "Отменить все подписки с домена {host}? Пожалуйста, применяйте это действие, если инстанс больше не существует." -userSuspended: "Эта учётная запись заморожена" -userSilenced: "Этот пользователь был заглушен" +removeAllFollowingDescription: "Отменить все подписки с домена {host}? Пожалуйста, + применяйте это действие, если сервер больше не существует." +userSuspended: "Эта учётная запись заморожена." +userSilenced: "Этот пользователь был заглушен." yourAccountSuspendedTitle: "Эта учетная запись заблокирована" -yourAccountSuspendedDescription: "Эта учетная запись была заблокирована из-за нарушения условий предоставления услуг сервера. Свяжитесь с администратором, если вы хотите узнать более подробную причину. Пожалуйста, не создавайте новую учетную запись." +yourAccountSuspendedDescription: "Эта учетная запись была заблокирована из-за нарушения + условий предоставления услуг сервера. Свяжитесь с администратором, если вы хотите + узнать более подробную причину. Пожалуйста, не создавайте новую учетную запись." menu: "Меню" divider: "Линия-разделитель" addItem: "Добавить элемент" @@ -545,7 +585,7 @@ addRelay: "Добавить ретранслятор" inboxUrl: "URL ящика входящих сообщений" addedRelays: "Добавленные ретрансляторы" serviceworkerInfo: "Нужно включить, чтобы работали push-уведомления." -deletedNote: "Удалённая заметка" +deletedNote: "Удалённый пост" invisibleNote: "Личное сообщение" enableInfiniteScroll: "Включить бесконечную прокрутку" visibility: "Видимость" @@ -577,12 +617,14 @@ permission: "Разрешения" enableAll: "Включить все" disableAll: "Выключить всё" tokenRequested: "Открыть доступ к учётной записи" -pluginTokenRequestedDescription: "Это расширение сможет пользоваться разрешениями, установленными здесь." +pluginTokenRequestedDescription: "Это расширение сможет пользоваться разрешениями, + установленными здесь." notificationType: "Тип уведомления" edit: "Изменить" emailServer: "Сервер электронной почты" enableEmail: "Включить обмен электронной почтой" -emailConfigInfo: "Используется для подтверждения адреса электронной почты и сброса пароля." +emailConfigInfo: "Используется для подтверждения адреса электронной почты и сброса + пароля" email: "Электронная почта" emailAddress: "Адрес электронной почты" smtpConfig: "Конфигурация SMTP-сервера" @@ -590,13 +632,14 @@ smtpHost: "Хост" smtpPort: "Порт" smtpUser: "Имя пользователя" smtpPass: "Пароль" -emptyToDisableSmtpAuth: "Не заполняйте имя пользователя и пароль, чтобы отключить аутентификацию в SMTP." +emptyToDisableSmtpAuth: "Не заполняйте имя пользователя и пароль, чтобы отключить + аутентификацию в SMTP" smtpSecure: "Использовать SSL/TLS для SMTP-соединений" -smtpSecureInfo: "Выключите при использовании STARTTLS." +smtpSecureInfo: "Выключите при использовании STARTTLS" testEmail: "Проверка доставки электронной почты" wordMute: "Скрытие слов" regexpError: "Ошибка в регулярном выражении" -instanceMute: "Глушение инстансов" +instanceMute: "Глушение серверов" userSaysSomething: "{name} что-то сообщает" makeActive: "Активировать" display: "Отображение" @@ -609,32 +652,38 @@ database: "База данных" channel: "Каналы" create: "Создать" notificationSetting: "Настройки уведомлений" -notificationSettingDesc: "Выберите тип уведомлений для отображения" +notificationSettingDesc: "Выберите тип уведомлений для отображения." useGlobalSetting: "Использовать глобальные настройки" -useGlobalSettingDesc: "Если включено, будут использоваться настройки учётной записи. Если включить, этот виджет можно будет настроить индивидуально." +useGlobalSettingDesc: "Если включено, будут использоваться настройки учётной записи. + Если включить, этот виджет можно будет настроить индивидуально." other: "Другие" regenerateLoginToken: "Создать новый токен для входа" -regenerateLoginTokenDescription: "Создаёт новый токен, используемый внутри программы во время входа. Обычно в этом нет необходимости. При создании все устройства будут отключены." -setMultipleBySeparatingWithSpace: "Можно написать несколько через пробел" +regenerateLoginTokenDescription: "Создаёт новый токен, используемый внутри программы + во время входа. Обычно в этом нет необходимости. При создании все устройства будут + отключены." +setMultipleBySeparatingWithSpace: "Можно написать несколько через пробел." fileIdOrUrl: "Идентификатор файла или ссылка" behavior: "Поведение" sample: "Пример" abuseReports: "Жалобы" reportAbuse: "Жалоба" reportAbuseOf: "Пожаловаться на пользователя {name}" -fillAbuseReportDescription: "Опишите, пожалуйста, причину жалобы подробнее. Если речь о конкретной заметке, будьте добры приложить ссылку на неё." +fillAbuseReportDescription: "Опишите, пожалуйста, причину жалобы подробнее. Если речь + о конкретном посте, будьте добры приложить ссылку на неё." abuseReported: "Жалоба отправлена. Большое спасибо за информацию." reporteeOrigin: "О ком сообщено" reporterOrigin: "Кто сообщил" -forwardReport: "Перенаправление отчета на инстант." -forwardReportIsAnonymous: "Удаленный инстант не сможет увидеть вашу информацию и будет отображаться как анонимная системная учетная запись." +forwardReport: "Переслать отчет на удалённый сервер" +forwardReportIsAnonymous: "Удаленный сервер не сможет увидеть вашу личную информацию + — отчёт будет отображаться как отправленный от анонимной системная учетной записи." send: "Отправить" abuseMarkAsResolved: "Отметить жалобу как решённую" openInNewTab: "Открыть в новой вкладке" openInSideView: "Открывать в боковой колонке" defaultNavigationBehaviour: "Поведение навигации по умолчанию" -editTheseSettingsMayBreakAccount: "От изменений в этих настройках ваша учётная запись может поломаться." -instanceTicker: "Строка с названием инстанса в заметках" +editTheseSettingsMayBreakAccount: "От изменений в этих настройках ваша учётная запись + может поломаться." +instanceTicker: "Информация про записи на сервере" waitingFor: "Ждём, когда {x} ответит" random: "Случайные" system: "Система" @@ -645,18 +694,19 @@ createNew: "Новый документ" optional: "Необязательно" createNewClip: "Новая подборка" public: "Общедоступно" -i18nInfo: "Calckey переводят на разные языки добровольцы со всего света. Ваша помощь тоже пригодится здесь: {link}." +i18nInfo: "Firefish переводят на разные языки добровольцы со всего света. Ваша помощь + тоже пригодится здесь: {link}." manageAccessTokens: "Управление токенами доступа" accountInfo: "Сведения об учётной записи" -notesCount: "Количество заметок" +notesCount: "Количество постов" repliesCount: "Сколько раз пользователь кому-то ответил" -renotesCount: "Сколько раз пользователь делился заметками" +renotesCount: "Сколько раз пользователь делился постами" repliedCount: "Сколько раз ответили пользователю" -renotedCount: "Сколько раз делились заметками пользователя" +renotedCount: "Сколько раз делились постами пользователя" followingCount: "Количество подписок" followersCount: "Количество подписавшихся" sentReactionsCount: "Количество реакций пользователя" -receivedReactionsCount: "Количество реакций на заметки пользователя" +receivedReactionsCount: "Количество реакций на посты пользователя" pollVotesCount: "Сколько раз пользователь участвовал в опросах" pollVotedCount: "Сколько раз участвовали в опросах пользователя" yes: "Да" @@ -664,14 +714,17 @@ no: "Нет" driveFilesCount: "Количество файлов на диске" driveUsage: "Занято места на диске" noCrawle: "Запретить паукам индексировать сайт" -noCrawleDescription: "Просьба поисковым системам не ходить по вашему профилю, по заметкам, страницам и не индексировать их." -lockedAccountInfo: "Даже если вы вручную подтверждаете подписки, кто угодно может читать ваши заметки, если вы не отмечаете их «для подписчиков»." +noCrawleDescription: "Просьба поисковым системам не ходить по вашему профилю, по постам, + страницам и не индексировать их." +lockedAccountInfo: "Даже если вы вручную подтверждаете подписки, кто угодно может + читать ваши посты, если вы не отмечаете их «для подписчиков»." alwaysMarkSensitive: "Отмечать файлы как «содержимое не для всех» по умолчанию" loadRawImages: "Сразу показывать изображения в полном размере" disableShowingAnimatedImages: "Не проигрывать анимацию" -verificationEmailSent: "Вам отправлено письмо для подтверждения. Пройдите, пожалуйста, по ссылке из письма, чтобы завершить проверку." +verificationEmailSent: "Вам отправлено письмо для подтверждения. Пройдите, пожалуйста, + по ссылке из письма, чтобы завершить проверку." notSet: "Не настроено" -emailVerified: "Адрес электронной почты подтверждён." +emailVerified: "Адрес электронной почты подтверждён" noteFavoritesCount: "Количество добавленного в избранное" pageLikesCount: "Количество понравившихся страниц" pageLikedCount: "Количество страниц, понравившихся другим" @@ -680,23 +733,28 @@ useSystemFont: "Использовать шрифт, предлагаемый с clips: "Подборки" experimentalFeatures: "Экспериментальные функции" developer: "Разработчик" -makeExplorable: "Опубликовать профиль в «Обзоре»." -makeExplorableDescription: "Если выключить, ваш профиль не будет показан в разделе «Обзор»." -showGapBetweenNotesInTimeline: "Показывать разделитель между заметками в ленте" +makeExplorable: "Опубликовать профиль в «Обзоре»" +makeExplorableDescription: "Если выключить, ваш профиль не будет показан в разделе + «Обзор»." +showGapBetweenNotesInTimeline: "Показывать разделитель между постами в ленте" duplicate: "Дубликат" left: "Влево" center: "По центру" wide: "Толстый" narrow: "Тонкий" -reloadToApplySetting: "Это настройка вступает в силу при загрузке страницы. Перезагрузить сейчас?" +reloadToApplySetting: "Это настройка вступает в силу при загрузке страницы. Перезагрузить + сейчас?" needReloadToApply: "Изменения вступят в силу после перезагрузки страницы." showTitlebar: "Показать заголовок" clearCache: "Очистить кэш" onlineUsersCount: "Пользователей сейчас в сети: {n}" nUsers: "Пользователей: {n}" -nNotes: "Заметок: {n}" +nNotes: "Постов: {n}" sendErrorReports: "Посылать отчёты о сбоях" -sendErrorReportsDescription: "Если включено, когда возникнет какая-нибудь техническая проблема, подробные сведения об этом будут отправлены разработчикам Misskey. Это очень помогает делать программу лучше. В отчёты попадают тип и версия ОС, браузера, журнал действий (что привело к сбою) и тому подобное." +sendErrorReportsDescription: "Если включено, когда возникнет какая-нибудь техническая + проблема, подробные сведения об этом будут отправлены разработчикам Firefish.\n Это + очень помогает делать программу лучше. В отчёты попадают тип и версия ОС, браузера, + журнал действий (что привело к сбою) и тому подобное." myTheme: "Личная тема" backgroundColor: "Фон" accentColor: "Акцент" @@ -720,12 +778,12 @@ capacity: "Ёмкость" inUse: "Занято" editCode: "Редактировать исходный текст" apply: "Применить" -receiveAnnouncementFromInstance: "Получать оповещения с инстанса" +receiveAnnouncementFromInstance: "Получать оповещения с этого сервера" emailNotification: "Уведомления по электронной почте" publish: "Опубликовать" inChannelSearch: "Поиск по каналу" useReactionPickerForContextMenu: "Открывать палитру реакций правой кнопкой" -typingUsers: "Стук клавиш. Это {users}…" +typingUsers: "{users} печатает" jumpToSpecifiedDate: "Перейти к заданной дате" showingPastTimeline: "Отображается старая лента" clear: "Очистить" @@ -735,37 +793,39 @@ unlikeConfirm: "В самом деле отменить «нравится»?" fullView: "Полный вид" quitFullView: "Закрыть полный вид" addDescription: "Добавить описание" -userPagePinTip: "Можно добавить сюда заметки, выбрав нужную, и включив в её меню пункт «Закрепить в профиле»." -notSpecifiedMentionWarning: "В этой заметке есть упоминание тех, кто не включён в адресаты" +userPagePinTip: "Можно добавить сюда посты, выбрав нужный, и включив в её меню пункт + «Закрепить в профиле»." +notSpecifiedMentionWarning: "В этом посте есть упоминание тех, кто не включён в адресаты" info: "Описание" userInfo: "Сведения о пользователе" unknown: "Неизвестно" onlineStatus: "Присутствие в сети" hideOnlineStatus: "Скрыть присутствие" -hideOnlineStatusDescription: "Сокрытие присутствия делает некоторые функции, такие как поиск, менее удобными." +hideOnlineStatusDescription: "Сокрытие присутствия делает некоторые функции, такие + как поиск, менее удобными." online: "В сети" active: "Действует" offline: "Не в сети" notRecommended: "Не рекомендуется" botProtection: "Ботозащита" -instanceBlocking: "Блокировка инстансов" +instanceBlocking: "Управление федерацией" selectAccount: "Выберите учётную запись" switchAccount: "Сменить учётную запись" -enabled: "Вкл." -disabled: "Откл." +enabled: "Включено" +disabled: "Отключено" quickAction: "Быстрое действие" user: "Пользователи" administration: "Управление" accounts: "Учётные записи" switch: "Переключение" -noMaintainerInformationWarning: "Не заполнены сведения об администраторах" -noBotProtectionWarning: "Ботозащита не настроена" +noMaintainerInformationWarning: "Не заполнены сведения об администраторах." +noBotProtectionWarning: "Ботозащита не настроена." configure: "Настроить" postToGallery: "Опубликовать в галерею" gallery: "Галерея" recentPosts: "Недавние публикации" popularPosts: "Популярные публикации" -shareWithNote: "Поделиться заметкой" +shareWithNote: "Поделиться постом" ads: "Реклама" expiration: "Опрос длится" memo: "Памятка" @@ -773,11 +833,12 @@ priority: "Приоритет" high: "Высокий" middle: "Средне" low: "Низкий" -emailNotConfiguredWarning: "Не указан адрес электронной почты" +emailNotConfiguredWarning: "Не указан адрес электронной почты." ratio: "Соотношение" previewNoteText: "Предварительный просмотр" customCss: "Индивидуальный CSS" -customCssWarn: "Используйте эту настройку только если знаете, что делаете. Ошибки здесь чреваты тем, что сайт перестанет нормально работать у вас." +customCssWarn: "Используйте эту настройку только если знаете, что делаете. Ошибки + здесь чреваты тем, что сайт перестанет нормально работать у вас." global: "Всеобщая" squareAvatars: "Квадратные аватарки" sent: "Отправить" @@ -787,12 +848,14 @@ hashtags: "Хэштег" troubleshooting: "Разрешение проблем" useBlurEffect: "Размытие в интерфейсе" learnMore: "Подробнее" -misskeyUpdated: "Misskey обновился!" -whatIsNew: "Что новенького?" +misskeyUpdated: "Firefish обновился!" +whatIsNew: "Показать изменения" translate: "Перевод" translatedFrom: "Перевод. Язык оригинала — {x}" accountDeletionInProgress: "В настоящее время выполняется удаление учетной записи" -usernameInfo: "Имя, которое отличает вашу учетную запись от других на этом сервере. Вы можете использовать алфавит (a~z, A~Z), цифры (0~9) или символы подчеркивания (_). Имена пользователей не могут быть изменены позже." +usernameInfo: "Имя, которое отличает вашу учетную запись от других на этом сервере. + Вы можете использовать алфавит (a~z, A~Z), цифры (0~9) или символы подчеркивания + (_). Имена пользователей не могут быть изменены позже." aiChanMode: "ИИ режим" keepCw: "Сохраняйте Предупреждения о содержимом" pubSub: "Учётные записи Pub/Sub" @@ -808,12 +871,14 @@ filter: "Фильтры" controlPanel: "Панель управления" manageAccounts: "Управление аккаунтом" makeReactionsPublic: "Опубликовать список реакций" -makeReactionsPublicDescription: "Список сделанных вами реакций доступен для просмотра всем желающим." -classic: "Классика" +makeReactionsPublicDescription: "Список сделанных вами реакций доступен для просмотра + всем желающим." +classic: "Центрированный" muteThread: "Заглушить цепочку" unmuteThread: "Отменить глушение цепочки" ffVisibility: "Видимость подписок и подписчиков" -ffVisibilityDescription: "Здесь можно настроить, кто будет видеть ваши подписки и подписчиков." +ffVisibilityDescription: "Здесь можно настроить, кто будет видеть ваши подписки и + подписчиков." continueThread: "Показать следующие ответы" deleteAccountConfirm: "Учётная запись будет безвозвратно удалена. Подтверждаете?" incorrectPassword: "Пароль неверен." @@ -822,18 +887,19 @@ hide: "Спрятать" leaveGroup: "Покинуть группу" leaveGroupConfirm: "Покинуть группу «{name}»?" useDrawerReactionPickerForMobile: "Выдвижная палитра на мобильном устройстве" -welcomeBackWithName: "С возвращением, {name}!" -clickToFinishEmailVerification: "Пожалуйста, нажмите [{ok}], чтобы завершить подтверждение адреса электронной почты." +welcomeBackWithName: "С возвращением, {name}" +clickToFinishEmailVerification: "Пожалуйста, нажмите [{ok}], чтобы завершить подтверждение + адреса электронной почты." overridedDeviceKind: "Тип устройства" smartphone: "Смартфон" tablet: "Планшет" auto: "Автоматически" -themeColor: "Цвет темы" +themeColor: "Цвет темы сервера" size: "Размер" numberOfColumn: "Количество столбцов" searchByGoogle: "Поиск" -instanceDefaultLightTheme: "Светлая тема по умолчанию" -instanceDefaultDarkTheme: "Темная тема по умолчанию" +instanceDefaultLightTheme: "Светлая тема по умолчанию для всего сервера" +instanceDefaultDarkTheme: "Темная тема по умолчанию для всего сервера" indefinitely: "вечно" file: "Файлы" recommended: "Рекомендуем" @@ -845,11 +911,23 @@ label: "Метка" localOnly: "Локально" beta: "Бета" enableAutoSensitive: "Автоматическое определение NSFW" -enableAutoSensitiveDescription: "Если доступно, используйте машинное обучение для автоматической установки флага NSFW на носителе. Даже если эта функция отключена, она может быть установлена ​​автоматически в зависимости от инстанта." +enableAutoSensitiveDescription: "Если доступно, используйте машинное обучение для + автоматической установки флага NSFW на носителе. Даже если эта функция отключена, + она может быть установлена автоматически в зависимости от инстанта." account: "Учётные записи" _sensitiveMediaDetection: - description: "Машинное обучение может быть использовано для автоматического обнаружения чувствительных медиа для модерации. Нагрузка на сервер увеличивается незначительно." + description: "Машинное обучение может быть использовано для автоматического обнаружения + чувствительных медиа для модерации. Нагрузка на сервер увеличивается незначительно." setSensitiveFlagAutomatically: "Установить флаг NSFW" + sensitivity: Чувствительность обнаружения + sensitivityDescription: Снижение чувствительности приведет к меньшему количеству + ошибочных обнаружений (ложноположительных результатов), в то время как ее увеличение + приведет к меньшему количеству пропущенных обнаружений (ложноотрицательных результатов). + setSensitiveFlagAutomaticallyDescription: Результаты внутреннего обнаружения будут + сохранены, даже если эта опция отключена. + analyzeVideos: Включить анализ видео + analyzeVideosDescription: Анализирует видео в дополнение к изображениям. Это немного + увеличит нагрузку на сервер. _emailUnavailable: used: "Уже используется" format: "Неверный формат" @@ -861,13 +939,16 @@ _ffVisibility: followers: "Показываются только подписчикам" private: "Показываются только вам" _signup: - almostThere: "Почти готово!" + almostThere: "Почти готово" emailAddressInfo: "Введите ваш адрес электронной почты." - emailSent: "На указанный вами адрес электронной почты ({email}) отправлено письмо. Перейдите по ссылке в письме, чтобы завершить регистрацию." + emailSent: "На указанный вами адрес электронной почты ({email}) отправлено письмо. + Перейдите по ссылке в письме, чтобы завершить регистрацию." _accountDelete: accountDelete: "Удалить свою учётную запись" - mayTakeTime: "Удаление учётной записи — ресурсозатратный процесс. Он может занять много времени, если вы много писали и загружали файлов." - sendEmail: "Когда ваша учетная запись будет удалена, мы сообщим на указанную вами электронную почту." + mayTakeTime: "Удаление учётной записи — ресурсозатратный процесс. Он может занять + много времени, если вы много писали и загружали файлов." + sendEmail: "Когда ваша учетная запись будет удалена, мы сообщим на указанную вами + электронную почту." requestAccountDelete: "Запросить удаление вашей учетной записи" started: "Процесс удаления начался." inProgress: "Удаление в процессе" @@ -875,19 +956,22 @@ _ad: back: "Выход" reduceFrequencyOfThisAd: "Реже показывать эту рекламу" _forgotPassword: - enterEmail: "Введите адрес электронной почты, который ввели при регистрации. На неё будет выслана ссылка для смены пароля." - ifNoEmail: "Если вы не ввели свой адрес электронной почты, свяжитесь с администратором ресурса, чтобы сменить пароль." - contactAdmin: "Здесь не используются адреса электронной почты, так что свяжитесь с администратором, чтобы поменять пароль." + enterEmail: "Введите адрес электронной почты, который ввели при регистрации. На + неё будет выслана ссылка для смены пароля." + ifNoEmail: "Если вы не ввели свой адрес электронной почты, свяжитесь с администратором + ресурса, чтобы сменить пароль." + contactAdmin: "Здесь не используются адреса электронной почты, так что свяжитесь + с администратором, чтобы поменять пароль." _gallery: my: "Личная" liked: "Понравившееся" - like: "Нравится!" + like: "Нравится" unlike: "Отменить «нравится»" _email: _follow: title: "Новый подписчик" _receiveFollowRequest: - title: "Новый запрос на подписку." + title: "Новый запрос на подписку" _plugin: install: "Установка расширений" installWarn: "Пожалуйста, не устанавливайте расширения, которым не доверяете." @@ -899,13 +983,15 @@ _registry: domain: "Домен" createKey: "Новый ключ" _aboutMisskey: - about: "Misskey — программа с открытым исходным кодом, которую разрабатывает syuilo с 2014 года." + about: "Firefish это форк Firefish, сделанный ThatOneCalculator, разработка которого + началась с 2022." contributors: "Основные соавторы" allContributors: "Все соавторы" source: "Исходный код" - translation: "Перевод Misskey" - donate: "Пожертвование на Misskey" - morePatrons: "Большое спасибо и многим другим, кто принял участие в этом проекте! 🥰" + translation: "Перевод Firefish" + donate: "Пожертвование на Firefish" + morePatrons: "Большое спасибо и многим другим, кто принял участие в этом проекте! + 🥰" patrons: "Материальная поддержка" _nsfw: respect: "Скрывать содержимое не для всех" @@ -913,10 +999,13 @@ _nsfw: force: "Скрывать вообще все файлы" _mfm: cheatSheet: "Подсказка по разметке MFM" - intro: "MFM — язык оформления текста, который придуман специально для Misskey и готов для применения во многих местах. На этой странице собраны и кратко изложены способы его использовать." - dummy: "Misskey расширяет границы Федиверса." + intro: "MFM — язык оформления текста,используемый в Firefish, Firefish, Akkoma и готов + для применения во многих местах. На этой странице собраны и кратко изложены способы + его использовать." + dummy: "Firefish расширяет границы Федиверса" mention: "Упоминание" - mentionDescription: "При помощи знака «собака» перед именем можно упомянуть какого-нибудь пользователя." + mentionDescription: "При помощи знака «собака» перед именем можно упомянуть какого-нибудь + пользователя." hashtag: "Хэштег" hashtagDescription: "При помощи знака «решётка» перед словом задаётся хэштег." url: "Простая ссылка (URL)" @@ -932,11 +1021,13 @@ _mfm: inlineCode: "Программа (в тексте)" inlineCodeDescription: "Подсвечивает фрагмент программы внутри сплошного текста." blockCode: "Программа (блок)" - blockCodeDescription: "Оформляет текст программы в виде отдельного блокоа. Он может состоять из множества строк." + blockCodeDescription: "Оформляет текст программы в виде отдельного блокоа. Он может + состоять из множества строк." inlineMath: "Математическое выражение (в тексте)" - inlineMathDescription: "Позволяет вставлять математические выражения внутрь текста при помощи языка KaTeX." + inlineMathDescription: "Позволяет вставлять математические выражения внутрь текста + при помощи языка KaTeX" blockMath: "Математическое выражение (блок)" - blockMathDescription: "Оформляет математическое выражение (KaTeX) на отдельной строке." + blockMathDescription: "Оформляет математическое выражение (KaTeX) на отдельной строке" quote: "Цитата" quoteDescription: "Так можно процитировать чей-то текст." emoji: "Собственные эмодзи" @@ -948,7 +1039,7 @@ _mfm: jelly: "Анимация желе (шлёп-плёп)" jellyDescription: "Напоминает горку джема, дёргающуюся от шлепков." tada: "Анимация (та-дам!)" - tadaDescription: "Получается нечто выпрыгивающее, как бы крича: «а вот и я!»" + tadaDescription: "Получается нечто выпрыгивающее, как бы крича: «а вот и я!»." jump: "Анимация прыжков (прыг-скок)" jumpDescription: "Побуждает радостно подпрыгивать." bounce: "Анимация отскоков (бум-бум)" @@ -956,7 +1047,7 @@ _mfm: shake: "Анимация дрожи (б-р-р-р)" shakeDescription: "Такое дрожит, словно от холода. Или от страха." twitch: "Анимация тряски" - twitchDescription: "Заставляет трястись как одержимого" + twitchDescription: "Заставляет трястись как одержимого." spin: "Вращение" spinDescription: "Так можно крутить содержимое в разных направлениях." x2: "Крупный шрифт" @@ -966,7 +1057,8 @@ _mfm: x4: "Совсем крупно" x4Description: "Увеличивает содержимое совсем сильно." blur: "Размытие" - blurDescription: "Размывает текст до нечитаемости, будто его поместили за матовое стекло. Наведение указателя мыши на размытый текст возвращает чёткость." + blurDescription: "Размывает текст до нечитаемости, будто его поместили за матовое + стекло. Наведение указателя мыши на размытый текст возвращает чёткость." font: "Шрифт" fontDescription: "Так можно писать произвольным шрифтом." rainbow: "Радуга" @@ -975,6 +1067,8 @@ _mfm: sparkleDescription: "Добавляет эффект искрящихся частиц." rotate: "Повернуть" rotateDescription: "Поворачивает на заданный угол." + plain: Обычный текст + plainDescription: Деактивирует эффекты всех MFM, содержащихся в этом эффекте MFM. _instanceTicker: none: "Не показывать" remote: "Только для других сайтов" @@ -983,6 +1077,7 @@ _serverDisconnectedBehavior: reload: "Автоматическая перезагрузка" dialog: "Предупреждение" quiet: "Показать ненавязчивое предупреждение" + nothing: Ничего не делать _channel: create: "Создать канал" edit: "Редактировать канал" @@ -992,7 +1087,7 @@ _channel: owned: "Собственные" following: "Подписки" usersCount: "Участников: {n}" - notesCount: "Заметок: {n}" + notesCount: "Постов: {n}" _menuDisplay: sideFull: "Сторона" sideIcon: "Сторона (иконки)" @@ -1000,26 +1095,34 @@ _menuDisplay: hide: "Спрятать" _wordMute: muteWords: "Скрыть слово" - muteWordsDescription: "Пишите слова через пробел в одной строке, чтобы фильтровать их появление вместе; а если хотите фильтровать любое из них, пишите в отдельных строках." - muteWordsDescription2: "Здесь можно использовать регулярные выражения — просто заключите их между двумя дробными чертами (/)." - softDescription: "Соответствующие условиям заметки будут спрятаны из вашей ленты." - hardDescription: "Соответстующие условиям заметки вообще не будут попадать в вашу ленту. Даже если вы поменяете условия, отсеенные таким образом заметки уже не появятся." + muteWordsDescription: "Пишите слова через пробел в одной строке, чтобы фильтровать + их появление вместе; а если хотите фильтровать любое из них, пишите в отдельных + строках." + muteWordsDescription2: "Здесь можно использовать регулярные выражения — просто заключите + их между двумя дробными чертами (/)." + softDescription: "Соответствующие условиям посты будут спрятаны из вашей ленты." + hardDescription: "Соответстующие условиям посты вообще не будут попадать в вашу + ленту. Даже если вы поменяете условия, отсеенные таким образом посты уже не появятся." soft: "Мягкий" hard: "Жёсткий" - mutedNotes: "Скрытые заметки" + mutedNotes: "Скрытые посты" _instanceMute: heading: "Список заглушенных инстансов" + instanceMuteDescription2: Разделить переносом строки + instanceMuteDescription: Это будет скрывать все посты/репосты с указанных инстансов, + включая ответы пользователю с заглушенного инстанса. + title: Скрывает посты с указанных инстансов. _theme: explore: "Обзор" install: "Установить тему" manage: "Менеджер тем" code: "Код темы" description: "Описание" - installed: "Тема «{name}» установлена." + installed: "Тема «{name}» установлена" installedThemes: "Установленные темы" builtinThemes: "Встроенные темы" - alreadyInstalled: "Тема уже установлена." - invalid: "Формат темы некорректный." + alreadyInstalled: "Тема уже установлена" + invalid: "Формат темы некорректный" make: "Создать тему" base: "Основа" addConstant: "Добавить константу" @@ -1036,8 +1139,9 @@ _theme: alpha: "Непрозрачность" darken: "Затемнение" lighten: "Осветление" - inputConstantName: "Введите имя для константы." - importInfo: "Если вы введете код темы здесь, вы можете импортировать его в редактор тем." + inputConstantName: "Введите имя для константы" + importInfo: "Если вы введете код темы здесь, вы можете импортировать его в редактор + тем" deleteConstantConfirm: "Вы действительно хотите удалить константу {const}?" keys: accent: "Акцент" @@ -1084,8 +1188,8 @@ _theme: accentLighten: "Фон (осветлённый)" fgHighlighted: "Подсвеченный текст" _sfx: - note: "Заметки" - noteMy: "Собственные заметки" + note: "Новый пост" + noteMy: "Собственные посты" notification: "Уведомления" chat: "Сообщения" chatBg: "Сообщения (фон)" @@ -1107,35 +1211,52 @@ _time: hour: "ч" day: "сут" _tutorial: - title: "Как использовать Calckey" + title: "Как использовать Firefish" step1_1: "Добро пожаловать!" step1_2: "Давайте настроим вас. Вы будете работать в кратчайшие сроки!" - step2_1: "Сначала, пожалуйста, заполните свой профиль" - step2_2: "Предоставив некоторую информацию о себе, другим людям будет легче понять, хотят ли они видеть ваши записи или следить за вами." + step2_1: "Сначала, пожалуйста, заполните свой профиль." + step2_2: "Предоставив некоторую информацию о себе, другим людям будет легче понять, + хотят ли они видеть ваши записи или следить за вами." step3_1: "Теперь пора следить за некоторыми людьми!" - step3_2: "Ваша домашняя и социальная ленты основаны на том, за кем вы следите, поэтому для начала попробуйте следить за парой аккаунтов.\nНажмите на кружок с плюсом в правом верхнем углу профиля, чтобы следить за ним." - step4_1: "Давайте выйдем на вас" - step4_2: "Для своего первого сообщения некоторые люди любят делать {introduction} сообщение или простое \"Hello world!\"" + step3_2: "Ваша домашняя и социальная ленты основаны на том, за кем вы следите, поэтому + для начала попробуйте следить за парой аккаунтов.\nНажмите на кружок с плюсом + в правом верхнем углу профиля, чтобы следить за ним." + step4_1: "Давайте выйдем на вас." + step4_2: "Для своего первого сообщения некоторые люди любят делать {introduction} + сообщение или простое \"Hello world!\"" step5_1: "Временные рамки, везде временные рамки!" step5_2: "В вашем экземпляре включены {timelines} различных временных линий." - step5_3: "Главная {icon} временная шкала - это шкала, где вы можете видеть сообщения ваших подписчиков." - step5_4: "Местная {icon} временная шкала - это шкала, где вы можете видеть сообщения всех остальных пользователей данного экземпляра" - step5_5: "Временная шкала Рекомендуемые {icon} - это шкала, где вы можете видеть сообщения от инстанций, рекомендованных администраторами." - step5_6: "На временной шкале Social {icon} отображаются сообщения от друзей ваших подписчиков" - step5_7: "Глобальная {icon} временная шкала - это место, где вы можете видеть сообщения от всех других подключенных экземпляров" + step5_3: "Главная {icon} лента - это лента, где вы можете видеть сообщения ваших + подписок и других на этом инстансе. Если вы хотите чтобы главная лента показывала + только посты ваших подписок вы можете легко это изменить в настройках!" + step5_4: "Местная {icon} лента - это лента где вы можете видеть сообщения всех остальных + пользователей данного инстанса." + step5_5: "Лента Социальная {icon} - это лента, где вы можете видеть посты только + от аккаунтов, на которые вы подписаны." + step5_6: "Лента Рекомендованная {icon} это лента, где вы можете видеть посты с инстансов, + рекомендованных администраторами." + step5_7: "Глобальная {icon} лента - это место, где вы можете видеть сообщения от + всех других подключенных экземпляров." step6_1: "Итак, что это за место?" - step6_2: "Ну, вы не просто присоединились к Кальки. Вы присоединились к порталу в Fediverse, взаимосвязанной сети из тысяч серверов, называемых \"инстансами\"." - step6_3: "Каждый сервер работает по-своему, и не на всех серверах работает Calckey. Но этот работает! Это немного сложно, но вы быстро разберетесь" + step6_2: "Ну, вы не просто присоединились к Кальки. Вы присоединились к порталу + в Fediverse, взаимосвязанной сети из тысяч серверов, называемых \"инстансами\"\ + ." + step6_3: "Каждый сервер работает по-своему, и не на всех серверах работает Firefish. + Но этот работает! Это немного сложно, но вы быстро разберетесь." step6_4: "Теперь идите, изучайте и развлекайтесь!" _2fa: alreadyRegistered: "Двухфакторная аутентификация уже настроена." - registerDevice: "Зарегистрируйте ваше устройство" - registerKey: "Зарегистрировать ключ" - step1: "Прежде всего, установите на устройство приложение для аутентификации, например, {a} или {b}." + registerTOTP: "Зарегистрируйте ваше устройство" + registerSecurityKey: "Зарегистрировать ключ" + step1: "Прежде всего, установите на устройство приложение для аутентификации, например, + {a} или {b}." step2: "Далее отсканируйте отображаемый QR-код при помощи приложения." step3: "И наконец, введите код, который покажет приложение." - step4: "Теперь при каждом входе на сайт вам нужно будет вводить код из приложения аналогичным образом." - securityKeyInfo: "Вы можете настроить вход с помощью аппаратного ключа безопасности, поддерживающего FIDO2, или отпечатка пальца или PIN-кода на устройстве." + step4: "Теперь при каждом входе на сайт вам нужно будет вводить код из приложения + аналогичным образом." + securityKeyInfo: "Вы можете настроить вход с помощью аппаратного ключа безопасности, + поддерживающего FIDO2, или отпечатка пальца или PIN-кода на устройстве." + step2Url: 'Вы также можете ввести этот URL если используете программу на компьютере:' _permissions: "read:account": "Просматривать данные учётной записи" "write:account": "Изменять данные учётной записи" @@ -1151,7 +1272,7 @@ _permissions: "write:messaging": "Писать и удалять сообщения" "read:mutes": "Смотреть спискок скрытых пользователей" "write:mutes": "Изменять список скрытых пользователей" - "write:notes": "Писать и удалять заметки" + "write:notes": "Писать и удалять посты" "read:notifications": "Смотреть уведомления" "write:notifications": "Изменять уведомления" "read:reactions": "Смотреть реакции" @@ -1172,16 +1293,18 @@ _permissions: _auth: shareAccess: "Дать доступ для «{name}» к вашей учётной записи?" shareAccessAsk: "Уверены, что хотите дать приложению доступ к своей учётной записи?" - permissionAsk: "Приложение запрашивает следующие разрешения:" + permissionAsk: "Приложение запрашивает следующие разрешения" pleaseGoBack: "Вернитесь, пожалуйста, в приложение" callback: "Возврат в приложение" denied: "Доступ закрыт" + copyAsk: Пожалуйста, вставьте следующий код авторизации в приложение _antennaSources: - all: "Все заметки" - homeTimeline: "Заметки тех на которых вы подписаны" - users: "Заметки выбранных пользователей" - userList: "Заметки пользователей из выбранных списков" - userGroup: "Заметки от пользователей из заданной группы" + all: "Все посты" + homeTimeline: "Посты тех на которых вы подписаны" + users: "Посты выбранных пользователей" + userList: "Посты пользователей из выбранных списков" + userGroup: "Посты от пользователей из заданной группы" + instances: Посты от всех пользователей на инстансе _weekday: sunday: "Воскресенье" monday: "Понедельник" @@ -1210,20 +1333,26 @@ _widgets: serverMetric: "Показатели сервера" aiscript: "Консоль AiScript" aichan: "Ай" + rssTicker: RSS-тикер + unixClock: UNIX часы + instanceCloud: Облачко инстансов + userList: Список пользователей + _userList: + chooseList: Выберите список _cw: hide: "Спрятать" show: "Показать еще" chars: "знаков: {count}" files: "файлов: {count}" _poll: - noOnlyOneChoice: "Нужно хотя бы два варианта." + noOnlyOneChoice: "Нужно хотя бы два варианта" choiceN: "Выбор {n}" noMore: "Больше вариантов добавить нельзя" canMultipleVote: "Можно выбрать несколько вариантов" expiration: "Опрос длится" infinite: "вечно" - at: "до указанной даты" - after: "заданное время" + at: "Заканчивается..." + after: "Заканчивается после..." deadlineDate: "Дата окончания" deadlineTime: "Время" duration: "Длительность" @@ -1240,7 +1369,7 @@ _poll: _visibility: public: "Общедоступно" publicDescription: "Открыто для всех" - home: "Домашняя" + home: "Скрытый" homeDescription: "Не для общих лент" followers: "Для подписчиков" followersDescription: "Только вашим подписчикам" @@ -1249,30 +1378,34 @@ _visibility: localOnly: "Локально" localOnlyDescription: "Только для этого сайта" _postForm: - replyPlaceholder: "Ответ на заметку..." + replyPlaceholder: "Ответ на пост..." quotePlaceholder: "Пояснение к цитате..." - channelPlaceholder: "Отправить в канал" + channelPlaceholder: "Отправить в канал..." _placeholders: a: "Как дела?" b: "Что интересного вокруг?" c: "Что грызёт тебя, дружище?" - d: "Есть что сказать?.." + d: "Есть что сказать?" e: "Напишите что-нибудь…" f: "В ожидании, когда вы напишете…" _profile: name: "Имя" username: "Имя пользователя" description: "О себе" - youCanIncludeHashtags: "Можете использовать здесь хэштеги" + youCanIncludeHashtags: "Можете использовать здесь хэштеги." metadata: "Дополнительные сведения" metadataEdit: "Редактировать дополнительные сведения" - metadataDescription: "Можно добавить до четырёх дополнительных граф в профиль." + metadataDescription: "Можно добавить до четырёх дополнительных граф в профиль. Вы + можете добавить тег {a} или тег {l} с {rel}, чтобы подтвердить ссылку в своем + профиле!" metadataLabel: "Метка" metadataContent: "Содержимое" changeAvatar: "Поменять аватар" changeBanner: "Поменять изображение в шапке" + locationDescription: Если вы сначала введете свой город, другим пользователям будет + показано ваше местное время. _exportOrImport: - allNotes: "Все записи\n" + allNotes: "Все посты" followingList: "Подписки" muteList: "Скрытые" blockingList: "Заблокированные" @@ -1285,10 +1418,10 @@ _charts: usersIncDec: "Изменение числа пользователей" usersTotal: "Количество пользователей" activeUsers: "Активные пользователи" - notesIncDec: "Изменение числа заметок" - localNotesIncDec: "Изменения числа локальных заметок" - remoteNotesIncDec: "Изменения числа заметок с других сайтов" - notesTotal: "Общее количество заметок" + notesIncDec: "Изменение числа постов" + localNotesIncDec: "Изменения числа локальных постов" + remoteNotesIncDec: "Изменения числа постов с других сайтов" + notesTotal: "Общее количество постов" filesIncDec: "Изменения числа файлов" filesTotal: "Суммарное количество файлов" storageUsageIncDec: "Изменения заполнения хранилища" @@ -1297,9 +1430,9 @@ _instanceCharts: requests: "Запросы" users: "Изменение числа пользователей" usersTotal: "Суммарное количество пользователей" - notes: "Изменение числа заметок" - notesTotal: "Суммарное количество заметок" - ff: "Изменения числа подписчиков" + notes: "Изменение числа постов" + notesTotal: "Суммарное количество постов" + ff: "Изменения числа подписчиков " ffTotal: "Суммарное количество подписчиков" cacheSize: "Изменения размера кэша" cacheSizeTotal: "Суммарный размер кэша" @@ -1310,17 +1443,18 @@ _timelines: local: "Местная" social: "Социальная" global: "Всеобщая" + recommended: Рекомендованная _pages: newPage: "Создать страницу" editPage: "Править страницу" readPage: "Читать страницу" - created: "Страница успешно создана." - updated: "Страница успешно обновлена." - deleted: "Страница успешно удалена." + created: "Страница успешно создана" + updated: "Страница успешно обновлена" + deleted: "Страница успешно удалена" pageSetting: "Настройки страницы" - nameAlreadyExists: "Указанный адрес страницы уже существует." - invalidNameTitle: "Указанный адрес страницы недопустим." - invalidNameText: "Проверьте, что не оставили поле пустым." + nameAlreadyExists: "Указанный адрес страницы уже существует" + invalidNameTitle: "Указанный адрес страницы недопустим" + invalidNameText: "Проверьте, что не оставили поле пустым" editThisPage: "Правка этой страницы" viewSource: "Просмотр исходника" viewPage: "Смотреть страницы" @@ -1359,7 +1493,7 @@ _pages: if: "Условный" _if: variable: "Переменная" - post: "Создание заметки" + post: "Создание поста" _post: text: "Текст" attachCanvasImage: "Прикрепить изображение с холста" @@ -1384,10 +1518,10 @@ _pages: id: "Метка холста" width: "Ширина" height: "Высота" - note: "Встроенная заметка" + note: "Встроенный пост" _note: - id: "Идентификатор заметки" - idDescription: "Можно также вставить ссылку на заметку." + id: "Идентификатор поста" + idDescription: "Можно также вставить ссылку на пост." detailed: "Подробный вид" switch: "Выключатель" _switch: @@ -1601,21 +1735,21 @@ _pages: argVariables: "Аргументы" _relayStatus: requesting: "В ожидании одобрения" - accepted: "Одобрено." - rejected: "Отказано." + accepted: "Одобрено" + rejected: "Отказано" _notification: - fileUploaded: "Файл успешно загружен." - youGotMention: "{name} упоминает вас." - youGotReply: "{name} отвечает вам." - youGotQuote: "{name} цитирует вас." - youRenoted: "{name} передаёт вашу заметку." - youGotPoll: "{name} участвует в вашем опросе." - youGotMessagingMessageFromUser: "{name} пишет вам." - youGotMessagingMessageFromGroup: "Новое сообщение в группе «{name}»." - youWereFollowed: "У вас новый подписчик." - youReceivedFollowRequest: "У вас новый запрос на подписку." - yourFollowRequestAccepted: "Ваш запрос на подписку одобрен." - youWereInvitedToGroup: "Вы приглашены в группу." + fileUploaded: "Файл успешно загружен" + youGotMention: "{name} упоминает вас" + youGotReply: "{name} отвечает вам" + youGotQuote: "{name} цитирует вас" + youRenoted: "{name} репостит ваш пост" + youGotPoll: "{name} участвует в вашем опросе" + youGotMessagingMessageFromUser: "{name} пишет вам" + youGotMessagingMessageFromGroup: "Новое сообщение в группе «{name}»" + youWereFollowed: "У вас новый подписчик" + youReceivedFollowRequest: "У вас новый запрос на подписку" + yourFollowRequestAccepted: "Ваш запрос на подписку одобрен" + youWereInvitedToGroup: "{userName} пригласил вас в группу" _types: all: "Все" follow: "Подписки" @@ -1629,9 +1763,13 @@ _notification: followRequestAccepted: "Запрос на подписку одобрен" groupInvited: "Приглашение в группы" app: "Уведомления из приложений" + pollEnded: Опрос закончен _actions: reply: "Ответить" renote: "Репост" + followBack: Подписался на вас обратно + emptyPushNotificationMessage: Пуш уведомления были обновлены + pollEnded: Результаты опроса стали доступны _deck: alwaysShowMainColumn: "Всегда показывать главную колонку" columnAlign: "Выравнивание колонок" @@ -1643,7 +1781,7 @@ _deck: swapDown: "Переставить ниже" stackLeft: "В столбик влево" popRight: "Из столбика вправо" - profile: "Профиль" + profile: "Воркспейс" _columns: main: "Основная" widgets: "Виджеты" @@ -1653,3 +1791,228 @@ _deck: list: "Списки" mentions: "Упоминания" direct: "Личное" + deleteProfile: Удалить воркспейс + introduction: Создайте идеальный интерфейс для себя, свободно расположив столбцы! + introduction2: Нажмите на + в правой части экрана, чтобы добавлять новые столбцы + в любое удобное для вас время. + widgetsIntroduction: Пожалуйста, выберите "Редактировать виджеты" в меню столбца + и добавьте виджет. + newProfile: Новый воркспейс + renameProfile: Переименовать воркспейс + nameAlreadyExists: Воркспейс с таким именем уже существует. +enableRecommendedTimeline: Включить рекомендованную ленту +regexpErrorDescription: 'Произошла ошибка в регулярном выражении на строке {line} + вашего {tab} списка скрытых слов:' +confirmToUnclipAlreadyClippedNote: Этот пост уже в подборке "{name}. Хотите ли вы + вместо этого удалить пост из подборки? +unclip: Удалить из подборки +secureMode: Безопасный Режим (Авторизованное Получение) +instanceSecurity: Безопасность сервера +seperateRenoteQuote: Разделить кнопки репоста и цитаты +accountMoved: 'Пользователь переместился на новый аккаунт:' +manageGroups: Управлять группами +allowedInstancesDescription: Список хостов, разрешённых для федерации, каждый разделён + новой строкой (применяется только в приватном режиме). +noThankYou: Нет, спасибо +addInstance: Добавить сервер +flagSpeakAsCat: Говорить как кот +flagSpeakAsCatDescription: Ваши будут посты няифицированы в режиме кота +selectInstance: Выбрать сервер +antennaInstancesDescription: Список серверов, каждый с новой строки +privateMode: Приватный режим +privateModeInfo: Только серверы в белом списке могут федерировать с вашим сервером. + Все посты будут скрыты из публичного доступа. +allowedInstances: Белый список серверов +userSaysSomethingReason: '{name} сказал {reason}' +renoteMute: Заглушить репосты +renoteUnmute: Разглушить репосты +hiddenTags: Скрытые хештеги +noInstances: Нет серверов +secureModeInfo: Не отправлять ответ на запросы с других серверов без подтверждения. +instanceDefaultThemeDescription: Введите код темы в формате объекта. +tenMinutes: 10 минут +oneHour: Один час +thereIsUnresolvedAbuseReportWarning: Есть не рассмотренные жалобы. +cropImage: Обрезать изображение +requireAdminForView: Вы должны войти с аккаунта администратора что просмотреть это. +refreshInterval: 'Интервал обновления ' +slow: Медленно +fast: Быстро +sensitiveMediaDetection: Обнаружение NSFW медиа +remoteOnly: Только другие сайты +navbar: Панель навигации +customMOTD: Своё MOTD (сообщения на заставке) +customMOTDDescription: Пользовательские сообщения для MOTD (заставки), разделенные + разрывами строк, будут отображаться случайным образом каждый раз, когда пользователь + загружает / перезагружает страницу. +recommendedInstancesDescription: Рекомендуемые инстансы, разделенные разрывами строк, + должны отображаться на рекомендуемой ленте. +caption: Автоматическая подпись +splash: Заставка +updateAvailable: Возможно, доступно обновление! +move: Переместить +swipeOnDesktop: Разрешить свайпы в мобильном стиле на десктопе +showAds: Показывать рекламу +noEmailServerWarning: Почтовый сервер не настроен. +type: Тип +numberOfPageCacheDescription: Увеличение этого числа повысит удобство для пользователей, + но приведет к увеличению нагрузки на сервер, а также к использованию большего объема + памяти. +statusbar: Панель статуса +speed: Скорость +oneDay: Один день +oneWeek: Одна неделя +failedToFetchAccountInformation: Не удалось получить информацию о аккаунте +cropImageAsk: Желаете ли вы обрезать это изображение? +recentNHours: Последние {n} часов +recentNDays: Последние {n} дней +typeToConfirm: Введите {x} чтобы подтвердить +document: Документация +logoutConfirm: Действительно выйти? +failedToUpload: Не удалось загрузить +pushNotification: Пуш уведомления +subscribePushNotification: Включить пуш уведомления +unsubscribePushNotification: Отключить пуш уведомления +pushNotificationAlreadySubscribed: Пуш уведомления уже включены +sendPushNotificationReadMessage: Удалять пуш уведомления после того как соответствующие + уведомления или сообщения были прочитаны +customSplashIcons: Свои иконки для заставки (URL) +customSplashIconsDescription: URL-адреса для пользовательских значков заставки, разделенных + разрывами строк, будут отображаться случайным образом каждый раз, когда пользователь + загружает / перезагружает страницу. Пожалуйста, убедитесь, что изображения находятся + на статическом URL-адресе, предпочтительно все с размером 192x192. +logoImageUrl: URL изображения логотипа +showAdminUpdates: Указать, что доступна новая версия Firefish (только для администратора) +replayTutorial: Перезапустить туториал +migration: Миграция +showLocalPosts: 'Показать локальные посты в:' +homeTimeline: Домашняя лента +socialTimeline: Социальная лента +driveCapOverrideCaption: Сбросить до настроек по умолчанию введя значение 0 или меньше. +deleteAccount: Удалить аккаунт +numberOfPageCache: Число кэшируемых страниц +pushNotificationNotSupported: Ваш браузер или инстанс не поддерживает пуш уведомления +sendPushNotificationReadMessageCaption: Уведомление содержащее текст "{emptyPushNotificationMessage}" + будет показано на короткое время. Это может увеличить расход батареи вашего устройства, + если это применимо. +cannotUploadBecauseNoFreeSpace: Загрузка не удалась из-за нехватки места на Диске. +cannotUploadBecauseInappropriate: Этот файл не может быть загружен потому что его + части были обнаружены как потенциальное NSFW. +adminCustomCssWarn: Этот параметр следует использовать только в том случае, если вы + знаете, что он делает. Ввод неправильных значений может привести к тому, что ВСЕ + клиенты перестанут нормально функционировать. Пожалуйста, убедитесь, что ваш CSS + работает должным образом, протестировав его в настройках вашего пользователя. +showUpdates: Показывать всплывающее окно при обновлении Firefish +recommendedInstances: Рекомендованные инстансы +defaultReaction: Эмодзи реакция по умолчанию для выходящих и исходящих постов +license: Лицензия +indexPosts: Индексировать посты +indexFrom: Индексировать начиная с идентификатора поста и далее +indexFromDescription: оставьте пустым для индексации каждого поста +indexNotice: Теперь индексирование. Вероятно, это займет некоторое время, пожалуйста, + не перезагружайте свой сервер по крайней мере в течение часа. +customKaTeXMacro: Кастомные KaTex макросы +enableCustomKaTeXMacro: Включить кастомные KaTeX макросы +noteId: Идентификатор поста +_preferencesBackups: + inputName: Введите имя для этой резервной копии + list: Созданные резервные копии + loadFile: Загрузить из файла + apply: Применить для этого устройства + save: Сохранить изменения + saveNew: Сохранить новую резервную копию + applyConfirm: Вы действительно хотите применить резервную копию "{name}" на этом + устройстве? Существует настройки на этом устройстве будут перезаписаны. + renameConfirm: Переименовать резервную копию "{old}" в "{new}"? + saveConfirm: Сохранить резервную как {name}? + cannotSave: Сохранение не удалось + nameAlreadyExists: Резервная копия с именем "{name}" уже существующует. Выберите + другое имя. + deleteConfirm: Удалить резервную копию {name}? + noBackups: Нет резервных копий. Вы может сделать резервную копию настроек клиента + на этом сервере используя "Создать новую резервную копию". + createdAt: 'Создано: {date} {time}' + updatedAt: 'Обновлено: {date} {time}' + cannotLoad: Загрузка не удалась + invalidFile: Неправильный формат файла +enableEmojiReactions: Включить эмодзи реакции +migrationConfirm: "Вы абсолютно уверены что хотите мигрировать ваш аккаунт на {account}? + Как только вы сделаете, вы не сможете отменить это и не сможете нормально использовать + аккаунт снова.\nТакже, пожалуйста, убедитесь, что вы установили эту текущую учетную + запись в качестве учетной записи, с которой вы переходите." +reporter: Автор жалобы +mutePeriod: Длительность глушения +reflectMayTakeTime: Это может занять некоторое время чтобы вступило в силу. +rateLimitExceeded: Превышен лимит +pleaseSelect: Выберите вариант +shuffle: Перемешать +moveFrom: Переместится на этот аккаунт с старого аккаунта +moveFromLabel: 'Аккаунт с которого перемещаетесь:' +moveAccountDescription: Этот процесс необратим. Убедитесь что вы сделали псевдоним + для этого аккаунта до перемещения. Пожалуйста введите аккаунт в формате @person@instance.com +moveTo: Переместить текущий аккаунт на новый аккаунт +_messaging: + groups: Группы + dms: Личные +isSystemAccount: Эта учетная запись создана и автоматически управляется системой. + Не рекомендуется модерировать, редактировать, удалять или каким либо другим образом + вмешивайтся в эту учётную запись — это может привести к поломке сервера. +activeEmailValidationDescription: Включить более строгую проверки адресов электронной + почты,что включает в себя проверку наличия одноразовых адресов и того, действительно + ли с ними можно связаться. Если флажок снят, проверяется только формат адреса. +moveToLabel: 'Аккаунт на который вы перемещаетесь:' +lastActiveDate: Последний раз использовался в +enterSendsMessage: Нажать Return в Сообщениях чтобы отправить сообщение (если выключено, + то Ctrl + Return) +moveAccount: Переместить аккаунт! +breakFollowConfirm: Вы действительно хотите удалить подписчика? +showEmojisInReactionNotifications: Показывать эмодзи в уведомлениях о реакциях +hiddenTagsDescription: 'Список хештегов (без #), которые вы желаете скрыть из "актуальное" + и "обзор". Скрытые хэштеги по-прежнему можно обнаружить в других местах.' +moveFromDescription: Это установит псевдоним для старого аккаунта, так что вы сможете + переместить тот аккаунт на текущий. Делайте это ДО перемещения со старого аккаунта. + Пожалуйста введите аккаунт в формате @person@instance.com +customKaTeXMacroDescription: 'Настройте макросы чтобы легко писать математические + выражения! Обозначение соответствует определениям команд LaTeX и записывается как + \newcommand{\название}{содержание} или \newcommand{\название}[количество аргументов]{содержание}. + Для примера, \add{3}[2]{#1 + #2} будет раскрывать \add{3}{foo} до 3 + foo. Фигурные + скобки, окружающие имя макроса, можно заменить на круглые или квадратные скобки. + Это влияет на квадратные скобки, используемые для аргументов. Для каждой строки + может быть определен один (и только один) макрос, и вы не можете прерывать строку + в середине определения. Недопустимые строки просто игнорируются. Поддерживаются + только простые функции подстановки строк; расширенный синтаксис, такой как условное + ветвление, здесь использоваться не может.' +cannotUploadBecauseExceedsFileSizeLimit: Этот файл не может быть загружен так как + он превышает максимально разрешённый размер. +apps: Приложения +silenceThisInstance: Заглушить сервер +silencedInstances: Заглушенные серверы +editNote: Редактировать заметку +edited: 'Редактировано в {date} {time}' +deleted: Удалённое +removeReaction: Удалить вашу реакцию +searchPlaceholder: Искать в Firefish +jumpToPrevious: Перейти к предыдущему +listsDesc: Списки позволяют вам создавать ленты с постами указанных пользователей. + Их можно найти на странице «Лента». +silenced: Игнорируется +antennasDesc: "Антенны отображают новые посты, отвечающие указанным критериям!\n К + ним можно перейти со страницы «Лента»." +expandOnNoteClickDesc: Если отключено, вы всё равно сможете открыть пост, воспользовавшись + меню на правой кнопке мыши или кликнув по времени публикации поста. +accessibility: Доступность +silencedInstancesDescription: Список адресов серверов, которые вы хотите заглушить. + Аккаунты на указанных серверах будут считаться «Заглушёнными», смогут только отправлять + запросы на подписку и не смогут упоминать локальных пользователей, если на них не + подписались. Эта настройка не влияет на заблокированные серверы. +clipsDesc: Подборки это категоризированные закладки, которыми можно делиться. Вы можете + создавать подборки из меню у конкретных постов. +alt: ALT +video: Видео +audio: Аудио +selectChannel: Выберите канал +expandOnNoteClick: Открывать пост по клику +channelFederationWarn: Каналы пока не федерируются с другими серверами +image: Изображение +cw: Предупреждение о содержании +xl: Очень крупно diff --git a/locales/sk-SK.yml b/locales/sk-SK.yml index ced0d67b7e..287606c9f4 100644 --- a/locales/sk-SK.yml +++ b/locales/sk-SK.yml @@ -1,7 +1,7 @@ --- _lang_: "Slovenčina" headlineMisskey: "Sieť prepojená poznámkami" -introMisskey: "Vitajte! Misskey je otvorená a decentralizovaná mikroblogovacia služba.\n\"Poznámkami\" môžete zdieľať svoje myšlienky so všetkými okolo. 📡\nPomocou \"reakcií\" môžete rýchlo vyjadri svoje pocity o každého poznámkach. 👍\nPoďte objavovať svet! 🚀" +introMisskey: "Vitajte! Firefish je otvorená a decentralizovaná mikroblogovacia služba.\n\"Poznámkami\" môžete zdieľať svoje myšlienky so všetkými okolo. 📡\nPomocou \"reakcií\" môžete rýchlo vyjadri svoje pocity o každého poznámkach. 👍\nPoďte objavovať svet! 🚀" monthAndDay: "{day}. {month}." search: "Hľadať" notifications: "Oznámenia" @@ -139,7 +139,7 @@ settingGuide: "Odporúčané nastavenia" cacheRemoteFiles: "Cachovanie vzdialených súborov" cacheRemoteFilesDescription: "Zakázanie tohoto nastavenia spôsobí, že vzdialené súbory budú odkazované priamo, namiesto ukladania do cache. Ušetrí sa tak miesto na serveri, ale zvýši sa dátový tok, pretože sa negenerujú miniatúry." flagAsBot: "Tento účet je bot" -flagAsBotDescription: "Ak je tento účet ovládaný programom, zaškrtnite túto voľbu. Ostatní uvidia, že je to bot a zabráni nekonečným interakciám s ďalšími botmi a upraví interné systémy Misskey, aby ho považoval za bota." +flagAsBotDescription: "Ak je tento účet ovládaný programom, zaškrtnite túto voľbu. Ostatní uvidia, že je to bot a zabráni nekonečným interakciám s ďalšími botmi a upraví interné systémy Firefish, aby ho považoval za bota." flagAsCat: "Tento účet je mačka" flagAsCatDescription: "Zvoľte túto voľbu, aby bol tento účet označený ako mačka." flagShowTimelineReplies: "Zobraziť odpovede na poznámky v časovej osi" @@ -177,7 +177,6 @@ operations: "Operácie" software: "Softvér" version: "Verzia" metadata: "Metadáta" -withNFiles: "{n} súbor(ov)" monitor: "Monitor" jobQueue: "Fronta úloh" cpuAndMemory: "CPU a pamäť" @@ -199,7 +198,7 @@ noUsers: "Žiadni používatelia" editProfile: "Upraviť profil" noteDeleteConfirm: "Naozaj chcete odstrániť túto poznámku?" pinLimitExceeded: "Ďalšie poznámky už nemôžete pripnúť." -intro: "Inštalácia Misskey je dokončená! Prosím vytvorte administrátora." +intro: "Inštalácia Firefish je dokončená! Prosím vytvorte administrátora." done: "Hotovo" processing: "Pracujem..." preview: "Náhľad" @@ -378,7 +377,7 @@ exploreFediverse: "Objavovať Fediverzum" popularTags: "Populárne značky" userList: "Zoznamy" about: "Informácie" -aboutMisskey: "O Misskey" +aboutFirefish: "O Firefish" administrator: "Administrátor" token: "Token" twoStepAuthentication: "Dvojfaktorová autentifikácia" @@ -524,7 +523,7 @@ sort: "Zoradiť" ascendingOrder: "Vzostupne" descendingOrder: "Zostupne" scratchpad: "Zápisník" -scratchpadDescription: "Zápisník poskytuje prostredia pre experimenty s AiScriptom. Môžete písať, spúšťať a skúšať vysledky pri interakcii s Misskey." +scratchpadDescription: "Zápisník poskytuje prostredia pre experimenty s AiScriptom. Môžete písať, spúšťať a skúšať vysledky pri interakcii s Firefish." output: "Výstup" script: "Skript" disablePagesScript: "Vypnúť AiScript na stránkach" @@ -648,7 +647,7 @@ createNewClip: "Vytvoriť nový klip" unclip: "Odopnúť" confirmToUnclipAlreadyClippedNote: "Táto poznámka je už pripnutá ako \"{name}\". Naozaj ju chcete odopnúť?" public: "Verejné" -i18nInfo: "Calckey je prekladaný do rôznych jazykov dobrovoľníkmi. Pomôcť môžete na {link}." +i18nInfo: "Firefish je prekladaný do rôznych jazykov dobrovoľníkmi. Pomôcť môžete na {link}." manageAccessTokens: "Spravovať prístupové tokeny" accountInfo: "Informácie o účte" notesCount: "Počet poznámok" @@ -699,7 +698,7 @@ onlineUsersCount: "{n} používateľov je online" nUsers: "{n} používateľov" nNotes: "{n} poznámok" sendErrorReports: "Poslať nahlásenie chyby" -sendErrorReportsDescription: "Keď je zapnuté, v prípade problému sa odošlú podrobné informácie o chybe do Misskey. Pomôžete tak zvýšiť kvalitu Misskey.\nTieto informácie zahŕňajú verziu vášho OS, použitý prehliadač, históriu aktivít, atď." +sendErrorReportsDescription: "Keď je zapnuté, v prípade problému sa odošlú podrobné informácie o chybe do Firefish. Pomôžete tak zvýšiť kvalitu Firefish.\nTieto informácie zahŕňajú verziu vášho OS, použitý prehliadač, históriu aktivít, atď." myTheme: "Moja téma" backgroundColor: "Pozadie" accentColor: "Akcent" @@ -790,7 +789,7 @@ hashtags: "Hashtagy" troubleshooting: "Riešenie problémov" useBlurEffect: "Používať efekty rozmazania v UI" learnMore: "Zistiť viac" -misskeyUpdated: "Misskey sa aktualizoval!" +misskeyUpdated: "Firefish sa aktualizoval!" whatIsNew: "Čo je nové?" translate: "Preložiť" translatedFrom: "Preložené z {x}" @@ -961,13 +960,13 @@ _registry: keys: "Kľúče" domain: "Doména" createKey: "Vytvoriť kľúč" -_aboutMisskey: +_aboutFirefish: about: "Misskey je open-source softvér, ktorý vyvíja syuilo od 2014." contributors: "Hlavní prispievatelia" allContributors: "Všetci prispievatelia" source: "Zdrojový kód" - translation: "Preložiť Misskey" - donate: "Podporiť Misskey" + translation: "Preložiť Firefish" + donate: "Podporiť Firefish" morePatrons: "Takisto oceňujeme podporu mnoých ďalších, ktorí tu nie sú uvedení. Ďakujeme! 🥰" patrons: "Prispievatelia" _nsfw: @@ -976,8 +975,8 @@ _nsfw: force: "Skryť všetky médiá" _mfm: cheatSheet: "MFM Cheatsheet" - intro: "MFM je Misskey exkluzívny značkovací jazyk, ktorý sa dá používať na viacerých miestach. Tu môžete vidieť zoznam všetkej dostupnej MFM syntaxe." - dummy: "Misskey rozširuje svet Fediverza" + intro: "MFM je Firefish exkluzívny značkovací jazyk, ktorý sa dá používať na viacerých miestach. Tu môžete vidieť zoznam všetkej dostupnej MFM syntaxe." + dummy: "Firefish rozširuje svet Fediverza" mention: "Zmienka" mentionDescription: "Používateľa spomeniete použítím zavináča a mena používateľa" hashtag: "Hashtag" @@ -1175,7 +1174,7 @@ _time: hour: "hod" day: "dní" _tutorial: - title: "How to use Calckey" + title: "How to use Firefish" step1_1: "Welcome!" step1_2: "Let's get you set up. You'll be up and running in no time!" step2_1: "First, please fill out your profile." @@ -1192,13 +1191,13 @@ _tutorial: step5_6: "The Social {icon} timeline is where you can see posts from friends of your followers." step5_7: "The Global {icon} timeline is where you can see posts from every other connected instance." step6_1: "So, what is this place?" - step6_2: "Well, you didn't just join Calckey. You joined a portal to the Fediverse, an interconnected network of thousands of servers, called \"instances\"." - step6_3: "Each server works in different ways, and not all servers run Calckey. This one does though! It's a bit complicated, but you'll get the hang of it in no time." + step6_2: "Well, you didn't just join Firefish. You joined a portal to the Fediverse, an interconnected network of thousands of servers, called \"instances\"." + step6_3: "Each server works in different ways, and not all servers run Firefish. This one does though! It's a bit complicated, but you'll get the hang of it in no time." step6_4: "Now go, explore, and have fun!" _2fa: alreadyRegistered: "Už ste zaregistrovali 2-faktorové autentifikačné zariadenie." - registerDevice: "Registrovať nové zariadenie" - registerKey: "Registrovať bezpečnostný kľúč" + registerTOTP: "Registrovať nové zariadenie" + registerSecurityKey: "Registrovať bezpečnostný kľúč" step1: "Najprv si nainštalujte autentifikačnú aplikáciu (napríklad {a} alebo {b}) na svoje zariadenie." step2: "Potom, naskenujte QR kód zobrazený na obrazovke." step2Url: "Do aplikácie zadajte nasledujúcu URL adresu:" @@ -1338,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/sv-SE.yml b/locales/sv-SE.yml index bd895135c7..dea1853299 100644 --- a/locales/sv-SE.yml +++ b/locales/sv-SE.yml @@ -1,7 +1,7 @@ --- _lang_: "Svenska" headlineMisskey: "Ett nätverk kopplat av noter" -introMisskey: "Välkommen! Misskey är en öppen och decentraliserad mikrobloggningstjänst.\nSkapa en \"not\" och dela dina tankar med alla runtomkring dig. 📡\nMed \"reaktioner\" kan du snabbt uttrycka dina känslor kring andras noter.👍\nLåt oss utforska en nya värld!🚀" +introMisskey: "Välkommen! Firefish är en öppen och decentraliserad mikrobloggningstjänst.\nSkapa en \"not\" och dela dina tankar med alla runtomkring dig. 📡\nMed \"reaktioner\" kan du snabbt uttrycka dina känslor kring andras noter.👍\nLåt oss utforska en nya värld!🚀" monthAndDay: "{day}/{month}" search: "Sök" notifications: "Notifikationer" @@ -176,7 +176,6 @@ operations: "Operationer" software: "Mjukvara" version: "Version" metadata: "Metadata" -withNFiles: "{n} fil(er)" monitor: "Övervakning" jobQueue: "Jobbkö" cpuAndMemory: "CPU och minne" @@ -198,7 +197,7 @@ noUsers: "Det finns inga användare" editProfile: "Redigera profil" noteDeleteConfirm: "Är du säker på att du vill ta bort denna not?" pinLimitExceeded: "Du kan inte fästa fler noter" -intro: "Misskey har installerats! Vänligen skapa en adminanvändare." +intro: "Firefish har installerats! Vänligen skapa en adminanvändare." done: "Klar" processing: "Bearbetar..." preview: "Förhandsvisning" diff --git a/locales/th-TH.yml b/locales/th-TH.yml index 173548e90e..35ad641640 100644 --- a/locales/th-TH.yml +++ b/locales/th-TH.yml @@ -1,7 +1,7 @@ --- _lang_: "ภาษาไทย" headlineMisskey: "เชื่อมต่อเครือข่ายโดยโน้ต" -introMisskey: "ยินดีต้อนรับจ้าาา! Misskey เป็นบริการไมโครบล็อกโอเพ่นซอร์ส แบบการกระจายอำนาจ\nสร้าง \"โน้ต\" เพื่อแบ่งปันความคิดของคุณกับทุกคนรอบตัวคุณกันเถอะ 📡\nด้วยการ \"รีแอคชั่นผู้คน\" คุณยังสามารถแสดงความรู้สึกของคุณเกี่ยวกับบันทึกของทุกคนได้อย่างรวดเร็ว 👍\n\nแล้วมาท่องสำรวจโลกใบใหม่กันเถอะ! 🚀" +introMisskey: "ยินดีต้อนรับจ้าาา! Firefish เป็นบริการไมโครบล็อกโอเพ่นซอร์ส แบบการกระจายอำนาจ\nสร้าง \"โน้ต\" เพื่อแบ่งปันความคิดของคุณกับทุกคนรอบตัวคุณกันเถอะ 📡\nด้วยการ \"รีแอคชั่นผู้คน\" คุณยังสามารถแสดงความรู้สึกของคุณเกี่ยวกับบันทึกของทุกคนได้อย่างรวดเร็ว 👍\n\nแล้วมาท่องสำรวจโลกใบใหม่กันเถอะ! 🚀" monthAndDay: "{เดือน}/{วัน}" search: "ค้นหา" notifications: "การเเจ้งเตือน" @@ -139,7 +139,7 @@ settingGuide: "การตั้งค่าที่แนะนำ" cacheRemoteFiles: "แคชไฟล์ระยะไกล" cacheRemoteFilesDescription: "เมื่อปิดใช้งานการตั้งค่านี้ ไฟล์ระยะไกลนั้นจะถูกโหลดโดยตรงจากอินสแตนซ์ระยะไกล แต่กรณีการปิดใช้งานนี้จะช่วยลดปริมาณการใช้พื้นที่จัดเก็บข้อมูล แต่เพิ่มปริมาณการใช้งาน เพราะเนื่องจากจะไม่มีการสร้างภาพขนาดย่อ" flagAsBot: "ทำเครื่องหมายบอกว่าบัญชีนี้เป็นบอท" -flagAsBotDescription: "การเปิดใช้งานตัวเลือกนี้หากบัญชีนี้ถูกควบคุมโดยนักเขียนโปรแกรม หรือ ถ้าหากเปิดใช้งาน มันจะทำหน้าที่เป็นแฟล็กสำหรับนักพัฒนารายอื่นๆ และเพื่อป้องกันการโต้ตอบแบบไม่มีที่สิ้นสุดกับบอทตัวอื่นๆ และยังสามารถปรับเปลี่ยนระบบภายในของ Misskey เพื่อปฏิบัติต่อบัญชีนี้เป็นบอท" +flagAsBotDescription: "การเปิดใช้งานตัวเลือกนี้หากบัญชีนี้ถูกควบคุมโดยนักเขียนโปรแกรม หรือ ถ้าหากเปิดใช้งาน มันจะทำหน้าที่เป็นแฟล็กสำหรับนักพัฒนารายอื่นๆ และเพื่อป้องกันการโต้ตอบแบบไม่มีที่สิ้นสุดกับบอทตัวอื่นๆ และยังสามารถปรับเปลี่ยนระบบภายในของ Firefish เพื่อปฏิบัติต่อบัญชีนี้เป็นบอท" flagAsCat: "ทำเครื่องหมายบอกว่าบัญชีนี้เป็นแมว" flagAsCatDescription: "การเปิดใช้งานตัวเลือกนี้เพื่อทำเครื่องหมายบอกว่าบัญชีนี้เป็นแมว" flagShowTimelineReplies: "แสดงตอบกลับ ในไทม์ไลน์" @@ -177,7 +177,6 @@ operations: "ดำเนินการ" software: "ซอฟต์แวร์" version: "เวอร์ชั่น" metadata: "ข้อมูลเมตา" -withNFiles: "{n} ไฟล์(s)" monitor: "มอนิเตอร์" jobQueue: "คิวงาน" cpuAndMemory: "ซีพียู และ หน่วยความจำ" @@ -199,7 +198,7 @@ noUsers: "ไม่พบผู้ใช้งาน" editProfile: "แก้ไขโปรไฟล์" noteDeleteConfirm: "นายแน่ใจแล้วหรอว่าต้องการลบโน้ตนี้นะ?" pinLimitExceeded: "คุณไม่สามารถปักหมุดโน้ตเพิ่มเติมใดๆได้อีก" -intro: "การติดตั้ง Misskey เสร็จสิ้นแล้วนะ! โปรดสร้างผู้ใช้งานที่เป็นผู้ดูแลระบบ" +intro: "การติดตั้ง Firefish เสร็จสิ้นแล้วนะ! โปรดสร้างผู้ใช้งานที่เป็นผู้ดูแลระบบ" done: "เสร็จสิ้น" processing: "กำลังประมวลผล..." preview: "แสดงตัวอย่าง" @@ -378,7 +377,7 @@ exploreFediverse: "สำรวจเฟดดิเวิร์ส" popularTags: "แท็กยอดนิยม" userList: "รายการ" about: "เกี่ยวกับ" -aboutMisskey: "เกี่ยวกับ Misskey" +aboutFirefish: "เกี่ยวกับ Firefish" administrator: "ผู้ดูแลระบบ" token: "โทเค็น" twoStepAuthentication: "ยืนยันตัวตน 2 ชั้น" @@ -524,7 +523,7 @@ sort: "เรียงลำดับ" ascendingOrder: "เรียงจากน้อยไปมาก" descendingOrder: "เรียงจากมากไปน้อย" scratchpad: "กระดานทดลอง" -scratchpadDescription: "Scratchpad เป็นการจัดเตรียมสภาพแวดล้อมสำหรับการทดลอง AiScript แต่คุณสามารถเขียน ดำเนินการ และตรวจสอบผลลัพธ์ของการโต้ตอบกับ Misskey มันได้ด้วยนะ" +scratchpadDescription: "Scratchpad เป็นการจัดเตรียมสภาพแวดล้อมสำหรับการทดลอง AiScript แต่คุณสามารถเขียน ดำเนินการ และตรวจสอบผลลัพธ์ของการโต้ตอบกับ Firefish มันได้ด้วยนะ" output: "เอาท์พุต" script: "สคริปต์" disablePagesScript: "ปิดการใช้งาน AiScript บนเพจ" @@ -649,7 +648,7 @@ createNewClip: "สร้างคลิปใหม่" unclip: "ลบคลิป" confirmToUnclipAlreadyClippedNote: "โน้ตนี้เป็นส่วนหนึ่งของคลิป \"{name}\" แล้ว คุณต้องการลบออกจากคลิปนี้แทนอย่างงั้นหรอ?" public: "สาธารณะ" -i18nInfo: "Calckey กำลังได้รับการแปลเป็นภาษาต่างๆ โดยอาสาสมัคร คุณสามารถช่วยเหลือได้ที่ {link}" +i18nInfo: "Firefish กำลังได้รับการแปลเป็นภาษาต่างๆ โดยอาสาสมัคร คุณสามารถช่วยเหลือได้ที่ {link}" manageAccessTokens: "การจัดการโทเค็นการเข้าถึง" accountInfo: "ข้อมูลบัญชี" notesCount: "จำนวนของโน้ต" @@ -700,7 +699,7 @@ onlineUsersCount: "{n} ผู้ใช้คนนี้กำลังออน nUsers: "{n} ผู้ใช้งาน" nNotes: "{n} โน้ต" sendErrorReports: "ส่งรายงานว่าข้อผิดพลาด" -sendErrorReportsDescription: "เมื่อเปิดใช้งาน ข้อมูลข้อผิดพลาดโดยรายละเอียดนั้นจะถูกแชร์ให้กับ Misskey เมื่อเกิดปัญหา ซึ่งช่วยปรับปรุงคุณภาพของ Misskey\nซึ่งจะรวมถึงข้อมูล เช่น เวอร์ชั่นของระบบปฏิบัติการ เบราว์เซอร์ที่คุณใช้ กิจกรรมของคุณใน Misskey เป็นต้น" +sendErrorReportsDescription: "เมื่อเปิดใช้งาน ข้อมูลข้อผิดพลาดโดยรายละเอียดนั้นจะถูกแชร์ให้กับ Firefish เมื่อเกิดปัญหา ซึ่งช่วยปรับปรุงคุณภาพของ Firefish\nซึ่งจะรวมถึงข้อมูล เช่น เวอร์ชั่นของระบบปฏิบัติการ เบราว์เซอร์ที่คุณใช้ กิจกรรมของคุณใน Firefish เป็นต้น" myTheme: "ธีมของฉัน" backgroundColor: "ภาพพื้นหลัง" accentColor: "รูปแบบสี" @@ -791,7 +790,7 @@ hashtags: "แฮชแท็ก" troubleshooting: "แก้ปัญหา" useBlurEffect: "ใช้เอฟเฟกต์เบลอใน UI" learnMore: "แสดงให้ดูหน่อย" -misskeyUpdated: "Misskey ได้รับการอัปเดตแล้ว!" +misskeyUpdated: "Firefish ได้รับการอัปเดตแล้ว!" whatIsNew: "แสดงการเปลี่ยนแปลง" translate: "แปลภาษา" translatedFrom: "แปลมาจาก {x}" @@ -966,13 +965,13 @@ _registry: keys: "คีย์" domain: "โดเมน" createKey: "สร้างคีย์" -_aboutMisskey: +_aboutFirefish: about: "Misskey เป็นซอฟต์แวร์โอเพ่นซอร์สที่ถูกพัฒนาโดย Syuilo ตั้งแต่ปี 2014" contributors: "ผู้สนับสนุนหลัก" allContributors: "ผู้มีส่วนร่วมทั้งหมด" source: "ซอร์สโค้ด" - translation: "รับแปลภาษา Misskey" - donate: "บริจาคให้กับ Misskey" + translation: "รับแปลภาษา Firefish" + donate: "บริจาคให้กับ Firefish" morePatrons: "เราขอขอบคุณสำหรับความช่วยเหลือจากผู้ช่วยอื่นๆ ที่ไม่ได้ระบุไว้ที่นี่นะ ขอขอบคุณ! 🥰" patrons: "สมาชิกพันธมิตร" _nsfw: @@ -981,8 +980,8 @@ _nsfw: force: "ซ่อนสื่อทั้งหมด" _mfm: cheatSheet: "โค้ด MFM Cheat Sheet" - intro: "MFM เป็นภาษามาร์กอัปพิเศษเฉพาะของ Misskey ที่สามารถใช้ได้ในหลายที่ คุณยังสามารถดูรายการไวยากรณ์ MFM ที่มีอยู่ทั้งหมดได้ที่นี่นะ" - dummy: "Misskey ขยายโลกของ Fediverse" + intro: "MFM เป็นภาษามาร์กอัปพิเศษเฉพาะของ Firefish ที่สามารถใช้ได้ในหลายที่ คุณยังสามารถดูรายการไวยากรณ์ MFM ที่มีอยู่ทั้งหมดได้ที่นี่นะ" + dummy: "Firefish ขยายโลกของ Fediverse" mention: "กล่าวถึง" mentionDescription: "คุณสามารถระบุผู้ใช้โดยใช้ At-Symbol และชื่อผู้ใช้ได้นะ" hashtag: "แฮชแท็ก" diff --git a/locales/tr-TR.yml b/locales/tr-TR.yml index aecb413de7..17df463adf 100644 --- a/locales/tr-TR.yml +++ b/locales/tr-TR.yml @@ -1,17 +1,17 @@ ---- _lang_: "Türkçe" -introMisskey: "Açık kaynaklı bir dağıtılmış mikroblog hizmeti olan Misskey'e hoş geldiniz.\nMisskey, neler olup bittiğini paylaşmak ve herkese sizden bahsetmek için \"notlar\" oluşturmanıza olanak tanıyan, açık kaynaklı, dağıtılmış bir mikroblog hizmetidir.\nHerkesin notlarına kendi tepkilerinizi hızlıca eklemek için \"Tepkiler\" özelliğini de kullanabilirsiniz👍.\nYeni bir dünyayı keşfedin🚀." +introMisskey: "Hoş geldin! Firefish, sonsuza kadar ücretsiz olan, açık kaynaklı, merkezi + olmayan bir sosyal medya platformudur! 🚀" monthAndDay: "{month}Ay {day}Gün" search: "Arama" -notifications: "Bildirim" +notifications: "Bildirimler" username: "Kullanıcı Adı" password: "Şifre" forgotPassword: "şifremi unuttum" ok: "TAMAM" -gotIt: "Anladım" +gotIt: "Anladım!" cancel: "İptal" enterUsername: "Kullanıcı adınızı giriniz" -noNotes: "Notlar mevcut değil." +noNotes: "Gönderiler mevcut değil" noNotifications: "Bildirim bulunmuyor" settings: "Ayarlar" basicSettings: "Temel Ayarlar" @@ -19,11 +19,11 @@ otherSettings: "Diğer Ayarlar" openInWindow: "Bir pencere ile aç" profile: "Profil" timeline: "Zaman çizelgesi" -noAccountDescription: "Bu kullanıcı henüz biyografisini yazmadı" -login: "Giriş Yap " +noAccountDescription: "Bu kullanıcı henüz hakkındasını yazmadı." +login: "Giriş Yap" logout: "Çıkış Yap" signup: "Kayıt Ol" -uploading: "Yükleniyor" +uploading: "Yükleniyor..." users: "Kullanıcı" addUser: "Kullanıcı Ekle" favorite: "Favoriler" @@ -37,7 +37,8 @@ copyContent: "İçeriği kopyala" copyLink: "Bağlantıyı Kopyala" delete: "Sil" deleteAndEdit: "Sil ve yeniden düzenle" -deleteAndEditConfirm: "Bu notu silip yeniden düzenlemek istiyor musunuz? Bu nota ilişkin tüm Tepkiler, Yeniden Notlar ve Yanıtlar da silinecektir." +deleteAndEditConfirm: "Bu gönderiyi silip yeniden düzenlemek istiyor musunuz? Bu gönderiye + ilişkin tüm tepkiler, destekler ve yanıtlar silinecektir." addToList: "Listeye ekle" sendMessage: "Mesaj Gönder" copyUsername: "Kullanıcı Adını Kopyala" @@ -50,14 +51,2076 @@ user: "Kullanıcı" searchByGoogle: "Arama" _mfm: search: "Arama" + play: MFM'i çal + stop: MFM'i durdur + cheatSheet: MFM Kopya Kağıdı + intro: MFM, Misskey, Firefish, Akkoma ve daha pek çok yerde kullanılabilen bir biçimlendirme + dilidir. Burada mevcut tüm MFM sözdiziminin bir listesini görüntüleyebilirsiniz. + link: Link + boldDescription: Harfleri kalınlaştırarak vurgular. + small: Küçük + smallDescription: İçeriği küçük ve ince görüntüler. + warn: MFM, hızla hareket eden veya gösterişli animasyonlar içerebilir + alwaysPlay: Her zaman tüm animasyonlu MFM'yi otomatik oynat + x4Description: İçeriği büyükten de büyükten daha büyük görüntüler. + rainbowDescription: İçeriğin gökkuşağı renklerinde görünmesini sağlar. + bounceDescription: İçeriğe sıçarayan bir animasyon verir. + sparkle: Işıltı + sparkleDescription: İçeriğe ışıltılı bir parçacık efekti verir. + rotateDescription: İçeriği belirli bir açıyla döndürür. + fadeDescription: İçeriği içeri ve dışarı karartır. + fade: Karart + position: Pozisyon + blockCode: Kod (Blok) + crop: Kırp + positionDescription: İçeriği belirli bir miktarda taşıyın. + scale: Ölçek + scaleDescription: İçeriği belirtilen bir miktara göre ölçeklendirin. + foreground: Ön plan rengi + mention: Bahset + mentionDescription: Bir et-sembolü (@) ve bir kullanıcı adı kullanarak bir kullanıcı + belirleyebilirsiniz. + hashtag: Etiket + dummy: Firefish, Fediverse dünyasını genişletiyor + hashtagDescription: Sayı işareti ve metin kullanarak bir etiket belirtebilirsiniz. + url: URL + urlDescription: URL'ler görüntülenebilir. + inlineMath: Matematik (Satır İçi) + blockCodeDescription: Bir blokta çok satırlı (program) kod için sözdizimi vurgulamasını + görüntüler. + inlineMathDescription: Matematik formüllerini (KaTeX) satır içinde görüntüleyin + quote: Alıntı + quoteDescription: İçeriği alıntı olarak görüntüler. + twitch: Animasyon (Seğir) + emoji: Özel Emoji + jelly: Animasyon (Jöle) + blur: Bulanık + blurDescription: İçeriği bulanıklaştırır. Fareyle üzerine gelindiğinde net bir şekilde + görüntülenecektir. + spinDescription: İçeriğe dönen bir animasyon verir. + plainDescription: Bu MFM efektinde bulunan tüm MFM'lerin etkilerini devre dışı bırakır. + background: Arka plan rengi + backgroundDescription: Metnin arka plan rengini değiştirin. + jump: Animasyon (Zıpla) + cropDescription: İçeriği kırpar. + advancedDescription: Devre dışı bırakılırsa, animasyonlu MFM oynatılmadığı sürece + yalnızca temel işaretlemeye izin verir + bold: Kalın + inlineCodeDescription: (Program) kodu için satır içi sözdizimi vurgulamasını görüntüler. + flip: Tersine Çevir + flipDescription: İçeriği yatay veya dikey olarak çevirir. + font: Yazı Tipi + twitchDescription: İçeriğe güçlü bir şekilde seğiren bir animasyon verir. + spin: Animasyon (Dön) + x2Description: İçeriği büyük gösterir. + rotate: Döndür + plain: Düz + linkDescription: Metnin belirli bölümleri bir URL olarak görüntülenebilir. + searchDescription: Önceden girilmiş metin içeren bir arama kutusu görüntüler. + blockMathDescription: Matematik formüllerini (KaTeX) bir blokta görüntüleyin + jumpDescription: İçeriğe zıplama animasyonu verir. + rainbow: Gökkuşağı + x4: İnanılmaz derecede büyük + tadaDescription: İçeriğe "Tada!" benzeri bir animasyon verir. + shake: Animasyon (Salla) + x3: Büyük göster + blockMath: Matematik (Blok) + x2: Büyük + fontDescription: İçeriğin görüntüleneceği yazı tipini ayarlar. + foregroundDescription: Metnin ön plan rengini değiştirin. + centerDescription: İçeriği ortada görüntüler. + inlineCode: Kod (Satır İçi) + advanced: Gelişmiş MFM + center: Ortala + x3Description: İçeriği daha büyük gösterir. + tada: Animasyon (Tada) + emojiDescription: Özel bir emoji adını iki nokta ile çevreleyerek, özel emoji görüntülenebilir. + jellyDescription: İçeriğe jöle benzeri bir animasyon verir. + shakeDescription: İçeriğe sallanan bir animasyon verir. + bounce: Animasyon (Sıçra) _sfx: notification: "Bildirim" + noteMy: Kendi Gönderim + note: Yeni gönderi + antenna: Anten + chat: Sohbet + channel: Kanal bildirimleri + chatBg: Sohbet (Arkaplan) _widgets: notifications: "Bildirim" timeline: "Zaman çizelgesi" + photos: Fotoğraflar + userList: Kullanıcı Listesi + _userList: + chooseList: Liste seç + onlineUsers: Aktif Kullanıcılar + aiscript: AiScript Konsolu + activity: Aktivite + digitalClock: Dijital Saat + unixClock: UNIX Saati + meiliIndexCount: Indexlenmiş gönderiler + calendar: Takvim + trends: Popüler + memo: Yapışkan Notlar + rssTicker: RSS Ticker + federation: Federasyon + instanceCloud: Sunucu Bulutu + postForm: Gönderi Formu + meiliSize: Index boyutu + slideshow: Slayt Gösterisi + button: Düğme + clock: Saat + rss: RSS Okuyucu + serverInfo: Sunucu Bilgisi + meiliStatus: Sunucu Durumu + jobQueue: İş Sırası + serverMetric: Sunucu Bilgileri _profile: username: "Kullanıcı Adı" + changeBanner: Afişini değiştir + locationDescription: Önce şehrinizi girerseniz, yerel saatinizi diğer kullanıcılara + gösterecektir. + youCanIncludeHashtags: Hakkımdan'da etiket kullanabilirsin. + description: Hakkımda + 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 + changeAvatar: Avatarını değiştir + name: İsim + metadataEdit: Ek Bilgini Düzenle _deck: _columns: notifications: "Bildirim" tl: "Zaman çizelgesi" + antenna: Anten + list: Liste + widgets: Araçlar + channel: Kanal + direct: Direkt mesajlar + main: Ana + mentions: Bahsetmeler + swapLeft: Sol sütunla değiştir + addColumn: Sütun ekle + configureColumn: Sütun ayarları + swapRight: Sağ sütunla değiştir + swapUp: Üstteki sütunla değiştir + stackLeft: Sol sütunla birleştir + swapDown: Alttaki sütunla değiştir + popRight: Sağdaki sütunu aç + introduction2: İstediğiniz zaman yeni sütunlar eklemek için ekranın sağındaki + + işaretini tıklayın. + alwaysShowMainColumn: Her zaman ana sütunu göster + columnAlign: Sütunları hizala + profile: Çalışma alanı + newProfile: Yeni çalışma alanı + renameProfile: Çalışma alanını yeniden adlandır + deleteProfile: Çalışma alanını sil + nameAlreadyExists: Bu çalışma alanı zaten mevcut. + introduction: Sütunları özgürce düzenleyerek sizin için mükemmel arayüzü oluşturun! + widgetsIntroduction: Lütfen sütun menüsünde "Araç'ları düzenle"yi seçin ve bir widget + ekleyin. +searchPlaceholder: Firefish'de Ara +reply: Yanıtla +jumpToPrevious: Öncekini görüntüle +deleted: Silindi +editNote: Notu düzenle +noThankYou: Hayır, teşekkürler +addInstance: Bir sunucu ekle +cantFavorite: Favorilere eklenemedi. +edited: '{date} tarihinde ve {time} vaktinde düzenlendi' +loggingIn: Giriş Yapılıyor +save: Kaydet +headlineMisskey: Sonsuza kadar ücretsiz, açık kaynak kodlu, merkeziyetsiz sosyal medya + platformu! 🚀 +loadMore: Daha fazla yükle +instance: Sunucu +fetchingAsApObject: Fediverse'den çekiliyor +removeReaction: Tepkini sil +rememberNoteVisibility: Gönderi görünürlüğü ayarlarını hatırla +attachCancel: Eklentiyi kaldır +suspend: Askıya Al +unsuspend: Askıya Almayı Kaldır +unmute: Susturmayı Kaldır +blockConfirm: Bu hesabı engellemek istediğinize emin misiniz? +unblockConfirm: Bu hesabın engelini kaldırmak istediğinize emin misiniz? +settingGuide: Tavsiye edilen ayarlar +cacheRemoteFilesDescription: Bu ayar devre dışı bırakıldığında, uzak dosyalar doğrudan + uzak sunucudan yüklenir. Bunun devre dışı bırakılması depolama kullanımını azaltacak, + ancak küçük resimler oluşturulmayacağından trafiği artıracaktır. +flagAsCatDescription: Kedi kulaklarına sahip olacak ve bir kedi gibi konuşacaksın! +flagSpeakAsCat: Kedi gibi konuş +setWallpaper: Arkaplan ayarla +removeWallpaper: Arkaplanı sil +operations: Operasyonlar +clearCachedFiles: Ön belleği temizle +clearCachedFilesConfirm: Önbelleğe alınan tüm uzak dosyaları silmek istediğinizden + emin misiniz? +blockedInstancesDescription: Engellemek istediğiniz sunucuların ana bilgisayar adlarını + listeleyin. Listelenen sunucular artık bu sunucularla iletişim kuramayacak. +blockedUsers: Engellenmiş kullanıcılar +editProfile: Profilini düzenle +intro: Firefish'in indirilmesi tamamlandı! Lütfen yönetici hesap oluşturun. +instanceUsers: Sunucunun kullanıcıları +changePassword: Şifreyi değiştir +security: Güvenlik +newPasswordRetype: Yeni şifreyi tekrarla +uploadFromUrlRequested: Yükleme istendi +syncDeviceDarkMode: Karanlık modu cihazının ayarları ile senkronize et +renameFolder: Bu klasörü yeniden adlandır +emptyFolder: Bu klasör boş +unableToDelete: Silinemiyor +inputNewDescription: Yeni başlık gir +hasChildFilesOrFolders: Bu klasör boş olduğundan silinemez. +disconnectedFromServer: Sunucuyla bağlantı kesildi +reload: Yenile +disablingTimelinesInfo: Yöneticiler ve Moderatörler, etkinleştirilmemiş olsalar bile + tüm zaman çizelgelerine her zaman erişebilir. +pinnedUsersDescription: '"Keşfet" sekmesinde sabitlenecek kullanıcı adlarını satır + sonlarıyla ayırarak listeleyin.' +pinnedPages: Sabitlenmiş Sayfalar +pinnedPagesDescription: Bu sunucunun üst sayfasına sabitlemek istediğiniz Sayfaların + yollarını satır sonları ile ayırarak girin. +enableHcaptcha: hCaptcha'yı Aktif Et +notifyAntenna: Yeni gönderileribildir +recentlyUpdatedUsers: En son aktif kullanıcılar +about: Hakkında +twoStepAuthentication: İki-adımlı doğrulama +securityKeyName: Key name +help: Yardım +inputMessageHere: Mesajını buraya gir +ownedGroups: Gruplarım +joinedGroups: Katılınmış gruplar +invites: Davetler +members: Kullanıcılar +transfer: Transfer +messagingWithGroup: Grup sohbeti +next: Sonraki +retype: Tekrar gir +dashboard: Panel +objectStorageBucket: Bucket +objectStorageBucketDesc: Sağlayıcınız tarafından kullanınan bucket ismini yazın. +showFixedPostForm: Gönderim formunu zaman çizelgesinin en üstünde görüntüleyin +newNoteRecived: Yeni gönderiler mevcut +none: Hiçbiri +details: Detaylar +recentUsed: Son kullanılan +installedApps: Yetkilendirilmiş Uygulamalar +removeAllFollowing: Takip edilen herkesi çıkar +yourAccountSuspendedDescription: Bu hesap, sunucunun hizmet şartlarını veya benzerlerini + ihlal ettiği için askıya alındı. Daha ayrıntılı bir neden öğrenmek istiyorsanız + yöneticiyle iletişime geçin. Lütfen yeni bir hesap oluşturmayın. +addedRelays: Eklenen Röleler +serviceworkerInfo: Push bildirimleri için aktif olması gerekiyor. +author: Sahip +tokenRequested: Hesaba erişim ver +useFullReactionPicker: Tam boyutunda tepki seçici kullan +small: Küçük +enableAll: Hepsine izin ver +disableAll: Hepsini kapat +regexpError: Regex hatası +emailConfigInfo: Kayıt sırasında veya şifrenizi unutursanız e-postanızı onaylamak + için kullanılır +smtpSecure: SMTP bağlantıları için SSL/TSL kullan +regexpErrorDescription: '{tab} kelimenizin {line} satırındaki normal ifadede bir hata + oluştu:' +instanceMute: Sunucu Susturmaları +reporter: Rapor eden +userSaysSomethingReason: '{name}, {reason} söyledi' +userSaysSomethingReasonRenote: '{name}, {reason} içeren bir gönderiyi öne çıkardı' +userSaysSomethingReasonQuote: '{name}, {reason} içeren bir gönderiden alıntı yaptı' +notificationSettingDesc: Görünecek bildirimleri seç. +other: Diğer +sample: Örnek +notSet: Ayarlanmadı +emailVerified: Mail doğrulandı +showGapBetweenNotesInTimeline: Zaman tünelinde gönderiler arasındaki boşluğu göster +sendErrorReports: Hata raporları gönder +followingCount: Takip edilen hesap sayısı +no: Hayır +myTheme: Temam +backgroundColor: Arkaplan rengi +accentColor: Vurgu rengi +textColor: Yazı rengi +createdAt: Oluşturuldu +updatedAt: Güncellendi +saveConfirm: Kaydet? +registry: Kayıt +currentVersion: Şuanki Sürüm +accountDeletionInProgress: Hesap silme şu anda devam ediyor +unresolved: Çözülmedi +newVersionOfClientAvailable: Yeni istemci sürümü mevcut. +shareWithNote: Gönderi ile paylaş +whatIsNew: Değişiklikleri göster +translate: Çevir +breakFollow: Takipçiyi sil +breakFollowConfirm: Takipçiyi kaldırmak istediğinizden emin misiniz? +unfollowConfirm: "{name}'i takibi bırakmak istediğinizden emin misiniz?" +importRequested: Bir içe aktarma isteğinde bulundunuz. Bu biraz zaman alabilir. +somethingHappened: Bir hata ile karşılaşıldı +retry: Tekrar Dene +youShouldUpgradeClient: Bu sayfayı görüntülemek için, lütfen istemcinizi güncelleyin. +reactionSetting: Tepki seçicide gösterilecek tepkiler +unmarkAsSensitive: NSFW işaretini kaldır +enterFileName: Dosya adı gir +noJobs: Hiçbir iş yok +instanceFollowing: Sunucuda takip ediliyor +instanceFollowers: Sunucunun takipçileri +currentPassword: Şuanki şifre +newPassword: Yeni şifre +saved: Kaydedildi +uploadFromUrlDescription: Yüklemek istediğiniz dosyanın URL'si +noMoreHistory: Başka geçmiş yok +startMessaging: Yeni sohbet oluştur +manageGroups: Grupları düzenle +nUsersRead: '{n} tarafından okundu' +images: Görseller +birthday: Doğumgünü +light: Aydınlık +dark: Karanlık +lightThemes: Aydınlık temalar +selectFiles: Dosyalar seç +selectFolders: Klasörler seç +renameFile: Dosyayı yeniden adlandır +folderName: Klasör adı +createFolder: Klasör oluştur +copyUrl: URL'yi Kopyala +maintainerName: Sahip +maintainerEmail: Sahibin e-postası +tosUrl: Kullanım Koşulları URL'si +monthX: '{month}' +basicInfo: Basit bilgi +pinnedUsers: Sabitlenmiş kullanıcılar +manageAntennas: Antenleri Düzenle +name: İsim +silence: Sustur +unsilence: Susturmayı geri al +exploreUsersCount: '{count} Kullanıcı var' +exploreFediverse: Fediversi keşfet +popularTags: Popüler etiketler +close: Kapat +group: Grup +text: Yazı +checking: Doğrulanıyor... +tooLong: Çok uzun +weakPassword: Zayıf şifre +normalPassword: Ortalama şifre +disableDrawer: Çekmece tarzı menüler kullanmayın +youHaveNoGroups: Grupların yok +joinOrCreateGroup: Bir gruba davet edil veya kendininkini oluştur. +regenerate: Yeniden Oluştur +fontSize: Yazı boyutu +noFollowRequests: Bekleyen takip isteğiniz yok +openImageInNewTab: Resmi yeni sekmede aç +useObjectStorage: Object Storage kullan +objectStorageUseProxy: Proxy üzerinden bağlan +installedDate: Yetkilendirilme tarihi +scratchpad: Karalama Defteri +deleteAllFiles: Tüm dosyaları isl +useCw: İçeriği gizle +plugins: Eklentiler +manage: Yönetmek +preferencesBackups: Tercih yedekleri +generateAccessToken: Erişim tokeni oluştur +enableEmail: E-posta dağıtımını etkinleştir +regenerateLoginToken: Giriş tokenini yeniden oluştur +regenerateLoginTokenDescription: Oturum açma sırasında dahili olarak kullanılan belirteci + yeniden oluşturur. Normalde bu eylem gerekli değildir. Yeniden oluşturulursa, tüm + cihazların oturumu kapatılacaktır. +followersCount: Takipçi sayısı +yes: Evet +lockedAccountInfo: Gönderi görünürlüğünüzü "Yalnızca takipçiler" olarak ayarlamazsanız, + takipçilerin manuel olarak onaylanmasını isteseniz bile gönderileriniz herkes tarafından + görülebilir. +unlikeConfirm: Beğeniyi kaldırmak istiyor musunuz? +notSpecifiedMentionWarning: Bu gönderi, alıcı olarak dahil edilmeyen kullanıcılardan + bahsetmektedir +hideOnlineStatus: Çevrimiçi bilgisini gizle +hideOnlineStatusDescription: Çevrimiçi durumunuzu gizlemek, arama gibi bazı özelliklerin + rahatlığını azaltır. +botProtection: Bot Koruması +selectAccount: Hesap seç +recentPosts: En son sayfalar +high: Yüksek +middle: Orta +secureModeInfo: Diğer sunuculardan talepte bulunurken kanıtlamadan geri göndermeyiniz. +previewNoteText: Önizlemeyi göster +customCss: Özel CSS +global: Global +makeReactionsPublic: Tepki geçmişini herkese açık olarak ayarla +clickToFinishEmailVerification: Mail doğrulamasını tamamlamak için lütfen [{ok}]'a + tıklayın. +overridedDeviceKind: Cihaz tipi +smartphone: Akıllı telefon +tablet: Tablet +auto: Otomatik +tenMinutes: 10 dakika +recentNDays: Son {n} gün +noEmailServerWarning: Mail sunucusu ayarlanmadı. +thereIsUnresolvedAbuseReportWarning: Çözülmemiş raporlar var. +statusbar: Durum çubuğu +pleaseSelect: Bir seçenek seçin +lastActiveDate: Son kullanılan +reverse: Tersi +logoutConfirm: Gerçekten oturum kapatılsın mı? +type: Tip +speed: Hız +slow: Yavaş +activeEmailValidationDescription: Tek kullanımlık adreslerin kontrol edilmesi ve gerçekten + iletişim kurup kurulamayacağına göre e-posta adreslerinin daha sıkı doğrulanmasını + sağlar. İşaretlenmediğinde, yalnızca e-postanın biçimi doğrulanır. +move: Taşı +defaultReaction: Giden ve gelen gönderiler için varsayılan emoji tepkisi +indexPosts: Dizin Gönderileri +youGotNewFollower: takip etti +receiveFollowRequest: Takip isteği alındı +followRequestAccepted: Takip isteği onaylandı +mention: Bahset +download: İndir +lists: Listeler +noLists: Hiç listen yok +cantRenote: Bu gönderi yükseltilemez. +cantReRenote: Yükseltme yükseltilemez. +mute: Sustur +block: Engelle +editWidgetsExit: Tamamlandı +customEmojis: Özel Tepki +cpuAndMemory: İşlemci ve Bellek +selectInstance: Sunucu seç +instances: Sunucular +silencedInstancesDescription: Susturmak istediğiniz sunucuların ana bilgisayar adlarını + listeleyin. Listelenen sunuculardaki hesaplar "Sessiz" olarak değerlendirilir, yalnızca + takip istekleri yapabilir ve takip edilmediği takdirde yerel hesaplardan bahsedemez. + Bu, engellenen sunucuları etkilemeyecektir. +muteAndBlock: Susturmalar ve Engeller +noteDeleteConfirm: Bu gönderiyi silmek istediğine emin misin? +resetAreYouSure: Gerçekten sıfırla? +remoteUserCaution: Uzak kullanıcılardan gelen bilgiler eksik olabilir. +yearsOld: '{age} yaşında' +removed: Başarıyla silindi +reject: Reddet +unwatch: İzlemeyi bırak +accept: Kabul et +normal: Normal +thisMonth: Ay +enableRecaptcha: reCAPTCHA'yı Aktif Et +antennas: Antenler +recaptchaSiteKey: Site key +withFileAntenna: Sadece dosyalı gönderiler +antennaInstancesDescription: Sunucu başı bir satır kullanın +moderator: Moderatör +moderation: Moderasyon +lastUsed: En son kullanılan +unregister: Kaydı sil +passwordLessLogin: Şifresiz giriş +uploadFolder: Yüklemeler için varsayılan klasör +markAsReadAllUnreadNotes: Tüm gönderileri okundu olarak işaretle +notFound: Bulunamadı +groups: Gruplar +quoteQuestion: Alıntı olarak eklensin mi? +signinRequired: Lütfen devam etmeden önce kayıt olun +noMessagesYet: Şuana kadar mesaj yok +newMessageExists: Yeni mesaj yok +invitations: Davetler +invitationCode: Davet kodu +signinWith: '{x} ile giriş yap' +strongPassword: Güçlü şifre +passwordNotMatched: Uyuşmuyor +signinFailed: Giriş yapılamadı. Şifre ve ya kullanıcı adı yanlış. +tapSecurityKey: Güvenlik anahtarınıza dokunun +or: veya +noHistory: Geçmiş bulunamadı +language: Dil +clientSettings: İstemci Ayarları +accountSettings: Hesap Ayarları +listen: Dinle +chooseEmoji: Emoji seç +promotion: Terfi Edildi +nothing: Burada görüntülenecek bir şey yok +lastUsedDate: Son kullanılma tarihi +updateRemoteUser: Uzak kullanıcı bilgilerini güncelle +width: Genişlik +height: Uzunluk +permission: İzinler +email: Mail +smtpSecureInfo: STARTTLS kullanırken bunu kapatın +alwaysMarkSensitive: Varsayılan olarak NSFW olarak işaretle +noteFavoritesCount: İşaretlenen gönderilerin sayısı +pageLikesCount: Beğenilen Sayfaların sayısı +duplicate: Kopyasını Oluştur +clearCache: Önbelleği Temizle +onlineUsersCount: '{n} kullanıcı aktif' +nUsers: '{n} Kullanıcı' +nNotes: '{n} Gönderi' +useReactionPickerForContextMenu: Sağ tık ile tepki seçiciyi aç +typingUsers: '{users} yazıyor' +jumpToSpecifiedDate: Spesifik tarihe atla +showingPastTimeline: Şuan eski bir zaman çizelgesini görüntülüyorsunuz +clear: Temizle +fullView: Tam görünüm +emailNotConfiguredWarning: Mail adresi seçilmedi. +privateMode: Özel Mod +fast: Hızlı +learnMore: Daha fazla bilgi edin +localOnly: Sadece yerel +delayed: Ertelenmiş +useGlobalSetting: Global ayaralrı kullan +switchAccount: Hesap değiştir +notRecommended: Tavsiye edilmiyor +onlineStatus: Çevrimiçi bilgisi +active: Aktif +instanceBlocking: Federasyon Yönetmek +enabled: Aktif +disabled: Deaktif +quickAction: Hızlı işlemler +configure: Yapılandır +blockedInstances: Engellenmiş Sunucular +silencedInstances: Susturulmuş Sunucular +lookup: Görüntüle +inputNewFolderName: Yeni klasör ismi gir +noteOf: Gönderi {user} tarafından +onlyOneFileCanBeAttached: Bir mesaja sadece 1 dosya ekleyebilirsin +install: İndir +uninstall: kALDIR +send: Gönder +noCrawleDescription: Arama motorlarından profil sayfanızı, gönderilerinizi, Sayfalarınızı + vb. indekslememesini isteyin. +emailNotification: Mail bildirimleri +goBack: Geri +online: Çevrimiçi +translatedFrom: "{x}'den çevrildi" +cropImage: Resmi kırp +deleteAccount: Hesabı Sil +navbar: Gezinti çubuğu +account: Hesap +instanceDefaultThemeDescription: Tema kodunu nesne biçiminde girin. +alt: ALT +mutePeriod: Sessiz süresi +indefinitely: Kalıcı olarak +oneHour: Bir saat +oneWeek: Bir hafta +colored: Renkli +sensitiveMediaDetection: Resim NSFW Belirleme +subscribePushNotification: Push bildirimlerini aktif et +pushNotificationAlreadySubscribed: Push bildirimler zaten açık +sendPushNotificationReadMessage: İlgili bildirimler veya mesajlar okunduktan sonra + push bildirimlerini silin +sendPushNotificationReadMessageCaption: Kısa bir süre için "{emptyPushNotificationMessage}" + metnini içeren bir bildirim görüntülenecektir. Bu, mümkünse cihazınızın pil kullanımını + artırabilir. +enterSendsMessage: Mesaj göndermek için Mesajlaşma'da Geri Dön'e basın (Ctrl + Return) +customMOTDDescription: Bir kullanıcı sayfayı her yüklediğinde/yeniden yüklediğinde + rastgele gösterilecek satır sonlarıyla ayrılmış MOTD (açılış ekranı) için özel mesajlar. +customSplashIconsDescription: Bir kullanıcı sayfayı her yüklediğinde/yeniden yüklediğinde + rastgele gösterilecek satır sonlarıyla ayrılmış özel açılış ekranı simgeleri için + URL'ler. Lütfen resimlerin statik bir URL'de olduğundan ve tercihen tümü 192x192 + olarak yeniden boyutlandırıldığından emin olun. +updateAvailable: Bir güncelleme mevcut olabilir! +splash: Açılış Ekranı +moveTo: Şimdiki hesabını yeni bir hesaba taşı +swipeOnMobile: Sayfalar arasında kaydırmaya izin ver +swipeOnDesktop: Masaüstünde mobil stil kaydırmaya izin ver +migration: Taşıma +moveAccount: Hesabını taşı! +moveFrom: Daha eski bir hesaptan bu hesaba taşıyın +moveFromLabel: 'Taşındığınız hesap:' +importAndExport: İçeri/Dışarı Aktar +manageLists: Listeleri düzenle +error: Hata +pageLoadError: Sayfayı yüklerken bir hata ile karşılaşıldı. +serverIsDead: Sunucu yanıt vermiyor. Biraz bekleyip tekrar deneyin. +defaultNoteVisibility: Varsayılan görünürlük +follow: Takip et +reactionSettingDescription2: Yeniden sıralamak için sürükleyin, silmek için tıklayın, + eklemek için "+"ya basın. +you: Sen +clickToShow: Görmek için tıkla +sensitive: NSFW +add: Ekle +reaction: Tepkiler +markAsSensitive: NSFW olarak işaretle +unblock: Engeli Kaldır +addAccount: Hesap ekle +network: İnternet +disk: Depolama +instanceInfo: Sunucu Bilgisi +statistics: İstatistikler +hiddenTagsDescription: Trendlerden gizlemek ve keşfetmek istediğiniz etiketlerin (# + olmadan)etiketlerini listeleyin. Gizli etiketler başka yollarla keşfedilebilir. +mutedUsers: Susturulmuş kullanıcılar +uploadFromUrlMayTakeTime: Yüklemenin tamamlanması zaman alabilir. +activity: Aktivite +theme: Temalar +themeForLightMode: Aydınlık modda kullanmak için temalar +reloadConfirm: Zaman çizelgesini yenilemek ister misiniz? +instanceName: Sunucu adı +circularReferenceFolder: Hedef klasör, taşımak istediğiniz klasörün bir alt klasörüdür. +instanceDescription: Sunucu açıklaması +driveCapacityPerLocalAccount: Kullanıcı başı Driver kapasitesi +driveCapacityPerRemoteAccount: Uzak kullanıcı başı Driver kapasitesi +inMb: Megabayt cinsinden +pinnedClipId: Sabitlenecek atacın ID'si +withFiles: Dosya içeren +recentlyRegisteredUsers: Yeni katılmış kullanıcılar +recentlyDiscoveredUsers: Yeni keşfedilmiş kullanıcılar +nUsersMentioned: '{n} kullanıcı tarafından bahsedildi' +securityKey: Security key +title: Başlık +total: Toplam +sounds: Sesler +objectStorageRegionDesc: "'xx-east-1' gibi bir bölge belirtin. Hizmetiniz bölgeler + arasında ayrım yapmıyorsa, bunu boş bırakın veya 'us-east-1' girin." +objectStorageUseSSL: SSL Kullan +popout: Açılır Pencere +volume: Ses Kuvveti +showInPage: Sayfada göster +masterVolume: Ana ses +undeck: Desteden çık +useBlurEffectForModal: Modallar için bulanıklık efekti uygula +leaveConfirm: Kaydedilmemiş değişiklikler var. Devam etmek istiyor musunuz? +testEmail: Email dağıtımını test et +wordMute: Kelime susturması +userSaysSomething: '{name} bir şey söyledi' +channel: Kanallar +create: Oluştur +useGlobalSettingDesc: Açıksa, hesap bildirim ayarlarınız kullanılacaktır. Kapatılırsa, + bireysel yapılandırmalar yapılabilir. +setMultipleBySeparatingWithSpace: Birden çok girişi boşluklarla ayırın. +fileIdOrUrl: Dosya ID veya URL'si +behavior: Davranış +abuseReported: Raporunuz gönderildi. Teşekkürler. +reporteeOrigin: Ana Raporcu +reporterOrigin: Ana Rapor Eden +defaultNavigationBehaviour: Varsayılan gezinme davranışı +editTheseSettingsMayBreakAccount: Bunları düzenlemek hesabınıza zarar verebilir. +renotedCount: Alınan yükseltme sayısı +driveFilesCount: Drive dosya sayısı +deleteConfirm: Sil? +invalidValue: Geçersiz değer. +instanceSecurity: Sunucu Güvenliği +searchResult: Arama sonuçları +useBlurEffect: Kullanıcı arayüzünde bulanıklaştırma efektleri kullanın +misskeyUpdated: Firefish güncellendi! +lastCommunication: Son iletişim +itsOn: Etkinleştirilmiş +emailRequiredForSignup: Kayıt olmak için mail gerekiyor +leaveGroup: Gruptan ayrıl +useDrawerReactionPickerForMobile: Reaksiyon seçiciyi mobil cihazda çekmece olarak + göster +leaveGroupConfirm: '"{name}"den ayrılmak istediğinizden emin misiniz?' +instanceDefaultLightTheme: Sunucu genelinde varsayılan aydınlık tema +document: Dökümanlar +numberOfPageCacheDescription: Bu sayının arttırılması, kullanıcılar için kolaylık + sağlayacaktır ancak daha fazla sunucu yükünün yanı sıra daha fazla bellek kullanılmasına + neden olacaktır. +refreshInterval: 'Güncelleme aralığı ' +label: Etiket +replayTutorial: Eğiticiyi tekrar oynat +moveAccountDescription: Bu süreç geri döndürülemez. Taşımadan önce yeni hesabınızda + bu hesap için bir takma ad ayarladığınızdan emin olun. Lütfen @person@server.com + şeklinde biçimlendirilmiş hesabın etiketini girin +emojis: Emoji +flagAsCat: Kedi misin? 😺 +selectChannel: Kanal seç +emojiName: Emoji adı +showOnRemote: Uzak sunucuda görüntüle +flagSpeakAsCatDescription: Gönderileriniz kedi modundayken nyanifiye edilecek +flagShowTimelineReplies: Yanıtları zaman çizelgesinde göster +silenceThisInstance: Bu sunucuyu sustur +proxyAccountDescription: Vekil hesabı, belirli koşullar altında kullanıcılar için + uzaktan takipçi işlevi gören bir hesaptır. Örneğin, bir kullanıcı listeye bir uzak + kullanıcı eklediğinde, o kullanıcıyı takip eden yerel bir kullanıcı yoksa uzak kullanıcının + etkinliği sunucuya teslim edilmeyecektir, bu nedenle onun yerine vekil hesabı takip + edilecektir. +clearQueueConfirmTitle: Bu sırayı temizlemek istediğine emin misin? +software: Yazılım +version: Sürüm +federating: Federasyon +preview: Ön izleme +retypedNotMatch: Girişler uyuşmuyor. +attachFile: Dosya ekle +noSuchUser: Kullanıcı bulunamadı +removeAreYouSure: '"{x}" kaldırmak istediğinize emin misiniz?' +keepOriginalUploading: Orjinal resmi sakla +messageRead: Oku +deleteAreYouSure: '"{x}" silmek istediğinize emin misiniz?' +messaging: Sohbet +upload: Yükle +fromUrl: URL'den +agreeTo: '{0} kabul ediyorum' +tos: Kullanım Koşulları +drive: Drive +selectFolder: Klasör seç +inputNewFileName: Yeni dosya ismi gir +whenServerDisconnected: Sunucuyla bağlantı kesildiğinde +avatar: Avatar +rename: Yeniden Adlandır +banner: Afiş +nsfw: NSFW +doNothing: Görmezden Gel +watch: İzle +connectService: Bağlan +registration: Kayıt +hcaptcha: hCaptcha +pinnedNotes: Sabitlenmiş gönderiler +hcaptchaSiteKey: Site key +hcaptchaSecretKey: Secret key +antennaSource: Anten kaynağı +antennaKeywords: Dinlenecek anahtar kelimeler +antennaExcludeKeywords: Hariç tutulacak anahtar kelimeler +antennaKeywordsDescription: AND koşulu için boşluklarla veya OR koşulu için satır + sonlarıyla ayırın. +caseSensitive: Büyük harf duyarlı +enableServiceworker: Tarayıcınız için Push-Bildirimleri Etkinleştirin +unsilenceConfirm: Bu kullanıcının susturma işlemini geri almak istediğinizden emin + misiniz? +userList: Listeler +antennaUsersDescription: Kullanıcı başı bir satır kullanın +administrator: Yönetici +token: Token +cacheClear: Önbelleği temizle +createGroup: Grup oluştur +newPasswordIs: Yeni şifren "{password}" +share: Paylaş +enable: Etkinleştir +groupName: Grup adı +available: Mevcut +unavailable: Mevcut değil +weekOverWeekChanges: Geçen haftadan beri değişiklikler +usernameInvalidFormat: Büyük ve küçük harfleri, sayıları ve alt çizgileri kullanabilirsiniz. +tooShort: Çok kısa +passwordMatched: Uyuşuyor +dayOverDayChanges: Dünden beri değişiklikler +appearance: Görünüm +objectStorageBaseUrl: Ana URL +objectStoragePrefix: Prefix +unableToProcess: Operasyon tamamlanamadı +deleteAllFilesConfirm: Tüm dosyaları silmek istediğine emin misin? +disablePagesScript: Sayfalardan AiScript'i deaktive et +expandOnNoteClick: Gönderileri basarak aç +expandOnNoteClickDesc: Kapatılırsa, gönderileri hala menüden veya sağtıklayarak açabilirsin. +removeAllFollowingDescription: Bunu gerçekleştirmek, {host} üzerindeki tüm hesapları + takip etmeyi bırakır. +deck: Deste +pluginTokenRequestedDescription: Bu eklenti, burada ayarlanan izinleri kullanabilecektir. +notificationType: Bildirim tipi +channelFederationWarn: Kanallar başka sunuculara federe edilmiyor +forwardReport: Raporu uzak sunucuya ilet +openInNewTab: Yeni sekmede aç +clip: Ataç +optional: Opsiyonel +manageAccessTokens: Erişim tokenlerini düzenle +clipsDesc: Ataçlar, paylaşılabilen kategorize yer imleri gibidir. Tek tek gönderiler + menüsünden ataçlar oluşturabilirsiniz. +makeExplorable: Hesabını "Keşfet" 'te göster +accountInfo: Hesap Bilgisi +makeExplorableDescription: Bunu kapatırsanız, hesabınız "Keşfet" bölümünde görünmez. +saveAs: Olarak kaydet... +advanced: Gelişmiş +value: Değer +youAreRunningUpToDateClient: En son istemci sürümünü kullanıyorsunuz. +accounts: Hesaplar +switch: Değiştir +popularPosts: Popüler sayfalar +inChannelSearch: Kanalda ara +administration: Yönetim +ads: Reklamlar +low: Düşük +seperateRenoteQuote: Ayrı destek ve fiyat teklifi düğmeleri +sent: Gönderildi +customMOTD: Özel MOTD +showUpdates: Firefish güncellendiğinde bir açılır pencere göster +logoImageUrl: Logo resim URL'si +showAdminUpdates: Yeni bir Firefish sürümünün mevcut olduğunu belirtin (yalnızca yönetici) +newer: asla +older: daha eski +exportRequested: Bir dışarı aktarma talebinde bulundunuz. Bu biraz zaman alabilir. + Tamamlandığında Drive'ınıza eklenecektir. +notes: Gönderiler +following: Takip Ediyor +followers: Takipçiler +followsYou: Seni takip ediyor +pageLoadErrorDescription: Buna normalde ağ hataları veya tarayıcının önbelleği neden + olur. Önbelleği temizlemeyi deneyin ve biraz bekledikten sonra tekrar deneyin. +quote: Alıntıla +pinnedNote: Sabitlenmiş gönderi +renote: Yükselt +unrenote: Yükseltmeyi geri al +emojiUrl: Emoji URL +suspendConfirm: Bu hesabı askıya almak istediğinize emin misiniz? +addEmoji: Ekle +autoAcceptFollowed: Takip ettiğiniz kullanıcıların takip isteklerini otomatik olarak + onaylayın +general: Genel +accountMoved: 'Bu kullanıcı yeni bir hesapa taşındı:' +wallpaper: Arkaplan +searchWith: 'Arat: {q}' +youHaveNoLists: Hiçbir listen yok +followConfirm: '{name} kullanıcısını takip etmek istediğine emin misin?' +metadata: Metadata +monitor: İzlengeç +jobQueue: İş Sırası +noUsers: Kullanıcı bulunamadı +noInstances: Sunucu bulunamadı +pinLimitExceeded: Daha fazla gönderi sabitleyemezsin +defaultValueIs: 'Varsayılan: {value}' +noCustomEmojis: Emoji yok +blocked: Engellenmiş +default: Varsayılan +all: Tümü +subscribing: Abone Olunuyor +publishing: Yayınlanmak +notResponding: Cevap vermiyor +more: Daha fazla! +featured: Önerilen +usernameOrUserId: Kullanıcı adı veya kullanıcı id'si +fromDrive: Drive'dan +uploadFromUrl: URL'den yükle +announcements: Duyurular +explore: Keşfet +imageUrl: Resim URL'si +thisYear: Yıl +deleteFolder: Bu klasörü sil +addFile: Dosya ekle +dayX: '{day}' +enableLocalTimeline: Yerel zaman çizgisini aktif et +disconnectService: Bağlantıyı kes +enableGlobalTimeline: Global zaman çizgisini aktif et +enableRegistration: Yeni kullanıcı kaydını aktif et +invite: Davet et +bannerUrl: Afiş resmi URL +backgroundImageUrl: Arkaplan URL'si +recaptcha: reCAPTCHA +iconUrl: Ikon URL +recaptchaSecretKey: Secret key +avoidMultiCaptchaConfirm: Birden fazla Captcha sistemi kullanmak aralarında etkileşime + neden olabilir. Şu anda etkin olan diğer Captcha sistemlerini devre dışı bırakmak + ister misiniz? Etkin kalmalarını istiyorsanız, iptal düğmesine basın. +aboutFirefish: Firefish Hakkında +popularUsers: Popüler kullanıcılar +notFoundDescription: Bu URL'ye karşılık gelen sayfa bulunamadı. +reduceUiAnimation: Arayüz animasyonlarını azalt +markAsReadAllNotifications: Tüm bildirimleri okundu olarak işaretle +markAsReadAllTalkMessages: Tüm mesajları okundu olarak işaretle +inviteToGroup: Gruba davet et +quoteAttached: Alıntıla +useOsNativeEmojis: Sistem Emojilerini Kullan +signinHistory: Giriş geçmişleri +disableAnimatedMfm: Animasyonlu MFM'yi devre dışı bırak +uiLanguage: Arayüz dili +groupInvited: Bir gruba davet edildin +createAccount: Hesap Oluştur +existingAccount: Var olan hesap +aboutX: '{x} Hakkında' +doing: İşleniyor... +category: Kategori +deleteAll: Hepsini sil +objectStorageEndpoint: Endpoint +output: Çıkış +userSuspended: Bu kullanıcı askıya alındı. +userSilenced: Bu kullanıcı susturuldu. +yourAccountSuspendedTitle: Bu hesap askıya alındı +relays: Röleler +inboxUrl: Gelen URL +menu: Menü +divider: Ayraç +addItem: Öğe Ekle +enableInfiniteScroll: Otomatik olarak daha fazla yükle +enablePlayer: Video oynatıcıyı aç +disablePlayer: Video oynatıcıyı kapat +expandTweet: Tweeti Büyüt +large: Büyük +medium: Orta +smtpConfig: SMTP Sunucusu Ayarları +smtpHost: Adres +emailServer: Mail sunucusu +edit: Düzenle +emailAddress: Mail adresi +smtpPort: Port +emptyToDisableSmtpAuth: SMTP doğrulamasını kapatmak için kullanıcı adı ve şifreyi + boş bırakın +makeActive: Aktif +display: Gösterim +copy: Kopyala +metrics: Metrikler +pollVotesCount: Gönderilen oylama sayısı +loadRawImages: Küçük resimleri göstermek yerine orijinal resimleri yükleyin +switchUi: Düzen +sentReactionsCount: Gönderilen tepki sayısı +receivedReactionsCount: Alınan tepki sayısı +pollVotedCount: Alınan oylama sayısı +pageLikedCount: Beğeni alan Sayfa sayısı +contact: Bağlantı +useSystemFont: Sistemin varsayılan yazı tipini kullan +usageAmount: Kullanım +inUse: Kullanılan +userInfo: Kullanıcı bilgisi +unknown: Bilinmiyor +customCssWarn: Bu ayar yalnızca ne işe yaradığını biliyorsanız kullanılmalıdır. Uygun + olmayan değerlerin girilmesi, istemcinin normal şekilde çalışmamasına neden olabilir. +memo: Not +allowedInstancesDescription: Her biri yeni bir satırla ayrılmış, federasyon için beyaz + listeye eklenecek sunucu ana bilgisayarları (yalnızca özel modda geçerlidir). +expiration: Bitiş +troubleshooting: Sorun giderme +usernameInfo: Hesabınızı bu sunucudaki diğerlerinden ayıran bir ad. Alfabeyi (a~z, + A~Z), rakamları (0~9) veya alt çizgileri (_) kullanabilirsiniz. Kullanıcı adları + daha sonra değiştirilemez. +size: Boyut +numberOfColumn: Sütun Sayısı +driveCapOverrideCaption: 0 veya daha düşük bir değer girerek kapasiteyi varsayılana + sıfırlayın. +requireAdminForView: Bunu görüntülemek için bir yönetici hesabıyla oturum açmalısınız. +userSaysSomethingReasonReply: '{name}, {reason} içeren bir gönderiye cevap verdi' +overview: Genel Bakış +logs: Günlükler +database: Veri Tabanı +reportAbuseOf: '{name} kullanıcısını raporla' +openInSideView: Yan görünümde aç +createNew: Yeni oluştur +createNewClip: Yeni ataç oluştur +unclip: Atacı Kaldır +notesCount: Gönderi sayısı +repliesCount: Gönderilen yanıt sayısı +renotesCount: Göndeirlen yükseltme sayısı +repliedCount: Alınan yanıt sayısı +driveUsage: Drive kullanımı +noCrawle: Tarayıcı dizine eklemeyi reddet +needReloadToApply: Bunun yansıtılması için bir yeniden yükleme gereklidir. +showTitlebar: Başlık çubuğunu göster +latestVersion: En Son Sürüm +capacity: Kapasite +userPagePinTip: Tek tek gönderiler menüsünden "Profile sabitle"yi seçerek gönderileri + burada görüntüleyebilirsiniz. +offline: Çevrimdışı +priority: Öncelik +ratio: Oran +secureMode: Güvenli Mod (Yetkili Getirme) +aiChanMode: Klasik kullanıcı arayüzünde Ai-chan +recommended: Önerilen +received: Alındı +classic: Ortalanmış +muteThread: Konuyu sessize al +deleteAccountConfirm: Bu, hesabınızı geri alınamaz bir şekilde silecektir. İlerle? +hide: Gizle +pubSub: Pub/Sub Hesapları +filter: Filtre +controlPanel: Kontrol Paneli +continueThread: Konuya devam et +incorrectPassword: Yanlış şifre. +voteConfirm: '"{choice}" için oyunuzu onaylıyor musunuz?' +failedToFetchAccountInformation: Hesap bilgileri getirilemedi +rateLimitExceeded: Hız limiti aşıldı +renotedBy: '{user} Yükseltti' +host: Host +objectStorage: Object Storage +objectStorageUseSSLDesc: API bağlantıları için HTTPS kullanmayacaksanız bunu kapatın +objectStorageUseProxyDesc: API bağlantıları için Proxy kullanmayacaksanız bunu kapatın +objectStorageSetPublicRead: Yüklendiğinde "public-read" kullan +serverLogs: Sunucu günlüğü +abuseReports: Raporlar +reportAbuse: Rapor +verificationEmailSent: Bir doğrulama maili gönderildi. Doğrulamayı tamamlamak için + lütfen verilen bağlantıyı takip edin. +hashtags: Etiketler +resolved: Çözüldü +flagShowTimelineRepliesDescription: Açıksa, kullanıcıların zaman çizelgesindeki diğer + kullanıcıların gönderilerine verdiği yanıtları gösterir. +clearQueueConfirmText: Kuyrukta kalan teslim edilmemiş gönderiler birleştirilmeyecektir. + Genellikle bu işleme gerek yoktur. +image: Resim +video: Video +showMore: Daha Fazla +showLess: Kapat +selectAntenna: Anten seç +selectWidget: Araç seç +unsuspendConfirm: Bu hesabın askıya almasını kaldırmak istediğinize emin misiniz? +selectList: Liste seç +editWidgets: Araçları düzenle +showEmojisInReactionNotifications: Tepki bildirimlerinde emojileri göster +renoteMute: Yükseltmeleri sustur +renoteUnmute: Yükseltmeleri susturmayı kaldır +loginFailed: Giriş yapılamadı +proxyAccount: Vekil Hesap +selectUser: Kullanıcı seç +recipient: Alıcı(lar) +annotation: Yorumlar +federation: Federasyon +registeredAt: Kayıtlı +latestRequestSentAt: Gönderilen son istek +latestRequestReceivedAt: Alınan son istek +latestStatus: Son durum +storageUsage: Depolama kullanımı +charts: Grafikler +perHour: Saat Başı +perDay: Gün Başı +stopActivityDelivery: Etkinlik göndermeyi durdur +blockThisInstance: Bu sunucuyu engelle +themeForDarkMode: Karanlık modda kullanmak için temalar +fileName: Dosya adı +selectFile: Dosya seç +emptyDrive: Drive'n boş +promote: Terfi +numberOfDays: Gün sayısı +hideThisNote: Bu gönderiyi gizle +file: Dosya +enableEmojiReactions: Emoji tepkilerini aç +cw: İçerik uyarısı +makeFollowManuallyApprove: Takip istekleri onay gerektirir +today: Bugün +enableRecommendedTimeline: Tavsiye edilen zaman çizgisini aktive et +state: Durum +sort: Sırala +script: Skript +keepCw: İçerik uyarılarını sakla +manageAccounts: Hesapları Düzenle +makeReactionsPublicDescription: Bu, tüm geçmiş tepkilerinizin listesini herkesin görebileceği + bir hale getirecektir. +unmuteThread: İleti dizisinin sesini aç +ffVisibility: Takipler/Takipçiler Görünürlüğü +reflectMayTakeTime: Bunun yansıması biraz zaman alabilir. +cropImageAsk: Bu resmi kırpmak istediğinize emin misiniz? +check: Kontrol Et +driveCapOverrideLabel: Bu kullanıcı için drive kapasitesini değiştirin +numberOfPageCache: Önbelleğe alınan sayfa sayısı +license: Lisans +indexFrom: Post ID'den itibaren dizin +xl: XL +notificationSetting: Bildirim ayarları +fillAbuseReportDescription: Lütfen bu raporla ilgili ayrıntıları doldurun. Belirli + bir gönderiyle ilgiliyse, lütfen URL'sini ekleyin. +forwardReportIsAnonymous: Uzak sunucuda, hesabınız yerine raportör olarak anonim bir + sistem hesabı görüntülenecektir. +abuseMarkAsResolved: Raporu çözüldü olarak işaretle +instanceTicker: Göndeirlerdeki sunucu bilgisi +waitingFor: '{x} bekleniyor' +random: Rastgele +public: Herkese açık +i18nInfo: Firefish, gönüllüler tarafından çeşitli dillere çevriliyor. {link} adresinden + yardımcı olabilirsiniz. +disableShowingAnimatedImages: Animasyonlu görüntüleri oynatma +clips: Ataçlar +experimentalFeatures: Deneysel özellikler +developer: Geliştirici +left: Sol +center: Orta +wide: Geniş +narrow: Dar +reloadToApplySetting: Bu ayar yalnızca bir sayfa yeniden yüklendikten sonra geçerli + olacaktır. Şimdi yeniden yüklensin mi? +editCode: Kodu düzenle +apply: Uygula +receiveAnnouncementFromInstance: Bu sunucudan bildirimleri al +publish: Paylaş +quitFullView: Tam görünümden çık +addDescription: Açıklama ekle +info: Hakkında +noMaintainerInformationWarning: Yönetici bilgileri yapılandırılmadı. +noBotProtectionWarning: Bot koruması yapılandırılmamış. +postToGallery: Yeni galeri gönderisi oluştur +gallery: Galeri +privateModeInfo: Etkinleştirildiğinde, yalnızca beyaz listedeki sunucular sunucunuzla + birleşebilir. Tüm gönderiler halktan gizlenecektir. +itsOff: Etkinsizleştirilmiş +ffVisibilityDescription: Kimleri takip ettiğinizi ve kimlerin sizi takip ettiğini + kimlerin görebileceğini yapılandırmanıza izin verir. +welcomeBackWithName: Tekrar hoş geldin {name} +themeColor: Sunucu Kayan Yazı Rengi +audio: Ses +recentNHours: Son {n} saat +isSystemAccount: Bu hesap sistem tarafından oluşturulur ve otomatik olarak işletilir. + Lütfen bu hesabı denetlemeyin, düzenlemeyin, silmeyin veya başka bir şekilde kurcalamayın, + aksi takdirde sunucunuz bozulabilir. +typeToConfirm: Lütfen onaylamak için {x} girin +remoteOnly: Sadece uzak +failedToUpload: Yükleme başarısız +cannotUploadBecauseInappropriate: Bu dosya, bazı bölümleri potansiyel olarak NSFW + olarak algılandığından yüklenemedi. +cannotUploadBecauseNoFreeSpace: Drive kapasitesi yetersiz olduğundan yükleme başarısız + oldu. +cannotUploadBecauseExceedsFileSizeLimit: Bu dosya, izin verilen maksimum boyutu aştığı + için yüklenemedi. +beta: Beta +enableAutoSensitive: Otomatik NSFW İşaretleme +enableAutoSensitiveDescription: Mümkün olduğunda Makine Öğrenimi yoluyla NSFW ortamının + otomatik olarak algılanmasına ve işaretlenmesine olanak tanır. Bu seçenek devre + dışı bırakılsa bile, sunucu çapında etkinleştirilebilir. +shuffle: Karıştır +pushNotification: Push bildirimleri +unsubscribePushNotification: Push bildirimlerini kapat +pushNotificationNotSupported: Tarayıcınız veya sunucunuz push bildirimleri desteklemiyor +caption: Otomatik Başlık +moveToLabel: 'Taşıyacağın hesap:' +moveFromDescription: Bu, eski hesabınızın bir takma adını belirleyecek ve böylece + o hesaptan bu mevcut hesaba geçebileceksiniz. Bunu eski hesabınızdan taşınmadan + ÖNCE yapın. Lütfen @person@server.com şeklinde biçimlendirilmiş hesabın etiketini + girin +migrationConfirm: "Hesabınızı {account} hesabına taşımak istediğinizden kesinlikle + emin misiniz? Bunu yaptığınızda, geri alamazsınız ve hesabınızı bir daha normal + şekilde kullanamazsınız.\nAyrıca, lütfen bu cari hesabı, taşındığınız hesap olarak + ayarladığınızdan emin olun." +indexFromDescription: Her gönderiyi dizine eklemek için boş bırakın +indexNotice: Şimdi indeksleniyor. Bu muhtemelen biraz zaman alacaktır, lütfen sunucunuzu + en az bir saat yeniden başlatmayın. +customKaTeXMacro: Özel KaTeX makroları +directNotes: Direkt Mesajlar +import: İçeri Aktar +export: Dışarı Aktar +mentions: Bahsetmeler +files: Dosyalar +driveFileDeleteConfirm: '"{name}" dosyasını silmek istediğinizden emin misiniz? Ek + olarak içeren tüm gönderilerden kaldırılacaktır.' +createList: Liste oluştur +listsDesc: Listeler, belirtilen kullanıcılarla zaman çizelgesi oluşturmanıza olanak + tanır. Zaman Çizelgesi sayfasından erişilebilirler. +note: Gönder +enterListName: Liste için isim gir +unfollow: Takipten Çık +privacy: Gizlilik +followRequestPending: Takip isteği bekleniyor +enterEmoji: Bir emoji gir +followRequest: Takip İsteği +followRequests: Takip istekleri +renoted: Yükseldi. +emoji: Emoji +cacheRemoteFiles: Uzak dosyaları önbellekle +flagAsBot: Bu hesabı robot olarak işaretle +flagAsBotDescription: Bu hesap bir program tarafından kontrol ediliyorsa bu seçeneği + etkinleştirin. Etkinleştirilirse, diğer geliştiricilerin diğer botlarla sonsuz etkileşim + zincirlerini önlemesi ve Firefish'nin dahili sistemlerini bu hesabı bir bot olarak + ele alacak şekilde ayarlaması için bir bayrak görevi görür. +clearQueue: Sırayı Temizle +hiddenTags: Gizlenmiş Etiketler +done: Tamamlandı +processing: İşleniyor +silenced: Susturulmuş +darkThemes: Karanlık temalar +suspended: Askıya Alınmış +keepOriginalUploadingDescription: Orijinal olarak yüklenen görüntüyü olduğu gibi kaydeder. + Kapatılırsa, yükleme sırasında web'de görüntülenecek bir sürüm oluşturulur. +start: Başla +home: Ev +location: Konum +registeredDate: Katılım tarihi +yearX: '{year}' +pages: Sayfalar +integration: Entegrasyonlar +antennasDesc: "Antenler, belirlediğiniz kriterlere uyan yeni gönderiler görüntüler!\n + Zaman çizelgeleri sayfasından erişilebilirler." +notesAndReplies: Gönderiler ve yanıtlar +withReplies: Yanıtları da içer +connectedTo: Aşağıdaki hesap(lar) bağlı +silenceConfirm: Bu kullanıcıyı susturmak istediğinize emin misiniz? +messagingWithUser: Özel sohbet +resetPassword: Şifreyi sıfırla +registerSecurityKey: Yeni security key tanımla +docSource: Bu dökümanın kaynağı +ascendingOrder: Artan +tags: Etiketler +descendingOrder: Azalan +scratchpadDescription: Karalama defteri, AiScript deneyleri için bir ortam sağlar. + İçinde Firefish ile etkileşime girerek sonuçlarını yazabilir, çalıştırabilir ve kontrol + edebilirsiniz. +local: Yerel +remote: Uzak +addRelay: Röle Ekle +accessibility: Erişilebilirlik +showFeaturedNotesInTimeline: Önerilen gönderileri zaman çizelgesinde göster +objectStorageBaseUrlDesc: "Referans olarak kullanılan URL. İkisinden birini kullanıyorsanız, + CDN veya Proxy'nizin URL'sini belirtin.\nS3 için 'https://.s3.amazonaws.com' + kullanın ve GCS veya eşdeğer hizmetler için 'https://storage.googleapis.com/' + vb. kullanın." +objectStoragePrefixDesc: Dosyalar bu prefix ile dizinler altında saklanacaktır. +objectStorageEndpointDesc: AWS S3 kullanıyorsanız bunu boş bırakın, aksi halde kullandığınız + hizmete bağlı olarak uç noktayı "" veya ":" olarak belirtin. +objectStorageRegion: Region +invisibleNote: Gizli Gönderi +deletedNote: Silinmiş Gönderi +visibility: Görünürlük +poll: Anket +themeEditor: Tema düzenleyicisi +enterFileDescription: Başlık gir +description: Açıklama +describeFile: Başlık ekle +system: Sistem +desktop: Masaüstü +confirmToUnclipAlreadyClippedNote: Bu gönderi zaten "{name}" atacının bir parçası. + Bunun yerine onu bu ataçtan kaldırmak istiyor musunuz? +sendErrorReportsDescription: "Açıldığında, bir sorun oluştuğunda ayrıntılı hata bilgileri + Firefish ile paylaşılarak Firefish kalitesinin artırılmasına yardımcı olur.\nBu, işletim + sisteminizin sürümü, kullandığınız tarayıcı, Firefish'deki etkinliğiniz vb. bilgileri + içerecektir." +closeAccount: Hesabı kapat +markAllAsRead: Okunmuş olarak işaretle +allowedInstances: Beyaz Listedeki Sunucular +squareAvatars: Kare avatarları göster +unread: Okunmaımş +instanceDefaultDarkTheme: Sunucu genelinde varsayılan karanlık tema +oneDay: Bir gün +showAds: Reklamları göster +adminCustomCssWarn: Bu ayar yalnızca ne işe yaradığını biliyorsanız kullanılmalıdır. + Yanlış değerler girilmesi, HERKESİN istemcilerinin normal şekilde çalışmamasına + neden olabilir. Lütfen CSS'nizi kullanıcı ayarlarınızda test ederek düzgün çalıştığından + emin olun. +customSplashIcons: Özel açılış ekranı simgeleri (url'ler) +recommendedInstancesDescription: Önerilen zaman çizelgesinde görünmesi için satır + sonlarıyla ayrılmış önerilen sunucular. +recommendedInstances: Önerilen sunucular +enableServerMachineStats: Sunucu donanımı istatistiklerini etkinleştir +_sensitiveMediaDetection: + sensitivityDescription: Hassasiyetin düşürülmesi daha az yanlış tespite yol açarken, + hassasiyeti artırmak daha az tespitin gözden kaçmasına yol açacaktır. + setSensitiveFlagAutomaticallyDescription: Bu seçenek kapatılsa bile dahili algılamanın + sonuçları korunacaktır. + description: Makine Öğrenimi yoluyla NSFW ortamını otomatik olarak tanıyarak sunucu + denetleme çabasını azaltır. Bu, sunucudaki yükü biraz artıracaktır. + sensitivity: Algılama hassasiyeti + analyzeVideos: Videoların analizini etkinleştir + setSensitiveFlagAutomatically: NSFW olarak işaretle + analyzeVideosDescription: Görüntülere ek olarak videoları da analiz eder. Bu, sunucudaki + yükü biraz artıracaktır. +enableIdenticonGeneration: Kimlik oluşturmayı etkinleştir +reactionPickerSkinTone: Tercih edilen emoji cilt tonu +noteId: Gönderi ID +preventAiLearning: AI bot öğrenmesini önleyin +preventAiLearningDescription: Gönderiler ve resimler gibi yüklediğiniz içeriği incelememek + için üçüncü taraf yapay zeka dil modellerini isteyin. +isAdmin: Yönetici +_emailUnavailable: + disposable: Tek kullanımlık mail adresleri kullanılamaz + smtp: Bu mail sunucusu cevap vermiyor + mx: Bu mail sunucusu hatalı + used: Bu mail zaten kullanılıyor + format: Bu mail adresi yanlış +apps: Uygulamalar +findOtherInstance: Başka bir sunucu bul +showWithSparkles: Parıltılarla göster +showPopup: Kullanıcıları pop-up ile bilgilendirin +silencedWarning: Bu sayfa, bu kullanıcılar yöneticinizin susturduğu sunuculardan olduğu + için gösteriliyor, bu nedenle potansiyel olarak spam olabilirler. +isPatron: Firefish Patronu +youHaveUnreadAnnouncements: Okunmamış duyurularınız var +donationLink: Bağış sayfası linki +neverShow: Birdaha gösterme +remindMeLater: Belki sonra +removeQuote: Alıntıyı sil +removeRecipient: Alıcıyı sil +removeMember: Kullanıcıyı sil +customKaTeXMacroDescription: 'Kolayca matematiksel ifadeler yazmak için makrolar kurun! + Gösterim, LaTeX komut tanımlarına uygundur ve \newcommand{\ name}{content} veya + \newcommand{\name}[argüman sayısı]{content} şeklinde yazılır. Örneğin, \newcommand{\add}[2]{#1 + + #2}, \add{3}{foo} öğesini 3 + foo olarak genişletir. Makro adını çevreleyen süslü + parantezler, yuvarlak veya köşeli parantezler olarak değiştirilebilir. Bu, bağımsız + değişkenler için kullanılan parantezleri etkiler. Satır başına bir (ve yalnızca + bir) makro tanımlanabilir ve satırı tanımın ortasından ayıramazsınız. Geçersiz satırlar + basitçe yoksayılır. Yalnızca basit dizi değiştirme işlevleri desteklenir; koşullu + dallanma gibi gelişmiş söz dizimi burada kullanılamaz.' +enableCustomKaTeXMacro: Özel KaTeX makrolarını aktif et +isLocked: Bu hesabın takip onayları var +isModerator: Moderatör +signupsDisabled: Bu sunucudaki kayıtlar şu anda devre dışı, ancak istediğiniz zaman + başka bir sunucuya kaydolabilirsiniz! Bu sunucu için bir davet kodunuz varsa, lütfen + aşağıya girin. +sendModMail: Moderasyon Bildirimi Gönder +noGraze: Firefish ile çakıştığı için lütfen "Graze for Mastodon" tarayıcı uzantısını + devre dışı bırakın. +isBot: Bu hesap bir bottur +_2fa: + renewTOTPOk: Yeniden Yapılandır + registerTOTP: Doğrulayıcı uygulamasını kaydedin + renewTOTPCancel: İptal Et + renewTOTPConfirm: Bu, önceki uygulamanızdaki doğrulama kodlarının çalışmamasına + neden olur + alreadyRegistered: Zaten bir 2 faktörlü kimlik doğrulama cihazını kaydettiniz. + chromePasskeyNotSupported: Chrome geçiş anahtarları şu anda desteklenmemektedir. + registerSecurityKey: Bir güvenlik veya geçiş anahtarı kaydedin + securityKeyName: Bir anahtar adı girin + removeKey: Güvenlik anahtarını kaldır + removeKeyConfirm: '{name} anahtarı gerçekten silinsin mi?' + renewTOTP: Kimlik doğrulayıcı uygulamasını yeniden yapılandırın + token: 2FA Tokeni + step1: Öncelikle, cihazınıza bir kimlik doğrulama uygulaması ({a} veya {b} gibi) + yükleyin. + step2Click: Bu QR koduna tıklamak, 2FA'yı güvenlik anahtarınıza veya telefon kimlik + doğrulayıcı uygulamanıza kaydetmenize olanak tanır. + step3Title: Bir kimlik doğrulama kodu girin + securityKeyNotSupported: Tarayıcınız güvenlik anahtarlarını desteklemiyor. + step2: Ardından, bu ekranda görüntülenen QR kodunu tarayın. + step2Url: "Bir masaüstü programı kullanıyorsanız bu URL'yi de girebilirsiniz:" + step3: Kurulumu tamamlamak için uygulamanız tarafından sağlanan tokeni girin. + step4: Şu andan itibaren, gelecekteki herhangi bir oturum açma denemesi böyle bir + oturum açma tokeni isteyecektir. + securityKeyInfo: Parmak izi veya PIN kimlik doğrulamasının yanı sıra, hesabınızın + güvenliğini daha da artırmak için FIDO2'yi destekleyen donanım güvenlik anahtarları + aracılığıyla kimlik doğrulama ayarlayabilirsiniz. + tapSecurityKey: Güvenlik veya geçiş anahtarını kaydetmek için lütfen tarayıcınızı + takip edin + registerTOTPBeforeKey: Bir güvenlik veya geçiş anahtarı kaydetmek için lütfen bir + kimlik doğrulama uygulaması kurun. + whyTOTPOnlyRenew: Kimlik doğrulayıcı uygulaması, bir güvenlik anahtarı kaydedildiği + sürece kaldırılamaz. +_poll: + voted: Oylandı + deadlineTime: Zaman + remainingDays: '{d} gün {h} saat kaldı' + remainingHours: '{h} saat {m} dakika kaldı' + remainingSeconds: '{s} saniye kaldı' + remainingMinutes: '{m} dakika {s} saniye kaldı' + noOnlyOneChoice: En az 2 seçenek gerekiyor + noMore: Daha fazla seçenek ekleyemezsin + at: Bitiş... + deadlineDate: Bitiş tarihi + duration: Süre + votesCount: '{n} oy' + expiration: Anketi bitir + totalVotes: toplam {n} oy + closed: Bitti + infinite: Asla + vote: Oyla + showResult: Sonuçları görüntüle + after: "'den sonra bitiş..." + choiceN: Seçenek {n} + canMultipleVote: Birden fazla seçime izin ver +_theme: + code: Tema kodu + description: Açıklama + builtinThemes: Yerleşik temalar + color: Renk + keys: + fgHighlighted: Vurgulanan Metin + infoWarnFg: Uyarı metni + mention: Bahsetme + mentionMe: Bahsetme (Kendim) + buttonBg: Düğme arka planı + buttonHoverBg: Düğme arka planı (Üstüne Gelince) + shadow: Gölge + navBg: Kenar çubuğu arka planı + accent: Vurgu + fg: Yazı + dateLabelFg: Tarih etiketi metni + navActive: Kenar çubuğu metni (Etkin) + wallpaperOverlay: Arkaplan arayüzü + messageBg: Sohbet arkaplanı + focus: Fokus + accentLighten: Vurgu (Aydınlık) + bg: Arkaplan + indicator: Gösterge + hashtag: Etiket + renote: Yükseltme + modalBg: Modal arka plan + divider: Bölücü + scrollbarHandle: Kaydırma çubuğu kolu + scrollbarHandleHover: Kaydırma çubuğu tutacağı (Üzerine Gelince) + infoBg: Bilgi geçmişi + accentDarken: Vurgu (Karanlık) + header: Başlık + navFg: Kenar çubuğu metni + navIndicator: Kenar çubuğu göstergesi + link: Link + infoFg: Bilgi metni + infoWarnBg: Uyarı arka planı + badge: Rozen + panel: Panel + navHoverFg: Kenar çubuğu metni (Üzerine Gelince) + cwBg: CW düğmesi arka planı + cwFg: CW düğmesi meni + cwHoverBg: CW düğmesi arka planı (Üzerine Gelince) + toastFg: Bildirim metni + inputBorder: Giriş alanı sınırı + listItemHoverBg: Liste öğesi arka planı (Üstüne Gelince) + toastBg: Bildirim arka planı + driveFolderBg: Drive klasörü arkaplanı + funcKind: Fonksiyon tipi + argument: Argüman + lighten: Aydınlat + inputConstantName: Bu sabit için bir ad girin + deleteConstantConfirm: '{const} sabitini gerçekten silmek istiyor musunuz?' + explore: Temaları Keşfet + darken: Karart + base: Temel + manage: Temaları düzenle + installedThemes: Yüklenen temalar + invalid: Bu temanın biçimi geçersiz + make: Tema oluştur + key: Anahtar + alpha: Opaklık + install: Tema yükle + installed: '{name} başarıyla yüklendi' + alreadyInstalled: Bu tema zaten yüklendi + importInfo: Buraya tema kodunu girerseniz, onu tema düzenleyiciye aktarabilirsiniz + func: Fonksiyonlar + basedProp: Referenslanan özellik + constant: Sabit + defaultValue: Varsayılan değer + refConst: Bir sabiti referansla + refProp: Bir mülkü referansla + addConstant: Sabit ekle +_menuDisplay: + sideIcon: Yan (Simgeler) + sideFull: Yan + hide: Gizle + top: Üst +_filters: + fromUser: Kullanıcıdan + withFile: Dosya ile + notesBefore: Gönderiden önce + notesAfter: Gönderiden sonra + followingOnly: Sadece takip ettiklerim + fromDomain: Alan adı ile + followersOnly: Sadece takipçiler +_permissions: + "write:blocks": Engelli kullanıcıları düzenle + "read:drive": Drive dosya ve klasörlerine eriş + "read:favorites": Yer imlerini görüntüle + "write:mutes": Susturulmuş kullanıcıları düzenle + "read:notifications": Bildirimleri görüntüle + "write:notifications": Bildirimleri düzenle + "write:page-likes": Sayfalardaki beğenilerini düzenle + "read:user-groups": Kullanıcı gruplarını göster + "write:reactions": Tepkilerini düzenle + "read:pages": Sayfalarını göster + "write:channels": Kanallarını düzenle + "read:gallery": Galerini göster + "read:gallery-likes": Beğenilen galeri gönderilerini göster + "write:gallery-likes": Galeri gönderilerini düzenle + "write:messaging": Sohbet mesajı oluştur veya sil + "write:user-groups": Kullanıcı gruplarını düzenle veya sil + "read:messaging": Sohbetlerini görüntüle + "read:mutes": Susturulmuş kullanıcıları göster + "write:votes": Bir ankete oy ver + "read:page-likes": Beğenilen sayfalarını göster + "read:reactions": Tepkilerini göster + "read:channels": Kanallarını göster + "write:notes": Gönderi oluştur veya sil + "write:drive": Drive dosya ve klasörlerini düzenle + "write:favorites": Yer imlerini düzenle + "read:following": Kimleri takip ettiğini göster + "write:account": Hesap bilgisini düzenle + "read:account": Hesap bilgisini görüntüle + "read:blocks": Engelli kullanıcıları gör + "write:following": Hesapları takip et veya takipten çıkar + "write:pages": Sayfalarını düzenle veya sil + "write:gallery": Galerini düzenle +_auth: + pleaseGoBack: Lütfen uygulamaya geri dönün + callback: Uygulamaya geri dönülüyor + shareAccess: '"{name}" adlı kişinin bu hesaba erişmesine izin vermek ister misiniz?' + permissionAsk: 'Bu uygulama aşağıdaki izinleri ister:' + allPermissions: Tam hesap erişimi + denied: Erişim reddedildi + copyAsk: 'Lütfen aşağıdaki yetkilendirme kodunu uygulamaya yapıştırın:' + shareAccessAsk: Bu uygulamanın hesabınıza erişmesine izin vermek istediğinizden + emin misiniz? +_antennaSources: + users: Belirli kullanıcılardan gönderiler + homeTimeline: Takip edilen kullanıcılardan gönderiler + all: Tüm gönderiler + instances: Bir sunucudaki tüm kullanıcılardan gelen gönderiler + userList: Belirli bir kullanıcı listesinden gönderiler + userGroup: Belirli bir gruptaki kullanıcıların gönderileri +_charts: + usersIncDec: Kullanıcı sayısı farkı + usersTotal: Toplam kullanıcı sayısı + remoteNotesIncDec: Uzak gönderilerin sayısındaki fark + notesTotal: Toplam gönderi sayısı + filesTotal: Toplam dosya sayısı + apRequest: İstekler + storageUsageIncDec: Depolama kullanımındaki fark + localNotesIncDec: Yerel gönderilerin sayısındaki fark + storageUsageTotal: Toplam depolama kullanımı + federation: Federasyon + notesIncDec: Gönderi sayısındaki fark + activeUsers: Aktif kullanıcılar + filesIncDec: Dosya sayısındaki fark +_pages: + fontSerif: Serif + fontSansSerif: Sans Serif + chooseBlock: Bloğu sil + blocks: + _canvas: + id: Tuval ID + height: Yükseklik + width: Genişlik + _button: + _action: + resetRandom: Rastgele çekirdeği sıfırla + _pushEvent: + no-variable: Hiçbiri + event: Etkinlik ismi + message: Aktif olduğunda gösterilecek mesaj + variable: Gönderilecek değişken + callAiScript: AiScript'i çağırın + _callAiScript: + functionName: Fonksiyon ismi + dialog: Dialog göster + _dialog: + content: İçerik + pushEvent: Etkinlik gönder + text: Başlık + action: Düğmeye basıldığında olacaklar + colored: Renkli + text: Yazı + if: Eğer + _if: + variable: Değişken + canvas: Tuval + note: Gömülü yazı + _note: + id: Gönderi ID + idDescription: Alternatif olarak gönderi URL'sini buraya yapıştırabilirsiniz. + detailed: Detaylı görüntüleme + _counter: + text: Başlık + inc: Adım + name: Değer ismi + radioButton: Seçenek + _radioButton: + name: Değişken ismi + values: Seçenekleri satırlarla ayırın + title: Başlık + default: Varsayılan değer + _post: + text: İçerik + attachCanvasImage: Tuval resmi ekle + canvasId: Tuval ID + _textInput: + text: Başlık + name: Değişken ismi + default: Varsayılan değer + _numberInput: + name: Değiken ismi + text: Başlık + default: Varsayılan değer + _textareaInput: + text: Başlık + name: Değişken ismi + default: Varsayılan değer + textarea: Yazı alanı + _switch: + name: Değişken ismi + default: Varsayılan değer + text: Başlık + counter: Sayaç + switch: Değiştir + post: Gönderi formu + image: Resimler + section: Bölüm + textareaInput: Çok satırlı yazı girişi + button: Düğme + textInput: Yazı girişi + numberInput: Sayısal giriş + script: + categories: + text: Yazı işlemleri + flow: Akış kontrolü + random: Rastgele + fn: Fonksiyonlar + convert: Dönüşümler + list: Listeler + logical: Mantıksal işlem + operation: Hesaplama + comparison: Karşılaştırma + value: Değerler + blocks: + and: A ve B + _or: + arg2: B + arg1: A + _lt: + arg1: A + arg2: B + _ltEq: + arg1: A + arg2: B + textList: Yazı listesi + strReverse: Yazıyı çevir + multiply: Çarp + subtract: Çıkar + _mod: + arg1: A + arg2: B + _divide: + arg2: B + arg1: A + round: Ondalık yuvarlama + _round: + arg1: Sayı + _eq: + arg1: A + arg2: B + notEq: A ve B farklıysa + _notEq: + arg1: A + arg2: B + or: A veya B + gt: "> A, B'den çoksa" + ltEq: <= A, B'den az veya eşitse + gtEq: ">= A, B'den çok veya eşitse" + _gtEq: + arg1: A + arg2: B + _not: + arg1: OLUMSUZ + random: Rastgele + randomPick: Listeden rastgele seç + seedRandom: Random (çekirdek ile) + _for: + arg1: Tekrarlama sayısı + arg2: Eylem + _seedRannum: + arg3: Maksimum değer + arg2: Minimum değer + arg1: Çekirdek + _strReplace: + arg3: ile değiştir + arg2: Değiştirilecek yazı + arg1: Yazı + _subtract: + arg2: B + arg1: A + mod: Kalan + _and: + arg1: A + arg2: B + _DRPWPM: + arg1: Yazı listesi + _fn: + slots-info: Her yuvayı bir satır sonu ile ayırın + arg1: Çıkış + slots: Yuvalar + for: dögü + dailyRandomPick: Listeden rastgele seçim yapın (Her kullanıcı için günde bir + kez değişir) + _dailyRannum: + arg1: Minimum değer + arg2: Maksimum değer + _seedRandomPick: + arg2: Liste + arg1: Çekirdek + _pick: + arg2: Pozisyon + arg1: Liste + number: Sayı + _if: + arg3: Yoksa + arg1: Eğer + arg2: Sonra + _rannum: + arg1: Minimum değer + arg2: Maksimum değer + eq: A ve B eşitse + _gt: + arg1: A + arg2: B + rannum: Rastgele sayı + _randomPick: + arg1: Liste + pick: Listeden seç + _listLen: + arg1: Liste + _multiply: + arg2: B + arg1: A + divide: Böl + strPick: Dize ayıklayın + _strPick: + arg1: Yazı + arg2: Dize konunumu + dailyRandom: Rastgele (Her kullanıcı için günde bir kez değişir) + _dailyRandom: + arg1: Olasılık + dailyRannum: Rastgele sayı (Her kullanıcı için günde bir kez değişir) + _stringToNumber: + arg1: Yazı + if: Şube + strReplace: Yedek dize + text: Yazı + _splitStrByLine: + arg1: Yazı + not: OLUMSUZ + _seedRandom: + arg1: Çekirdek + arg2: Olasılık + seedRandomPick: Listeden rastgele seçim yapın (çekirdek ile) + fn: Fonksiyon + multiLineText: Yazı (çok satırlı) + _textList: + info: Her girişi satırlar ile ayırın + _strReverse: + arg1: Yazı + join: Yazıyı birleştirme + _join: + arg1: Listeler + arg2: Ayraç + add: Ekle + _add: + arg1: A + arg2: B + _strLen: + arg1: Yazu + aiScriptVar: AiScript Değişkeni + ref: Değişken + splitStrByLine: Yazıyı satır sonlarına göre bölme + strLen: Yazı uzunluğu + lt: < A, B'den azsa + _random: + arg1: Olasılık + DRPWPM: Ağırlıklı listeden rastgele seçim yapın (Her kullanıcı için günde bir + kez değişir) + listLen: Listenin uzunluğunu al + numberToString: Sayıdan yazıya + _dailyRandomPick: + arg1: Liste + stringToNumber: Yazıdan Sayıya + seedRannum: Rastgele sayı (çekirdek ile) + _numberToString: + arg1: Sayı + types: + number: Sayı + boolean: Etiket + array: Liste + stringArray: Yazı listesi + string: Yazı + emptySlot: Boş yuva + enviromentVariables: Ortam değikenleri + argVariables: Giriş yuvaları + thereIsEmptySlot: Yuva {slot} boş! + typeError: Yuva {slot}, "{expect}" türündeki değerleri kabul eder, ancak sağlanan + değer "{actual}" türündedir! + pageVariables: Sayfa değişkenleri + readPage: Bu sayfanın kaynağını görüntüle + created: Sayfa başarıyla oluşturuldu + eyeCatchingImageRemove: Afişi sil + selectType: Tip seç + pageSetting: Sayfa ayarları + viewSource: Kaynağı görüntüle + variables: Değişkenler + url: Sayfa URL'si + unlike: Beğeniyi kaldır + my: Sayfalarım + content: Sayfa bloğu + deleted: Sayfa başarıyla silindi + newPage: Yeni sayfa oluştur + editPage: Bu sayfayı düzenle + viewPage: Sayfalarını görüntüle + like: Beğen + nameAlreadyExists: Belirtilen Sayfa URL'si zaten var + invalidNameTitle: Belirtilen Sayfa URL'si geçersiz + invalidNameText: Sayfa başlığının boş olmadığından emin olun + editThisPage: Sayfayı düzenle + featured: Popüler + inspector: Denetçi + contents: İçerik + title: Başlık + liked: Beğenilen Sayfalar + font: Yazı Tipi + alignCenter: İçerikleri ortala + eyeCatchingImageSet: Afiş ayarla + enterVariableName: Değişken ismi ekle + hideTitleWhenPinned: Profile sabitlendiğinde Sayfa başlığını gizle + variableNameIsAlreadyUsed: Bu değişken adı zaten kullanımda + contentBlocks: İçerik + inputBlocks: Giriş + specialBlocks: Özel + updated: Sayfa başarıyla düzenlendi + summary: Sayfa özeti +_notification: + _types: + follow: Yeni takipçiler + mention: Bahsetmeler + app: Bağlı uygulamalardan bildirimler + pollEnded: Biten anket + receiveFollowRequest: Takip istekleri alındı + reaction: Tepkiler + all: Hepsi + followRequestAccepted: Takip istekleri kabul edildi + pollVote: Anket oylamaları + renote: Yükseltmeler + reply: Yanıtlar + groupInvited: Grup davetleri + quote: Alıntılar + pollEnded: Anket sonuçları açıklandı + fileUploaded: Dosya başarıyla yüklendi + youRenoted: '{name} tarafından yükseltildin' + _actions: + followBack: Seni geri takip etti + reply: Yanıtla + renote: Yükseltmeler + youGotMention: '{name} senden bahsetti' + youWereFollowed: seni takip etti + youGotMessagingMessageFromGroup: '{name} grubuna bir sohbet mesajı gönderildi' + renoted: gönderini yükseltti + youGotQuote: '{name} seni alıntıladı' + youGotReply: '{name} seni yanıtladı' + reacted: gönderine tepki ekledi + yourFollowRequestAccepted: Takip isteğin kabul edildi + emptyPushNotificationMessage: Push bildirimleri güncellendi + youWereInvitedToGroup: '{userName} seni gruba davet etti' + voted: anketine oy verdi + youReceivedFollowRequest: Bir takip isteği geldi + youGotPoll: '{name} anketinde oylama yaptı' + youGotMessagingMessageFromUser: '{name} sana bir sohbet mesajı gönderdi' +_experiments: + title: Deneyler + postImportsCaption: Kullanıcıların geçmiş Firefish, Misskey, Mastodon, Akkoma ve + Pleroma hesaplarından gönderilerini içe aktarmalarına izin verir. Kuyruğunuz tıkanırsa + yükleme sırasında yavaşlamalara neden olabilir. + enablePostImports: Gönderi içeri aktarmasını aktif et +_dialog: + charactersExceeded: 'Maksimum karakter aşıldı! Geçerli: {current}/Sınır: {max}' + charactersBelow: 'Yeterli karakter yok! Geçerli: {current}/Sınır: {min}' +_signup: + emailSent: Mail adresinize ({email}) bir onay maili gönderildi. Hesap oluşturmayı + tamamlamak için lütfen verilen bağlantıya tıklayın. + almostThere: Neredeyse vardık + emailAddressInfo: Lütfen mail adresinizi giriniz. Herkese açık gözükmeyecektir. +_ad: + back: Geri + reduceFrequencyOfThisAd: Daha az reklam göster +_accountDelete: + accountDelete: Hesabı sil + mayTakeTime: Hesap silme, kaynak yoğun bir işlem olduğundan, ne kadar içerik oluşturduğunuza + ve ne kadar dosya yüklediğinize bağlı olarak tamamlanması biraz zaman alabilir. + sendEmail: Hesap silme işlemi tamamlandıktan sonra, bu hesapta kayıtlı olan mail + adresine bir mail gönderilecektir. + started: Silme işlemi başlatıldı. + requestAccountDelete: Hesap silme talebinde bulun + inProgress: Silme işlemi şu anda devam ediyor +_forgotPassword: + enterEmail: Kaydolmak için kullandığınız mail adresini girin. Parolanızı sıfırlayabileceğiniz + bir bağlantı daha sonra ona gönderilecektir. + contactAdmin: Bu sunucu, mail adreslerinin kullanılmasını desteklemiyor, bunun yerine + şifrenizi sıfırlamak için lütfen sunucu yöneticisiyle iletişime geçin. + ifNoEmail: Kayıt sırasında bir mail kullanmadıysanız, sunucu yöneticisiyle iletişime + geçin. +_gallery: + my: Galerim + liked: Beğenilen Gönderiler + like: Beğen + unlike: Beğeniyi kaldır +_registry: + key: Anahtar + scope: Kapsam + keys: Anahtarlar + createKey: Anahtar oluştur + domain: Alan adı +_email: + _follow: + title: Yeni bir takipçin var + _receiveFollowRequest: + title: Yeni bir takip isteğin var +_preferencesBackups: + apply: Bu cihaza uygula + invalidFile: Geçersiz dosya formatı + applyConfirm: '"{name}" yedeğini bu cihaza gerçekten uygulamak istiyor musunuz? + Bu cihazın mevcut ayarlarının üzerine yazılacak.' + inputName: Lütfen bu yedekleme için bir ad girin + cannotSave: Kaydedilemedi + saveConfirm: Yedekleme {name} olarak kaydedilsin mi? + renameConfirm: '"{old}" olan bu yedeğin adı "{new}" olarak değiştirilsin mi?' + createdAt: 'Oluşturma tarihi: {date} {time}' + save: Değişiklikleri Kaydet + nameAlreadyExists: '"{name}" adlı bir yedek zaten var. Lütfen farklı bir ad girin.' + deleteConfirm: '{name} yedeği silinsin mi?' + noBackups: Yedekleme yok. "Yeni yedekleme oluştur" seçeneğini kullanarak bu sunucudaki + istemci ayarlarınızı yedekleyebilirsiniz. + list: Oluşturulan yedekler + saveNew: Yeni bir yedek oluştur + loadFile: Dosyadan yükle + updatedAt: 'Güncelleme tarihi: {date} {time}' + cannotLoad: Yüklenemedi +_aboutMisskey: + patronsList: Bağış büyüklüğüne göre değil, kronolojik olarak listelenmiştir. Adınızı + buraya almak için yukarıdaki bağlantıyla bağış yapın! + about: Firefish, 2022'den beri geliştirilmekte olan ThatOneCalculator tarafından + yapılan bir Misskey çatalıdır. + allContributors: Tüm katkıda bulunanlar + patrons: Firefish patronları + morePatrons: Burada listelenmeyen diğer birçok yardımcının desteğini de takdir ediyoruz. + Teşekkür ederim! 🥰 + donate: Firefish'e bağışta bulunun + contributors: Ana katkıda bulunanlar + source: Kaynak Kodu + translation: Firefish'i tercüme et + donateTitle: Firefish'den hoşlanıyor musunuz? + pleaseDonateToFirefish: Lütfen gelişimini desteklemek için Firefish'e bağış yapmayı + düşünün. + pleaseDonateToHost: İşletme maliyetlerini desteklemek için lütfen ev sunucunuz {host}'a + bağış yapmayı da düşünün. + donateHost: '{ev sahibi} için bağış yapın' + sponsors: Firefish sponsorları +_weekday: + saturday: Cumartesi + sunday: Pazar + wednesday: Çarşamba + friday: Cuma + thursday: Perşembe + monday: Pazartesi + tuesday: Salı +_serverDisconnectedBehavior: + reload: Otomatik olarak yenile + quiet: Göze çarpmayan uyarı göster + nothing: Hiçbir şey yapma + dialog: Uyarı mesajını göster +_channel: + removeBanner: Afişi sil + owned: Sahip Olunan + nameOnly: Sadece isim + featured: Popüler + setBanner: Afiş ayarla + usersCount: '{n} Katılımcı' + create: Kanal oluştur + following: Takip + notesCount: '{n} Gönderi' + nameAndDescription: İsim ve açıklama + edit: Kanalı düzenle +_messaging: + groups: Gruplar + dms: Özel +_tutorial: + step5_5: Sosyal {icon} zaman çizelgesi, Ev ve Yerel zaman çizelgelerinin bir kombinasyonudur. + step5_6: Önerilen {icon} zaman çizelgesi, yöneticilerin önerdiği sunuculardan gelen + gönderileri görebileceğiniz yerdir. + step6_1: Peki burası neresi? + title: Firefish nasıl kullanılır + step3_2: "Ev ve sosyal zaman çizelgeleriniz, kimi takip ettiğinize bağlıdır, bu + nedenle başlamak için birkaç hesabı takip etmeyi deneyin.\nTakip etmek için bir + profilin sağ üstündeki artı dairesine tıklayın." + step5_3: Ana Sayfa {icon} zaman çizelgesi, takip ettiğiniz hesaplardan gelen gönderileri + görebileceğiniz yerdir. + step5_4: Yerel {icon} zaman çizelgesi, bu sunucudaki diğer herkesin gönderilerini + görebileceğiniz yerdir. + step6_2: Firefish'e öylece katılmadın. Binlerce sunucudan oluşan birbirine bağlı + bir ağ olan Fediverse'e giden bir portala katıldınız. + step6_4: Şimdi gidin, keşfedin ve eğlenin! + step5_7: Global {icon} zaman çizelgesi, bağlı diğer tüm sunuculardan gelen gönderileri + görebileceğiniz yerdir. + step2_1: Öncelikle lütfen profilinizi doldurunuz. + step2_2: Kim olduğunuz hakkında biraz bilgi vermeniz, başkalarının gönderilerinizi + görmek mi yoksa sizi takip etmek mi istediklerini anlamalarını kolaylaştıracaktır. + step3_1: Şimdi birkaç kullanıcı takip etme zamanı! + step1_1: Hoşgeldin! + step1_2: Hadi seni hazırlayalım. Kısa sürede kullanmaya başlayacaksınız! + step5_1: Zaman çizelgeleri, her yerde zaman çizelgeleri! + step6_3: Her sunucu farklı şekillerde çalışır ve tüm sunucular Firefish'i çalıştırmaz. + Ama bu sunucu kullanıyor! Biraz karışık ama kısa sürede anlayacaksın. + step4_1: Seni oradan çıkaralım. + step5_2: Sunucunuzda etkinleştirilmiş {timelines} farklı zaman çizelgesi var. + step4_2: İlk gönderiniz için, bazı insanlar bir {introduction} gönderisi veya basit + bir "Merhaba dünya!" gönderir +_visibility: + public: Herkese açık + publicDescription: Gönderiniz herkese açık tüm zaman çizelgelerinde görünür olacak + specified: Direkt + followersDescription: Yalnızca takipçilerinize ve adı geçen kullanıcılara görünür + kılın + localOnlyDescription: Uzak kullanıcılara gözükmez + home: Listelenmemiş + homeDescription: Yalnızca ev zaman çizelgesine yayınla + followers: Takipçiler + specifiedDescription: Belirli kullanıcılara özel yapın + localOnly: Sadece yerel +_postForm: + quotePlaceholder: Bu gönderiyi alıntıla... + _placeholders: + a: Ne ile meşgulsün? + b: Etrafında neler oluyor? + f: Yazman bekleniyor... + c: Aklınızdan ne geçiyor? + d: Ne demek istiyorsun? + e: Yazmaya başka... + replyPlaceholder: Bu gönderiyi yanıtla... + channelPlaceholder: Bir kanala gönder... +_exportOrImport: + allNotes: Tüm gönderiler + followingList: Takip edilen kullanıcılar + muteList: Susturulmuş kullanıcılar + excludeMutingUsers: Susturulmuş kullanıcıları hariç tut + excludeInactiveUsers: Aktif olmayan kullanıcıları hariç tut + userLists: Kullanıcı listeleri + blockingList: Engellenimş kullanıcılar +_instanceCharts: + notes: Gönderi sayısındaki fark + notesTotal: Toplu gönderi sayısı + files: Dosya sayısındaki fark + filesTotal: Toplu dosya sayısı + requests: İstekler + usersTotal: Toplu kullanıcı sayısı + users: Kullanıcı sayısı farkı + cacheSize: Önbellek boyutundaki fark + ff: 'Takip edilen / Takipçi sayısındaki fark ' + cacheSizeTotal: Toplam önbellek boyutu + ffTotal: Toplu Takip edilen / Takipçi sayısı +_wordMute: + soft: Yumuşak + muteWords: Susturulmuş kelimeler + muteWordsDescription: AND koşulu için boşluklarla veya OR koşulu için satır sonlarıyla + ayırın. + softDescription: Belirlenen koşulları karşılayan gönderileri zaman çizelgesinden + gizleyin. + hardDescription: Belirlenen koşulları sağlayan gönderilerin zaman çizelgesineeklenmesini + engeller. Ayrıca bu gönderiler, koşullar değişse dahi zaman tüneline eklenmeyecektir. + mutedNotes: Susturulmuş gönderiler + hard: Sert + muteWordsDescription2: Normal ifadeleri kullanmak için anahtar kelimeleri eğik çizgilerle + çevreleyin. +_ago: + weeksAgo: '{n}hafta önce' + minutesAgo: '{n}dakika önce' + daysAgo: '{n}gün önce' + future: Gelecek + justNow: Şimdi + secondsAgo: '{n}saniye önce' + hoursAgo: '{n}saat önce' + monthsAgo: '{n}ay önce' + yearsAgo: '{n}yıl önce' +_timelines: + home: Ev + local: Yerel + social: Sosyal + global: Global + recommended: Tavsiye Edilen +_nsfw: + respect: NSFW medyasını gizle + force: Tüm medyayı gizle + ignore: NSFW medyasını gizleme +_cw: + files: '{count} dosya(lar)' + chars: '{count} harf' + hide: Gizle + show: İçeriği göster +_relayStatus: + rejected: Reddedildi + accepted: Kabul edildi + requesting: Bekleniyor +_time: + day: Gün(ler) + hour: Saat(ler) + second: Saniye(ler) + minute: Dakika(lar) +_skinTones: + light: Aydınlık + medium: Orta + mediumLight: Orta Aydınlık + dark: Karanlık + yellow: Sarı + mediumDark: Orta Karanlık +_plugin: + install: Eklenti indir + installWarn: Lütfen güvenli olmayan eklentiler kurmayınız. + manage: Eklentileri yönet +_instanceTicker: + remote: Uzak kullanıcılar için göster + always: Her zaman göster + none: Asla gösterme +_instanceMute: + heading: Sessize alınacak sunucuların listesi + instanceMuteDescription2: Yeni satırlarla ayırın + title: Listelenen sunuculardan gönderileri gizler. + instanceMuteDescription: Bu, sessize alınmış bir sunucudan bir kullanıcıya yanıt + veren kullanıcılarınkiler de dahil olmak üzere, listelenen sunuculardan gelen + tüm gönderileri/yükseltmeleri sessize alacaktır. +_ffVisibility: + followers: Takipçilere açık + private: Gizli + public: Herkese açık diff --git a/locales/uk-UA.yml b/locales/uk-UA.yml index 63caf22c1a..839cc26e0e 100644 --- a/locales/uk-UA.yml +++ b/locales/uk-UA.yml @@ -1,7 +1,9 @@ ---- _lang_: "Українська" headlineMisskey: "Мережа об'єднана записами" -introMisskey: "Ласкаво просимо! Misskey - децентралізована служба мікроблогів з відкритим кодом.\nСтворюйте \"нотатки\", щоб поділитися тим, що відбувається, і розповісти всім про себе 📡\nЗа допомогою \"реакцій\" ви також можете швидко висловити свої почуття щодо нотаток інших 👍\nДосліджуймо новий світ! 🚀" +introMisskey: "Ласкаво просимо! Firefish - децентралізована служба мікроблогів з відкритим + кодом.\nСтворюйте \"нотатки\", щоб поділитися тим, що відбувається, і розповісти + всім про себе 📡\nЗа допомогою \"реакцій\" ви також можете швидко висловити свої + почуття щодо нотаток інших 👍\nДосліджуймо новий світ! 🚀" monthAndDay: "{month}/{day}" search: "Пошук" notifications: "Сповіщення" @@ -14,16 +16,16 @@ gotIt: "Зрозуміло!" cancel: "Скасувати" enterUsername: "Введіть ім'я користувача" renotedBy: "Поширено {user}" -noNotes: "Немає нотаток" +noNotes: "Немає записів" noNotifications: "Немає сповіщень" -instance: "Інстанс" +instance: "Сервер" settings: "Налаштування" basicSettings: "Основні налаштування" otherSettings: "Інші налаштування" openInWindow: "Відкрити у вікні" profile: "Профіль" timeline: "Стрічка" -noAccountDescription: "Цей користувач ще нічого не написав про себе" +noAccountDescription: "Цей користувач ще нічого не написав про себе." login: "Увійти" loggingIn: "Здійснюємо вхід..." logout: "Вийти" @@ -44,7 +46,8 @@ copyContent: "Скопіювати контент" copyLink: "Скопіювати посилання" delete: "Видалити" deleteAndEdit: "Видалити й редагувати" -deleteAndEditConfirm: "Ви впевнені, що хочете видалити цю нотатку та відредагувати її? Ви втратите всі реакції, поширення та відповіді на неї." +deleteAndEditConfirm: "Ви впевнені, що хочете видалити цей запис та відредагувати + його? Ви втратите всі реакції, поширення та відповіді на нього." addToList: "Додати до списку" sendMessage: "Надіслати повідомлення" copyUsername: "Скопіювати ім’я користувача" @@ -64,9 +67,11 @@ import: "Імпорт" export: "Експорт" files: "Файли" download: "Завантажити" -driveFileDeleteConfirm: "Ви впевнені, що хочете видалити файл {name}? Нотатки із цим файлом також буде видалено." +driveFileDeleteConfirm: "Ви впевнені, що хочете видалити файл {name}? Його буде видалено + з усіх записів які містили його." unfollowConfirm: "Ви впевнені, що хочете відписатися від {name}?" -exportRequested: "Експортування розпочато. Це може зайняти деякий час. Після завершення експорту отриманий файл буде додано на диск." +exportRequested: "Експортування розпочато. Це може зайняти деякий час. Після завершення + експорту отриманий файл буде додано на диск." importRequested: "Імпортування розпочато. Це може зайняти деякий час." lists: "Списки" noLists: "Немає списків" @@ -80,10 +85,12 @@ manageLists: "Управління списками" error: "Помилка" somethingHappened: "Щось пішло не так" retry: "Спробувати знову" -pageLoadError: "Помилка при завантаженні сторінки" -pageLoadErrorDescription: "Зазвичай це пов’язано з помилками мережі або кешем браузера. Очистіть кеш або почекайте трохи й спробуйте ще раз." +pageLoadError: "Помилка при завантаженні сторінки." +pageLoadErrorDescription: "Зазвичай це пов’язано з помилками мережі або кешем браузера. + Очистіть кеш або почекайте трохи й спробуйте ще раз." serverIsDead: "Відповіді від сервера немає. Зачекайте деякий час і повторіть спробу." -youShouldUpgradeClient: "Перезавантажте та використовуйте нову версію клієнта, щоб переглянути цю сторінку." +youShouldUpgradeClient: "Перезавантажте та використовуйте нову версію клієнта, щоб + переглянути цю сторінку." enterListName: "Введіть назву списку" privacy: "Конфіденційність" makeFollowManuallyApprove: "Підтверджувати підписників уручну" @@ -95,10 +102,10 @@ unfollow: "Відписатись" followRequestPending: "Очікуючі запити на підписку" enterEmoji: "Введіть емодзі" renote: "Поширити" -unrenote: "Відміна поширення" -renoted: "Поширити запис." -cantRenote: "Неможливо поширити." -cantReRenote: "Поширення не можливо поширити." +unrenote: "скасувати поширення" +renoted: "Поширено." +cantRenote: "Цей запис неможливо поширити." +cantReRenote: "Поширення неможливо поширити." quote: "Цитата" pinnedNote: "Закріплений запис" pinned: "Закріпити" @@ -108,7 +115,8 @@ sensitive: "NSFW" add: "Додати" reaction: "Реакції" reactionSetting: "Налаштування реакцій" -reactionSettingDescription2: "Перемістити щоб змінити порядок, Клацнути мишою щоб видалити, Натиснути \"+\" щоб додати." +reactionSettingDescription2: "Перемістити щоб змінити порядок, Клацнути мишою щоб + видалити, Натиснути \"+\" щоб додати." rememberNoteVisibility: "Пам’ятати параметри видимісті" attachCancel: "Видалити вкладення" markAsSensitive: "Позначити як NSFW" @@ -137,14 +145,21 @@ emojiUrl: "URL емодзі" addEmoji: "Додати емодзі" settingGuide: "Рекомендована конфігурація" cacheRemoteFiles: "Кешувати дані з інших інстансів" -cacheRemoteFilesDescription: "Якщо кешування вимкнено, віддалені файли завантажуються безпосередньо з віддаленого інстансу. Це зменшує використання сховища, але збільшує трафік, оскільки не генеруются ескізи." +cacheRemoteFilesDescription: "Якщо кешування вимкнено, віддалені файли завантажуються + безпосередньо з віддаленого серверу. Це зменшує використання сховища, але збільшує + трафік, оскільки не генеруются ескізи." flagAsBot: "Акаунт бота" -flagAsBotDescription: "Ввімкніть якщо цей обліковий запис використовується ботом. Ця опція позначить обліковий запис як бота. Це потрібно щоб виключити безкінечну інтеракцію між ботами а також відповідного підлаштування Misskey." +flagAsBotDescription: "Ввімкніть якщо цей обліковий запис використовується ботом. + Ця опція позначить обліковий запис як бота. Це потрібно щоб виключити безкінечну + інтеракцію між ботами а також відповідного підлаштування Firefish." flagAsCat: "Акаунт кота" -flagAsCatDescription: "Ввімкніть, щоб позначити, що обліковий запис є котиком." -flagShowTimelineReplies: "Показувати відповіді на нотатки на часовій шкалі" -flagShowTimelineRepliesDescription: "Показує відповіді користувачів на нотатки інших користувачів на часовій шкалі." -autoAcceptFollowed: "Автоматично приймати запити на підписку від користувачів, на яких ви підписані" +flagAsCatDescription: "Ввімкніть, щоб позначити, що обліковий запис є котиком, та + отримати котячі вуха!" +flagShowTimelineReplies: "Показувати відповіді на записи в стрічці" +flagShowTimelineRepliesDescription: "Показує відповіді користувачів на записи інших + користувачів у стрічці." +autoAcceptFollowed: "Автоматично приймати запити на підписку від користувачів, на + яких ви підписані" addAccount: "Додати акаунт" loginFailed: "Не вдалося увійти" showOnRemote: "Переглянути в оригіналі" @@ -155,14 +170,18 @@ removeWallpaper: "Прибрати шпалери" searchWith: "Пошук: {q}" youHaveNoLists: "У вас немає списків" followConfirm: "Підписатися на {name}?" -proxyAccount: "Проксі-акаунт" -proxyAccountDescription: "Обліковий запис проксі – це обліковий запис, який діє як віддалений підписник для користувачів за певних умов. Наприклад, коли користувач додає віддаленого користувача до списку, активність віддаленого користувача не буде доставлена на сервер, якщо жоден локальний користувач не стежить за цим користувачем, то замість нього буде використовуватися обліковий запис проксі-сервера." +proxyAccount: "Обліковий запис проксі" +proxyAccountDescription: "Обліковий запис проксі – це обліковий запис, який діє як + віддалений підписник для користувачів за певних умов. Наприклад, коли користувач + додає віддаленого користувача до списку, активність віддаленого користувача не буде + доставлена на сервер, якщо жоден локальний користувач не стежить за цим користувачем, + то замість нього буде використовуватися обліковий запис проксі-сервера." host: "Хост" selectUser: "Виберіть користувача" recipient: "Отримувач" annotation: "Коментарі" federation: "Федіверс" -instances: "Інстанс" +instances: "Сервери" registeredAt: "Приєднався(лась)" latestRequestSentAt: "Останній запит надіслано" latestRequestReceivedAt: "Останній запит прийнято" @@ -172,34 +191,35 @@ charts: "Графіки" perHour: "Щогодинно" perDay: "Щоденно" stopActivityDelivery: "Припинити розсилання активності" -blockThisInstance: "Заблокувати цей інстанс" +blockThisInstance: "Заблокувати цей сервер" operations: "Операції" software: "Програмне забезпечення" version: "Версія" metadata: "Метадані" -withNFiles: "файли: {n}" monitor: "Монітор" jobQueue: "Черга завдань" cpuAndMemory: "ЦП та пам'ять" network: "Мережа" disk: "Диск" -instanceInfo: "Про цей інстанс" +instanceInfo: "Про цей сервер" statistics: "Статистика" clearQueue: "Очистити чергу" clearQueueConfirmTitle: "Ви впевнені, що хочете очистити чергу?" -clearQueueConfirmText: "Будь-які невідправлені нотатки, що залишилися в черзі, не будуть передані. Зазвичай ця операція НЕ потрібна." +clearQueueConfirmText: "Будь-які невідправлені записи, що залишилися в черзі, не будуть + передані. Зазвичай ця операція НЕ потрібна." clearCachedFiles: "Очистити кеш" clearCachedFilesConfirm: "Ви впевнені, що хочете видалити всі кешовані файли?" -blockedInstances: "Заблоковані інстанси" -blockedInstancesDescription: "Вкажіть інстанси, які потрібно заблокувати. Перелічені інстанси більше не зможуть спілкуватися з цим інстансом." +blockedInstances: "Заблоковані сервери" +blockedInstancesDescription: "Вкажіть сервери, які потрібно заблокувати. Перелічені + сервери більше не зможуть спілкуватися з цим сервером." muteAndBlock: "Заглушення і блокування" mutedUsers: "Заглушені користувачі" blockedUsers: "Заблоковані користувачі" noUsers: "Немає користувачів" editProfile: "Редагувати обліковий запис" noteDeleteConfirm: "Ви дійсно хочете видалити цей запис?" -pinLimitExceeded: "Більше записів не можна закріпити" -intro: "Встановлення Misskey завершено! Будь ласка, створіть обліковий запис адміністратора." +pinLimitExceeded: "Ви не можете закріпити більше записів" +intro: "Встановлення Firefish завершено! Будь ласка, створіть обліковий запис адміністратора." done: "Готово" processing: "Обробка" preview: "Попередній перегляд" @@ -213,9 +233,9 @@ all: "Всі" subscribing: "Підписка" publishing: "Публікація" notResponding: "Не відповідає" -instanceFollowing: "Підписка на інстанс" -instanceFollowers: "Підписники інстансу" -instanceUsers: "Користувачі цього інстансу" +instanceFollowing: "Підписка на сервер" +instanceFollowers: "Підписники серверу" +instanceUsers: "Користувачі цього серверу" changePassword: "Змінити пароль" security: "Безпека" retypedNotMatch: "Введені дані не збігаються." @@ -239,7 +259,8 @@ saved: "Збережено" messaging: "Чати" upload: "Завантажити" keepOriginalUploading: "Зберегти оригінальне зображення" -keepOriginalUploadingDescription: "Зберігає початково завантажене зображення як є. Якщо вимкнено, версія для відображення в Інтернеті буде створена під час завантаження." +keepOriginalUploadingDescription: "Зберігає початково завантажене зображення як є. + Якщо вимкнено, версія для відображення в Інтернеті буде створена під час завантаження." fromDrive: "З диска" fromUrl: "З посилання" uploadFromUrl: "Завантажити з посилання" @@ -289,7 +310,7 @@ inputNewFileName: "Введіть ім'я нового файлу" inputNewDescription: "Введіть новий заголовок" inputNewFolderName: "Введіть ім'я нової теки" circularReferenceFolder: "Ви намагаєтесь перемістити папку в її підпапку." -hasChildFilesOrFolders: "Ця тека не порожня і не може бути видалена" +hasChildFilesOrFolders: "Ця тека не порожня і не може бути видалена." copyUrl: "Копіювати URL" rename: "Перейменувати" avatar: "Аватар" @@ -305,8 +326,8 @@ unwatch: "Не стежити" accept: "Прийняти" reject: "Відхилити" normal: "Нормальний" -instanceName: "Назва інстансу" -instanceDescription: "Описання інстансу" +instanceName: "Назва серверу" +instanceDescription: "Опис серверу" maintainerName: "Ім'я адміністратора" maintainerEmail: "Email адміністратора" tosUrl: "URL умов використання" @@ -317,12 +338,13 @@ dayX: "{day}" monthX: "{month}" yearX: "{year}" pages: "Сторінки" -integration: "Інтеграція" +integration: "Інтеграції" connectService: "Під’єднати" disconnectService: "Відключитися" enableLocalTimeline: "Увімкнути локальну стрічку" enableGlobalTimeline: "Увімкнути глобальну стрічку" -disablingTimelinesInfo: "Адміністратори та модератори завжди мають доступ до всіх стрічок, навіть якщо вони вимкнуті." +disablingTimelinesInfo: "Адміністратори та модератори завжди мають доступ до всіх + стрічок, навіть якщо вони вимкнуті." registration: "Реєстрація" enableRegistration: "Дозволити реєстрацію" invite: "Запросити" @@ -334,11 +356,13 @@ bannerUrl: "URL банера" backgroundImageUrl: "URL-адреса фонового зображення" basicInfo: "Основна інформація" pinnedUsers: "Закріплені користувачі" -pinnedUsersDescription: "Впишіть в список користувачів, яких хочете закріпити на сторінці \"Знайти\", ім'я в стовпчик." +pinnedUsersDescription: "Впишіть в список користувачів, яких хочете закріпити на сторінці + \"Знайти\", ім'я в стовпчик." pinnedPages: "Закріплені сторінки" -pinnedPagesDescription: "Введіть шляхи сторінок, які ви бажаєте закріпити на головній сторінці цього інстанса, розділені новими рядками." -pinnedClipId: "Ідентифікатор закріпленої замітки." -pinnedNotes: "Закріплена нотатка" +pinnedPagesDescription: "Введіть шляхи сторінок, які ви бажаєте закріпити на головній + сторінці цього сервера, розділені новими рядками." +pinnedClipId: "Ідентифікатор закріпленої замітки" +pinnedNotes: "Закріплений запис" hcaptcha: "hCaptcha" enableHcaptcha: "Увімкнути hCaptcha" hcaptchaSiteKey: "Ключ сайту" @@ -347,22 +371,25 @@ recaptcha: "reCAPTCHA" enableRecaptcha: "Увімкнути reCAPTCHA" recaptchaSiteKey: "Ключ сайту" recaptchaSecretKey: "Секретний ключ" -avoidMultiCaptchaConfirm: "Використання кількох систем Captcha може спричинити перешкоди між ними. Бажаєте вимкнути інші активні системи Captcha? Якщо ви хочете, щоб вони залишалися ввімкненими, натисніть «Скасувати»." +avoidMultiCaptchaConfirm: "Використання кількох систем Captcha може спричинити перешкоди + між ними. Бажаєте вимкнути інші активні системи Captcha? Якщо ви хочете, щоб вони + залишалися ввімкненими, натисніть «Скасувати»." antennas: "Антени" manageAntennas: "Налаштування антен" name: "Ім'я" antennaSource: "Джерело антени" antennaKeywords: "Ключові слова антени" antennaExcludeKeywords: "Винятки" -antennaKeywordsDescription: "Розділення ключових слів пробілами для \"І\" або з нової лінійки для \"АБО\"" -notifyAntenna: "Сповіщати про нові нотатки" -withFileAntenna: "Тільки нотатки з вкладеними файлами" +antennaKeywordsDescription: "Відокремте пробілами для умови \"І\" або перенесенням + до нового рядка для умови \"АБО\"." +notifyAntenna: "Сповіщати про нові записи" +withFileAntenna: "Тільки записи з вкладеними файлами" enableServiceworker: "Ввімкнути ServiceWorker" antennaUsersDescription: "Список імя користувачів в стопчик" caseSensitive: "З урахуванням регістру" withReplies: "Включаючи відповіді" connectedTo: "Наступні акаунти під'єднані" -notesAndReplies: "Нотатки та відповіді" +notesAndReplies: "Записи та відповіді" withFiles: "Файли" silence: "Заглушити" silenceConfirm: "Ви впевнені, що хочете заглушити цього користувача?" @@ -377,7 +404,7 @@ exploreFediverse: "Огляд федіверсу" popularTags: "Популярні теги" userList: "Списки" about: "Інформація" -aboutMisskey: "Про Misskey" +aboutFirefish: "Про Firefish" administrator: "Адмін" token: "Токен" twoStepAuthentication: "Двохфакторна аутентифікація" @@ -398,7 +425,7 @@ notFoundDescription: "Сторінка за вказаною адресою не uploadFolder: "Місце для завантаження за замовчуванням" cacheClear: "Очистити кеш" markAsReadAllNotifications: "Позначити всі сповіщення як прочитані" -markAsReadAllUnreadNotes: "Позначити всі нотатки як прочитані" +markAsReadAllUnreadNotes: "Позначити всі записи як прочитані" markAsReadAllTalkMessages: "Позначити всі повідомлення як прочитані" help: "Допомога" inputMessageHere: "Введіть повідомлення тут" @@ -419,7 +446,7 @@ text: "Текст" enable: "Увімкнути" next: "Далі" retype: "Введіть ще раз" -noteOf: "Нотатка {user}" +noteOf: "Запис {user}" inviteToGroup: "Запрошення до групи" quoteAttached: "Цитата" quoteQuestion: "Ви хочете додати цитату?" @@ -432,7 +459,8 @@ invitationCode: "Код запрошення" checking: "Перевірка…" available: "Доступно" unavailable: "Недоступно" -usernameInvalidFormat: "літери, цифри та _ є прийнятними" +usernameInvalidFormat: "Ви можете використовувати великі та малі літери, цифри та + підкреслення." tooShort: "Занадто короткий" tooLong: "Занадто довгий" weakPassword: "Слабкий пароль" @@ -455,7 +483,7 @@ joinOrCreateGroup: "Отримуйте запрошення до груп або noHistory: "Історія порожня" signinHistory: "Історія входів" disableAnimatedMfm: "Відключити анімації MFM" -doing: "Виконується" +doing: "Виконується..." category: "Категорія" tags: "Теги" docSource: "Джерело цього документа" @@ -477,29 +505,36 @@ accountSettings: "Налаштування акаунта" promotion: "Виділене" promote: "Виділити" numberOfDays: "Кількість днів" -hideThisNote: "Сховати цю нотатку" -showFeaturedNotesInTimeline: "Показувати популярні нотатки у стрічці" -objectStorage: "Object Storage" +hideThisNote: "Сховати цей запис" +showFeaturedNotesInTimeline: "Показувати популярні записи у стрічці" +objectStorage: "Сховище" useObjectStorage: "Використовувати object storage" -objectStorageBaseUrl: "Base URL" -objectStorageBaseUrlDesc: "Це початкова частина адреси, що використовується CDN або проксі, наприклад для S3: https://.s3.amazonaws.com, або GCS: 'https://storage.googleapis.com/'" -objectStorageBucket: "Bucket" +objectStorageBaseUrl: "Базовий URL" +objectStorageBaseUrlDesc: "URL-адреса, що використовується як джерело. Вкажіть URL-адресу + вашого CDN або проксі-сервера, якщо ви їх використовуєте.\nДля S3 використовуйте + 'https://.s3.amazonaws.com', а для GCS або подібних сервісів - 'https://storage.googleapis.com/', + тощо." +objectStorageBucket: "Сховище (Bucket)" objectStorageBucketDesc: "Будь ласка вкажіть назву відра в налаштованому сервісі." objectStoragePrefix: "Prefix" objectStoragePrefixDesc: "Файли будуть зберігатись у розташуванні з цим префіксом." -objectStorageEndpoint: "Endpoint" -objectStorageEndpointDesc: "Залиште пустим при використанні AWS S3. Інакше введіть кінцевий пункт як '' або ':' слідуючи інструкціям сервісу, який використовується." +objectStorageEndpoint: "Кінцевий пункт" +objectStorageEndpointDesc: "Залиште пустим при використанні AWS S3. Інакше введіть + кінцевий пункт як '' або ':' слідуючи інструкціям сервісу, який + використовується." objectStorageRegion: "Region" -objectStorageRegionDesc: "Введіть регіон у формі 'xx-east-1'. Залиште пустим, якщо ваш сервіс не різниться відповідно до регіонів, або введіть 'us-east-1'." +objectStorageRegionDesc: "Введіть регіон у формі 'xx-east-1'. Залиште пустим, якщо + ваш сервіс не різниться відповідно до регіонів, або введіть 'us-east-1'." objectStorageUseSSL: "Використовувати SSL" objectStorageUseSSLDesc: "Вимкніть коли не використовується HTTPS для з'єднання API" objectStorageUseProxy: "Використовувати Proxy" -objectStorageUseProxyDesc: "Вимкніть коли проксі не використовується для з'єднання ObjectStorage" +objectStorageUseProxyDesc: "Вимкніть коли проксі не використовується для з'єднання + ObjectStorage" objectStorageSetPublicRead: "Встановіть 'публічне читання' при завантаженні" serverLogs: "Журнал сервера" deleteAll: "Видалити все" -showFixedPostForm: "Показати форму запису над стрічкою новин." -newNoteRecived: "Є нові нотатки" +showFixedPostForm: "Показати форму запису над стрічкою новин" +newNoteRecived: "Є нові записи" sounds: "Звуки" listen: "Слухати" none: "Відсутній" @@ -522,7 +557,8 @@ sort: "Сортування" ascendingOrder: "За зростанням" descendingOrder: "За спаданням" scratchpad: "Чернетка" -scratchpadDescription: "Scratchpad надає середовище для експериментів з AiScript. Ви можете писати, виконувати його і тестувати взаємодію з Misskey." +scratchpadDescription: "Scratchpad надає середовище для експериментів з AiScript. + Ви можете писати, виконувати його і тестувати взаємодію з Firefish." output: "Вихід" script: "Скрипт" disablePagesScript: "Вимкнути AiScript на Сторінках" @@ -530,11 +566,14 @@ updateRemoteUser: "Оновити інформацію про віддалено deleteAllFiles: "Видалити всі файли" deleteAllFilesConfirm: "Ви дійсно хочете видалити всі файли?" removeAllFollowing: "Скасувати всі підписки" -removeAllFollowingDescription: "Скасувати підписку на всі акаунти з {host}. Будь ласка, робіть це, якщо інстанс більше не існує." +removeAllFollowingDescription: "Скасувати підписку на всі акаунти з {host}. Будь ласка, + робіть це, якщо сервер більше не існує." userSuspended: "Обліковий запис заблокований." userSilenced: "Обліковий запис приглушений." yourAccountSuspendedTitle: "Цей обліковий запис заблоковано" -yourAccountSuspendedDescription: "Цей обліковий запис було заблоковано через порушення умов надання послуг сервера. Зв'яжіться з адміністратором, якщо ви хочете дізнатися докладнішу причину. Будь ласка, не створюйте новий обліковий запис." +yourAccountSuspendedDescription: "Цей обліковий запис було заблоковано через порушення + умов надання послуг сервера. Зв'яжіться з адміністратором, якщо ви хочете дізнатися + докладнішу причину. Будь ласка, не створюйте новий обліковий запис." menu: "Меню" divider: "Розділювач" addItem: "Додати елемент" @@ -543,8 +582,8 @@ addRelay: "Додати ретранслятор" inboxUrl: "Inbox URL" addedRelays: "Додані ретранслятори" serviceworkerInfo: "Повинен бути ввімкнений для push-сповіщень." -deletedNote: "Видалена нотатка" -invisibleNote: "Приховані записи" +deletedNote: "Видалений запис" +invisibleNote: "Прихований запис" enableInfiniteScroll: "Увімкнути нескінченну прокрутку" visibility: "Видимість" poll: "Опитування" @@ -574,12 +613,14 @@ permission: "Права" enableAll: "Увімкнути все" disableAll: "Вимкнути все" tokenRequested: "Надати доступ до акаунту" -pluginTokenRequestedDescription: "Цей плагін зможе використовувати дозволи які тут вказані." +pluginTokenRequestedDescription: "Цей плагін зможе використовувати дозволи які тут + вказані." notificationType: "Тип сповіщення" edit: "Редагувати" emailServer: "Сервер електронної пошти" enableEmail: "Увімкнути функцію доставки пошти" -emailConfigInfo: "Використовується для підтвердження електронної пошти підчас реєстрації, а також для відновлення паролю." +emailConfigInfo: "Використовується для підтвердження електронної пошти під час реєстрації, + а також для відновлення паролю" email: "E-mail" emailAddress: "E-mail адреса" smtpConfig: "Налаштування сервера SMTP" @@ -587,14 +628,16 @@ smtpHost: "Хост" smtpPort: "Порт" smtpUser: "Ім'я користувача" smtpPass: "Пароль" -emptyToDisableSmtpAuth: "Залиште назву користувача і пароль пустими для вимкнення підтвердження SMTP" +emptyToDisableSmtpAuth: "Залиште назву користувача і пароль пустими для вимкнення + підтвердження SMTP" smtpSecure: "Використовувати безумовне шифрування SSL/TLS для з'єднань SMTP" -smtpSecureInfo: "Вимкніть при використанні STARTTLS " +smtpSecureInfo: "Вимкніть при використанні STARTTLS" testEmail: "Тестовий email" wordMute: "Блокування слів" regexpError: "Помилка регулярного виразу" -regexpErrorDescription: "Сталася помилка в регулярному виразі в рядку {line} вашого слова {tab} слова що ігноруються:" -instanceMute: "Приглушення інстансів" +regexpErrorDescription: "Сталася помилка в регулярному виразі в рядку {line} вашого + слова {tab} слова що ігноруються:" +instanceMute: "Приглушення серверів" userSaysSomething: "{name} щось сказав(ла)" makeActive: "Активувати" display: "Відображення" @@ -607,12 +650,15 @@ database: "База даних" channel: "Канали" create: "Створити" notificationSetting: "Параметри сповіщень" -notificationSettingDesc: "Виберіть типи сповіщень для відображення" +notificationSettingDesc: "Оберіть типи сповіщень для відображення." useGlobalSetting: "Застосувати глобальнi параметри" -useGlobalSettingDesc: "Якщо увімкнено, то будуть використовуватись налаштування повідомлень облікового запису, інакше можливо налаштувати індивідуально." +useGlobalSettingDesc: "Якщо увімкнено, то будуть використовуватись налаштування повідомлень + облікового запису, інакше можливо налаштувати індивідуально." other: "Інше" regenerateLoginToken: "Оновити Login Token" -regenerateLoginTokenDescription: "Регенерувати внутрішній ключ використовуваний під час входу. Зазвичай цього не потрібно робити. При регенерації всі пристрої вийдуть з системи." +regenerateLoginTokenDescription: "Регенерувати внутрішній ключ використовуваний під + час входу. Зазвичай цього не потрібно робити. При регенерації всі пристрої вийдуть + з системи." setMultipleBySeparatingWithSpace: "Можна вказати кілька значень, відділивши їх пробілом." fileIdOrUrl: "Ідентифікатор файлу або посилання" behavior: "Поведінка" @@ -620,34 +666,38 @@ sample: "Приклад" abuseReports: "Скарги" reportAbuse: "Поскаржитись" reportAbuseOf: "Поскаржитись на {name}" -fillAbuseReportDescription: "Будь ласка вкажіть подробиці скарги. Якщо скарга стосується запису, вкажіть посилання на нього." -abuseReported: "Дякуємо, вашу скаргу було відправлено. " +fillAbuseReportDescription: "Будь ласка вкажіть подробиці скарги. Якщо скарга стосується + запису, вкажіть посилання на нього." +abuseReported: "Дякуємо. Ваш звіт було відправлено." reporter: "Репортер" reporteeOrigin: "Про кого повідомлено" reporterOrigin: "Хто повідомив" -forwardReport: "Переслати звіт на віддалений інстанс" -forwardReportIsAnonymous: "Замість вашого облікового запису анонімний системний обліковий запис буде відображатися як доповідач на віддаленому інстансі" +forwardReport: "Переслати звіт на віддалений сервер" +forwardReportIsAnonymous: "Замість вашого облікового запису, анонімний системний обліковий + запис буде відображатися як доповідач на віддаленому сервері." send: "Відправити" abuseMarkAsResolved: "Позначити скаргу як вирішену" openInNewTab: "Відкрити в новій вкладці" openInSideView: "Відкрити збоку" defaultNavigationBehaviour: "Поведінка навігації за замовчуванням" -editTheseSettingsMayBreakAccount: "Зміна цих параметрів може призвести до пошкодження вашого акаунта." -instanceTicker: "Мітка з назвою інстанса в нотатках" +editTheseSettingsMayBreakAccount: "Зміна цих параметрів може призвести до пошкодження + вашого акаунта." +instanceTicker: "Інформація про записи на сервері" waitingFor: "Чекаємо на {x}" random: "Випадковий" system: "Система" switchUi: "Інтерфейс" desktop: "Десктоп" -clip: "Добірка" +clip: "Підбірка" createNew: "Створити новий" optional: "Необов'язково" -createNewClip: "Створити нотатку" +createNewClip: "Створити підбірку" public: "Публічний" -i18nInfo: "Calckey перекладається на різні мови волонтерами. Ви можете допомогти: {link}" +i18nInfo: "Firefish перекладається на різні мови волонтерами. Ви можете допомогти за + посиланням: {link}." manageAccessTokens: "Керування токенами доступу" accountInfo: "Інформація про акаунт" -notesCount: "Кількість нотаток" +notesCount: "Кількість записів" repliesCount: "Кількість надісланих відповідей" renotesCount: "Кількість поширень" repliedCount: "Кількість отриманих відповідей" @@ -663,15 +713,19 @@ no: "Ні" driveFilesCount: "Кількість файлів на диску" driveUsage: "Використання місця на диску" noCrawle: "Заборонити індексацію" -noCrawleDescription: "Просити пошукові системи не індексувати ваш профіль, нотатки, сторінки тощо." -lockedAccountInfo: "Якщо видимість вашого запису не встановлена як \"Тільки підписники\", то кожен зможе побачити ваш запис, навіть якщо ви вимагаєте підтвердження підписок вручну." -alwaysMarkSensitive: "Позначати NSFW за замовчуванням" +noCrawleDescription: "Просити пошукові системи не індексувати ваш профіль, записи, + сторінки тощо." +lockedAccountInfo: "Якщо видимість вашого запису не встановлена як \"Тільки підписники\"\ + , то кожен зможе побачити ваш запис, навіть якщо ви вимагаєте підтвердження підписок + вручну." +alwaysMarkSensitive: "Позначати як NSFW за замовчуванням" loadRawImages: "Відображати вкладені зображення повністю замість ескізів" disableShowingAnimatedImages: "Не програвати анімовані зображення" -verificationEmailSent: "Електронний лист з підтвердженням відісланий. Будь ласка перейдіть по посиланню в листі для підтвердження." +verificationEmailSent: "Електронний лист з підтвердженням відісланий. Будь ласка перейдіть + по посиланню в листі для підтвердження." notSet: "Не налаштовано" -emailVerified: "Електронну пошту підтверджено." -noteFavoritesCount: "Кількість улюблених нотаток" +emailVerified: "Електронну пошту підтверджено" +noteFavoritesCount: "Кількість улюблених записів" pageLikesCount: "Кількість отриманих вподобань сторінки" pageLikedCount: "Кількість вподобаних сторінок" contact: "Контакт" @@ -680,7 +734,8 @@ clips: "Добірка" experimentalFeatures: "Експериментальні функції" developer: "Розробник" makeExplorable: "Зробіть обліковий запис видимим у розділі \"Огляд\"" -makeExplorableDescription: "Вимкніть, щоб обліковий запис не показувався у розділі \"Огляд\"." +makeExplorableDescription: "Вимкніть, щоб обліковий запис не показувався у розділі + \"Огляд\"." showGapBetweenNotesInTimeline: "Показувати розрив між записами у стрічці новин" duplicate: "Дублікат" left: "Лівий" @@ -695,7 +750,10 @@ onlineUsersCount: "{n} користувачів онлайн" nUsers: "{n} Користувачів" nNotes: "{n} Записів" sendErrorReports: "Надіслати звіт про помилки" -sendErrorReportsDescription: "При увімкненні детальна інформація про помилки буде надана Misskey у разі виникнення проблем, що дасть можливість покращити Misskey." +sendErrorReportsDescription: "Якщо увімкнено, детальна інформація про помилки буде + передаватися до Firefish, коли виникає проблема, це допоможе покращити якість роботи + Firefish.\nЦе буде включати інформацію таку як: версія вашої ОС, який браузер ви + використовуєте, ваша активність в Firefish тощо." myTheme: "Моя тема" backgroundColor: "Фон" accentColor: "Акцент" @@ -719,7 +777,7 @@ capacity: "Ємність" inUse: "Зайнято" editCode: "Редагувати вихідний текст" apply: "Застосувати" -receiveAnnouncementFromInstance: "Отримувати оповіщення з інстансу" +receiveAnnouncementFromInstance: "Отримувати сповіщення з серверу" emailNotification: "Сповіщення електронною поштою" publish: "Опублікувати" inChannelSearch: "Пошук за каналом" @@ -727,88 +785,110 @@ useReactionPickerForContextMenu: "Відкривати палітру реакц typingUsers: "Стук клавіш. Це {users}…" goBack: "Назад" info: "Інформація" -user: "Користувачі" +user: "Користувач" administration: "Управління" expiration: "Опитування закінчується" middle: "Середній" global: "Глобальна" -sent: "Відправити" +sent: "Відправлене" hashtags: "Хештеґ" hide: "Сховати" searchByGoogle: "Пошук" indefinitely: "Ніколи" file: "Файли" -reverse: "Перевернути" +reverse: "Переворот" colored: "Кольоровий" label: "Назва" localOnly: "Локально" _ffVisibility: public: "Опублікувати" + private: Приватні + followers: Доступно тільки для підписників _ad: back: "Назад" + reduceFrequencyOfThisAd: Менше показувати цю рекламу _gallery: unlike: "Не вподобати" + liked: Вподобані записи + like: Подобається + my: Моя галерея _email: _follow: title: "Новий підписник" + _receiveFollowRequest: + title: Ви отримали запит на підписку _registry: key: "Ключ" keys: "Ключі" domain: "Домен" createKey: "Створити ключ" + scope: Область _aboutMisskey: - about: "Misskey - це програмне забезпечення з відкритим кодом, яке розробляє syuilo з 2014 року." + about: "Misskey - це програмне забезпечення з відкритим кодом, яке розробляє syuilo + з 2014 року." contributors: "Головні помічники" allContributors: "Всі помічники" source: "Вихідний код" - translation: "Перекладати Misskey" - donate: "Пожертвувати Misskey" - morePatrons: "Ми дуже цінуємо підтримку багатьох інших помічників, не перелічених тут. Дякуємо! 🥰" + translation: "Перекладати Firefish" + donate: "Пожертвувати Firefish" + morePatrons: "Ми дуже цінуємо підтримку багатьох інших помічників, не перелічених + тут. Дякуємо! 🥰" patrons: "Підтримали" + patronsList: Перераховані в хронологічному порядку, а не за розміром пожертви. Зробіть + внесок за посиланням вище, щоб ваше ім'я було тут! + donateTitle: Сподобався Firefish? + pleaseDonateToFirefish: Будь ласка, підтримайте розробку Firefish. + pleaseDonateToHost: Також не забудьте підтримати ваш домашній сервер {host}, щоб + допомогти з його операційними витратами. + donateHost: Зробити внесок на рахунок {host} + sponsors: Спонсори Firefish _nsfw: respect: "Приховувати NSFW медіа" ignore: "Не приховувати NSFW медіа" force: "Приховувати всі медіа файли" _mfm: - cheatSheet: " Довідка MFM" - intro: "MFM це ексклюзивна мова розмітки тексту в Misskey, яку можна використовувати в багатьох місцях. Тут ви можете переглянути приклади її синтаксису." - dummy: "Misskey розширює світ Федіверсу" + cheatSheet: "Довідка MFM" + intro: "MFM це ексклюзивна мова розмітки тексту в Firefish, яку можна використовувати + в багатьох місцях. Тут ви можете переглянути приклади її синтаксису." + dummy: "Firefish розширює світ Федіверсу" mention: "Згадка" - mentionDescription: "За допомогою знака \"@\" перед ім'ям можна згадати конкретного користувача." + mentionDescription: "За допомогою знака \"@\" перед ім'ям можна згадати конкретного + користувача." hashtag: "Хештеґ" hashtagDescription: "За допомогою знака \"решітка\" перед словом задається хештег." url: "URL" urlDescription: "Відображаються URL-адреси." link: "Посилання" - linkDescription: "Окремі частини тексту можуть містити посилання" + linkDescription: "Окремі частини тексту можуть містити посилання." bold: "Жирний шрифт" - boldDescription: "Виділяє літери, роблячи їх товще" + boldDescription: "Виділяє літери, роблячи їх товщими." small: "Дрібний шрифт" - smallDescription: "Робить текст маленьким і тонким" + smallDescription: "Робить текст маленьким і тонким." center: "По центру" - centerDescription: "Показує вміст у центрі" + centerDescription: "Показує вміст у центрі." inlineCode: "Код (у рядку)" - inlineCodeDescription: "Показує фрагмент тексту у рядку як програмний код" + inlineCodeDescription: "Відображає підсвічування синтаксису для коду (програми)." blockCode: "Код (блок)" - blockCodeDescription: "Показує кілька рядків тексту як блок програмного кода" + blockCodeDescription: "Відображає підсвічування синтаксису для багаторядкового (програмного) + коду в блоці." inlineMath: "Формула (у рядку)" inlineMathDescription: "Відображення математичних формул (KaTeX) у рядку" blockMath: "Формули (блок)" - blockMathDescription: "Відображати багаторядкові формули (KaTeX) блоками" + blockMathDescription: "Відображати математичні формули (KaTeX) блоками" quote: "Цитата" quoteDescription: "Відображає зміст як цитату." emoji: "Кастомні емоджі" emojiDescription: "Щоб показати нетиповий емоджі, потрібно ввести його назву в двокрапках." search: "Пошук" - searchDescription: "Відображає вікно пошуку з попередньо введеним текстом" + searchDescription: "Відображає вікно пошуку з попередньо введеним текстом." flip: "Перевернути" - flipDescription: "Віддзеркалює вміст по горизонталі або вертикалі" + flipDescription: "Віддзеркалює вміст по горизонталі або вертикалі." jelly: "Анімація (желе)" - jellyDescription: "Створює желеподібну анімацію" + jellyDescription: "Створює желеподібну анімацію." tada: "Анімація (Тада!)" - tadaDescription: "Створює анімацію з відчуттям \"Тада!\"" + tadaDescription: "Створює анімацію з відчуттям \"Тада!\"." jump: "Анімація (стрибки)" - jumpDescription: "Показує стрибаючу анімацію" + jumpDescription: "Надає вмісту стрибучу анімацію." bounce: "Анімація (пружина)" shake: "Анімація (Shake)" twitch: "Анімація (Twitch)" @@ -820,10 +900,41 @@ _mfm: x4: "Надзвичайно великий" x4Description: "Показує контент надзвичайно великим." blur: "Розмиття" - blurDescription: "Цей ефект зробить контент розмитим. Контент можна зробити чітким, якщо навести на нього вказівник миші." + blurDescription: "Цей ефект зробить контент розмитим. Контент можна зробити чітким, + якщо навести на нього вказівник миші." font: "Шрифт" fontDescription: "Встановлює шрифт для контенту." rotate: "Обертати" + play: Відтворити MFM + alwaysPlay: Завжди автозапускати всі анімовані MFM + twitchDescription: Надає контенту анімацію, що сильно сіпається. + spinDescription: Надає контенту анімацію обертання. + sparkle: Блиск + sparkleDescription: Надає вмісту ефект мерехтливого блиску. + fade: Згасання + fadeDescription: Зменшує та збільшує видимість контенту. + crop: Обрізати + cropDescription: Обрізати вміст. + scale: Масштабувати + positionDescription: Перемістити вміст на вказане значення. + scaleDescription: Масштабувати вміст на вказану величину. + background: Фоновий колір + foreground: Колір переднього плану + foregroundDescription: Змінити колір тексту на передньому плані. + bounceDescription: Надає контенту пружної анімації. + shakeDescription: Надає контенту тремтливої анімації. + rainbowDescription: Робить вміст веселковим. + rotateDescription: Повертає вміст на вказаний кут. + advancedDescription: Якщо вимкнено, дозволяє лише базову розмітку, якщо не відтворюється + анімований MFM + plainDescription: Вимикає ефекти всіх MFM, що містяться в цьому MFM-ефекті. + stop: Зупинити MFM + plain: Звичайний текст + advanced: Розширені MFM + warn: MFM може містити швидко-рухому або яскраву анімацію + position: Розташування + rainbow: Веселка + backgroundDescription: Змінити колір фону тексту. _instanceTicker: none: "Не відображати" remote: "Відображати для віддалених користувачів" @@ -832,6 +943,7 @@ _serverDisconnectedBehavior: reload: "Автоматично перезавантажити" dialog: "Показати діалогове вікно" quiet: "Показати ненав’язливе попередження" + nothing: Нічого не робити _channel: create: "Створити канал" edit: "Редагувати канал" @@ -840,18 +952,27 @@ _channel: featured: "Тренди" following: "Підписки" usersCount: "{n} учасників" - notesCount: "{n} дописів" + notesCount: "{n} записів" + nameOnly: Тільки назва + nameAndDescription: Назва та опис + owned: Власні _menuDisplay: hide: "Сховати" + sideFull: Збоку + sideIcon: Збоку (тільки іконки) + top: Верх _wordMute: muteWords: "Заглушені слова" - muteWordsDescription: "Розділення ключових слів пробілами для \"І\" або з нової лінійки для \"АБО\"" - muteWordsDescription2: "Для використання RegEx, ключові слова потрібно вписати поміж слешів \"/\"." - softDescription: "Приховати записи які відповідають критеріям зі стрічки подій." - hardDescription: "Приховати записи які відповідають критеріям зі стрічки подій. Також приховані записи не будуть додані до стрічки подій навіть якщо критерії буде змінено." + muteWordsDescription: "Відокремліть ключові слова пробілами для умови \"І\" або + з нового рядку для умови \"АБО\"." + muteWordsDescription2: "Для використання RegEx, ключові слова потрібно вписати поміж + слешів \"/\"." + softDescription: "Приховати записи які відповідають критеріям зі стрічки." + hardDescription: "Приховати записи які відповідають критеріям зі стрічки подій. + Також приховані записи не будуть додані до стрічки навіть якщо критерії буде змінено." soft: "М'яко" hard: "Жорстко" - mutedNotes: "Заблоковані нотатки" + mutedNotes: "Ігноровані записи" _theme: explore: "Оглянути теми" install: "Встановити тему" @@ -915,9 +1036,20 @@ _theme: accentDarken: "Акцент (Затемлений)" accentLighten: "Акцент (Освітлений)" fgHighlighted: "Виділений текст" + color: Колір + refProp: Посилання на властивість + alpha: Прозорість + constant: Стала + refConst: Посилання на сталу + key: Ключ + funcKind: Тип функції + darken: Затемнення + argument: Аргумент + basedProp: Початкова властивість + addConstant: Додати сталу _sfx: - note: "Нотатки" - noteMy: "Мої нотатки" + note: "Новий запис" + noteMy: "Мої записи" notification: "Сповіщення" chat: "Чати" chatBg: "Чати (фон)" @@ -927,7 +1059,7 @@ _ago: future: "Майбутнє" justNow: "Щойно" secondsAgo: "{n}с тому" - minutesAgo: "{n}х тому" + minutesAgo: "{n}хв тому" hoursAgo: "{n}г тому" daysAgo: "{n}д тому" weeksAgo: "{n} тиж. тому" @@ -939,28 +1071,69 @@ _time: hour: "г" day: "д" _tutorial: - title: "Як використовувати Calckey" + title: "Як використовувати Firefish" step1_1: "Ласкаво просимо!" step1_2: "Давайте налаштуємо вас. Ви будете працювати в найкоротші терміни!" - step2_1: "Спочатку, будь ласка, заповніть свій профіль" - step2_2: "Надавши деяку інформацію про себе, іншим людям буде легше зрозуміти, чи хочуть вони бачити ваші записи або стежити за вами." - step3_1: "Тепер настав час стежити за деякими людьми!" - step3_2: "Ваша домашня і соціальна стрічки ґрунтуються на тому, за ким ви стежите, тому для початку спробуйте стежити за кількома акаунтами.\nНатисніть на гурток із плюсом у правому верхньому кутку профілю, щоб стежити за ним." - step4_1: "Давайте вийдемо на вас" - step4_2: "Для свого першого повідомлення деякі люди люблять робити {introduction} повідомлення або просте \"Hello world!\"" - step5_1: "Тимчасові рамки, скрізь тимчасові рамки!" - step5_2: "У вашому екземплярі включені {timelines} різних часових ліній." - step5_3: "Головна {icon} часова шкала - це шкала, де ви можете бачити повідомлення ваших підписників." - step5_4: "Місцева {icon} тимчасова шкала - це шкала, де ви можете бачити повідомлення всіх інших користувачів даного екземпляра" - step5_5: "Тимчасова шкала Рекомендовані {icon} - це шкала, де ви можете бачити повідомлення від інстанцій, рекомендованих адміністраторами." - step5_6: "На часовій шкалі Social {icon} відображаються повідомлення від друзів ваших підписників" - step5_7: "Глобальна {icon} часова шкала - це місце, де ви можете бачити повідомлення від усіх інших підключених екземплярів" + step2_1: "Спочатку, будь ласка, заповніть свій профіль." + step2_2: "Після надання інформації про себе, іншим людям буде легше зрозуміти, чи + хочуть вони бачити ваші записи або стежити за вами." + step3_1: "Тепер настав час на когось підписатися!" + step3_2: "Ваша домашня і соціальна стрічки ґрунтуються на тому, за ким ви стежите, + тому для початку спробуйте стежити за кількома акаунтами.\nНатисніть на гурток + із плюсом у правому верхньому кутку профілю, щоб стежити за ним." + step4_1: "Давайте вийдемо на вас." + step4_2: "Для свого першого повідомлення деякі люди люблять робити {introduction} + повідомлення або просте \"Hello world!\"" + step5_1: "Стрічки, скрізь одні стрічки!" + step5_2: "У вашому сервері включені {timelines} різні стрічки." + step5_3: "Головна {icon} стрічка - це стрічка, де ви можете бачити записи тих, на + кого ви підписалися." + step5_4: "Місцева {icon} стрічка - це стрічка, де ви можете бачити записи всіх інших + користувачів даного серверу." + step5_5: "Стрічка рекомендованих {icon} - це комбінація домашньої та місцевої стрічок." + step5_6: "На стрічці Рекомендованих {icon} ви можете бачити записи з серверів, які + рекомендують адміністратори." + step5_7: "Глобальна {icon} стрічка - це місце, де ви можете бачити записи від усіх + інших приєднаних серверів." step6_1: "Отже, що це за місце?" - step6_2: "Ну, ви не просто приєдналися до Кальки. Ви приєдналися до порталу в Fediverse, взаємопов'язаної мережі з тисяч серверів, званих \"інстансами\"." - step6_3: "Кожен сервер працює по-своєму, і не на всіх серверах працює Calckey. Але цей працює! Це трохи складно, але ви швидко розберетеся" + step6_2: "Ну, ви не просто приєдналися до Firefish. Ви увійшли в Fediverse, взаємопов'язану + мережу з тисяч серверів." + step6_3: "Кожен сервер працює по-своєму, і не на всіх серверах працює Firefish. Але + цей працює! Це трохи складно, але ви швидко розберетеся." step6_4: "Тепер ідіть, вивчайте і розважайтеся!" _2fa: - registerKey: "Зареєструвати новий ключ безпеки" + registerSecurityKey: "Зареєструвати новий ключ безпеки" + registerTOTP: Зареєструйте новий пристрій + tapSecurityKey: Будь ласка, дотримуйтесь інструкцій вашого браузера, щоб зареєструвати + апаратний ключ безпеки або ключ-пароль + securityKeyName: Введіть назву ключа + chromePasskeyNotSupported: Паролі Chrome наразі не підтримуються. + renewTOTPOk: Переналаштувати + removeKey: Видалити ключ безпеки + alreadyRegistered: 2FA вже налаштовано. + step2Click: Натиснувши на цей QR-код, ви зможете зареєструвати 2FA у вашому ключі + безпеки або додатку-автентифікаторі для телефону. + step3Title: Введіть код автентифікації + step1: По-перше, встановіть програму 2FA (наприклад, {a} або {b}) на свій пристрій. + securityKeyNotSupported: Ваш браузер не підтримує ключі безпеки. + step4: Відтепер при наступних спробах входу в систему буде запитуватися такий токен. + securityKeyInfo: Окрім автентифікації за відбитком пальця або PIN-кодом, ви також + можете налаштувати автентифікацію за допомогою апаратних ключів безпеки, які підтримують + FIDO2, щоб додатково захистити свій обліковий запис. + removeKeyConfirm: Дійсно видалити ключ {name}? + whyTOTPOnlyRenew: Додаток автентифікатора не можна видалити, доки зареєстровано + ключ безпеки. + renewTOTP: Переналаштувати додаток-автентифікатор + renewTOTPCancel: Скасувати + renewTOTPConfirm: Це призведе до того, що коди підтвердження з попереднього додатку + перестануть працювати + token: 2FA Токен + registerTOTPBeforeKey: Будь ласка, налаштуйте додаток-автентифікатор, щоб зареєструвати + ключ безпеки або пароль. + step2Url: 'Також, ви можете ввести цю URL-адресу, якщо використовуєте десктопну + програму:' + step3: Введіть токен, наданий вашим додатком, щоб завершити налаштування. + step2: Потім відскануйте QR-код, що відображається на цьому екрані. _permissions: "read:account": "Переглядати дані профілю" "write:account": "Змінити дані акаунту" @@ -976,7 +1149,7 @@ _permissions: "write:messaging": "Створювати та видаляти повідомлення" "read:mutes": "Переглядати список ігнорованих" "write:mutes": "Змінювати список ігнорованих" - "write:notes": "Писати і видаляти нотатки" + "write:notes": "Створення та видалення записів" "read:notifications": "Переглядати сповіщення" "read:reactions": "Переглядати реакції" "write:reactions": "Змінювати реакції" @@ -989,13 +1162,27 @@ _permissions: "write:user-groups": "Змінювати групи користувача" "read:channels": "Переглядати канали" "write:channels": "Змінювати канали" + "read:gallery": Переглянути галерею + "write:gallery": Редагування галереї + "read:gallery-likes": Переглянути список вподобаних записів галереї + "write:notifications": Керування сповіщеннями + "write:gallery-likes": Редагувати список вподобаних записів галереї _auth: shareAccess: "Ви хочете надати \"{name}\" доступ до цього акаунту?" shareAccessAsk: "Ви впевнені, що хочете надати цій програмі доступ до вашого акаунту?" denied: "У доступі відмовлено" + allPermissions: Повний доступ до облікового запису + permissionAsk: 'Цей додаток запитує наступні дозволи:' + copyAsk: 'Будь ласка, вставте наступний код авторизації в додаток:' + pleaseGoBack: Будь ласка, поверніться до додатку + callback: Повернення до додатку _antennaSources: - all: "Всі нотатки" - homeTimeline: "Нотатки тих, на кого ви підписані" + all: "Усі записи" + homeTimeline: "Записи тих, на кого ви підписані" + instances: Записи від усіх користувачів на сервері + userGroup: Записи від користувачів у вказаній групі + users: Записи обраних користувачів + userList: Дописи користувачів із вказаного списку _weekday: sunday: "Неділя" monday: "Понеділок" @@ -1016,20 +1203,30 @@ _widgets: photos: "Фото" digitalClock: "Цифровий годинник" federation: "Федіверс" - postForm: "Створення нотатки" + postForm: "Створення запису" slideshow: "Слайд-шоу" button: "Кнопка" onlineUsers: "Користувачі онлайн" jobQueue: "Черга завдань" - serverMetric: "Показники сервера " + serverMetric: "Показники сервера" aiscript: "Консоль AiScript" + _userList: + chooseList: Оберіть список + meiliStatus: Стан сервера + meiliSize: Розмір індексу + rssTicker: RSS-тікер + instanceCloud: Хмара серверів + unixClock: Годинник UNIX + userList: Список користувачів + serverInfo: Інформація про сервер + meiliIndexCount: Індексовані записи _cw: hide: "Сховати" show: "Показати більше" chars: "{count} символів" files: "{count} файлів" _poll: - noOnlyOneChoice: "Потрібні принаймні два варіанти." + noOnlyOneChoice: "Потрібні принаймні два варіанти" choiceN: "Варіант {n}" noMore: "Більше варіантів додати не можна" canMultipleVote: "Можна вибрати кілька варіантів" @@ -1052,19 +1249,19 @@ _poll: remainingSeconds: "Залишилось {s} секунд" _visibility: public: "Публічний" - publicDescription: "Для всіх користувачів" - home: "Домівка" + publicDescription: "Ваш запис буде видно в усіх публічних стрічках" + home: "Домашній" homeDescription: "Лише на домашній стрічці" followers: "Підписники" - followersDescription: "Тільки для підписників" + followersDescription: "Зробити видимим тільки для ваших підписників і згаданих користувачів" specified: "Особисто" specifiedDescription: "Лише для певних користувачів" localOnly: "Локально" localOnlyDescription: "Приховано для віддалених користувачів" _postForm: - replyPlaceholder: "Відповідь на цю нотатку..." - quotePlaceholder: "Прокоментуйте цю нотатку..." - channelPlaceholder: "Опублікувати в каналі" + replyPlaceholder: "Відповідь на цей запис..." + quotePlaceholder: "Прокоментуйте цей запис..." + channelPlaceholder: "Опублікувати в каналі..." _placeholders: a: "Чим займаєтесь?" b: "Що відбувається навколо вас?" @@ -1079,51 +1276,66 @@ _profile: youCanIncludeHashtags: "Ви також можете включити хештеги у свій опис." metadata: "Додаткова інформація" metadataEdit: "Редагувати додаткову інформацію" - metadataDescription: "Ви можете вказати до чотирьох пунктів додаткової інформації у своєму профілі." + metadataDescription: "Ви можете вказати до чотирьох пунктів додаткової інформації + у своєму профілі. Ви можете додати тег {a} або {l} за допомогою {rel}, щоб підтвердити + посилання у своєму профілі!" metadataLabel: "Назва" metadataContent: "Вміст" changeAvatar: "Змінити аватар" changeBanner: "Змінити банер" + locationDescription: Якщо ви спочатку введете своє місто, іншим користувачам буде + показано ваш місцевий час. _exportOrImport: - allNotes: "Всі нотатки" + allNotes: "Всі записи" followingList: "Підписки" muteList: "Ігнорувати" blockingList: "Заблокувати" userLists: "Списки" + excludeInactiveUsers: Вилучити неактивних користувачів + excludeMutingUsers: Вилучити заглушених користувачів _charts: federation: "Федіверс" apRequest: "Запити" usersTotal: "Загальна кількість користувачів" activeUsers: "Активні користувачі" - notesTotal: "Загальна кількість нотаток" + notesTotal: "Загальна кількість записів" filesIncDec: "Зміни кількості файлів" filesTotal: "Загальна кількість файлів" + storageUsageIncDec: Різниця в використанні ємності диску + remoteNotesIncDec: Різниця в кількості віддалених записів + notesIncDec: Різниця в кількості записів + localNotesIncDec: Різниця в кількості локальних записів + storageUsageTotal: Загальне використання пам'яті + usersIncDec: Різниця в кількості користувачів _instanceCharts: requests: "Запити" usersTotal: "Сумарна кількість користувачів" - notes: "Різниця кількості зроблених записів" - notesTotal: "Сумарна кількість нотаток" - ff: "Різниця кількості підписників" + notes: "Різниця в кількості зроблених записів" + notesTotal: "Сумарна кількість записів" + ff: "Різниця кількості підписників " ffTotal: "Кількість підписників" cacheSizeTotal: "Сумарний розмір кешу" files: "Різниця в кількості файлів" filesTotal: "Сумарна кількість файлів" + users: Різниця в кількості користувачів + cacheSize: Різниця в розмірі кешу _timelines: home: "Домівка" local: "Локальна" social: "Соціальна" global: "Глобальна" + recommended: Рекомендована _pages: newPage: "Створити сторінку" editPage: "Редагувати сторінку" readPage: "Перегляд вихідного коду" - created: "Сторінка успішно створена." - updated: "Сторінка успішно оновлена." + created: "Сторінка успішно створена" + updated: "Сторінка успішно оновлена" deleted: "Сторінку видалено" pageSetting: "Налаштування сторінки" - nameAlreadyExists: "Вказана адреса сторінки вже існує." - invalidNameTitle: "Вказана адреса сторінки неприпустима." - invalidNameText: "Переконайтеся, що не залишили порожнім." + nameAlreadyExists: "Вказана адреса сторінки вже існує" + invalidNameTitle: "Вказана адреса сторінки неприпустима" + invalidNameText: "Переконайтеся, що поле заголовка сторінки не порожнє" editThisPage: "Редагувати цю сторінку" viewSource: "Переглянути вихідний код" viewPage: "Переглянути свої сторінки" @@ -1166,6 +1378,7 @@ _pages: _post: text: "Вміст" canvasId: "Ідентифікатор полотна" + attachCanvasImage: Прикріпити зображення полотна textInput: "Введення тексту" _textInput: name: "Ім'я змінної" @@ -1186,10 +1399,10 @@ _pages: id: "Ідентифікатор полотна" width: "Ширина" height: "Висота" - note: "Вбудована нотатка" + note: "Вбудований запис" _note: - id: "Ідентифікатор нотатки" - idDescription: "Також можна вказати посилання на нотатку" + id: "Ідентифікатор запису" + idDescription: "Також можна вказати посилання на запис." detailed: "Детальний вигляд" switch: "Перемикач" _switch: @@ -1380,7 +1593,7 @@ _pages: arg1: "Текст" ref: "Змінні" aiScriptVar: "Змінна AiScript" - fn: "Функції" + fn: "Функція" _fn: slots: "Паз" slots-info: "Використовувати нову лінію як роздільник пазів" @@ -1389,7 +1602,8 @@ _pages: _for: arg1: "Кількість повторень" arg2: "Дія" - typeError: "Паз {slot} приймає \"{expect}\" тип, але надана змінна має тип \"{actual}\"!" + typeError: "Паз {slot} приймає \"{expect}\" тип, але надана змінна має тип \"\ + {actual}\"!" thereIsEmptySlot: "Паз {slot} пустий!" types: string: "Текст" @@ -1431,9 +1645,16 @@ _notification: followRequestAccepted: "Прийняті підписки" groupInvited: "Запрошення до груп" app: "Сповіщення від додатків" + pollEnded: Опитування закінчено _actions: reply: "Відповісти" - renote: "Поширити" + renote: "Поширення" + followBack: також підписався на вас + emptyPushNotificationMessage: Push-сповіщення були оновлені + voted: проголосував на вашому опитуванні + renoted: поширив ваш запис + reacted: відреагував на ваш запис + pollEnded: Стали доступні результати опитування _deck: alwaysShowMainColumn: "Завжди показувати головну колонку" columnAlign: "Вирівняти стовпці" @@ -1444,13 +1665,471 @@ _deck: swapDown: "Пересунути вниз" stackLeft: "У стовпчик вліво" popRight: "Витягнути вправо" - profile: "Обліковий запис" + profile: "Простір" _columns: main: "Головна" widgets: "Віджети" notifications: "Сповіщення" tl: "Стрічка" - antenna: "Антени" + antenna: "Антена" list: "Списки" mentions: "Згадки" - direct: "Особисте" + direct: "Особисті повідомлення" + channel: Канал + newProfile: Новий простір + introduction2: Натисніть на + у правій частині екрана, щоб додавати нові стовпці + по бажанню. + configureColumn: Налаштування стовпців + introduction: Створіть ідеальний інтерфейс для себе, вільно розташовуючи стовпці! + widgetsIntroduction: Будь ласка, виберіть "Редагувати віджети" в меню колонки і + додайте віджет. + renameProfile: Перейменувати простір + deleteProfile: Видалити простір + nameAlreadyExists: Простір із такою назвою вже існує. +removeReaction: Видалити вашу реакцію +renoteMute: Ігнорувати поширення +renoteUnmute: Показувати поширення +flagSpeakAsCat: Говорити як кішка +accessibility: Доступність +priority: Пріорітет +high: Високий +customCss: Користувацькі CSS +itsOn: Увімкнено +showingPastTimeline: Наразі відображається стара стрічка +enabled: Увімкнено +noMaintainerInformationWarning: Інформація про супровідника не налаштована. +recommended: Рекомендоване +resolved: Вирішено +itsOff: Вимкнено +emailRequiredForSignup: Вимагати адресу електронної пошти для реєстрації +moderation: Модерація +selectInstance: Оберіть сервер +instanceSecurity: Безпека сервера +searchPlaceholder: Шукати в Firefish +editNote: Відредагувати запис +enableEmojiReactions: Ввімкнути реакції емодзі +low: Низький +emailNotConfiguredWarning: Адрес електронної пошти не встановлено. +unresolved: Не вирішено +offline: Не в мережі +disabled: Вимкнено +configure: Налаштувати +popularPosts: Популярні сторінки +silenced: Ігнорується +manageGroups: Керування групами +active: Активний +whatIsNew: Показати зміни +deleted: Видалено +selectChannel: Виберіть канал +flagSpeakAsCatDescription: Ваші записи будуть няніфіковані у режимі кота +userSaysSomethingReason: '{name} сказав(ла) {reason}' +clear: Очистити +userInfo: Інформація про користувача +selectAccount: Оберіть обліковий запис +switchAccount: Змінити обліковий запис +accounts: Облікові записи +switch: Змінити +noBotProtectionWarning: Захист від ботів не налаштовано. +gallery: Галерея +recentPosts: Недавні сторінки +privateModeInfo: Якщо увімкнено, лише сервери з білого списку можуть федеруватися + з вашим сервером. Всі повідомлення будуть приховані від публіки. +troubleshooting: Вирішення проблем +customCssWarn: Цей параметр слід використовувати лише тоді, коли ви знаєте, що він + робить. Введення неправильних значень може призвести до того, що клієнт перестане + нормально функціонувати. +newer: новіші +older: старіші +addDescription: Додати опис +notSpecifiedMentionWarning: У цьому записі згадуються користувачі, яких не було включено + до списку одержувачів +markAllAsRead: Позначити все як прочитане +userPagePinTip: Ви можете відображати записи тут, вибравши "Прикріпити до профілю" + в меню окремих записів. +unknown: Невідомо +onlineStatus: Онлайн-статус +hideOnlineStatus: Приховати онлайн-статус +online: В мережі +breakFollow: Видалити підписника +translate: Перекласти +translatedFrom: Перекладено з {x} +userSaysSomethingReasonQuote: '{name} цитував запис з {reason}' +userSaysSomethingReasonRenote: '{name} поширив запис з {reason}' +notRecommended: Не рекомендується +botProtection: Захист від ботів +instanceBlocking: Керування Федерацією +privateMode: Приватний режим +allowedInstances: Сервери у білому списку +previewNoteText: Показати прев'ю +antennaInstancesDescription: Введіть по одному хосту сервера на рядок +breakFollowConfirm: Ви дійсно бажаєте видалити підписника? +ads: Реклама +cw: Попередження про вміст +hiddenTags: Приховані хештеги +noInstances: Немає серверів +misskeyUpdated: Firefish оновлено! +received: Отримане +xl: Надвеликий +searchResult: Результати пошуку +useBlurEffect: Використовувати ефекти розмиття в інтерфейсі +learnMore: Дізнатися більше +usernameInfo: Ім'я, яке ідентифікує ваш обліковий запис серед інших на цьому сервері. Ви + можете використовувати алфавіт (a~z, A~Z), цифри (0~9) або знаки підкреслення (_). + Ім'я користувача не може бути змінено пізніше. +noThankYou: Ні, дякую +keepCw: Зберігати попередження про вміст +showEmojisInReactionNotifications: Показувати емодзі у сповіщеннях про реакції +accountMoved: 'Користувач переїхав до нового облікового запису:' +expandOnNoteClickDesc: Якщо цю опцію вимкнено, ви все одно зможете відкривати дописи + в меню, клацнувши правою кнопкою миші або натиснувши на мітку часу. +deleteAccountConfirm: Це призведе до незворотного видалення вашого облікового запису. + Приступити? +unread: Непрочитане +filter: Фільтри +useDrawerReactionPickerForMobile: Відображати вибирач реакцій як шухляду на мобільному + телефоні +leaveGroupConfirm: Ви впевнені, що хочете залишити "{name}"? +clickToFinishEmailVerification: Будь ласка, натисніть [{ok}], щоб завершити перевірку + електронної пошти. +welcomeBackWithName: Ласкаво просимо назад, {name} +overridedDeviceKind: Тип пристрою +themeColor: Колір теми серверу +oneDay: Один день +instanceDefaultLightTheme: Світла тема за замовчуванням для сервера +oneWeek: Одна неділя +instanceDefaultDarkTheme: Темна тема за замовчуванням для сервера +video: Відео +audio: Аудіо +rateLimitExceeded: Перевищено ліміт +numberOfPageCacheDescription: Збільшення цієї величини покращить зручність для користувачів, + але призведе до збільшення навантаження на сервер та використання більшої кількості + пам'яті. +lastActiveDate: Останній раз використовувався у +statusbar: Панель статусу +speed: Швидкість +sensitiveMediaDetection: Виявлення NSFW медіа +cannotUploadBecauseNoFreeSpace: Завантаження не вдалося через брак місця на Диску. +cannotUploadBecauseExceedsFileSizeLimit: Цей файл не може бути завантажений, оскільки + він перевищує максимально дозволений розмір. +account: Обліковий запис +move: Перемістити +pushNotification: Push-сповіщення +subscribePushNotification: Увімкнути push-сповіщення +unsubscribePushNotification: Вимкнути push-сповіщення +pushNotificationAlreadySubscribed: Push-сповіщення вже увімкнено +enterSendsMessage: Натисніть Enter у повідомленнях, щоб надіслати повідомлення (якщо + вимкнено, то Ctrl + Enter) +showAds: Показувати рекламу +customMOTD: Користувацькі MOTD (повідомлення на заставці) +customSplashIcons: Користувацькі іконки заставки (URL) +splash: Заставка +adminCustomCssWarn: Цей параметр слід використовувати, тільки якщо ви знаєте, що він + робить. Введення неправильних значень може призвести до того, що ВСІ клієнти перестануть + нормально працювати. Будь ласка, переконайтеся, що ваш CSS працює належним чином, + протестувавши його в налаштуваннях користувача. +_filters: + followersOnly: Тільки підписники + fromUser: Від користувача + notesBefore: Записи до + withFile: З файлом + fromDomain: З домену + notesAfter: Записи після + followingOnly: Тільки підписки +sendModMail: Надіслати повідомлення про модерацію +enableServerMachineStats: Увімкнути статистику серверного обладнання +enableIdenticonGeneration: Увімкнути генерацію Identicon +_sensitiveMediaDetection: + analyzeVideosDescription: Аналізує відео так само як і зображення. Це трохи збільшить + навантаження на сервер. + description: Зменшує навантаження на серверну модерацію завдяки автоматичному розпізнаванню + NSFW медіа за допомогою машинного навчання. Це трохи збільшить навантаження на + сервер. + sensitivity: Чутливість виявлення + sensitivityDescription: Зменшення чутливості призведе до зменшення кількості хибних + спрацьовувань, тоді як збільшення чутливості призведе до зменшення кількості пропущених + спрацьовувань. + setSensitiveFlagAutomatically: Позначити як NSFW + setSensitiveFlagAutomaticallyDescription: Результати внутрішнього виявлення будуть + збережені, навіть якщо цю опцію вимкнено. + analyzeVideos: Ввімкнути аналіз відео +_emailUnavailable: + used: Ця електронна пошта вже використовується + format: Формат цієї адреси електронної пошти є неправильним + mx: Цей сервер електронної пошти є недійсним + disposable: Використовувати одноразові адреси електронної пошти заборонено + smtp: Цей поштовий сервер не відповідає +_messaging: + dms: Приватні + groups: Групи +_instanceMute: + instanceMuteDescription: Це приховає всі записи/поширення із вказаних серверів, + включно з відповідями користувачам заглушеного серверу. + title: Приховує записи з перелічених серверів. + instanceMuteDescription2: Розділити новими рядками + heading: Список серверів для заглушення +_experiments: + enablePostImports: Ввімкнути імпорт записів + title: Експерименти + postImportsCaption: Дозволяє користувачам імпортувати свої публікації з минулих + облікових записів Firefish, Misskey, Mastodon, Akkoma і Pleroma. Це може спричинити + зниження швидкості під час завантаження, якщо ваша черга перевантажена. +_dialog: + charactersExceeded: 'Перевищено максимальну кількість символів! Обмеження: {current}/{max}' + charactersBelow: 'Недостатньо символів! Обмеження: {current}/{min}' +jumpToSpecifiedDate: Перейти до конкретної дати +quitFullView: Закрити повний вигляд +ffVisibility: Видимість підписок/підписників +numberOfColumn: Кількість стовпців +failedToFetchAccountInformation: Не вдалося отримати інформацію про обліковий запис +reflectMayTakeTime: Може пройти деякий час, перш ніж зміни набудуть чинності. +recentNHours: Останні {n} годин +logoutConfirm: Ви впевнені, що хочете вийти? +enableRecommendedTimeline: Увімкнути рекомендовану стрічку +_accountDelete: + requestAccountDelete: Запросити видалення облікового запису + accountDelete: Видалити обліковий запис + mayTakeTime: Оскільки видалення облікового запису є ресурсоємним процесом, він може + зайняти деякий час, залежно від того, скільки контенту ви створили та скільки + файлів завантажили. + sendEmail: Коли ваш обліковий запис буде видалено, ми повідомимо на вказану вами + електронну пошту. + started: Процес видалення розпочався. + inProgress: Наразі триває видалення +_preferencesBackups: + deleteConfirm: Видалити резервну копію {name}? + applyConfirm: Ви дійсно хочете застосувати резервну копію "{name}" до цього пристрою? + Існуючі налаштування цього пристрою буде замінено. + saveConfirm: Зберегти резервну копію як {name}? + saveNew: Зберегти нову резервну копію + save: Зберегти зміни + inputName: Будь ласка, введіть назву для цієї резервної копії + loadFile: Завантажити з файлу + updatedAt: 'Оновлено: {date} {time}' + invalidFile: Неправильний формат файлу + apply: Застосувати до цього пристрою + list: Створені резервні копії + cannotSave: Збереження невдале + nameAlreadyExists: Резервна копія з назвою "{name}" вже існує. Будь ласка, введіть + іншу назву. + renameConfirm: Перейменувати цю резервну копію з "{old}" на "{new}"? + noBackups: Резервних копій немає. Ви можете створити резервну копію налаштувань + клієнта на цьому сервері за допомогою "Створити нову резервну копію". + createdAt: 'Створено: {date} {time}' + cannotLoad: Не вдалося завантажити +beta: Бета +customMOTDDescription: Користувацькі повідомлення для MOTD (заставки), розділені новими + рядками, які будуть показуватися випадковим чином щоразу, коли користувач завантажує/перезавантажує + сторінку. +replayTutorial: Перезапустити туторіал +_forgotPassword: + ifNoEmail: Якщо ви не використовували електронну пошту під час реєстрації, зверніться + до адміністратора серверу. + enterEmail: Введіть адресу електронної пошти, яку ви використовували для реєстрації. + На неї буде надіслано посилання, за яким ви зможете скинути пароль. + contactAdmin: Цей сервер не підтримує використання адрес електронної пошти, будь + ласка, зверніться до адміністратора сервера, щоб скинути пароль. +reactionPickerSkinTone: Бажаний колір шкіри емодзі +addInstance: Додати сервер +jumpToPrevious: Перейти до попереднього +listsDesc: Списки дозволяють створювати стрічки із вказаними користувачами. Доступ + до них можна отримати на сторінці стрічок. +channelFederationWarn: Канали наразі федеруються з іншими серверами +lastCommunication: Останнє повідомлення +edited: Відредаговано {date} о {time} +confirmToUnclipAlreadyClippedNote: Цей запис уже в підбірці "{name}". Чи бажаєте ви + натомість видалити пост із підбірки? +quickAction: Швидкі дії +remoteOnly: Тільки віддалені +failedToUpload: Помилка завантаження +moveFrom: Мігрувати на цей обліковий запис зі старого облікового запису +preventAiLearning: Захист від скрепінгу ШІ-ботів +moveAccountDescription: Цей процес є незворотнім. Переконайтеся, що ви створили псевдонім + для цього акаунта в новому акаунті перед переїздом. Будь ласка, введіть тег акаунта + у форматі @person@server.com +_signup: + almostThere: Майже готово + emailAddressInfo: Будь ласка, введіть свою адресу електронної пошти. Вона не буде + опублікована. + emailSent: На вашу електронну адресу ({email}) було надіслано лист із підтвердженням. + Будь ласка, перейдіть за посиланням, щоб завершити створення облікового запису. +defaultValueIs: 'За замовчуванням: {value}' +shareWithNote: Поділитися з записом +classic: Відцентрований +size: Розмір +slow: Повільно +alt: ALT +auto: Автоматично +oneHour: Одна година +instanceDefaultThemeDescription: Введіть код теми в об'єктному форматі. +cropImageAsk: Чи бажаєте ви обрізати це зображення? +noEmailServerWarning: Поштовий сервер не налаштовано. +thereIsUnresolvedAbuseReportWarning: Є не розглянуті звіти. +image: Зображення +check: Перевірити +isSystemAccount: Цей акаунт створений і автоматично управляється системою. Будь ласка, + не модеруйте, не редагуйте, не видаляйте та не втручайтеся в цей акаунт будь-яким + іншим чином, інакше це може призвести до поломки вашого серверу. +document: Документація +driveCapOverrideCaption: Ви можете скинути ємність до значення за замовчуванням, ввівши + значення 0 або менше. +numberOfPageCache: Кількість кешованих сторінок +pleaseSelect: Оберіть варіант +refreshInterval: 'Інтервал оновлення ' +enableAutoSensitive: Автоматичне маркування NSFW +cannotUploadBecauseInappropriate: Цей файл не може бути завантажений тому що його + частини були виявлені як потенційне NSFW. +sendPushNotificationReadMessageCaption: На короткий час буде показано сповіщення з + текстом "{emptyPushNotificationMessage}". Це може призвести до збільшення споживання + заряду акумулятора вашого пристрою, якщо це можливо. +pushNotificationNotSupported: Ваш браузер або сервер не підтримує push-сповіщення +showUpdates: Показувати спливаюче вікно при оновленні Firefish +updateAvailable: Можливо, є доступне оновлення! +recommendedInstancesDescription: Рекомендовані сервери відокремлюються переведенням + рядка, щоб з'явитися на стрічці рекомендацій. +caption: Автоматичний підпис +showAdminUpdates: Вказати, що доступна нова версія Firefish (тільки для адміністратора) +defaultReaction: Емодзі реакція за замовчуванням для вихідних і вхідних записів +license: Ліцензія +indexPosts: Індексувати пости +indexFrom: Індексувати записи з ID +indexFromDescription: Залиште порожнім, щоб індексувати кожен запис +indexNotice: Зараз відбувається індексація. Це, ймовірно, займе деякий час, будь ласка, + не перезавантажуйте сервер принаймні годину. +signupsDisabled: Реєстрація на цьому сервері наразі відключена, але ви завжди можете + зареєструватися на іншому сервері! Якщо у вас є код запрошення на цей сервер, будь + ласка, введіть його нижче. +findOtherInstance: Знайти інший сервер +customKaTeXMacro: Користувацькі макроси KaTeX +enableCustomKaTeXMacro: Увімкнути користувацькі макроси KaTeX +apps: Додатки +isModerator: Модератор +isAdmin: Адміністратор +isPatron: Патрон Firefish +swipeOnMobile: Дозволити гортання між сторінками +migration: Міграція +swipeOnDesktop: Дозволити свайп у мобільному стилі на десктопі +logoImageUrl: URL-адреса зображення логотипу +moveTo: Перенести поточний обліковий запис на новий +moveFromDescription: Це встановить псевдонім вашого старого облікового запису, щоб + ви могли перейти зі старого облікового запису до цього поточного. Зробіть це ДО + переходу зі старого акаунта. Будь ласка, введіть тег акаунта у форматі @person@server.com +moveToLabel: 'Обліковий запис, на який ви мігруєте:' +moveAccount: Перемістити обліковий запис! +moveFromLabel: 'Обліковий запис, з якого ви мігруєте:' +_plugin: + install: Встановлення плагінів + manage: Керування плагінами + installWarn: Будь ласка, не встановлюйте ненадійні плагіни. +_skinTones: + yellow: Жовтий + mediumLight: Помірно-світлий + medium: Помірний + mediumDark: Помірно-темний + dark: Темний + light: Світлий +tenMinutes: 10 хвилин +expandOnNoteClick: Відкрити запис кліком +preferencesBackups: Резервне копіювання +unlikeConfirm: Дійсно видалити вподобайку? +fullView: Повний вигляд +postToGallery: Опублікувати в галереї +memo: Нотатки +allowedInstancesDescription: Хости серверів, які будуть допущені до федерації, кожен + з яких відокремлюється новим рядком (стосується лише приватного режиму). +squareAvatars: Квадратні аватарки +aiChanMode: Режим ШІ +controlPanel: Панель керування +manageAccounts: Керування обліковими записами +incorrectPassword: Неправильний пароль. +voteConfirm: Підтвердити свій голос за "{choice}"? +leaveGroup: Залишити групу +smartphone: Смартфон +mutePeriod: Тривалість глушіння +requireAdminForView: Ви маєте увійти з облікового запису адміністратора, щоб переглянути + це. +fast: Швидко +isBot: Цей обліковий запис є ботом +isLocked: Цей обліковий запис має схвалення запитів на підписку +silenceThisInstance: Ігнорувати цей сервер +hideOnlineStatusDescription: Приховування вашого онлайн-статусу знижує зручність деяких + функцій, таких як пошук. +accountDeletionInProgress: Наразі триває видалення облікового запису +makeReactionsPublic: Зробити історію реакцій публічною +continueThread: Показати наступні відповіді +unmuteThread: Скасувати глушіння гілки +ffVisibilityDescription: Дозволяє налаштувати, хто може бачити, на кого ви підписані + і хто підписаний на вас. +tablet: Планшет +cropImage: Обрізати зображення +recentNDays: Останні {n} днів +navbar: Панель навігації +noGraze: Будь ласка, вимкніть розширення браузера "Graze для Mastodon", оскільки воно + заважає роботі Firefish. +preventAiLearningDescription: Попросити сторонні мовні моделі ШІ не вивчати вміст, + який ви завантажуєте, наприклад, записи та зображення. +userSaysSomethingReasonReply: '{name} відповів на пост з {reason}' +secureMode: Безпечний режим (Authorized Fetch) +seperateRenoteQuote: Розділити кнопки поширення та цитати +makeReactionsPublicDescription: Це зробить список усіх ваших минулих реакцій публічно + видимим. +muteThread: Заглушити гілку +sendPushNotificationReadMessage: Видаляти push-сповіщення після того, як відповідні + сповіщення або повідомлення будуть прочитані +unclip: Видалити з підбірки +silencedInstances: Ігноровані сервери +typeToConfirm: Введіть {x} щоб підтвердити +silencedWarning: Ця сторінка відображається тому, що ці користувачі з серверів, які + ваш адміністратор заглушив, тому вони потенційно можуть бути спамом. +shuffle: Перетасувати +ratio: Співвідношення +secureModeInfo: У разі запитів з інших серверів не надсилати непідтверджену відповідь. +pubSub: Облікові записи Pub/Sub +driveCapOverrideLabel: Змінити ємність диску для цього користувача +deleteAccount: Видалити обліковий запис +type: Тип +enableAutoSensitiveDescription: Дозволяє автоматично виявляти та позначати медіафайли + NSFW за допомогою машинного навчання, де це можливо. Навіть якщо цю опцію вимкнено, + вона може бути увімкнена на всьому сервері. +recommendedInstances: Рекомендовані сервери +noteId: Ідентифікатор запису +showPopup: Сповіщати користувачів спливаючим вікном +showWithSparkles: Показати з блиском +youHaveUnreadAnnouncements: У вас є непрочитані оголошення +donationLink: Посилання на сторінку для внесків +neverShow: Не показувати знову +remindMeLater: Можливо пізніше +removeQuote: Видалити цитату +removeRecipient: Видалити одержувача +removeMember: Видалити члена +silencedInstancesDescription: Вкажіть імена хостів серверів, які ви хочете ігнорувати. + Облікові записи на перелічених серверах вважаються "Ігнорованими", можуть робити + лише запити на підписку і не можуть згадувати локальні облікові записи, якщо на + них не підписалися. Це не вплине на заблоковані сервери. +hiddenTagsDescription: 'Перелічіть хештеги (без #), які ви хочете приховати з трендів + і дослідження. Приховані хештеги все одно можна знайти іншими способами.' +antennasDesc: "Антени показують нові дописи, що відповідають встановленим вами критеріям!\n + Доступ до них можна отримати зі сторінки стрічок." +clipsDesc: Підбірки схожі на категоризовані закладки, до яких можна надавати спільний + доступ. Ви можете створювати підбірки з меню окремих записів. +migrationConfirm: "Ви точно впевнені, що хочете перенести свій обліковий запис на + {account}? Якщо ви це зробите, ви не зможете скасувати цю операцію і не зможете + користуватися своїм обліковим записом як раніше.\nТакож, будь ласка, переконайтеся, + що ви вибрали цей поточний обліковий запис як обліковий запис, з якого ви переходите." +customKaTeXMacroDescription: 'Налаштуйте макроси, щоб легко писати математичні вирази! + Позначення відповідає визначенню команд LaTeX і записується у вигляді \newcommand{\ + name}{content} або \newcommand{\name}[number of arguments]{content}. Наприклад, + \newcommand{\add}[2]{#1 + #2} розширить \add{3}{foo} to 3 + foo. Фігурні дужки навколо + назви макросу можна змінити на круглі або квадратні. Це вплине на дужки, що використовуються + для аргументів. В одному рядку можна визначити один (і тільки один) макрос, і жоден + рядок не можна розривати посередині визначення. Неправильні рядки просто ігноруються. + Підтримуються лише прості функції заміни рядків; розширений синтаксис, такий як + умовне розгалуження, не може бути використаний тут.' +activeEmailValidationDescription: Вмикає більш сувору перевірку адрес електронної + пошти, яка включає перевірку на наявність одноразових адрес і перевірку того, чи + дійсно з нею можна зв'язатися. Якщо цей прапорець знято, перевіряється лише формат + електронної пошти. +customSplashIconsDescription: URL-адреси іконок для заставки, розділені новими рядками, + які будуть показуватися випадковим чином щоразу, коли користувач завантажує/перезавантажує + сторінку. Будь ласка, переконайтеся, що зображення знаходяться на статичній URL-адресі, + бажано, щоб вони були змінені до розміру 192x192. +verifiedLink: Перевірене посилання diff --git a/locales/vi-VN.yml b/locales/vi-VN.yml index 4f38b80c84..2604a9b2a9 100644 --- a/locales/vi-VN.yml +++ b/locales/vi-VN.yml @@ -1,7 +1,7 @@ --- _lang_: "Tiếng Việt" headlineMisskey: "Mạng xã hội liên hợp" -introMisskey: "Xin chào! Misskey là một nền tảng tiểu blog phi tập trung mã nguồn mở.\nViết \"tút\" để chia sẻ những suy nghĩ của bạn 📡\nBằng \"biểu cảm\", bạn có thể bày tỏ nhanh chóng cảm xúc của bạn với các tút 👍\nHãy khám phá một thế giới mới! 🚀" +introMisskey: "Xin chào! Firefish là một nền tảng tiểu blog phi tập trung mã nguồn mở.\nViết \"tút\" để chia sẻ những suy nghĩ của bạn 📡\nBằng \"biểu cảm\", bạn có thể bày tỏ nhanh chóng cảm xúc của bạn với các tút 👍\nHãy khám phá một thế giới mới! 🚀" monthAndDay: "{day} tháng {month}" search: "Tìm kiếm" notifications: "Thông báo" @@ -139,7 +139,7 @@ settingGuide: "Cài đặt đề xuất" cacheRemoteFiles: "Tập tin cache từ xa" cacheRemoteFilesDescription: "Khi tùy chọn này bị tắt, các tập tin từ xa sẽ được tải trực tiếp từ máy chủ khác. Điều này sẽ giúp giảm dung lượng lưu trữ nhưng lại tăng lưu lượng truy cập, vì hình thu nhỏ sẽ không được tạo." flagAsBot: "Đánh dấu đây là tài khoản bot" -flagAsBotDescription: "Bật tùy chọn này nếu tài khoản này được kiểm soát bởi một chương trình. Nếu được bật, nó sẽ được đánh dấu để các nhà phát triển khác ngăn chặn chuỗi tương tác vô tận với các bot khác và điều chỉnh hệ thống nội bộ của Misskey để coi tài khoản này như một bot." +flagAsBotDescription: "Bật tùy chọn này nếu tài khoản này được kiểm soát bởi một chương trình. Nếu được bật, nó sẽ được đánh dấu để các nhà phát triển khác ngăn chặn chuỗi tương tác vô tận với các bot khác và điều chỉnh hệ thống nội bộ của Firefish để coi tài khoản này như một bot." flagAsCat: "Tài khoản này là mèo" flagAsCatDescription: "Bật tùy chọn này để đánh dấu tài khoản là một con mèo." flagShowTimelineReplies: "Hiện lượt trả lời trong bảng tin" @@ -177,7 +177,6 @@ operations: "Vận hành" software: "Phần mềm" version: "Phiên bản" metadata: "Metadata" -withNFiles: "{n} tập tin" monitor: "Giám sát" jobQueue: "Công việc chờ xử lý" cpuAndMemory: "CPU và Dung lượng" @@ -199,7 +198,7 @@ noUsers: "Chưa có ai" editProfile: "Sửa hồ sơ" noteDeleteConfirm: "Bạn có chắc muốn xóa tút này?" pinLimitExceeded: "Bạn đã đạt giới hạn số lượng tút có thể ghim" -intro: "Đã cài đặt Misskey! Xin hãy tạo tài khoản admin." +intro: "Đã cài đặt Firefish! Xin hãy tạo tài khoản admin." done: "Xong" processing: "Đang xử lý" preview: "Xem trước" @@ -378,7 +377,7 @@ exploreFediverse: "Khám phá Fediverse" popularTags: "Hashtag thông dụng" userList: "Danh sách" about: "Giới thiệu" -aboutMisskey: "Về Misskey" +aboutFirefish: "Về Misskey" administrator: "Quản trị viên" token: "Token" twoStepAuthentication: "Xác minh 2 bước" @@ -524,7 +523,7 @@ sort: "Sắp xếp" ascendingOrder: "Tăng dần" descendingOrder: "Giảm dần" scratchpad: "Scratchpad" -scratchpadDescription: "Scratchpad cung cấp môi trường cho các thử nghiệm AiScript. Bạn có thể viết, thực thi và kiểm tra kết quả tương tác với Misskey trong đó." +scratchpadDescription: "Scratchpad cung cấp môi trường cho các thử nghiệm AiScript. Bạn có thể viết, thực thi và kiểm tra kết quả tương tác với Firefish trong đó." output: "Nguồn ra" script: "Kịch bản" disablePagesScript: "Tắt AiScript trên Trang" @@ -649,7 +648,7 @@ createNewClip: "Tạo một ghim mới" unclip: "Bỏ ghim" confirmToUnclipAlreadyClippedNote: "Bài đăng này là một phần của \"{name}\" ghim. Bạn có muốn bỏ khỏi ghim?" public: "Công khai" -i18nInfo: "Calckey đang được các tình nguyện viên dịch sang nhiều thứ tiếng khác nhau. Bạn có thể hỗ trợ tại {link}." +i18nInfo: "Firefish đang được các tình nguyện viên dịch sang nhiều thứ tiếng khác nhau. Bạn có thể hỗ trợ tại {link}." manageAccessTokens: "Tạo mã truy cập" accountInfo: "Thông tin tài khoản" notesCount: "Số lượng tút" @@ -700,7 +699,7 @@ onlineUsersCount: "{n} người đang online" nUsers: "{n} Người" nNotes: "{n} Tút" sendErrorReports: "Báo lỗi" -sendErrorReportsDescription: "Khi được bật, thông tin chi tiết về lỗi sẽ được chia sẻ với Misskey khi xảy ra sự cố, giúp nâng cao chất lượng của Misskey.\nBao gồm thông tin như phiên bản hệ điều hành của bạn, trình duyệt bạn đang sử dụng, hoạt động của bạn trong Misskey, v.v." +sendErrorReportsDescription: "Khi được bật, thông tin chi tiết về lỗi sẽ được chia sẻ với Firefish khi xảy ra sự cố, giúp nâng cao chất lượng của Firefish.\nBao gồm thông tin như phiên bản hệ điều hành của bạn, trình duyệt bạn đang sử dụng, hoạt động của bạn trong Firefish, v.v." myTheme: "Theme của tôi" backgroundColor: "Màu nền" accentColor: "Màu phụ" @@ -791,7 +790,7 @@ hashtags: "Hashtag" troubleshooting: "Khắc phục sự cố" useBlurEffect: "Dùng hiệu ứng làm mờ trong giao diện" learnMore: "Tìm hiểu thêm" -misskeyUpdated: "Misskey vừa được cập nhật!" +misskeyUpdated: "Firefish vừa được cập nhật!" whatIsNew: "Hiện những thay đổi" translate: "Dịch" translatedFrom: "Dịch từ {x}" @@ -966,13 +965,13 @@ _registry: keys: "Các mã" domain: "Tên miền" createKey: "Tạo mã" -_aboutMisskey: +_aboutFirefish: about: "Misskey là phần mềm mã nguồn mở được phát triển bởi syuilo từ năm 2014." contributors: "Những người đóng góp nổi bật" allContributors: "Toàn bộ người đóng góp" source: "Mã nguồn" - translation: "Dịch Misskey" - donate: "Ủng hộ Misskey" + translation: "Dịch Firefish" + donate: "Ủng hộ Firefish" morePatrons: "Chúng tôi cũng trân trọng sự hỗ trợ của nhiều người đóng góp khác không được liệt kê ở đây. Cảm ơn! 🥰" patrons: "Người ủng hộ" _nsfw: @@ -981,8 +980,8 @@ _nsfw: force: "Ẩn mọi media" _mfm: cheatSheet: "MFM Cheatsheet" - intro: "MFM là ngôn ngữ phát triển độc quyền của Misskey có thể được sử dụng ở nhiều nơi. Tại đây bạn có thể xem danh sách tất cả các cú pháp MFM có sẵn." - dummy: "Misskey mở rộng thế giới Fediverse" + intro: "MFM là ngôn ngữ phát triển độc quyền của Firefish có thể được sử dụng ở nhiều nơi. Tại đây bạn có thể xem danh sách tất cả các cú pháp MFM có sẵn." + dummy: "Firefish mở rộng thế giới Fediverse" mention: "Nhắc đến" mentionDescription: "Bạn có thể nhắc đến ai đó bằng cách sử dụng @tên người dùng." hashtag: "Hashtag" @@ -1180,7 +1179,7 @@ _time: hour: "giờ" day: "ngày" _tutorial: - title: "How to use Calckey" + title: "How to use Firefish" step1_1: "Welcome!" step1_2: "Let's get you set up. You'll be up and running in no time!" step2_1: "First, please fill out your profile." @@ -1197,13 +1196,13 @@ _tutorial: step5_6: "The Social {icon} timeline is where you can see posts from friends of your followers." step5_7: "The Global {icon} timeline is where you can see posts from every other connected instance." step6_1: "So, what is this place?" - step6_2: "Well, you didn't just join Calckey. You joined a portal to the Fediverse, an interconnected network of thousands of servers, called \"instances\"." - step6_3: "Each server works in different ways, and not all servers run Calckey. This one does though! It's a bit complicated, but you'll get the hang of it in no time." + step6_2: "Well, you didn't just join Firefish. You joined a portal to the Fediverse, an interconnected network of thousands of servers, called \"instances\"." + step6_3: "Each server works in different ways, and not all servers run Firefish. This one does though! It's a bit complicated, but you'll get the hang of it in no time." step6_4: "Now go, explore, and have fun!" _2fa: alreadyRegistered: "Bạn đã đăng ký thiết bị xác minh 2 bước." - registerDevice: "Đăng ký một thiết bị" - registerKey: "Đăng ký một mã bảo vệ" + registerTOTP: "Đăng ký một thiết bị" + registerSecurityKey: "Đăng ký một mã bảo vệ" step1: "Trước tiên, hãy cài đặt một ứng dụng xác minh (chẳng hạn như {a} hoặc {b}) trên thiết bị của bạn." step2: "Sau đó, quét mã QR hiển thị trên màn hình này." step2Url: "Bạn cũng có thể nhập URL này nếu sử dụng một chương trình máy tính:" @@ -1343,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 310121c5ae..214b249c82 100644 --- a/locales/zh-CN.yml +++ b/locales/zh-CN.yml @@ -1,71 +1,70 @@ ---- -_lang_: "中文(简体)" -headlineMisskey: "通过帖子连接在一起的网络" -introMisskey: "欢迎!Misskey是一个开源的、去中心化的“微博客”服务。\n通过编写「帖文」来和大家分享你的以及你周围的事情吧!📡\n通过「回应」功能,可以让你快速地对大家的帖文表达反馈👍\n来探索新的世界吧!🚀" -monthAndDay: "{month}月 {day}日" +_lang_: "简体中文" +headlineMisskey: "一个开源、去中心化的社交媒体平台,永远免费!🚀" +introMisskey: "欢迎! Firefish 是一个开源、去中心化的社交媒体平台,永久免费!🚀" +monthAndDay: "{month} 月 {day} 日" search: "搜索" notifications: "通知" username: "用户名" password: "密码" forgotPassword: "忘记密码" -fetchingAsApObject: "正在联邦宇宙查询中" -ok: "OK" -gotIt: "我明白了" +fetchingAsApObject: "正在从联邦宇宙查询" +ok: "好" +gotIt: "知道了!" cancel: "取消" enterUsername: "输入用户名" -renotedBy: "由 {user} 转贴" -noNotes: "没有帖文" -noNotifications: "无通知" -instance: "实例" +renotedBy: "转发自 {user}" +noNotes: "没有帖子" +noNotifications: "没有通知" +instance: "服务器" settings: "设置" basicSettings: "基本设置" -otherSettings: "其他设置" +otherSettings: "其它设置" openInWindow: "在新窗口中打开" profile: "个人资料" timeline: "时间线" -noAccountDescription: "这个人很懒,没有写自我介绍" +noAccountDescription: "这个人很懒,没有写自我介绍。" login: "登录" -loggingIn: "正在登录..." +loggingIn: "正在登录" logout: "登出" signup: "新用户注册" -uploading: "正在上传" +uploading: "正在上传..." save: "保存" users: "用户" addUser: "添加用户" -favorite: "收藏" -favorites: "收藏" +favorite: "添加到书签" +favorites: "书签" unfavorite: "取消收藏" -favorited: "已加入收藏夹。" -alreadyFavorited: "收藏夹中已存在。" -cantFavorite: "无法添加到收藏夹。" +favorited: "已添加到书签。" +alreadyFavorited: "书签中已存在。" +cantFavorite: "无法添加到书签。" pin: "置顶" unpin: "取消置顶" copyContent: "复制内容" copyLink: "复制链接" delete: "删除" deleteAndEdit: "删除并编辑" -deleteAndEditConfirm: "要删除此帖并再次编辑吗?对此帖的所有回应、转发和回复也将被删除。" +deleteAndEditConfirm: "要删除此帖子并再次编辑吗?对此帖子的所有回应、转发和回复也将被删除。" addToList: "添加至列表" sendMessage: "发送" copyUsername: "复制用户名" searchUser: "搜索用户" reply: "回复" -loadMore: "查看更多" +loadMore: "加载更多" showMore: "查看更多" showLess: "关闭" -youGotNewFollower: "你有新的关注者" -receiveFollowRequest: "您收到了关注请求" -followRequestAccepted: "您的关注请求被通过了" +youGotNewFollower: "关注了您" +receiveFollowRequest: "收到了关注请求" +followRequestAccepted: "关注请求已通过" mention: "提及" mentions: "提及" directNotes: "私信" -importAndExport: "导入和导出" +importAndExport: "导入/导出数据" import: "导入" export: "导出" files: "文件" download: "下载" -driveFileDeleteConfirm: "要删除「{name}」文件吗?附加此文件的帖子也会被删除。" -unfollowConfirm: "要取消对{name}的关注吗?" +driveFileDeleteConfirm: "要删除文件「{name}」吗?它将从所有作为附件包含它的帖子中删除。" +unfollowConfirm: "要取消关注 {name} 吗?" exportRequested: "导出请求已提交,这可能需要花一些时间,导出的文件将保存到网盘中。" importRequested: "导入请求已提交,这可能需要花一点时间。" lists: "列表" @@ -74,14 +73,14 @@ note: "帖子" notes: "帖子" following: "关注中" followers: "关注者" -followsYou: "正在关注你" +followsYou: "关注了您" createList: "创建列表" manageLists: "管理列表" error: "错误" -somethingHappened: "出现了一些问题!" +somethingHappened: "发生了一个错误" retry: "重试" -pageLoadError: "页面加载失败。" -pageLoadErrorDescription: "这通常是由于网络或浏览器缓存的原因。请清除缓存或等待片刻后重试。" +pageLoadError: "页面加载时发生错误。" +pageLoadErrorDescription: "这通常是由于网络错误或浏览器缓存的原因。请清除缓存或等待片刻后重试。" serverIsDead: "服务器没有响应。 请稍等片刻,然后重试。" youShouldUpgradeClient: "请重新加载并使用新版本的客户端查看此页面。" enterListName: "输入列表名称" @@ -89,15 +88,15 @@ privacy: "隐私" makeFollowManuallyApprove: "关注请求需要批准" defaultNoteVisibility: "默认可见性" follow: "关注" -followRequest: "关注申请" -followRequests: "关注申请" +followRequest: "关注请求" +followRequests: "关注请求" unfollow: "取消关注" -followRequestPending: "发送关注请求" +followRequestPending: "关注请求待批准" enterEmoji: "输入表情符号" renote: "转发" unrenote: "取消转发" renoted: "已转发。" -cantRenote: "该帖无法转发。" +cantRenote: "此帖子无法被转发。" cantReRenote: "转发无法被再次转发。" quote: "引用" pinnedNote: "已置顶的帖子" @@ -107,63 +106,68 @@ clickToShow: "点击以显示" sensitive: "敏感内容" add: "添加" reaction: "回应" -reactionSetting: "在选择器中显示的回应" +enableEmojiReaction: "启用表情符号回应" +showEmojisInReactionNotifications: "在回应通知中显示表情符号" +reactionSetting: "在回应选择器中显示的回应" reactionSettingDescription2: "拖动重新排序,单击删除,点击 + 添加。" -rememberNoteVisibility: "保存上次设置的可见性" +rememberNoteVisibility: "保存帖子可见性设置" attachCancel: "删除附件" markAsSensitive: "标记为敏感内容" unmarkAsSensitive: "取消标记为敏感内容" enterFileName: "请输入文件名" -mute: "屏蔽" -unmute: "解除屏蔽" -block: "拉黑" -unblock: "取消拉黑" +mute: "静音" +unmute: "取消静音" +renoteMute: "静音转发" +renoteUnmute: "取消静音转发" +block: "屏蔽" +unblock: "取消屏蔽" suspend: "冻结" unsuspend: "解除冻结" -blockConfirm: "确定要拉黑吗?" -unblockConfirm: "确定要解除拉黑吗?" -suspendConfirm: "要冻结吗?" -unsuspendConfirm: "要解除冻结吗?" +blockConfirm: "确定要屏蔽吗?" +unblockConfirm: "确定要取消屏蔽吗?" +suspendConfirm: "确定要冻结吗?" +unsuspendConfirm: "确定要解除冻结吗?" selectList: "选择列表" selectAntenna: "选择天线" -selectWidget: "选择小工具" -editWidgets: "编辑部件" +selectWidget: "选择小部件" +editWidgets: "编辑小部件" editWidgetsExit: "完成编辑" customEmojis: "自定义表情符号" emoji: "表情符号" emojis: "表情符号" emojiName: "表情符号名称" -emojiUrl: "表情符号地址" +emojiUrl: "表情符号 URL" addEmoji: "添加表情符号" settingGuide: "推荐配置" -cacheRemoteFiles: "远程文件缓存" -cacheRemoteFilesDescription: "当禁用此设定时远程文件将直接从远程实例载入。禁用后会减小储存空间需求,但是会增加流量,因为缩略图不会被生成。" +cacheRemoteFiles: "缓存远程文件" +cacheRemoteFilesDescription: "当禁用此设定时远程文件将直接从远程服务器载入。禁用后会减小储存空间需求,但是会增加流量,因为缩略图不会被生成。" flagAsBot: "这是一个机器人账号" -flagAsBotDescription: "如果此帐户由程序控制,请启用此项。启用后,此标志可以帮助其他开发人员防止机器人之间产生无限互动的行为,并让Misskey的内部系统将此帐户识别为机器人。" -flagAsCat: "将这个账户设定为一只猫" -flagAsCatDescription: "如果您想表明此帐户是一只猫,请打开此标志。\n开启后,会在您的头像上出现猫耳朵,并将你的帖子中的「na」替换为「nya」,日文同理。" +flagAsBotDescription: "如果此账号由程序控制,请启用此项。启用后,此标志可以帮助其它开发人员防止机器人之间产生无限互动的行为,并让 Firefish + 的内部系统将此账号识别为机器人。" +flagAsCat: "将这个账号设定为一只猫😺" +flagAsCatDescription: "您会长出猫耳朵并像猫一样说话!" flagShowTimelineReplies: "在时间线上显示帖子的回复" -flagShowTimelineRepliesDescription: "启用时,时间线除了显示用户的帖子外,还会显示其他用户对帖子的回复。" -autoAcceptFollowed: "自动允许关注者的关注" -addAccount: "添加账户" +flagShowTimelineRepliesDescription: "启用后,时间线除了显示用户的帖子外,还会显示其它用户对帖子的回复。" +autoAcceptFollowed: "自动批准来自关注者的关注请求" +addAccount: "添加账号" loginFailed: "登录失败" -showOnRemote: "转到所在实例显示" +showOnRemote: "打开原网页" general: "常规设置" wallpaper: "壁纸" setWallpaper: "设置壁纸" removeWallpaper: "移除壁纸" -searchWith: "搜索:{q}" +searchWith: "搜索:{q}" youHaveNoLists: "列表为空" -followConfirm: "你确定要关注{name}吗?" -proxyAccount: "代理账户" -proxyAccountDescription: "代理账户是在某些情况下充当用户的远程关注者的账户。 例如,当一个用户列出一个远程用户时,如果没有人跟随该列出的用户,则该活动将不会传递到该实例,因此将代之以代理账户。" +followConfirm: "您确定要关注 {name} 吗?" +proxyAccount: "代理账号" +proxyAccountDescription: "代理账号是在某些情况下充当用户的远程关注者的账号。 例如,当一个用户添加一个远程用户为代理账号时,如果没有本地用户关注该用户,远程用户的活动将不会被传递到服务器,因此代理账号将被关注。" host: "主机名" selectUser: "选择用户" -recipient: "收件人" +recipient: "接收者" annotation: "注解" federation: "联合" -instances: "实例" -registeredAt: "初次观测" +instances: "服务器" +registeredAt: "初次观测于" latestRequestSentAt: "上次发送的请求" latestRequestReceivedAt: "上次收到的请求" latestStatus: "最后状态" @@ -172,95 +176,94 @@ charts: "图表" perHour: "每小时" perDay: "每天" stopActivityDelivery: "停止发送活动" -blockThisInstance: "阻止此实例向本实例推流" +blockThisInstance: "屏蔽此服务器" operations: "操作" software: "软件" version: "版本" metadata: "元数据" -withNFiles: "{n}个文件" -monitor: "服务器状态" +monitor: "监测" jobQueue: "作业队列" -cpuAndMemory: "CPU和内存" +cpuAndMemory: "CPU 和内存" network: "网络" -disk: "存储" -instanceInfo: "实例信息" +disk: "磁盘" +instanceInfo: "服务器信息" statistics: "统计" clearQueue: "清除队列" clearQueueConfirmTitle: "确定清除队列?" -clearQueueConfirmText: "未送达的帖子将不会送达。 通常,您不需要这样做。" +clearQueueConfirmText: "队列中任何未送达的帖子将不会发送。 通常,您不需要这样做。" clearCachedFiles: "清除缓存" -clearCachedFilesConfirm: "确定要清除缓存文件?" -blockedInstances: "被阻拦的实例" -blockedInstancesDescription: "设定要阻拦的实例,以换行来进行分割。被阻拦的实例将无法与本实例进行交换通讯。" -muteAndBlock: "屏蔽/拉黑" -mutedUsers: "已屏蔽用户" -blockedUsers: "被拉黑的用户" +clearCachedFilesConfirm: "确定要删除所有缓存的远程文件?" +blockedInstances: "已屏蔽的服务器" +blockedInstancesDescription: "设定要屏蔽的服务器,一行一个。被屏蔽的服务器将无法与本服务器进行交换通讯。" +muteAndBlock: "静音与屏蔽" +mutedUsers: "已静音的用户" +blockedUsers: "已屏蔽的用户" noUsers: "无用户" -editProfile: "编辑资料" -noteDeleteConfirm: "要删除该帖子吗?" -pinLimitExceeded: "无法置顶更多了" -intro: "Misskey的部署结束啦!填写管理员账号吧!" +editProfile: "编辑个人资料" +noteDeleteConfirm: "确定要删除此帖子吗?" +pinLimitExceeded: "无法置顶更多帖子了" +intro: "Firefish 安装完成!请创建一个管理员用户。" done: "完成" processing: "正在处理" preview: "预览" default: "默认" -defaultValueIs: "默认值: {value}" +defaultValueIs: "默认值:{value}" noCustomEmojis: "没有自定义表情符号" noJobs: "没有任务" federating: "联合中" -blocked: "已拉黑" +blocked: "已屏蔽" suspended: "停止推流" all: "全部" -subscribing: "已订阅" +subscribing: "订阅中" publishing: "直播中" notResponding: "没有响应" -instanceFollowing: "关注实例" -instanceFollowers: "关注实例" -instanceUsers: "实例用户" +instanceFollowing: "关注服务器" +instanceFollowers: "服务器的关注者" +instanceUsers: "此服务器的用户" changePassword: "修改密码" security: "安全" -retypedNotMatch: "两次输入不一致!" +retypedNotMatch: "两次输入不匹配。" currentPassword: "现在的密码" newPassword: "新密码" -newPasswordRetype: "重新输入密码:" +newPasswordRetype: "重新输入新密码" attachFile: "插入附件" more: "更多!" featured: "热门" -usernameOrUserId: "用户名或用户ID" +usernameOrUserId: "用户名或用户 ID" noSuchUser: "用户不存在" lookup: "查询" announcements: "公告" -imageUrl: "图片URL" +imageUrl: "图片 URL" remove: "删除" removed: "已删除" -removeAreYouSure: "要删掉「{x}」吗?" -deleteAreYouSure: "要删掉「{x}」吗?" -resetAreYouSure: "恢复默认设置?" +removeAreYouSure: "确定要删除「{x}」吗?" +deleteAreYouSure: "确定要删除「{x}」吗?" +resetAreYouSure: "确定重置为默认设置?" saved: "已保存" messaging: "聊天" upload: "本地上传" keepOriginalUploading: "保留原图" -keepOriginalUploadingDescription: "上传图片时保留原始图片。关闭时,浏览器会在上传时生成一张用于web发布的图片。" +keepOriginalUploadingDescription: "上传图片时保留原始图片。如果关闭,会在上传时生成一张用于 web 发布的图片。" fromDrive: "从网盘中" fromUrl: "从 URL" -uploadFromUrl: "从网址上传" -uploadFromUrlDescription: "输入文件的URL" -uploadFromUrlRequested: "请求上传" +uploadFromUrl: "从 URL 上传" +uploadFromUrlDescription: "输入文件的 URL" +uploadFromUrlRequested: "已请求上传" uploadFromUrlMayTakeTime: "上传可能需要一些时间完成。" explore: "发现" messageRead: "已读" noMoreHistory: "没有更多的历史记录" -startMessaging: "添加聊天" -nUsersRead: "{n}人已读" -agreeTo: "勾选则表示已阅读并同意{0}" +startMessaging: "开始聊天" +nUsersRead: "{n} 人已读" +agreeTo: "我同意 {0}" tos: "服务条款" start: "开始" home: "首页" -remoteUserCaution: "由于此用户来自其它实例,显示的信息可能不完整。" +remoteUserCaution: "由于此用户来自其它服务器,显示的信息可能不完整。" activity: "活动" images: "图片" birthday: "生日" -yearsOld: "{age}岁" +yearsOld: "{age} 岁" registeredDate: "注册于" location: "位置" theme: "主题" @@ -270,9 +273,9 @@ light: "浅色" dark: "深色" lightThemes: "浅色主题" darkThemes: "深色主题" -syncDeviceDarkMode: "将深色模式与设备设置同步" +syncDeviceDarkMode: "将深色模式设置与设备同步" drive: "网盘" -fileName: "文件名称" +fileName: "文件名" selectFile: "选择文件" selectFiles: "选择文件" selectFolder: "选择文件夹" @@ -299,72 +302,72 @@ nsfw: "敏感内容" whenServerDisconnected: "与服务器连接中断时" disconnectedFromServer: "已和服务器断开连接" reload: "重新加载" -doNothing: "关闭弹窗" +doNothing: "忽略" reloadConfirm: "确定要重新加载吗?" watch: "关注" unwatch: "取消关注" -accept: "允许" +accept: "接受" reject: "拒绝" normal: "正常" -instanceName: "实例名称" -instanceDescription: "实例介绍" +instanceName: "服务器名称" +instanceDescription: "服务器简介" maintainerName: "管理员名称" maintainerEmail: "管理员电子邮箱" -tosUrl: "服务条款URL" +tosUrl: "服务条款 URL" thisYear: "今年" thisMonth: "本月" today: "今天" -dayX: "{day}日" -monthX: "{month}月" -yearX: "{year}年" +dayX: "{day} 日" +monthX: "{month} 月" +yearX: "{year} 年" pages: "页面" -integration: "关联" +integration: "整合" connectService: "连接" disconnectService: "断开连接" enableLocalTimeline: "启用本地时间线功能" enableGlobalTimeline: "启用全局时间线" -disablingTimelinesInfo: "即使时间线功能被禁用,出于方便,管理员和数据图表也可以继续使用。" +disablingTimelinesInfo: "管理员和监察员将始终拥有对所有时间线的访问权,即使它们没有被启用。" registration: "注册" enableRegistration: "允许新用户注册" invite: "邀请" -driveCapacityPerLocalAccount: "每个用户的网盘空间" +driveCapacityPerLocalAccount: "每个本地用户的网盘容量" driveCapacityPerRemoteAccount: "每个远程用户的网盘容量" -inMb: "以兆字节(MegaByte)为单位" -iconUrl: "图标URL" -bannerUrl: "横幅URL" -backgroundImageUrl: "背景图URL" +inMb: "以兆字节 (MegaByte) 为单位" +iconUrl: "图标 URL" +bannerUrl: "横幅图 URL" +backgroundImageUrl: "背景图 URL" basicInfo: "基本信息" pinnedUsers: "置顶用户" -pinnedUsersDescription: "在「发现」页面中使用换行标记想要置顶的用户。" +pinnedUsersDescription: "列出要在「发现」页面中置顶的用户,一行一个。" pinnedPages: "固定页面" -pinnedPagesDescription: "输入您要固定到实例首页的页面路径,以换行符分隔。" -pinnedClipId: "置顶的便签ID" +pinnedPagesDescription: "输入您要固定到服务器首页的页面路径,一行一个。" +pinnedClipId: "置顶的便签 ID" pinnedNotes: "已置顶的帖子" hcaptcha: "hCaptcha" enableHcaptcha: "启用 hCaptcha" -hcaptchaSiteKey: "网站密钥" -hcaptchaSecretKey: "密钥" +hcaptchaSiteKey: "网站密钥 (Site key)" +hcaptchaSecretKey: "密钥 (Secret key)" recaptcha: "reCAPTCHA" -enableRecaptcha: "启用 reCAPTCHA\n(请注意, 此功能在中国大陆不可用. 如果启用, 可能导致无法正常使用登录或注册等功能)" -recaptchaSiteKey: "网站密钥" -recaptchaSecretKey: "reCAPTCHA 密钥" -avoidMultiCaptchaConfirm: "使用多种验证方式可能会造成干扰,您要禁用其他验证方式吗?您可以按“取消”按钮,仍然保持启用多种验证方式。" +enableRecaptcha: "启用 reCAPTCHA\n(请注意,reCAPTCHA 在中国大陆无法访问,如果启用,可能导致无法正常使用登录或注册等功能)" +recaptchaSiteKey: "网站密钥 (Site key)" +recaptchaSecretKey: "密钥 (Secret key)" +avoidMultiCaptchaConfirm: "使用多种验证方式可能会造成干扰,您要禁用其它现已激活的验证方式吗?如果您希望它们继续被启用,请点击 「取消」。" antennas: "天线" -manageAntennas: "天线管理" +manageAntennas: "管理天线" name: "名称" antennaSource: "接收来源" antennaKeywords: "包含关键字" antennaExcludeKeywords: "排除关键字" -antennaKeywordsDescription: "使用空格分隔会产生AND规范,并且使用换行符分隔会产生OR规范" -notifyAntenna: "开启通知" -withFileAntenna: "仅带有附件的帖子" -enableServiceworker: "启用ServiceWorker" -antennaUsersDescription: "指定用户名,用换行符分隔" +antennaKeywordsDescription: "AND 条件用空格分隔,OR 条件用换行符分隔。" +notifyAntenna: "新帖子通知" +withFileAntenna: "仅显示带有附件的帖子" +enableServiceworker: "为浏览器启用推送通知 (ServiceWorker)" +antennaUsersDescription: "指定用户名,一行一个" caseSensitive: "区分大小写" withReplies: "包括回复" connectedTo: "您的账号已连到接以下第三方账号" notesAndReplies: "帖子与回复" -withFiles: "附件" +withFiles: "包含文件" silence: "禁言" silenceConfirm: "确认要禁言吗?" unsilence: "解除禁言" @@ -373,37 +376,37 @@ popularUsers: "热门用户" recentlyUpdatedUsers: "最近投稿的用户" recentlyRegisteredUsers: "最近登录的用户" recentlyDiscoveredUsers: "最近发现的用户" -exploreUsersCount: "有{count}个用户" +exploreUsersCount: "有 {count} 个用户" exploreFediverse: "探索联邦宇宙" popularTags: "热门标签" userList: "列表" about: "关于" -aboutMisskey: "关于 Misskey" +aboutFirefish: "关于 Firefish" administrator: "管理员" -token: "Token (令牌)" +token: "令牌" twoStepAuthentication: "两步验证" moderator: "监察员" moderation: "管理" -nUsersMentioned: "{n} 被提到" +nUsersMentioned: "被 {n} 人提到" securityKey: "安全密钥" securityKeyName: "密钥名称" -registerSecurityKey: "注册硬件安全密钥" -lastUsed: "最后使用:" -unregister: "删除账户" +registerSecurityKey: "注册安全密钥" +lastUsed: "最近使用" +unregister: "删除账号" passwordLessLogin: "无密码登录" resetPassword: "重置密码" -newPasswordIs: "新的密码是「{password}」" -reduceUiAnimation: "减少UI动画" +newPasswordIs: "新的密码是 {password}" +reduceUiAnimation: "减少 UI 动画" share: "分享" notFound: "未找到" -notFoundDescription: "没有与指定URL对应的页面。" +notFoundDescription: "没有与指定 URL 对应的页面。" uploadFolder: "默认上传文件夹" cacheClear: "清空缓存" markAsReadAllNotifications: "将所有通知标为已读" markAsReadAllUnreadNotes: "将所有帖子标记为已读" markAsReadAllTalkMessages: "将所有聊天标记为已读" help: "帮助" -inputMessageHere: "在此键入信息" +inputMessageHere: "在此输入信息" close: "关闭" group: "群组" groups: "群组" @@ -414,37 +417,37 @@ invites: "邀请" groupName: "群组名" members: "成员" transfer: "转让" -messagingWithUser: "与用户聊天" -messagingWithGroup: "与群组聊天" +messagingWithUser: "私聊" +messagingWithGroup: "群聊" title: "标题" text: "文本" enable: "启用" next: "下一个" retype: "重新输入" -noteOf: "{user}的帖子" +noteOf: "{user} 的帖子" inviteToGroup: "群组邀请" quoteAttached: "已引用" -quoteQuestion: "是否引用此链接内容?" -noMessagesYet: "现在没有新的聊天" +quoteQuestion: "是否引用?" +noMessagesYet: "暂无消息" newMessageExists: "新信息" onlyOneFileCanBeAttached: "只能添加一个附件" signinRequired: "请先登录" invitations: "邀请" invitationCode: "邀请码" -checking: "正在确认" +checking: "正在确认..." available: "可用" unavailable: "不可用" usernameInvalidFormat: "可使用大小写英文字母、数字和下划线。" -tooShort: "过短" -tooLong: "过长" +tooShort: "太短" +tooLong: "太长" weakPassword: "密码强度:弱" normalPassword: "密码强度:中等" strongPassword: "密码强度:强" passwordMatched: "密码一致" passwordNotMatched: "密码不一致" -signinWith: "以{x}登录" +signinWith: "以 {x} 登录" signinFailed: "无法登录,请检查您的用户名和密码是否正确。" -tapSecurityKey: "轻触硬件安全密钥" +tapSecurityKey: "轻触您的安全密钥" or: "或者" language: "语言" uiLanguage: "显示语言" @@ -456,26 +459,26 @@ youHaveNoGroups: "没有群组" joinOrCreateGroup: "请加入一个现有的群组,或者创建新群组。" noHistory: "没有历史记录" signinHistory: "登录历史" -disableAnimatedMfm: "禁用MFM动画" -doing: "正在进行" +disableAnimatedMfm: "禁用 MFM 动画" +doing: "正在处理…" category: "类别" tags: "标签" docSource: "文件来源" -createAccount: "注册账户" -existingAccount: "现有的账户" +createAccount: "注册账号" +existingAccount: "现有的账号" regenerate: "重新生成" fontSize: "字体大小" -noFollowRequests: "没有关注申请" +noFollowRequests: "没有待批准的关注申请" openImageInNewTab: "在新标签页中打开图片" dashboard: "管理面板" local: "本地" remote: "远程" total: "总计" weekOverWeekChanges: "与前一周相比" -dayOverDayChanges: "与前一日相比" +dayOverDayChanges: "与昨日相比" appearance: "外观" clientSettings: "客户端设置" -accountSettings: "账户设置" +accountSettings: "账号设置" promotion: "推广" promote: "推广" numberOfDays: "天数" @@ -484,24 +487,26 @@ showFeaturedNotesInTimeline: "在时间线上显示热门推荐" objectStorage: "对象存储" useObjectStorage: "使用对象存储" objectStorageBaseUrl: "Base URL" -objectStorageBaseUrlDesc: "用于引用的URL。如果您正在使用CDN或反向代理,请指定其URL,例如S3:“https://.s3.amazonaws.com”,GCS:“https://storage.googleapis.com/”" +objectStorageBaseUrlDesc: "用于引用的 URL。如果您正在使用 CDN 或反向代理,请指定其 URL。\n例如S3:“https://.s3.amazonaws.com”,GCS:“https://storage.googleapis.com/”,其它同理。" objectStorageBucket: "存储桶" objectStorageBucketDesc: "请指定使用的对象存储服务的存储桶名称。" objectStoragePrefix: "前缀" objectStoragePrefixDesc: "文件将存储在此前缀的目录下。" -objectStorageEndpoint: "端点" -objectStorageEndpointDesc: "如果你使用AWS S3请留空。否则请根据你使用的服务商的说明来进行设置,指定端点形式为“”或“:”。" +objectStorageEndpoint: "Endpoint" +objectStorageEndpointDesc: "如果您使用 AWS S3 请留空。否则请根据您使用的服务商的说明来进行设置,指定 Endpoint 形式为 + \"\" 或 \":\"。" objectStorageRegion: "可用区" -objectStorageRegionDesc: "指定一个可用区,例如“xx-east-1”。 如果您的对象存储服务没有可用区概念,请将其留空或填写“us-east-1”。" -objectStorageUseSSL: "使用SSL" -objectStorageUseSSLDesc: "如果不使用https进行API连接,请关闭。" +objectStorageRegionDesc: "指定一个可用区,例如 \"xx-east-1\"。 如果您的对象存储服务没有可用区概念,请将其留空或填写 \"\ + us-east-1\"。\n对于 Cloudflare R2,可以填为 \"auto\"。" +objectStorageUseSSL: "使用 SSL" +objectStorageUseSSLDesc: "如果不使用 HTTPS 进行 API 连接,请关闭" objectStorageUseProxy: "使用代理" -objectStorageUseProxyDesc: "如果您不使用代理进行API连接,请将其关闭。" -objectStorageSetPublicRead: "上传时设置为public-read" +objectStorageUseProxyDesc: "如果您不使用代理进行 API 连接,请将其关闭" +objectStorageSetPublicRead: "上传时设置为 public-read" serverLogs: "服务器日志" deleteAll: "全部删除" showFixedPostForm: "在时间线顶部显示发帖框" -newNoteRecived: "有新的帖子" +newNoteRecived: "新帖子" sounds: "提示音" listen: "试听" none: "无" @@ -518,25 +523,25 @@ uninstall: "卸载" installedApps: "已授权的应用" nothing: "没有" installedDate: "授权日期" -lastUsedDate: "最近使用" +lastUsedDate: "最近使用时间" state: "状态" sort: "排序" ascendingOrder: "升序" descendingOrder: "降序" -scratchpad: "AiScript控制台" -scratchpadDescription: "AiScript控制台为AiScript提供了实验环境。您可以编写代码以与Misskey交互,运行它并查看结果。" +scratchpad: "AiScript 控制台" +scratchpadDescription: "AiScript 控制台为 AiScript 提供了实验环境。您可以编写代码以与 Firefish 交互,运行它并查看结果。" output: "输出" script: "脚本" -disablePagesScript: "禁用页面脚本" +disablePagesScript: "在页面中禁用 AiScript" updateRemoteUser: "更新远程用户信息" deleteAllFiles: "删除所有文件" -deleteAllFilesConfirm: "要删除所有文件吗?" +deleteAllFilesConfirm: "确定要删除所有文件吗?" removeAllFollowing: "取消所有关注" -removeAllFollowingDescription: "取消{host}的所有关注者。当实例不存在时执行。" +removeAllFollowingDescription: "取消 {host} 的所有关注者。如果服务器已不存在,请执行它。" userSuspended: "该用户已被冻结。" userSilenced: "该用户已被禁言。" -yourAccountSuspendedTitle: "账户已被冻结" -yourAccountSuspendedDescription: "由于违反了服务器的服务条款或其他原因,该账户已被冻结。 您可以与管理员联系以了解更多信息。 请不要创建一个新的账户。" +yourAccountSuspendedTitle: "账号已被冻结" +yourAccountSuspendedDescription: "由于违反了服务器的服务条款或其它原因,该账号已被冻结。 您可以与管理员联系以了解更多信息。 请不要创建一个新的账号。" menu: "菜单" divider: "分割线" addItem: "添加项目" @@ -544,10 +549,10 @@ relays: "中继" addRelay: "添加中继" inboxUrl: "Inbox URL" addedRelays: "已添加的中继" -serviceworkerInfo: "您需要启用推送通知" +serviceworkerInfo: "需要启用推送通知。" deletedNote: "已删除的帖子" invisibleNote: "隐藏的帖子" -enableInfiniteScroll: "启用自动滚动页面模式" +enableInfiniteScroll: "滚动页面以载入更多内容" visibility: "可见性" poll: "调查问卷" useCw: "隐藏内容" @@ -564,9 +569,9 @@ manage: "管理" plugins: "插件" preferencesBackups: "备份设置" deck: "Deck" -undeck: "取消Deck" +undeck: "取消 Deck" useBlurEffectForModal: "对话框使用模糊效果" -useFullReactionPicker: "使用全功能的回应工具栏" +useFullReactionPicker: "使用全尺寸的回应选择栏" width: "宽度" height: "高度" large: "大" @@ -576,8 +581,8 @@ generateAccessToken: "生成访问令牌" permission: "权限" enableAll: "启用全部" disableAll: "禁用全部" -tokenRequested: "允许访问账户" -pluginTokenRequestedDescription: "此插件将能够拥有此处设置的权限" +tokenRequested: "允许访问账号" +pluginTokenRequestedDescription: "此插件将能够拥有这里设置的权限。" notificationType: "通知类型" edit: "编辑" emailServer: "邮件服务器" @@ -585,24 +590,24 @@ enableEmail: "启用发送邮件功能" emailConfigInfo: "用于确认电子邮件和密码重置" email: "邮箱" emailAddress: "电子邮件地址" -smtpConfig: "SMTP服务器设置" +smtpConfig: "SMTP 服务器设置" smtpHost: "主机名" smtpPort: "端口" smtpUser: "用户名" smtpPass: "密码" -emptyToDisableSmtpAuth: "用户名和密码留空可以禁用SMTP验证" +emptyToDisableSmtpAuth: "留空用户名和密码以禁用 SMTP 验证" smtpSecure: "在 SMTP 连接中使用隐式 SSL / TLS" -smtpSecureInfo: "使用STARTTLS时关闭。" +smtpSecureInfo: "使用 STARTTLS 时关闭" testEmail: "邮件发送测试" -wordMute: "文字屏蔽" +wordMute: "文字过滤" regexpError: "正则表达式错误" -regexpErrorDescription: "{tab} 屏蔽文字的第 {line} 行的正则表达式有错误:" -instanceMute: "实例的屏蔽" -userSaysSomething: "{name}说了什么" +regexpErrorDescription: "{tab} 文字过滤的第 {line} 行的正则表达式有错误:" +instanceMute: "服务器静音" +userSaysSomething: "{name} 说了什么" makeActive: "启用" display: "显示" copy: "复制" -metrics: "服务器监控" +metrics: "指标" overview: "服务器概况" logs: "日志" delayed: "滞后" @@ -612,52 +617,52 @@ create: "创建" notificationSetting: "通知设置" notificationSettingDesc: "选择要显示的通知类型。" useGlobalSetting: "使用全局设置" -useGlobalSettingDesc: "启用时,将使用账户通知设置。关闭时,则可以单独设置。" -other: "其他" +useGlobalSettingDesc: "启用时,将使用账号通知设置。关闭时,则可以单独设置。" +other: "其它" regenerateLoginToken: "重新生成登录令牌" regenerateLoginTokenDescription: "重新生成用于登录的内部令牌。通常您不需要这样做。重新生成后,您将在所有设备上登出。" setMultipleBySeparatingWithSpace: "您可以使用空格分隔多个项目。" -fileIdOrUrl: "文件ID或者URL" +fileIdOrUrl: "文件 ID 或者 URL" behavior: "行为" sample: "示例" abuseReports: "举报" reportAbuse: "举报" -reportAbuseOf: "举报{name}" -fillAbuseReportDescription: "请填写举报的详细原因。如果有对方发的帖子,请同时填写URL地址。" -abuseReported: "内容已发送。感谢您提交信息。" +reportAbuseOf: "举报 {name}" +fillAbuseReportDescription: "请填写举报的详细原因。如果有对方发的帖子,请同时填写 URL 地址。" +abuseReported: "您的举报已发送。非常感谢您。" reporter: "举报者" reporteeOrigin: "举报来源" reporterOrigin: "举报者来源" -forwardReport: "将该举报信息转发给远程实例" -forwardReportIsAnonymous: "勾选则在远程实例上显示的举报者是匿名的系统账号,而不是您的账号。" +forwardReport: "将该举报信息转发给远程服务器" +forwardReportIsAnonymous: "勾选则在远程服务器上显示的举报者是匿名的系统账号,而不是您的账号。" send: "发送" -abuseMarkAsResolved: "处理完毕" +abuseMarkAsResolved: "标记举报为已解决" openInNewTab: "在新标签页中打开" openInSideView: "在侧边栏中打开" defaultNavigationBehaviour: "默认导航" -editTheseSettingsMayBreakAccount: "编辑这些设置可以会损坏您的账号" -instanceTicker: "帖子的实例信息" -waitingFor: "等待{x}" +editTheseSettingsMayBreakAccount: "编辑这些设置可能会损坏您的账号。" +instanceTicker: "帖子所在的服务器信息" +waitingFor: "等待 {x}" random: "随机" system: "系统" -switchUi: "切换界面" +switchUi: "界面" desktop: "桌面" clip: "便签" createNew: "新建" optional: "可选" createNewClip: "新建便签" unclip: "移除便签" -confirmToUnclipAlreadyClippedNote: "本帖已包含在便签\"{name}\"里。您想要将本帖从该便签中移除吗?" +confirmToUnclipAlreadyClippedNote: "本帖已包含在便签 \"{name}\" 里。您想要将本帖从该便签中移除吗?" public: "公开" -i18nInfo: "Calckey已经被志愿者们翻译成了各种语言。如果你也有兴趣,可以通过{link}帮助翻译。" -manageAccessTokens: "管理 Access Tokens" -accountInfo: "账户信息" +i18nInfo: "Firefish 已经被志愿者们翻译成了各种语言。如果您也有兴趣,可以通过 {link} 帮助翻译。" +manageAccessTokens: "管理访问令牌" +accountInfo: "账号信息" notesCount: "帖子数量" repliesCount: "回复数量" -renotesCount: "转帖数量" +renotesCount: "转发数量" repliedCount: "回复数" -renotedCount: "转发数" -followingCount: "正在关注数量" +renotedCount: "转发数量" +followingCount: "关注中数量" followersCount: "关注者数量" sentReactionsCount: "发送回应数" receivedReactionsCount: "收到回应数" @@ -671,12 +676,12 @@ noCrawle: "要求搜索引擎不索引该用户" noCrawleDescription: "要求搜索引擎不要收录(索引)您的用户页面,帖子,页面等。" lockedAccountInfo: "即使通过了关注请求,只要您不将帖子可见范围设置成“关注者”,任何人都可以看到您的帖子。" alwaysMarkSensitive: "默认将媒体文件标记为敏感内容" -loadRawImages: "添加附件图像的缩略图时使用原始图像质量" +loadRawImages: "加载原始图像而不是显示缩略图" disableShowingAnimatedImages: "不播放动画" -verificationEmailSent: "已发送确认电子邮件。请访问电子邮件中的链接以完成设置。" +verificationEmailSent: "已发送确认电子邮件。请访问电子邮件中的链接以完成验证。" notSet: "未设置" emailVerified: "电子邮件地址已验证" -noteFavoritesCount: "收藏的帖子数" +noteFavoritesCount: "加入书签的帖子数" pageLikesCount: "页面点赞次数" pageLikedCount: "页面被点赞次数" contact: "联系人" @@ -684,28 +689,29 @@ useSystemFont: "使用系统默认字体" clips: "便签" experimentalFeatures: "实验性功能" developer: "开发者" -makeExplorable: "使账号可见。" +makeExplorable: "使账号在“发现”中可见" makeExplorableDescription: "关闭时,账号不会显示在\"发现\"中。" -showGapBetweenNotesInTimeline: "时间线上的帖子分开显示。" +showGapBetweenNotesInTimeline: "时间线上的帖子分开显示" duplicate: "复制" left: "左" center: "中央" wide: "宽" narrow: "窄" reloadToApplySetting: "页面刷新后设置才会生效。是否现在刷新页面?" -needReloadToApply: "重启后应用才会生效。" +needReloadToApply: "需要重新加载才能生效。" showTitlebar: "显示标题栏" clearCache: "清除缓存" -onlineUsersCount: "{n}人在线" -nUsers: "{n}用户" -nNotes: "{n}帖子" +onlineUsersCount: "{n} 人在线" +nUsers: "{n} 用户" +nNotes: "{n} 帖子" sendErrorReports: "发送错误报告" -sendErrorReportsDescription: "启用后,如果出现问题,可以与Misskey共享详细的错误信息,从而帮助提高软件的质量。" +sendErrorReportsDescription: "启用后,如果出现问题,可以与 Firefish 共享详细的错误信息,从而帮助提高软件的质量。\n这将包括您的操作系统版本、您使用的浏览器、您在 + Firefish 中的活动等信息。" myTheme: "我的主题" -backgroundColor: "背景" +backgroundColor: "背景色" accentColor: "强调色" -textColor: "文本" -saveAs: "另存为" +textColor: "文本颜色" +saveAs: "另存为..." advanced: "高级" value: "值" createdAt: "创建日期" @@ -714,7 +720,7 @@ saveConfirm: "确定保存?" deleteConfirm: "确定删除?" invalidValue: "无效值。" registry: "注册表" -closeAccount: "永久注销账户" +closeAccount: "永久注销账号" currentVersion: "当前版本" latestVersion: "最新版本" youAreRunningUpToDateClient: "您所使用的客户端已经是最新的。" @@ -724,16 +730,16 @@ capacity: "容量" inUse: "已使用" editCode: "编辑代码" apply: "应用" -receiveAnnouncementFromInstance: "从实例接收通知" +receiveAnnouncementFromInstance: "从服务器接收通知" emailNotification: "邮件通知" publish: "发布" inChannelSearch: "频道内搜索" useReactionPickerForContextMenu: "单击右键打开回应工具栏" -typingUsers: "{users}正在输入" +typingUsers: "{users} 正在输入" jumpToSpecifiedDate: "跳转到特定日期" showingPastTimeline: "显示过去的时间线" clear: "清除" -markAllAsRead: "全部标记为已读" +markAllAsRead: "将全部标记为已读" goBack: "返回" unlikeConfirm: "取消赞?" fullView: "全屏" @@ -748,22 +754,22 @@ onlineStatus: "在线状态" hideOnlineStatus: "隐藏在线状态" hideOnlineStatusDescription: "隐藏在线状态后,可能会降低搜索等功能的便利性。" online: "在线" -active: "活动" +active: "活跃" offline: "离线" notRecommended: "不推荐" -botProtection: "Bot防御" -instanceBlocking: "被阻拦的实例" -selectAccount: "选择账户" -switchAccount: "切换账户" +botProtection: "Bot 防护" +instanceBlocking: "联合管理" +selectAccount: "选择账号" +switchAccount: "切换账号" enabled: "已启用" -disabled: "已禁用 " +disabled: "已禁用" quickAction: "快捷操作" user: "用户" administration: "管理" -accounts: "账户" +accounts: "账号" switch: "切换" -noMaintainerInformationWarning: "管理人员信息未设置。" -noBotProtectionWarning: "Bot保护未设置。" +noMaintainerInformationWarning: "管理员信息未设置。" +noBotProtectionWarning: "Bot 防御未设置。" configure: "设置" postToGallery: "发送到图库" gallery: "图库" @@ -781,7 +787,7 @@ emailNotConfiguredWarning: "电子邮件地址未设置。" ratio: "比率" previewNoteText: "预览文本" customCss: "自定义 CSS" -customCssWarn: "这些设置必须有相关的基础知识,不当的配置可能导致客户端无法正常使用!" +customCssWarn: "这些设置必须有相关的基础知识,不当的配置可能导致客户端无法正常使用。" global: "全局" squareAvatars: "显示方形头像图标" sent: "发送" @@ -789,39 +795,39 @@ received: "收取" searchResult: "搜索结果" hashtags: "话题标签" troubleshooting: "故障排除" -useBlurEffect: "在UI上使用模糊效果" +useBlurEffect: "在 UI 上使用模糊效果" learnMore: "更多信息" -misskeyUpdated: "Misskey更新完成!" +misskeyUpdated: "Firefish 更新完成!" whatIsNew: "显示更新信息" translate: "翻译" translatedFrom: "从 {x} 翻译" -accountDeletionInProgress: "正在删除账户" -usernameInfo: "在服务器上唯一标识您的帐户的名称。您可以使用字母 (a ~ z, A ~ Z)、数字 (0 ~ 9) 和下划线 (_)。用户名以后不能更改。" +accountDeletionInProgress: "正在删除账号" +usernameInfo: "在服务器上唯一标识您的账号的名称。您可以使用字母 (a ~ z, A ~ Z)、数字 (0 ~ 9) 和下划线 (_)。用户名以后不能更改。" aiChanMode: "小蓝模式" -keepCw: "保留CW" -pubSub: "Pub/Sub账户" +keepCw: "保留内容警告" +pubSub: "推送 (Pub)/订阅 (Sub) 账号" lastCommunication: "最近通信" resolved: "已解决" unresolved: "未解决" breakFollow: "移除关注者" itsOn: "已开启" itsOff: "已关闭" -emailRequiredForSignup: "注册账户需要电子邮件地址" +emailRequiredForSignup: "注册账号需要电子邮件地址" unread: "未读" filter: "筛选" controlPanel: "控制面板" -manageAccounts: "管理账户" +manageAccounts: "管理账号" makeReactionsPublic: "将回应设置为公开" makeReactionsPublicDescription: "将您发表过的回应设置成公开可见。" -classic: "经典" -muteThread: "屏蔽帖子列表" -unmuteThread: "取消屏蔽帖子列表" -ffVisibility: "连接的可见范围" -ffVisibilityDescription: "您可以设置您的关注/关注者信息的公开范围" +classic: "居中" +muteThread: "静音帖子串" +unmuteThread: "取消静音帖子串" +ffVisibility: "关注/关注者 可见性" +ffVisibilityDescription: "您可以设置谁可以看到您的关注/关注者信息。" continueThread: "查看更多帖子" -deleteAccountConfirm: "将要删除账户。是否确认?" -incorrectPassword: "密码错误" -voteConfirm: "确定投给“{choice}” ?" +deleteAccountConfirm: "将要删除账号。是否继续?" +incorrectPassword: "密码错误。" +voteConfirm: "确定投给 “{choice}” ?" hide: "隐藏" leaveGroup: "离开群组" leaveGroupConfirm: "确定离开「{name}」?" @@ -832,66 +838,72 @@ overridedDeviceKind: "设备类型" smartphone: "智能手机" tablet: "平板" auto: "自动" -themeColor: "主题颜色" +themeColor: "服务器滚动条颜色" size: "大小" numberOfColumn: "列数" searchByGoogle: "Google" -instanceDefaultLightTheme: "实例默认浅色主题" -instanceDefaultDarkTheme: "实例默认深色主题" -instanceDefaultThemeDescription: "以对象格式键入主题代码" -mutePeriod: "屏蔽期限" +instanceDefaultLightTheme: "服务器默认浅色主题" +instanceDefaultDarkTheme: "服务器默认深色主题" +instanceDefaultThemeDescription: "以对象格式键入主题代码。" +mutePeriod: "静音时间" indefinitely: "永久" tenMinutes: "10分钟" -oneHour: "1小时" -oneDay: "1天" -oneWeek: "1周" +oneHour: "1 小时" +oneDay: "1 天" +oneWeek: "1 周" reflectMayTakeTime: "可能需要一些时间才能体现出效果。" -failedToFetchAccountInformation: "获取账户信息失败" -rateLimitExceeded: "已超過速率限制" +failedToFetchAccountInformation: "获取账号信息失败" +rateLimitExceeded: "已超过速率限制" cropImage: "剪裁图像" -cropImageAsk: "是否要裁剪图像?" +cropImageAsk: "您想要裁剪图像吗?" file: "文件" -recentNHours: "最近{n}小时" -recentNDays: "最近{n}天" +recentNHours: "最近 {n} 小时" +recentNDays: "最近 {n} 天" noEmailServerWarning: "电子邮件服务器未设置。" -thereIsUnresolvedAbuseReportWarning: "有未解决的报告" +thereIsUnresolvedAbuseReportWarning: "有未处理的举报。" recommended: "推荐" check: "检查" -driveCapOverrideLabel: "變更此用戶的雲端硬碟容量上限" -driveCapOverrideCaption: "设定为 0 以下则会解除此限制。" -requireAdminForView: "需要使用管理员账户登录才能查看。" -isSystemAccount: "该账号由系统自动创建和管理。" -typeToConfirm: "输入 {x} 以确认操作。" -deleteAccount: "删除账户" +driveCapOverrideLabel: "修改此用户的网盘容量" +driveCapOverrideCaption: "输入 0 或以下的值将容量重置为默认值。" +requireAdminForView: "需要使用管理员账号登录才能查看。" +isSystemAccount: "该账号由系统自动创建和管理。请不要修改、编辑、删除或以其它方式篡改这个账号,否则可能会破坏您的服务器。" +typeToConfirm: "输入 {x} 以确认操作" +deleteAccount: "删除账号" document: "文档" numberOfPageCache: "缓存页数" numberOfPageCacheDescription: "设置较高的值会更方便用户,但设备的负载和内存使用量会增加。" logoutConfirm: "是否确认登出?" -lastActiveDate: "最后活跃时间" +lastActiveDate: "最近使用时间" statusbar: "状态栏" pleaseSelect: "请选择" reverse: "翻转" colored: "彩色" -refreshInterval: "刷新间隔" +refreshInterval: "更新间隔 " label: "标签" type: "类型" speed: "速度" slow: "慢" fast: "快" -sensitiveMediaDetection: "检测到敏感媒体" +sensitiveMediaDetection: "检测到敏感媒体内容" localOnly: "仅限本地" remoteOnly: "仅远程" failedToUpload: "上传失败" -cannotUploadBecauseInappropriate: "因为可能含有不适宜的内容,无法上传。" -cannotUploadBecauseNoFreeSpace: "因为已无可用空间,无法上传。" +cannotUploadBecauseInappropriate: "无法上传此文件,因为它可能包含不适宜的内容。" +cannotUploadBecauseNoFreeSpace: "由于已无可用网盘空间,无法上传。" beta: "测试" -enableAutoSensitive: "自动 NSFW 识别" -enableAutoSensitiveDescription: "如果可用,请使用机器学习在媒体上自动设置 NSFW 标志。即使关闭此功能,也可能会根据实例自动设置。" -activeEmailValidationDescription: "积极地验证用户的电子邮件地址,判断它是一次性的电子邮件地址,还是可以实际通信的地址。关闭时,则只检查字符串是否正确。" +enableAutoSensitive: "自动 NSFW 标记" +enableAutoSensitiveDescription: "允许通过机器学习对媒体文件自动设置 NSFW 标志。即使关闭此功能,也可能会根据服务器自动设置。" +activeEmailValidationDescription: "启用更严格的电子邮件地址验证,包括判断它是一次性的电子邮件地址还是可以实际通信的地址。关闭时,则只检查字符串是否正确。" navbar: "导航栏" shuffle: "随机" -account: "账户" -move: "移动" +account: "账号" +move: "迁移" +customKaTeXMacro: "自定义 KaTeX 宏" +customKaTeXMacroDescription: "使用宏来轻松的输入数学表达式吧!宏的用法与 LaTeX 中的命令定义相同。您可以使用 \\newcommand{\\ + name}{content} 或 \\newcommand{\\name}[number of arguments]{content} 来输入数学表达式。举个例子,\\ + newcommand{\\add}[2]{#1 + #2} 会将 \\add{3}{foo} 展开为 3 + foo。此外,宏名称外的花括号 {} 可以被替换为圆括号 + () 和方括号 [],这会影响用于参数的括号。每行只能够定义一个宏,无法在中间换行,且无效的行将被忽略。只支持简单字符串替换功能,不支持高级语法,如条件分支等。" +enableCustomKaTeXMacro: "启用自定义 KaTeX 宏" _sensitiveMediaDetection: description: "可以使用机器学习技术自动检测敏感媒体,以便进行审核。服务器负载将略微增加。" sensitivity: "检测敏感度" @@ -901,33 +913,33 @@ _sensitiveMediaDetection: analyzeVideos: "启用对视频的检测" analyzeVideosDescription: "除了静止图像之外,还对视频进行分析。服务器负载会略微增加。" _emailUnavailable: - used: "已经被使用过" + used: "这个电子邮件地址已经被使用过" format: "无效的格式" - disposable: "不是永久可用的地址" + disposable: "不得使用一次性电子邮件地址" mx: "邮件服务器不正确" smtp: "邮件服务器没有响应" _ffVisibility: - public: "发布" - followers: "只有关注你的用户能看到" - private: "私密" + public: "公开" + followers: "仅对关注者可见" + private: "私信" _signup: almostThere: "即将完成" - emailAddressInfo: "请输入您所使用的电子邮件地址" - emailSent: "已将确认邮件发送至您输入的电子邮件地址 ({email})。请访问电子邮件中的链接以完成帐户创建。" + emailAddressInfo: "请输入您所使用的电子邮件地址,它不会公开显示。" + emailSent: "已将确认邮件发送至您输入的电子邮件地址 ({email})。请访问电子邮件中的链接以完成账号创建。" _accountDelete: - accountDelete: "删除帐户" - mayTakeTime: "删除账号是一个性能损耗较大的处理,如果账号持有的内容数量和上传的文件数量较多的话,完成需要花费一段时间。" - sendEmail: "账户删除完成后,将向注册的电子邮件地址发送通知。" - requestAccountDelete: "请求删除账户" - started: "账户删除过程已开始。" + accountDelete: "删除账号" + mayTakeTime: "删除账号是一个性能损耗较大的过程,如果账号持有的内容数量和上传的文件数量较多的话,完成需要花费一段时间。" + sendEmail: "账号删除完成后,将向注册的电子邮件地址发送通知。" + requestAccountDelete: "请求删除账号" + started: "账号删除过程已开始。" inProgress: "正在删除" _ad: back: "返回" reduceFrequencyOfThisAd: "减少此广告的频率" _forgotPassword: - enterEmail: "请输入您验证账号时用的电子邮箱地址,密码重置链接将发送至该邮箱上。" - ifNoEmail: "如果您没有使用电子邮件地址进行验证,请联系管理员。" - contactAdmin: "该实例不支持发送电子邮件。如果您想重设密码,请联系管理员。" + enterEmail: "请输入您注册账号时用的电子邮箱地址,密码重置链接将发送至该邮箱上。" + ifNoEmail: "如果您在注册时没有输入电子邮件地址,请联系服务器管理员。" + contactAdmin: "该服务器不支持发送电子邮件。如果您想重设密码,请联系管理员。" _gallery: my: "我的图库" liked: "喜欢的图片" @@ -935,97 +947,103 @@ _gallery: unlike: "取消喜欢" _email: _follow: - title: "你有新的关注者" + title: "您有新的关注者" _receiveFollowRequest: - title: "收到了关注请求" + title: "您收到了关注请求" _plugin: install: "安装插件" installWarn: "请不要安装不可信的插件。" - manage: "管理插件..." + manage: "管理插件" _preferencesBackups: list: "已创建的备份" saveNew: "另存为" - loadFile: "导入文件" + loadFile: "从文件导入" apply: "应用于本设备" save: "覆盖存档" inputName: "请输入备份的名称" - cannotSave: "无法保存" - nameAlreadyExists: "备份名称\"{name}\"已经存在,请指定其他名称。" - applyConfirm: "您是否要将备份\"{name}\"应用到当前设备上?当前设备现有配置将被丢弃。" + cannotSave: "保存失败" + nameAlreadyExists: "备份名称 \"{name}\" 已经存在,请指定其它名称。" + applyConfirm: "您是否要将备份 \"{name}\" 应用到当前设备上?当前设备现有配置将被丢弃。" saveConfirm: "您确定要覆盖保存 {name} 吗?" deleteConfirm: "您确定要删除 {name} 吗?" - renameConfirm: "您确定要把“{old}”改为“{new}”吗?" - noBackups: "当前没有备份,“另存为”允许您在服务器上保存当前客户端的配置。" + renameConfirm: "您确定要把 \"{old}\" 改为 \"{new}\" 吗?" + noBackups: "没有备份。您可以使用“创建新的备份”来备份您在该服务器上的客户设置。" createdAt: "创建日期:{date} {time}" updatedAt: "更新日期:{date} {time}" cannotLoad: "无法加载" - invalidFile: "无效的的文件格式。" + invalidFile: "无效的的文件格式" _registry: scope: "范围" - key: "主要" - keys: "主要" + key: "键" + keys: "键" domain: "域" createKey: "创建键" -_aboutMisskey: - about: "Misskey是由syuilo于2014年开发的开源软件。" +_aboutFirefish: + about: "Firefish 是由 ThatOneCalculator 创建的 Misskey 的一个分支,自 2022 年开始开发。" contributors: "主要贡献者" allContributors: "全体贡献者" source: "源代码" - translation: "翻译Misskey" - donate: "赞助Misskey" - morePatrons: "还有很多其他的人也在支持我们,非常感谢🥰" - patrons: "支持者" + translation: "翻译 Firefish" + donate: "赞助 Firefish" + morePatrons: "还有很多其它的人也在支持我们,非常感谢🥰" + patrons: "Firefish 赞助者" + patronsList: 按时间顺序而不是捐赠金额排列。通过上面的链接捐款,让您的名字出现在这里! + sponsors: Calckey 赞助者们 + donateTitle: 喜欢 Calckey 吗? + pleaseDonateToCalckey: 请考虑赞助 Calckey 以支持其开发。 + pleaseDonateToHost: 也请考虑赞助您的主服务器 {host},以帮助支持其运营成本。 + donateHost: 赞助 {host} _nsfw: respect: "隐藏敏感内容" ignore: "不隐藏敏感内容" force: "总是隐藏内容" _mfm: - cheatSheet: "MFM代码速查表" - intro: "MFM是一种在Misskey中的各个位置使用的专用标记语言。在这里您可以看到MFM中可用的语法列表。" - dummy: "通过Misskey扩展联邦宇宙的世界" + cheatSheet: "MFM 代码速查表" + intro: "MFM 是一种在 Misskey、Firefish、Akkoma 中使用的标记语言,可以在很多地方使用。您可以在此处查看所有可用的 MFM 语法的列表。" + dummy: "通过 Firefish 扩展联邦宇宙的世界" mention: "提及" - mentionDescription: "可以使用 @+用户名 来指示特定用户" + mentionDescription: "可以使用 @+用户名 来指示特定用户。" hashtag: "话题标签" hashtagDescription: "可以使用井号+文字来表示话题标签。" url: "URL" - urlDescription: "可以表示URL地址。" + urlDescription: "可以表示 URL 地址。" link: "链接" - linkDescription: "可以将部分文字和URL关联起来。" + linkDescription: "可以将部分文字和 URL 关联起来。" bold: "粗体" boldDescription: "可以将文字显示为粗体来表示强调。" small: "缩小" smallDescription: "可以使内容文字变小、变淡。" center: "居中" centerDescription: "可以将内容居中显示。" - inlineCode: "代码(内嵌)" + inlineCode: "代码(内嵌)" inlineCodeDescription: "将文字中的程序代码语法高亮显示。" - blockCode: "代码(块)" + blockCode: "代码(块)" blockCodeDescription: "语法高亮显示整块程序代码。" - inlineMath: "数学公式(内嵌)" - inlineMathDescription: "显示内嵌的KaTex公式。" - blockMath: "数学公式(块)" - blockMathDescription: "显示整块的多行KaTex数学公式。" + inlineMath: "数学公式(内嵌)" + inlineMathDescription: "显示内嵌的 KaTeX 公式" + blockMath: "数学公式(块)" + blockMathDescription: "显示整块的 KaTeX 数学公式" quote: "引用" - quoteDescription: "可以用来表示引用的内容。" + quoteDescription: "将内容显示为引用。" emoji: "自定义表情符号" emojiDescription: "可以将自定义表情符号使用冒号括起来,就可以显示自定义表情符号了。" search: "搜索" searchDescription: "显示含有搜索内容示例的搜索框。" flip: "翻转" flipDescription: "将内容上下或左右翻转。" - jelly: "动画(果冻)" + jelly: "动画(果冻)" jellyDescription: "显示果冻一样的动画效果。" - tada: "动画(锵锵)" + tada: "动画(锵锵)" tadaDescription: "显示\"锵锵!\"的动画效果。" - jump: "动画(跳动)" + jump: "动画(跳动)" jumpDescription: "显示跳动的动画效果。" - bounce: "动画(弹性)" + bounce: "动画(弹性)" bounceDescription: "显示弹性一样的动画效果。" - shake: "动画(摇晃)" + shake: "动画(摇晃)" shakeDescription: "显示摇晃的动画效果。" - twitch: "动画(颤抖)" + twitch: "动画(颤抖)" twitchDescription: "显示强烈颤抖的动画效果。" - spin: "动画(回转)" + spin: "动画(回转)" spinDescription: "显示回转的动画效果。" x2: "大" x2Description: "以大尺寸显示内容。" @@ -1045,6 +1063,24 @@ _mfm: rotateDescription: "旋转指定的角度。" plain: "简洁" plainDescription: "禁用所有内部语法。" + crop: 裁剪 + scale: 缩放 + position: 位置 + fade: 渐淡 + advanced: 高级 MFM + background: 背景色 + fadeDescription: 内容淡入和淡出。 + warn: MFM 可能包含快速移动或华丽的动画 + advancedDescription: 如果禁用,则仅允许基本标记,除非正在播放动态 MFM + foreground: 前景色 + backgroundDescription: 更改文本的背景色。 + play: 播放 MFM + alwaysPlay: 始终自动播放所有动态的 MFM + stop: 停止播放 MFM + positionDescription: 将内容移动指定的量。 + cropDescription: 裁剪内容。 + scaleDescription: 按指定量缩放内容。 + foregroundDescription: 更改文本的前景色。 _instanceTicker: none: "不显示" remote: "仅远程用户" @@ -1053,6 +1089,7 @@ _serverDisconnectedBehavior: reload: "自动重载" dialog: "对话框警告" quiet: "安静警告" + nothing: 什么也不做 _channel: create: "创建频道" edit: "编辑频道" @@ -1060,28 +1097,30 @@ _channel: removeBanner: "删除横幅" featured: "热点" owned: "管理中" - following: "正在关注" - usersCount: "有{n}人参与" - notesCount: "有{n}个帖子" + following: "关注中" + usersCount: "有 {n} 人参与" + notesCount: "{n} 帖子" + nameAndDescription: "名称与描述" + nameOnly: "仅名称" _menuDisplay: sideFull: "横向" - sideIcon: "横向(图标)" + sideIcon: "横向(图标)" top: "顶部" hide: "隐藏" _wordMute: - muteWords: "禁用词" - muteWordsDescription: "使用空格分隔表示AND逻辑,使用换行符分隔表示OR逻辑。" + muteWords: "过滤词" + muteWordsDescription: "AND 条件用空格分隔,OR 条件用换行符分隔。" muteWordsDescription2: "将关键字用斜线括起来表示正则表达式。" softDescription: "隐藏时间线中指定条件的帖子。" - hardDescription: "防止将具有指定条件的帖子添加到时间线。 即使您更改条件,未添加的帖文也会被排除在外。" - soft: "软屏蔽" - hard: "硬屏蔽" - mutedNotes: "被屏蔽的帖子" + hardDescription: "防止将具有指定条件的帖子添加到时间线。 即使您更改条件,原先未添加的帖文也会被排除在外。" + soft: "软过滤" + hard: "硬过滤" + mutedNotes: "已过滤的帖子" _instanceMute: - instanceMuteDescription: "屏蔽配置实例中的所有帖子和转帖,包括实例的用户回复。" + instanceMuteDescription: "静音列出服务器中的所有帖子和转帖,包括服务器的用户回复。" instanceMuteDescription2: "设置时用换行符来分隔" - title: "隐藏实例已设置的帖子。" - heading: "屏蔽实例" + title: "隐藏列出的服务器中的帖子。" + heading: "要静音的服务器列表" _theme: explore: "寻找主题" install: "安装主题" @@ -1111,7 +1150,7 @@ _theme: lighten: "浅色" inputConstantName: "请输入常量名称" importInfo: "您可以在此处粘贴主题代码,将其导入到编辑器中" - deleteConstantConfirm: "确定要删除常量{const}吗?" + deleteConstantConfirm: "确定要删除常量 {const} 吗?" keys: accent: "强调色" bg: "背景" @@ -1129,7 +1168,7 @@ _theme: link: "链接" hashtag: "话题标签" mention: "提及" - mentionMe: "提及" + mentionMe: "提及(自己)" renote: "转发" modalBg: "对话框背景" divider: "分割线" @@ -1143,8 +1182,8 @@ _theme: cwBg: "CW 按钮背景" cwFg: "CW 按钮文本" cwHoverBg: "CW 按钮背景(悬停)" - toastBg: "Toast通知背景" - toastFg: "Toast通知文本" + toastBg: "Toast 通知背景" + toastFg: "Toast 通知文本" buttonBg: "按钮背景" buttonHoverBg: "按钮背景(悬停)" inputBorder: "输入框边框" @@ -1153,11 +1192,11 @@ _theme: wallpaperOverlay: "壁纸叠加层" badge: "徽章" messageBg: "聊天背景" - accentDarken: "强调色(深)" - accentLighten: "强调色(浅)" + accentDarken: "强调色(深)" + accentLighten: "强调色(浅)" fgHighlighted: "高亮显示文本" _sfx: - note: "帖子" + note: "新的帖子" noteMy: "我的帖子" notification: "通知" chat: "聊天" @@ -1165,97 +1204,115 @@ _sfx: antenna: "天线接收" channel: "频道通知" _ago: - future: "未来" - justNow: "最近" - secondsAgo: "{n}秒前" - minutesAgo: "{n}分前" - hoursAgo: "{n}小时前" - daysAgo: "{n}日前" - weeksAgo: "{n}周前" - monthsAgo: "{n}月前" - yearsAgo: "{n}年前" + future: "将来" + justNow: "刚刚" + secondsAgo: "{n} 秒前" + minutesAgo: "{n} 分前" + hoursAgo: "{n} 时前" + daysAgo: "{n} 天前" + weeksAgo: "{n} 周前" + monthsAgo: "{n} 月前" + yearsAgo: "{n} 年前" _time: second: "秒" minute: "分" hour: "小时" day: "日" _tutorial: - title: "如何使用Calckey" + title: "如何使用 Firefish" step1_1: "欢迎!" - step1_2: "让我们把你安排好。你很快就会启动并运行!" + step1_2: "让我们帮您设置一下。您很快就能开始畅游联邦宇宙!" step2_1: "首先,请完成您的个人资料。" - step2_2: "通过提供一些关于你自己的信息,其他人会更容易了解他们是否想看到你的帖子或关注你。" - step3_1: "现在是时候跟随一些人了!" - step3_2: "你的主页和社交馈送是基于你所关注的人,所以试着先关注几个账户。{n点击个人资料右上角的加号圈就可以关注它。" - step4_1: "让我们出去找你。" - step4_2: "对于他们的第一条信息,有些人喜欢做{introduction}或一个简单的 \"hello world!\"" - step5_1: "时间限制,到处是时间限制!" - step5_2: "您的实例已启用各种时间线的{timelines}。" - step5_3: "主{icon}时间线是你可以看到你的订阅者的帖子的时间线。" - step5_4: "本地{icon}时间线是你可以看到实例中所有其他用户的信息的时间线。" - step5_5: "推荐的{icon}时间线 - 是时间轴,你可以看到管理员推荐的实例的信息" - step5_6: "社交{icon}时间线显示来自你的订阅者朋友的信息。" - step5_7: "全球{icon}时间线是你可以看到来自所有其他连接的实例的消息。" + step2_2: "提供一些关于您的信息,让其它人更容易知道他们是否想看您的帖子或关注您。" + step3_1: "现在是时候关注一些人了!" + step3_2: "您的主页和社交馈送是基于您所关注的人,所以试着先关注几个账号。\n点击个人资料右上角的加号圈就可以关注它。" + step4_1: "让我们出发把。" + step4_2: "对于第一条帖子,可以做一个 {introduction} 或一个简单的 \"hello world!\"" + step5_1: "时间线,无处不在的时间线!" + step5_2: "您的服务器已启用 {timelines} 种不同的时间线。" + step5_3: "主页 {icon} 时间线是您可以看到您关注账号的帖子的时间线。" + step5_4: "本地 {icon} 时间线是您可以看到此服务器上其它用户的帖子的时间线。" + step5_5: "社交 {icon} 时间线是主页和本地时间线的结合。" + step5_6: "推荐 {icon} 时间线是您可以看到管理员推荐服务器的帖子的时间线。" + step5_7: "全球 {icon} 时间线是您可以看到来自其它所有互联服务器的帖子的时间线。" step6_1: "那么,这里是什么地方?" - step6_2: "好吧,你不只是加入卡尔基。你已经加入了Fediverse的一个门户,这是一个由成千上万台服务器组成的互联网络,被称为 \"实例\"" - step6_3: "每个服务器的工作方式不同,并不是所有的服务器都运行Calckey。但这个人确实如此! 这有点复杂,但你很快就会明白的。" - step6_4: "现在去学习并享受乐趣!" + step6_2: "好吧,您不只是加入 Firefish。您已经加入了 Fediverse 的一个门户,这是一个由成千上万台服务器组成的互联网络。" + step6_3: "每个服务器的工作方式不同,并不是所有的服务器都运行 Firefish。但这个服务器是的! 这有点复杂,但您很快就会明白的。" + step6_4: "现在,去吧,去探索,去享受乐趣吧!" _2fa: - alreadyRegistered: "此设备已被注册" - registerDevice: "注册设备" - registerKey: "注册密钥" - step1: "首先,在您的设备上安装验证应用,例如{a}或{b}。" + alreadyRegistered: "您已经注册了两步验证设备。" + registerTOTP: "注册身份验证器应用" + registerSecurityKey: "注册安全或通行密钥" + step1: "首先,在您的设备上安装身份验证器应用,例如 {a} 或 {b}。" step2: "然后,扫描屏幕上显示的二维码。" - step2Url: "在桌面应用程序中输入以下URL:" - step3: "输入您的应用提供的动态口令以完成设置。" - step4: "从现在开始,任何登录操作都将要求您提供动态口令。" - securityKeyInfo: "您可以设置使用支持FIDO2的硬件安全密钥、设备上的指纹或PIN来保护您的登录过程。" + step2Url: "如果您使用的是桌面程序,您也可以输入这个URL:" + step3: "输入您的应用提供的令牌以完成设置。" + step4: "从现在开始,任何登录操作都将要求您提供这样一个登录令牌。" + securityKeyInfo: "除了指纹或 PIN 身份验证外,您还可以通过支持 FIDO2 的硬件安全密钥设置身份验证,以进一步保护您的账号。" + renewTOTPOk: 重新配置 + renewTOTPCancel: 取消 + token: 2FA 令牌 + renewTOTP: 重新配置身份验证器应用 + registerTOTPBeforeKey: 请先设置认证器应用以注册安全或通行密钥。 + renewTOTPConfirm: 这将导致您之前应用中的验证码失效 + step3Title: 输入验证码 + step2Click: 点击此二维码将允许您在安全密钥或手机身份验证器应用中注册 2FA。 + securityKeyNotSupported: 您的浏览器不支持安全密钥。 + securityKeyName: 输入密钥名称 + chromePasskeyNotSupported: 暂不支持 Chrome 通行密钥。 + tapSecurityKey: 请按照您的浏览器的指示注册安全或通行密钥 + removeKey: 移除安全密钥 + removeKeyConfirm: 真的要删除 {name} 密钥吗? + whyTOTPOnlyRenew: 只要注册了安全密钥,就无法删除身份验证器应用。 _permissions: - "read:account": "查看账户信息" - "write:account": "更改帐户信息" - "read:blocks": "查看黑名单" - "write:blocks": "编辑黑名单" + "read:account": "查看账号信息" + "write:account": "更改账号信息" + "read:blocks": "查看屏蔽名单" + "write:blocks": "编辑屏蔽名单" "read:drive": "查看网盘" "write:drive": "管理网盘文件" "read:favorites": "查看收藏夹" "write:favorites": "编辑收藏夹" "read:following": "查看关注信息" - "write:following": "关注/取消关注" - "read:messaging": "查看消息" - "write:messaging": "撰写或删除消息" - "read:mutes": "查看屏蔽列表" - "write:mutes": "编辑屏蔽列表" + "write:following": "关注/取消关注其它账号" + "read:messaging": "查看聊天消息" + "write:messaging": "撰写或删除聊天消息" + "read:mutes": "查看静音用户列表" + "write:mutes": "编辑静音用户列表" "write:notes": "撰写或删除帖子" "read:notifications": "查看通知" "write:notifications": "管理通知" "read:reactions": "查看回应" - "write:reactions": "回应操作" + "write:reactions": "编辑回应" "write:votes": "投票" "read:pages": "查看页面" - "write:pages": "操作页面" - "read:page-likes": "查看喜欢的页面" - "write:page-likes": "操作喜欢的页面" + "write:pages": "编辑或删除页面" + "read:page-likes": "查看页面上的喜欢" + "write:page-likes": "编辑页面上的喜欢" "read:user-groups": "查看用户组" "write:user-groups": "操作用户组" "read:channels": "查看频道" "write:channels": "管理频道" "read:gallery": "浏览图库" - "write:gallery": "操作图库" + "write:gallery": "编辑图库" "read:gallery-likes": "读取喜欢的图片" - "write:gallery-likes": "操作喜欢的图片" + "write:gallery-likes": "编辑喜欢的图片" _auth: - shareAccess: "您要授权允许“{name}”访问您的帐户吗?" - shareAccessAsk: "您确定要授权此应用程序访问您的帐户吗?" - permissionAsk: "这个应用程序需要以下权限" - pleaseGoBack: "请返回到应用程序" - callback: "回到应用程序" + shareAccess: "您要授权允许 \"{name}\" 访问您的账号吗?" + shareAccessAsk: "您确定要授权此应用访问您的账号吗?" + permissionAsk: "此应用请求以下权限:" + pleaseGoBack: "请返回至应用" + callback: "正在返回至应用" denied: "拒绝访问" + allPermissions: 完全的账号访问权限 + copyAsk: 请将以下授权码粘贴到应用中: _antennaSources: all: "所有帖子" homeTimeline: "已关注用户的帖子" users: "来自指定用户的帖子" userList: "来自指定列表中的帖子" userGroup: "来自指定群组中用户的帖子" + instances: 服务器上所有用户的帖子 _weekday: sunday: "星期日" monday: "星期一" @@ -1271,30 +1328,37 @@ _widgets: calendar: "日历" trends: "趋势" clock: "时钟" - rss: "RSS阅读器" - rssTicker: "RSS Ticker" + rss: "RSS 阅读器" + rssTicker: "RSS 滚动条" activity: "活动" photos: "照片" digitalClock: "数字时钟" - unixClock: "UNIX时钟" + unixClock: "UNIX 时钟" federation: "联邦宇宙" - instanceCloud: "实例云" - postForm: "投稿窗口" + instanceCloud: "服务器云端" + postForm: "发布窗口" slideshow: "幻灯片展示" button: "按钮" onlineUsers: "在线用户" jobQueue: "作业队列" - serverMetric: "服务器监控" - aiscript: "AiScript控制台" + serverMetric: "服务器指标" + aiscript: "AiScript 控制台" aichan: "小蓝" + userList: 用户列表 + meiliStatus: 服务器状态 + meiliIndexCount: 已索引的帖子 + meiliSize: 索引大小 + serverInfo: 服务器信息 + _userList: + chooseList: 选择列表 _cw: hide: "隐藏" show: "查看更多" - chars: "{count}个字符" + chars: "{count} 个字符" files: "{count} 个文件" _poll: noOnlyOneChoice: "需要至少两个选项" - choiceN: "选择{n}" + choiceN: "选择 {n}" noMore: "无法再添加更多了" canMultipleVote: "允许多个投票" expiration: "截止时间" @@ -1304,23 +1368,23 @@ _poll: deadlineDate: "截止日期" deadlineTime: "小时" duration: "时长" - votesCount: "{n}票" - totalVotes: "总票数{n}" + votesCount: "{n} 票" + totalVotes: "总票数 {n}" vote: "投票" showResult: "显示结果" voted: "已投票" closed: "已截止" - remainingDays: "{d}天{h}小时后截止" - remainingHours: "{h}小时{m}分后截止" - remainingMinutes: "{m}分{s}秒后截止" - remainingSeconds: "{s}秒后截止" + remainingDays: "{d} 天 {h} 小时后截止" + remainingHours: "{h} 小时 {m} 分后截止" + remainingMinutes: "{m} 分 {s} 秒后截止" + remainingSeconds: "{s} 秒后截止" _visibility: public: "公开" - publicDescription: "您的帖子将出现在全局时间线上" - home: "首页" - homeDescription: "仅发送至首页的时间线" + publicDescription: "您的帖子将出现在公共时间线上" + home: "不公开" + homeDescription: "仅发送至首页时间线" followers: "仅关注者" - followersDescription: "仅发送至关注者" + followersDescription: "仅对您的关注者和提及的用户可见" specified: "指定用户" specifiedDescription: "仅发送至指定用户" localOnly: "仅限本地" @@ -1332,29 +1396,30 @@ _postForm: _placeholders: a: "现在如何?" b: "发生了什么?" - c: "你有什么想法?" - d: "你想要发布些什么吗?" + c: "您有什么想法?" + d: "您想要发布些什么吗?" e: "请写下来吧" f: "等待您的发布..." _profile: name: "昵称" username: "用户名" description: "个人简介" - youCanIncludeHashtags: "您可以包含一个哈希标签。" + youCanIncludeHashtags: "您可以包含一个话题标签。" metadata: "附加信息" metadataEdit: "附加信息编辑" - metadataDescription: "最多可以在个人资料中以表格形式显示四条其他信息。" + metadataDescription: "使用这些,您可以在您的个人资料中显示其它信息字段。您可以添加带有 {rel} 的 {a} 标签或 {l} 标签来验证您个人资料上的链接!" metadataLabel: "标签" metadataContent: "内容" changeAvatar: "修改头像" changeBanner: "修改横幅" + locationDescription: 如果您先输入您的城市,它将向其它用户显示您的当地时间。 _exportOrImport: allNotes: "所有帖子" - followingList: "关注中" - muteList: "屏蔽" - blockingList: "拉黑" + followingList: "已关注用户" + muteList: "已静音用户" + blockingList: "已屏蔽用户" userLists: "列表" - excludeMutingUsers: "排除屏蔽用户" + excludeMutingUsers: "排除已静音用户" excludeInactiveUsers: "排除不活跃用户" _charts: federation: "联合" @@ -1376,7 +1441,7 @@ _instanceCharts: usersTotal: "用户总计" notes: "帖子:增加/减少" notesTotal: "帖子总计" - ff: "关注/被关注:数量变化" + ff: "被关注用户/关注者的数量差异 " ffTotal: "关注/被关注者总计" cacheSize: "缓存大小:增加/减少" cacheSizeTotal: "缓存大小总计" @@ -1387,6 +1452,7 @@ _timelines: local: "本地" social: "社交" global: "全局" + recommended: 推荐 _pages: newPage: "创建页面" editPage: "编辑页面" @@ -1395,8 +1461,8 @@ _pages: updated: "页面已更新" deleted: "该页面已被删除" pageSetting: "页面设置" - nameAlreadyExists: "该页面URL已存在" - invalidNameTitle: "无效的页面URL" + nameAlreadyExists: "该页面 URL 已存在" + invalidNameTitle: "无效的页面 URL" invalidNameText: "请确认该项不为空" editThisPage: "编辑此页面" viewSource: "查看源代码" @@ -1411,7 +1477,7 @@ _pages: content: "页面内容" variables: "变量" title: "标题" - url: "页面URL" + url: "页面 URL" summary: "页面摘要" alignCenter: "居中" hideTitleWhenPinned: "置顶时隐藏标题" @@ -1440,7 +1506,7 @@ _pages: _post: text: "内容" attachCanvasImage: "附加画布图像" - canvasId: "画布ID" + canvasId: "画布 ID" textInput: "文本输入" _textInput: name: "变量名" @@ -1458,13 +1524,13 @@ _pages: default: "默认值" canvas: "画布" _canvas: - id: "画布ID" + id: "画布 ID" width: "宽度" height: "高度" note: "嵌入的帖子" _note: - id: "帖子ID" - idDescription: "您也可以通过粘贴帖子的URL来进行设置。" + id: "帖子 ID" + idDescription: "您也可以将帖子 URL 粘贴到此处。" detailed: "显示详细信息" switch: "开关" _switch: @@ -1491,7 +1557,7 @@ _pages: message: "按下时显示的消息" variable: "发送的变量" no-variable: "空" - callAiScript: "调用AiScript" + callAiScript: "调用 AiScript" _callAiScript: functionName: "函数名" radioButton: "选择项" @@ -1514,7 +1580,7 @@ _pages: list: "列表" blocks: text: "文本" - multiLineText: "文本 (多行)" + multiLineText: "文本(多行)" textList: "文本列表" _textList: info: "请使用换行符分隔每行" @@ -1553,42 +1619,42 @@ _pages: _divide: arg1: "A" arg2: "B" - mod: "取模(MOD)" + mod: "取模 (MOD)" _mod: arg1: "A" arg2: "B" round: "四舍五入" _round: arg1: "数值" - eq: "A和B相等" + eq: "A 和 B 相等" _eq: arg1: "A" arg2: "B" - notEq: "A和B不等" + notEq: "A 和 B 不等" _notEq: arg1: "A" arg2: "B" - and: "A和B" + and: "A 和 B" _and: arg1: "A" arg2: "B" - or: "A或B" + or: "A 或 B" _or: arg1: "A" arg2: "B" - lt: "< A小于B" + lt: "< A 小于 B" _lt: arg1: "A" arg2: "B" - gt: "> A大于B" + gt: "> A 大于 B" _gt: arg1: "A" arg2: "B" - ltEq: "<= A小于等于B" + ltEq: "<= A 小于等于 B" _ltEq: arg1: "A" arg2: "B" - gtEq: ">= A大于等于B" + gtEq: ">= A 大于等于 B" _gtEq: arg1: "A" arg2: "B" @@ -1610,30 +1676,30 @@ _pages: randomPick: "从列表中随机选择" _randomPick: arg1: "列表" - dailyRandom: "随机(每个用户每日)" + dailyRandom: "随机(每个用户每日)" _dailyRandom: arg1: "概率" - dailyRannum: "随机数(每个用户每日)" + dailyRannum: "随机数(每个用户每日)" _dailyRannum: arg1: "最小值" arg2: "最大值" - dailyRandomPick: "从列表中随机选择(每个用户每日)" + dailyRandomPick: "从列表中随机选择(每个用户每日)" _dailyRandomPick: arg1: "列表" - seedRandom: "随机 (种子)" + seedRandom: "随机(种子)" _seedRandom: arg1: "种子" arg2: "概率" - seedRannum: "随机数(种子)" + seedRannum: "随机数(种子)" _seedRannum: arg1: "种子" arg2: "最小值" arg3: "最大值" - seedRandomPick: "从列表中随机选择 (种子)" + seedRandomPick: "从列表中随机选择(种子)" _seedRandomPick: arg1: "种子" arg2: "列表" - DRPWPM: "从概率列表中随机选择(每用户每天)" + DRPWPM: "从概率列表中随机选择(每个用户每日)" _DRPWPM: arg1: "文本列表" pick: "从列表中选择" @@ -1654,7 +1720,7 @@ _pages: _splitStrByLine: arg1: "文本" ref: "变量" - aiScriptVar: "AiScript变量" + aiScriptVar: "AiScript 变量" fn: "函数" _fn: slots: "槽函数" @@ -1664,8 +1730,8 @@ _pages: _for: arg1: "次数" arg2: "处理" - typeError: "槽函数{slot}需要传入“{expect}”,但是实际传入为“{actual}”!" - thereIsEmptySlot: "槽函数{slot}为空!" + typeError: "槽函数 {slot} 需要传入 \"{expect}\",但是实际传入为 \"{actual}\"!" + thereIsEmptySlot: "槽函数 {slot} 为空!" types: string: "文字" number: "数值" @@ -1682,37 +1748,40 @@ _relayStatus: rejected: "已拒绝" _notification: fileUploaded: "文件已上传" - youGotMention: "来自{name}的提及" - youGotReply: "来自{name}的回复" - youGotQuote: "来自{name}的引用" - youRenoted: "来自{name}的转发" - youGotPoll: "来自{name}的投票" - youGotMessagingMessageFromUser: "来自{name}的聊天" - youGotMessagingMessageFromGroup: "来自{name}的群聊" - youWereFollowed: "关注了你。" + youGotMention: "来自 {name} 的提及" + youGotReply: "来自 {name} 的回复" + youGotQuote: "来自 {name} 的引用" + youRenoted: "来自 {name} 的转发" + youGotPoll: "来自 {name} 的投票" + youGotMessagingMessageFromUser: "来自 {name} 的聊天" + youGotMessagingMessageFromGroup: "来自 {name} 的群聊" + youWereFollowed: "关注了您" youReceivedFollowRequest: "您有新的关注请求" yourFollowRequestAccepted: "您的关注请求已通过" - youWereInvitedToGroup: "您有新的群组邀请" - pollEnded: "问卷调查结果已生成。" + youWereInvitedToGroup: "{userName} 邀请您加入一个群组" + pollEnded: "问卷调查结果已生成" emptyPushNotificationMessage: "推送通知已更新" _types: all: "全部" - follow: "关注中" + follow: "新关注者" mention: "提及" reply: "回复" renote: "转发" quote: "引用" reaction: "回应" - pollVote: "问卷调查被投票" + pollVote: "问卷调查投票" pollEnded: "问卷调查结束" - receiveFollowRequest: "收到关注请求" - followRequestAccepted: "关注请求已通过" - groupInvited: "加入群组邀请" + receiveFollowRequest: "收到的关注请求" + followRequestAccepted: "已通过的关注请求" + groupInvited: "群组加入邀请" app: "关联应用的通知" _actions: followBack: "回关" reply: "回复" renote: "转发" + reacted: 回应了您的帖子 + voted: 在您的问卷调查中投了票 + renoted: 转发了您的帖子 _deck: alwaysShowMainColumn: "总是显示主列" columnAlign: "列对齐" @@ -1724,18 +1793,175 @@ _deck: swapDown: "向下移动" stackLeft: "向左折叠" popRight: "向右弹出" - profile: "配置文件" - newProfile: "新建配置文件" - deleteProfile: "删除配置文件" + profile: "工作区" + newProfile: "新建工作区" + renameProfile: "重命名工作区" + deleteProfile: "删除工作区" + nameAlreadyExists: "该工作区名已存在。" introduction: "将各列进行组合以创建您自己的界面!" - introduction2: "您可以随时通过屏幕右侧的 + 来添加列" - widgetsIntroduction: "从列菜单中,选择“小工具编辑”来添加小工具" + introduction2: "您可以随时通过屏幕右侧的 + 来添加列。" + widgetsIntroduction: "从列菜单中,选择“编辑小部件”以添加小部件。" _columns: main: "主列" - widgets: "小工具" + widgets: "小部件" notifications: "通知" tl: "时间线" antenna: "天线" list: "列表" mentions: "提及" - direct: "指定用户" + direct: "私信" + channel: 频道 +apps: 应用 +_messaging: + dms: 私信 + groups: 群组 +migration: 迁移 +_experiments: + title: 实验性功能 + postImportsCaption: 允许用户从过去的 Firefish、Misskey、Mastodon、Akkoma 和 Pleroma 账号导入帖子。如果您的队列出现拥堵,则可能会导致加载速度减慢。 + enablePostImports: 启用帖子导入 +license: 许可证 +flagSpeakAsCatDescription: 在猫模式下您的帖子会喵化 +allowedInstances: 白名单服务器 +listsDesc: 列表可以让您创建含有指定用户的时间线,它们可以从时间线页面访问。 +flagSpeakAsCat: 像猫一样说话 +removeReaction: 移除您的回应 +expandOnNoteClick: 点击打开帖子 +expandOnNoteClickDesc: 如果禁用,您仍然可以在右键菜单中或通过点击时间戳打开帖子。 +sendPushNotificationReadMessage: 已读后删除推送通知 +customMOTD: 自定义 MOTD(启动屏幕消息) +sendPushNotificationReadMessageCaption: 会短暂显示 "{emptyPushNotificationMessage}" 的通知,如果启用,可能会增加您的设备的耗电量。 +adminCustomCssWarn: 仅当您知道此设置的作用时才应使用它。输入不正确的值可能会导致每个人的客户端停止正常运行。请在用户设置中进行测试来确保您的 CSS + 正常工作。 +customMOTDDescription: 自定义 MOTD(启动屏幕)消息,一行一个,每次用户加载/刷新页面时都会随机显示。 +customSplashIconsDescription: 用换行符隔开的自定义启动屏幕图标的 URL,在用户每次加载/重新载入页面时随机显示。请确保图片是在一个静态的 + URL 上,最好全部调整为 192x192 的大小。 +recommendedInstancesDescription: 推荐的服务器一行一个,它们将出现在推荐时间线中。 +splash: 启动画面 +showUpdates: Firefish 更新后显示弹出窗口 +selectInstance: 选择服务器 +silencedInstances: 禁言的服务器 +antennaInstancesDescription: 列出服务器主机名,一行一个 +pushNotification: 推送通知 +subscribePushNotification: 启用推送通知 +showAdminUpdates: 提示新的 Firefish 版本可用(仅对于管理员) +searchPlaceholder: 搜索 Firefish +addInstance: 添加服务器 +jumpToPrevious: 跳转到上一个 +silenceThisInstance: 禁言此服务器 +manageGroups: 管理群组 +antennasDesc: "天线会显示符合您设置条件的新帖子!\n可以从时间线页面访问它们。" +channelFederationWarn: 频道还没有与其它服务器联合 +seperateRenoteQuote: 单独的转发和引用按钮 +customSplashIcons: 自定义启动屏幕图标(urls) +alt: 替代文字 +pushNotificationNotSupported: 您的浏览器或者服务器不支持推送通知 +showAds: 显示广告 +enterSendsMessage: 按回车键发送信息(关闭则是 Ctrl + Retun 发送) +recommendedInstances: 推荐服务器 +updateAvailable: 可能有可用更新! +swipeOnMobile: 允许在页面之间滑动 +swipeOnDesktop: 允许在桌面端以移动设备方式滑动 +logoImageUrl: Logo 图像 URL +deleted: 已删除 +editNote: 编辑帖子 +edited: 于 {date} {time} 编辑 +selectChannel: 选择一个频道 +accountMoved: 用户已迁移至新账号: +silencedInstancesDescription: 列出您想禁言的服务器的主机名。列出的服务器中的账号被视为 "禁言",只能发出关注请求,如果不被关注,就不能提及本地账号。这不会影响被屏蔽的服务器。 +hiddenTags: 隐藏的话题标签 +userSaysSomethingReason: '{name} 说了 {reason}' +clipsDesc: 便签就像可共享的分类书签。您可以从各个帖子的菜单中创建便签。 +privateModeInfo: 当启用时,只有白名单上的服务器可以与您的服务器联合,所有的帖子都会对公共时间线隐藏。 +allowedInstancesDescription: 要列入联合白名单的服务器的主机名,一行一个(仅适用于私密模式)。 +breakFollowConfirm: 确定要移除关注者吗? +caption: 自动显示说明文字 +newer: 更新的 +older: 更旧的 +noInstances: 没有服务器 +silenced: 禁言的 +accessibility: 无障碍 +secureMode: 安全模式(仅允许授权的拉取) +replayTutorial: 重播教程 +userSaysSomethingReasonReply: '{name} 回复了包含 {reason} 的帖子' +userSaysSomethingReasonQuote: '{name} 引用了一篇包含 {reason} 的帖子' +userSaysSomethingReasonRenote: '{name} 转发了一个包含 {reason} 的帖子' +noThankYou: 不,谢谢 +secureModeInfo: 当向其它服务器请求时,不要在没有验证的情况下发回。 +privateMode: 私密模式 +instanceSecurity: 服务器安全 +image: 图像 +video: 视频 +audio: 音频 +cannotUploadBecauseExceedsFileSizeLimit: 无法上传此文件,因为它超出了允许的最大大小。 +unsubscribePushNotification: 禁用推送通知 +pushNotificationAlreadySubscribed: 推送通知已启用 +enableEmojiReactions: 启用表情符号回应 +cw: 内容警告 +hiddenTagsDescription: 列出您想隐藏的话题标签(不带#)以避免在趋势和探索中显示。隐藏的标签仍然可以通过其它方式被发现。 +enableRecommendedTimeline: 启用推荐时间线 +_skinTones: + medium: 中等 + light: 浅色 + yellow: 黄色 + dark: 深色 + mediumLight: 中等偏淡 + mediumDark: 中等偏深 +isModerator: 监察员 +isAdmin: 管理员 +findOtherInstance: 寻找其它服务器 +moveFromDescription: 这将为您的旧账号设置一个别名,以便您可以从该旧账号迁移到当前账号。在从旧账号迁移之前执行此操作。请输入格式如@person@server.com + 的账号标签 +indexPosts: 索引帖子 +signupsDisabled: 该服务器目前关闭注册,但您随时可以在另一台服务器上注册!如果您有该服务器的邀请码,请在下面输入。 +silencedWarning: 显示这个页面是因为这些用户来自您的管理员设置的禁言服务器,所以他们有可能是垃圾信息。 +isBot: 这个账号是一个机器人 +moveAccountDescription: 这个过程是不可逆的。在迁移之前,请确保您已在新账号上为当前账号设置了别名。请输入格式如 @person@server.com + 账号标签 +moveFromLabel: 您要迁移出的旧账号: +preventAiLearning: 阻止 AI 机器人抓取 +preventAiLearningDescription: 请求第三方人工智能语言模型不要研究您上传的内容,例如帖子和图像。 +noGraze: 请禁用 "Graze for Mastodon" 浏览器扩展,因为它会干扰 Firefish。 +moveTo: 将当前账号迁移至新账号 +moveToLabel: 您要迁移到的目标账号: +moveAccount: 迁移账号! +migrationConfirm: "您确实确定要将账号迁移到 {account} 吗?此操作无法撤消,并且您将无法再次正常使用旧账号。\n另外,请确保您已将此当前账号设置为要移出的账号。" +indexFromDescription: 留空以索引每个帖子 +noteId: 帖子 ID +moveFrom: 从旧账号迁移至此账号 +defaultReaction: 发出和收到帖子的默认表情符号反应 +indexNotice: 现在开始索引。这可能需要一段时间,请至少一个小时内不要重新启动服务器。 +indexFrom: 从帖子 ID 开始的索引 +sendModMail: 发送审核通知 +isLocked: 该账号设置了关注请求 +_filters: + notesBefore: 帖子早于 + followingOnly: 仅关注中 + notesAfter: 帖子晚于 + fromDomain: 来自域名 + withFile: 带有文件 + fromUser: 来自用户 + followersOnly: 仅关注者 +reactionPickerSkinTone: 首选的表情符号肤色 +isPatron: Firefish 赞助 +_dialog: + charactersExceeded: 超出了最大字符数!当前:{current} / 限制:{max} + charactersBelow: 没有足够的字符!当前:{current} / 限制:{min} +enableIdenticonGeneration: 启用 Identicon 生成 +enableServerMachineStats: 启用服务器硬件统计 +_feeds: + atom: Atom + rss: RSS + jsonFeed: JSON 订阅源 + copyFeed: 复制订阅源 +verifiedLink: 已验证链接 +xl: 特大 +showPopup: 以弹出窗口通知用户 +showWithSparkles: 闪闪发光地展示 +youHaveUnreadAnnouncements: 您有未读的公告 +donationLink: 赞助页面链接 +neverShow: 不再显示 +remindMeLater: 稍后再说 +removeQuote: 移除引用 +removeRecipient: 移除接收者 +removeMember: 移除成员 diff --git a/locales/zh-TW.yml b/locales/zh-TW.yml index e7528f9a2d..d639523d96 100644 --- a/locales/zh-TW.yml +++ b/locales/zh-TW.yml @@ -1,7 +1,6 @@ ---- _lang_: "繁體中文" headlineMisskey: "貼文連繫網路" -introMisskey: "歡迎! Misskey是一個開放原始碼且去中心化的社群網路。\n透過「貼文」分享周邊新鮮事,並告訴其他人您的想法!📡\n透過「反應」功能,對大家的貼文表達情感!👍\n一起來探索這個新的世界吧!🚀" +introMisskey: "歡迎! Firefish是一個開源、去中心化且永遠免費的社群網路平台!🚀" monthAndDay: "{month}月 {day}日" search: "搜尋" notifications: "通知" @@ -10,32 +9,32 @@ password: "密碼" forgotPassword: "忘記密碼" fetchingAsApObject: "從聯邦宇宙取得中" ok: "OK" -gotIt: "知道了" +gotIt: "知道了!" cancel: "取消" enterUsername: "輸入使用者名稱" renotedBy: "{user} 轉傳了" -noNotes: "無貼文。" +noNotes: "無貼文" noNotifications: "沒有通知" -instance: "實例" +instance: "伺服器" settings: "設定" basicSettings: "基本設定" otherSettings: "其他設定" openInWindow: "在新視窗開啟" profile: "個人檔案" -timeline: "時間軸" -noAccountDescription: "此用戶還沒有自我介紹" +timeline: "時間線" +noAccountDescription: "此用戶還沒有自我介紹。" login: "登入" loggingIn: "登入中" logout: "登出" signup: "註冊" -uploading: "上傳中" +uploading: "上傳中..." save: "儲存" users: "使用者" addUser: "新增使用者" -favorite: "我的最愛" +favorite: "添加至我的最愛" favorites: "我的最愛" unfavorite: "從我的最愛中移除" -favorited: "已添加至我的最愛" +favorited: "已添加至我的最愛。" alreadyFavorited: "我的最愛中已存在。" cantFavorite: "無法加入至我的最愛。" pin: "置頂" @@ -44,7 +43,7 @@ copyContent: "複製內容" copyLink: "複製連結" delete: "刪除" deleteAndEdit: "刪除並編輯" -deleteAndEditConfirm: "要刪除並再次編輯嗎?此貼文的所有情感、轉發和回覆也將會消失。" +deleteAndEditConfirm: "要刪除並再次編輯嗎?此貼文的所有反應、轉發和回覆也會消失。" addToList: "加入至清單" sendMessage: "發送訊息" copyUsername: "複製使用者名稱" @@ -64,10 +63,10 @@ import: "匯入" export: "匯出" files: "檔案" download: "下載" -driveFileDeleteConfirm: "確定要刪除檔案「{name}」嗎?使用此附件的貼文也會跟著消失。\n" -unfollowConfirm: "確定要取消追隨{name}嗎?" +driveFileDeleteConfirm: "確定要刪除檔案「{name}」嗎?使用此附件的貼文也會跟著消失。" +unfollowConfirm: "確定要取消追隨 「{name}」 嗎?" exportRequested: "已請求匯出。這可能會花一點時間。結束後檔案將會被放到雲端裡。" -importRequested: "已請求匯入。這可能會花一點時間" +importRequested: "已請求匯入。這可能會花一點時間。" lists: "清單" noLists: "你沒有任何清單" note: "貼文" @@ -80,10 +79,10 @@ manageLists: "管理清單" error: "錯誤" somethingHappened: "發生錯誤" retry: "重試" -pageLoadError: "載入頁面失敗" -pageLoadErrorDescription: "這通常是因為網路錯誤或是瀏覽器快取殘留的原因。請先清除瀏覽器快取,稍後再重試" +pageLoadError: "載入頁面失敗。" +pageLoadErrorDescription: "這通常是因為網路錯誤或是瀏覽器快取殘留的原因。請先清除瀏覽器快取,稍後再重試。" serverIsDead: "伺服器沒有回應。請稍等片刻,然後重試。" -youShouldUpgradeClient: "請重新載入以使用新版本的客戶端顯示此頁面" +youShouldUpgradeClient: "請重新載入以使用新版本的客戶端顯示此頁面。" enterListName: "輸入清單名稱" privacy: "隱私" makeFollowManuallyApprove: "手動審核追隨請求" @@ -96,9 +95,9 @@ followRequestPending: "追隨許可批准中" enterEmoji: "輸入表情符號" renote: "轉發" unrenote: "取消轉發" -renoted: "轉傳成功" +renoted: "已轉發。" cantRenote: "無法轉發此貼文。" -cantReRenote: "無法轉傳之前已經轉傳過的內容。" +cantReRenote: "無法轉發之前已經轉發過的內容。" quote: "引用" pinnedNote: "已置頂的貼文" pinned: "置頂" @@ -106,7 +105,9 @@ you: "您" clickToShow: "按一下以顯示" sensitive: "敏感內容" add: "新增" -reaction: "情感" +reaction: "反應" +enableEmojiReaction: "啟用表情符號反應" +showEmojisInReactionNotifications: "在反應通知中顯示表情符號" reactionSetting: "在選擇器中顯示反應" reactionSettingDescription2: "拖動以重新列序,點擊以刪除,按下 + 添加。" rememberNoteVisibility: "記住貼文可見性" @@ -116,6 +117,8 @@ unmarkAsSensitive: "取消標記為敏感內容" enterFileName: "請輸入檔案名稱" mute: "靜音" unmute: "解除靜音" +renoteMute: "靜音轉發貼文" +renoteUnmute: "解除靜音轉發貼文" block: "封鎖" unblock: "解除封鎖" suspend: "凍結" @@ -137,24 +140,24 @@ emojiUrl: "表情符號URL" addEmoji: "加入表情符號" settingGuide: "推薦設定" cacheRemoteFiles: "快取遠端檔案" -cacheRemoteFilesDescription: "禁用此設定會停止遠端檔案的緩存,從而節省儲存空間,但資料會因直接連線從而產生額外連接數據。" -flagAsBot: "此使用者是機器人" -flagAsBotDescription: "如果本帳戶是由程式控制,請啟用此選項。啟用後,會作為標示幫助其他開發者防止機器人之間產生無限互動的行為,並會調整Misskey內部系統將本帳戶識別為機器人" -flagAsCat: "此使用者是貓" -flagAsCatDescription: "如果想將本帳戶標示為一隻貓,請開啟此標示" -flagShowTimelineReplies: "在時間軸上顯示貼文的回覆" +cacheRemoteFilesDescription: "禁用此設定會停止遠端檔案的緩存,從而節省儲存空間,但資料會因直接連線從而產生額外數據花費。" +flagAsBot: "標記此帳號是機器人" +flagAsBotDescription: "如果本帳戶是由程式控制,請啟用此選項。啟用後,會作為標示幫助其他開發者防止機器人之間產生無限互動的行為,並會調整Firefish內部系統將本帳戶識別為機器人。" +flagAsCat: "你是喵咪嗎?w😺" +flagAsCatDescription: "如果想將本帳戶標示為一隻貓,請開啟此標示!" +flagShowTimelineReplies: "在時間線上顯示貼文的回覆" flagShowTimelineRepliesDescription: "啟用時,時間線除了顯示用戶的貼文以外,還會顯示用戶對其他貼文的回覆。" -autoAcceptFollowed: "自動追隨中使用者的追隨請求" +autoAcceptFollowed: "自動准予追隨中使用者的追隨請求" addAccount: "添加帳戶" loginFailed: "登入失敗" -showOnRemote: "轉到所在實例顯示" +showOnRemote: "轉到所在伺服器顯示" general: "一般" wallpaper: "桌布" setWallpaper: "設定桌布" removeWallpaper: "移除桌布" searchWith: "搜尋: {q}" youHaveNoLists: "你沒有任何清單" -followConfirm: "你真的要追隨{name}嗎?" +followConfirm: "你真的要追隨 「{name}」 嗎?" proxyAccount: "代理帳戶" proxyAccountDescription: "代理帳戶是在某些情況下充當其他伺服器用戶的帳戶。例如,當使用者將一個來自其他伺服器的帳戶放在列表中時,由於沒有其他使用者追蹤該帳戶,該指令不會傳送到該伺服器上,因此會由代理帳戶追蹤。" host: "主機" @@ -162,8 +165,8 @@ selectUser: "選取使用者" recipient: "收件人" annotation: "註解" federation: "站台聯邦" -instances: "實例" -registeredAt: "初次觀測" +instances: "伺服器" +registeredAt: "初次註冊" latestRequestSentAt: "上次發送的請求" latestRequestReceivedAt: "上次收到的請求" latestStatus: "最後狀態" @@ -172,26 +175,25 @@ charts: "圖表" perHour: "每小時" perDay: "每日" stopActivityDelivery: "停止發送活動" -blockThisInstance: "封鎖此實例" +blockThisInstance: "封鎖此伺服器" operations: "操作" software: "軟體" version: "版本" metadata: "元資料" -withNFiles: "{n}個檔案" monitor: "監視器" jobQueue: "佇列" cpuAndMemory: "CPU及記憶體用量" network: "網路" disk: "硬碟" -instanceInfo: "實例資訊" +instanceInfo: "伺服器資訊" statistics: "統計" clearQueue: "清除佇列" clearQueueConfirmTitle: "確定要清除佇列嗎?" clearQueueConfirmText: "未發佈的貼文將不會發佈。您通常不需要確認。" clearCachedFiles: "清除快取資料" clearCachedFilesConfirm: "確定要清除所有遠端暫存資料嗎?" -blockedInstances: "已封鎖的實例" -blockedInstancesDescription: "請逐行輸入需要封鎖的實例。已封鎖的實例將無法與本實例進行通訊。" +blockedInstances: "已封鎖的伺服器" +blockedInstancesDescription: "請逐行輸入需要封鎖的伺服器。已封鎖的伺服器將無法與本伺服器進行通訊。" muteAndBlock: "靜音和封鎖" mutedUsers: "已靜音用戶" blockedUsers: "已封鎖用戶" @@ -199,7 +201,7 @@ noUsers: "沒有任何使用者" editProfile: "編輯個人檔案" noteDeleteConfirm: "確定刪除此貼文嗎?" pinLimitExceeded: "不能置頂更多貼文了" -intro: "Misskey 部署完成!請建立管理員帳戶。" +intro: "Firefish 部署完成!請建立管理員帳戶。" done: "完成" processing: "處理中" preview: "預覽" @@ -214,9 +216,9 @@ all: "全部" subscribing: "訂閱中" publishing: "直播中" notResponding: "沒有回應" -instanceFollowing: "追蹤實例" -instanceFollowers: "追蹤實例" -instanceUsers: "用戶" +instanceFollowing: "追蹤伺服器" +instanceFollowers: "伺服器的追蹤者" +instanceUsers: "此伺服器的用戶" changePassword: "修改密碼" security: "安全性" retypedNotMatch: "兩次輸入不一致。" @@ -232,19 +234,19 @@ lookup: "查詢" announcements: "公告" imageUrl: "圖片URL" remove: "刪除" -removed: "已刪除" +removed: "已成功刪除" removeAreYouSure: "確定要刪掉「{x}」嗎?" deleteAreYouSure: "確定要刪掉「{x}」嗎?" resetAreYouSure: "確定要重設嗎?" saved: "已儲存" -messaging: "傳送訊息" +messaging: "訊息" upload: "上傳" keepOriginalUploading: "保留原圖" -keepOriginalUploadingDescription: "上傳圖片時保留原始圖片。關閉時,瀏覽器會在上傳時生成一張用於web發布的圖片。" +keepOriginalUploadingDescription: "上傳圖片時保留原始圖片。關閉時,瀏覽器會在上傳時自動產生用於貼文發布的圖片。" fromDrive: "從雲端空間" -fromUrl: "從URL" +fromUrl: "從網址" uploadFromUrl: "從網址上傳" -uploadFromUrlDescription: "您要上傳的文件的URL" +uploadFromUrlDescription: "您要上傳的文件的網址" uploadFromUrlRequested: "已請求上傳" uploadFromUrlMayTakeTime: "還需要一些時間才能完成上傳。" explore: "探索" @@ -256,7 +258,7 @@ agreeTo: "我同意{0}" tos: "使用條款" start: "開始" home: "首頁" -remoteUserCaution: "由於該使用者來自遠端實例,因此資訊可能非即時的。" +remoteUserCaution: "由於該使用者來自遠端實例,因此資料可能是非即時的。" activity: "動態" images: "圖片" birthday: "生日" @@ -265,12 +267,12 @@ registeredDate: "註冊日期" location: "位置" theme: "外觀主題" themeForLightMode: "在淺色模式下使用的主題" -themeForDarkMode: "在黑暗模式下使用的主題" +themeForDarkMode: "在闇黑模式下使用的主題" light: "淺色" -dark: "黑暗" +dark: "闇黑" lightThemes: "明亮主題" -darkThemes: "黑暗主題" -syncDeviceDarkMode: "將黑暗模式與設備設置同步" +darkThemes: "闇黑主題" +syncDeviceDarkMode: "闇黑模式使用裝置設定" drive: "雲端硬碟" fileName: "檔案名稱" selectFile: "選擇檔案" @@ -279,19 +281,19 @@ selectFolder: "選擇資料夾" selectFolders: "選擇資料夾" renameFile: "重新命名檔案" folderName: "資料夾名稱" -createFolder: "新增資料夾" +createFolder: "創建資料夾" renameFolder: "重新命名資料夾" deleteFolder: "刪除資料夾" addFile: "加入附件" -emptyDrive: "雲端硬碟為空" -emptyFolder: "資料夾為空" +emptyDrive: "你的雲端硬碟沒有任何東西( ̄▽ ̄)\"" +emptyFolder: "資料夾裡面沒有東西(⊙_⊙;)" unableToDelete: "無法刪除" inputNewFileName: "輸入檔案名稱" -inputNewDescription: "請輸入新標題 " +inputNewDescription: "請輸入新標題" inputNewFolderName: "輸入新資料夾的名稱" circularReferenceFolder: "目標文件夾是您要移動的文件夾的子文件夾。" hasChildFilesOrFolders: "此文件夾不是空的,無法刪除。" -copyUrl: "複製URL" +copyUrl: "複製網址" rename: "重新命名" avatar: "大頭貼" banner: "橫幅" @@ -302,15 +304,15 @@ reload: "重新整理" doNothing: "無視" reloadConfirm: "確定要重新整理嗎?" watch: "關注" -unwatch: "取消追隨" +unwatch: "取消關注" accept: "接受" reject: "拒絕" normal: "正常" -instanceName: "實例名稱" -instanceDescription: "實例說明" +instanceName: "伺服器名稱" +instanceDescription: "伺服器說明" maintainerName: "管理員名稱" maintainerEmail: "管理員郵箱" -tosUrl: "服務條款URL" +tosUrl: "服務條款網址" thisYear: "本年" thisMonth: "本月" today: "本日" @@ -320,24 +322,24 @@ yearX: "{year}年" pages: "頁面" integration: "整合" connectService: "己連結" -disconnectService: "己斷開 " -enableLocalTimeline: "開啟本地時間軸" -enableGlobalTimeline: "啟用公開時間軸" -disablingTimelinesInfo: "即使您關閉了時間線功能,管理員和協調人仍可以繼續使用,以方便您。" +disconnectService: "己斷開" +enableLocalTimeline: "開啟本地時間線" +enableGlobalTimeline: "啟用公開時間線" +disablingTimelinesInfo: "即使您關閉了時間線功能,管理員和版主始終可以訪問所有的時間線。" registration: "註冊" enableRegistration: "開啟新使用者註冊" invite: "邀請" driveCapacityPerLocalAccount: "每個本地用戶的雲端空間大小" driveCapacityPerRemoteAccount: "每個非本地用戶的雲端容量" -inMb: "以Mbps為單位" -iconUrl: "圖像URL" -bannerUrl: "橫幅圖像URL" -backgroundImageUrl: "背景圖片的來源網址 " +inMb: "以MB為單位" +iconUrl: "圖標網址" +bannerUrl: "橫幅圖像網址" +backgroundImageUrl: "背景圖片的來源網址" basicInfo: "基本資訊" pinnedUsers: "置頂用戶" -pinnedUsersDescription: "在「發現」頁面中使用換行標記想要置頂的使用者。" -pinnedPages: "釘選頁面" -pinnedPagesDescription: "輸入要固定至實例首頁的頁面路徑,以換行符分隔。" +pinnedUsersDescription: "在「探索」頁面中使用換行標記想要置頂的使用者。" +pinnedPages: "已釘選的頁面" +pinnedPagesDescription: "輸入要固定至伺服器首頁的頁面路徑,一行一個。" pinnedClipId: "置頂的摘錄ID" pinnedNotes: "已置頂的貼文" hcaptcha: "hCaptcha" @@ -355,7 +357,7 @@ name: "名稱" antennaSource: "接收來源" antennaKeywords: "包含關鍵字" antennaExcludeKeywords: "排除關鍵字" -antennaKeywordsDescription: "用空格分隔指定AND、用換行符分隔指定OR" +antennaKeywordsDescription: "用空格分隔指定AND、用換行符分隔指定OR。" notifyAntenna: "通知有新貼文" withFileAntenna: "僅帶有附件的貼文" enableServiceworker: "開啟 ServiceWorker" @@ -378,7 +380,7 @@ exploreFediverse: "探索聯邦世界" popularTags: "熱門標籤" userList: "清單" about: "資訊" -aboutMisskey: "關於 Misskey" +aboutFirefish: "關於 Firefish" administrator: "管理員" token: "權杖" twoStepAuthentication: "兩階段驗證" @@ -396,7 +398,7 @@ newPasswordIs: "新密碼為「{password}」" reduceUiAnimation: "減少介面的動態視覺" share: "分享" notFound: "找不到" -notFoundDescription: "找不到與指定URL回應的頁面" +notFoundDescription: "找不到與指定URL回應的頁面。" uploadFolder: "預設上傳資料夾" cacheClear: "清除快取" markAsReadAllNotifications: "標記所有通知為已讀" @@ -431,10 +433,10 @@ onlyOneFileCanBeAttached: "只能加入一個附件" signinRequired: "請先登入" invitations: "邀請" invitationCode: "邀請碼" -checking: "確認中" +checking: "確認中..." available: "可用的" unavailable: "不可用的" -usernameInvalidFormat: "可使用大小寫英文字母、數字和底線" +usernameInvalidFormat: "可使用大小寫英文字母、數字和底線。" tooShort: "過短" tooLong: "過長" weakPassword: "密碼強度過弱" @@ -457,7 +459,7 @@ joinOrCreateGroup: "請加入現有群組,或創建新群組。" noHistory: "沒有歷史紀錄" signinHistory: "登入歷史" disableAnimatedMfm: "禁用MFM動畫" -doing: "正在進行" +doing: "正在處理..." category: "類別" tags: "標籤" docSource: "文件來源" @@ -480,13 +482,13 @@ promotion: "推廣" promote: "推廣" numberOfDays: "有效天數" hideThisNote: "隱藏此貼文" -showFeaturedNotesInTimeline: "在時間軸上顯示熱門推薦" +showFeaturedNotesInTimeline: "在時間線上顯示熱門推薦" objectStorage: "Object Storage (物件儲存)" useObjectStorage: "使用Object Storage" -objectStorageBaseUrl: "Base URL" -objectStorageBaseUrlDesc: "引用時的URL。如果您使用的是CDN或反向代理,请指定其URL,例如S3:“https://.s3.amazonaws.com”,GCS:“https://storage.googleapis.com/”" +objectStorageBaseUrl: "根URL" +objectStorageBaseUrlDesc: "引用時的URL。如果你使用的是CDN或反向代理,請指定其網址URL。\n例如S3:“https://.s3.amazonaws.com”,GCS:“https://storage.googleapis.com/”。" objectStorageBucket: "儲存空間(Bucket)" -objectStorageBucketDesc: "請指定您正在使用的服務的存儲桶名稱。 " +objectStorageBucketDesc: "請指定您正在使用的服務的存儲桶名稱。" objectStoragePrefix: "前綴" objectStoragePrefixDesc: "它存儲在此前綴目錄下。" objectStorageEndpoint: "端點(Endpoint)" @@ -500,7 +502,7 @@ objectStorageUseProxyDesc: "如果不使用代理進行API連接,請關閉" objectStorageSetPublicRead: "上傳時設定為\"public-read\"" serverLogs: "伺服器日誌" deleteAll: "刪除所有記錄" -showFixedPostForm: "於時間軸頁頂顯示「發送貼文」方框" +showFixedPostForm: "於時間線頁頂顯示「發送貼文」方框" newNoteRecived: "發現新的貼文" sounds: "音效" listen: "聆聽" @@ -524,7 +526,7 @@ sort: "排序" ascendingOrder: "昇冪" descendingOrder: "降冪" scratchpad: "暫存記憶體" -scratchpadDescription: "AiScript控制台為AiScript提供了實驗環境。您可以在此編寫、執行和確認代碼與Misskey互動的结果。" +scratchpadDescription: "AiScript控制台為AiScript提供了實驗環境。您可以在此編寫、執行和確認代碼與Firefish互動的结果。" output: "輸出" script: "腳本" disablePagesScript: "停用頁面的AiScript腳本" @@ -532,8 +534,8 @@ updateRemoteUser: "更新遠端使用者資訊" deleteAllFiles: "刪除所有檔案" deleteAllFilesConfirm: "要删除所有檔案嗎?" removeAllFollowing: "解除所有追蹤" -removeAllFollowingDescription: "解除{host}所有的追蹤。在實例不再存在時執行。" -userSuspended: "該使用者已被停用" +removeAllFollowingDescription: "解除{host}所有的追蹤。在伺服器不再存在時執行。" +userSuspended: "此使用者已被停用。" userSilenced: "該用戶已被禁言。" yourAccountSuspendedTitle: "帳戶已被凍結" yourAccountSuspendedDescription: "由於違反了伺服器的服務條款或其他原因,該帳戶已被凍結。 您可以與管理員連繫以了解更多訊息。 請不要創建一個新的帳戶。" @@ -544,7 +546,7 @@ relays: "中繼" addRelay: "新增中繼" inboxUrl: "收件夾URL" addedRelays: "已加入的中繼" -serviceworkerInfo: "您需要啟用推送通知" +serviceworkerInfo: "您需要啟用推送通知。" deletedNote: "已删除的貼文" invisibleNote: "隱藏的貼文" enableInfiniteScroll: "啟用自動滾動頁面模式" @@ -556,8 +558,8 @@ disablePlayer: "關閉播放器" expandTweet: "展開推文" themeEditor: "主題編輯器" description: "描述" -describeFile: "添加標題 " -enterFileDescription: "輸入標題 " +describeFile: "添加標題" +enterFileDescription: "輸入標題" author: "作者" leaveConfirm: "有未保存的更改。要放棄嗎?" manage: "管理" @@ -590,14 +592,14 @@ smtpHost: "主機" smtpPort: "埠" smtpUser: "使用者名稱" smtpPass: "密碼" -emptyToDisableSmtpAuth: "留空使用者名稱和密碼以關閉SMTP驗證。" +emptyToDisableSmtpAuth: "留空使用者名稱及密碼以關閉SMTP驗證" smtpSecure: "在 SMTP 連接中使用隱式 SSL/TLS" -smtpSecureInfo: "使用STARTTLS時關閉。" +smtpSecureInfo: "如使用STARTTLS,請關閉" testEmail: "測試郵件發送" wordMute: "被靜音的文字" regexpError: "正規表達式錯誤" regexpErrorDescription: "{tab} 靜音文字的第 {line} 行的正規表達式有錯誤:" -instanceMute: "實例的靜音" +instanceMute: "伺服器的靜音" userSaysSomething: "{name}說了什麼" makeActive: "啟用" display: "檢視" @@ -610,7 +612,7 @@ database: "資料庫" channel: "頻道" create: "新增" notificationSetting: "通知設定" -notificationSettingDesc: "選擇顯示通知的類型" +notificationSettingDesc: "選擇顯示通知的類型。" useGlobalSetting: "使用全域設定" useGlobalSettingDesc: "啟用時,將使用帳戶通知設定。停用時,則可以單獨設定。" other: "其他" @@ -628,19 +630,19 @@ abuseReported: "回報已送出。感謝您的報告。" reporter: "檢舉者" reporteeOrigin: "檢舉來源" reporterOrigin: "檢舉者來源" -forwardReport: "將報告轉送給遠端實例" -forwardReportIsAnonymous: "在遠端實例上看不到您的資訊,顯示的報告者是匿名的系统帳戶。" +forwardReport: "將報告轉送給遠端伺服器" +forwardReportIsAnonymous: "在遠端伺服器上看不到您的資訊,顯示的報告者是匿名的系統帳戶。" send: "發送" abuseMarkAsResolved: "處理完畢" openInNewTab: "在新分頁中開啟" openInSideView: "在側欄中開啟" defaultNavigationBehaviour: "默認導航" -editTheseSettingsMayBreakAccount: "修改這些設定可能會毀損您的帳戶" -instanceTicker: "貼文的實例來源" +editTheseSettingsMayBreakAccount: "修改這些設定可能會毀損你的帳戶。" +instanceTicker: "貼文的伺服器資訊" waitingFor: "等待{x}" random: "隨機" system: "系統" -switchUi: "切換界面" +switchUi: "界面" desktop: "桌面" clip: "摘錄" createNew: "新建" @@ -649,7 +651,7 @@ createNewClip: "建立新摘錄" unclip: "解除摘錄" confirmToUnclipAlreadyClippedNote: "此貼文已包含在摘錄「{name}」中。 你想將貼文從這個摘錄中排除嗎?" public: "公開" -i18nInfo: "Calckey已經被志願者們翻譯成各種語言版本,如果想要幫忙的話,可以進入{link}幫助翻譯。" +i18nInfo: "Firefish已經被志願者們翻譯成各種語言版本,如果想要幫忙的話,可以進入{link}幫助翻譯。" manageAccessTokens: "管理存取權杖" accountInfo: "帳戶資訊" notesCount: "貼文數量" @@ -659,8 +661,8 @@ repliedCount: "回覆數量" renotedCount: "轉發次數" followingCount: "正在跟隨的用戶數量" followersCount: "跟隨者數量" -sentReactionsCount: "情感發送次數" -receivedReactionsCount: "情感收到次數" +sentReactionsCount: "反應發送次數" +receivedReactionsCount: "反應收到次數" pollVotesCount: "已統計的投票數" pollVotedCount: "已投票數" yes: "確定" @@ -686,7 +688,7 @@ experimentalFeatures: "實驗中的功能" developer: "開發者" makeExplorable: "使自己的帳戶能夠在“探索”頁面中顯示" makeExplorableDescription: "如果關閉,帳戶將不會被顯示在\"探索\"頁面中。" -showGapBetweenNotesInTimeline: "分開顯示時間線上的貼文。" +showGapBetweenNotesInTimeline: "分開顯示時間線上的貼文" duplicate: "複製" left: "左" center: "置中" @@ -700,7 +702,8 @@ onlineUsersCount: "{n}人正在線上" nUsers: "{n}用戶" nNotes: "{n}貼文" sendErrorReports: "傳送錯誤報告" -sendErrorReportsDescription: "啟用後,問題報告將傳送至開發者以提升軟體品質。問題報告可能包括OS版本,瀏覽器類型,行為歷史記錄等。" +sendErrorReportsDescription: "開啟後,錯誤出現時將會與 Firefish 分享詳細紀錄,對於 Firefish 的開發會有非常大的幫助。\n + 這將包括您的操作系統版本、使用的瀏覽器、您在 Firefish 中的活動等資料。" myTheme: "我的佈景主題" backgroundColor: "背景" accentColor: "重點色彩" @@ -724,7 +727,7 @@ capacity: "容量" inUse: "已使用" editCode: "編輯代碼" apply: "套用" -receiveAnnouncementFromInstance: "接收由本實例發出的電郵通知" +receiveAnnouncementFromInstance: "接收由本伺服器發出的電郵通知" emailNotification: "郵件通知" publish: "發佈" inChannelSearch: "頻道内搜尋" @@ -752,7 +755,7 @@ active: "最近活躍" offline: "離線" notRecommended: "不推薦" botProtection: "Bot防護" -instanceBlocking: "已封鎖的實例" +instanceBlocking: "聯邦管理" selectAccount: "選擇帳戶" switchAccount: "切換帳戶" enabled: "已啟用" @@ -777,7 +780,7 @@ priority: "優先級" high: "高" middle: "中" low: "低" -emailNotConfiguredWarning: "沒有設定電子郵件地址" +emailNotConfiguredWarning: "沒有設定電郵地址。" ratio: "%" previewNoteText: "預覽文本" customCss: "自定義 CSS" @@ -791,7 +794,7 @@ hashtags: "#tag" troubleshooting: "故障排除" useBlurEffect: "在 UI 上使用模糊效果" learnMore: "更多資訊" -misskeyUpdated: "Misskey 更新完成!" +misskeyUpdated: "Firefish 更新完成!" whatIsNew: "顯示更新資訊" translate: "翻譯" translatedFrom: "從 {x} 翻譯" @@ -813,11 +816,11 @@ controlPanel: "控制台" manageAccounts: "管理帳戶" makeReactionsPublic: "將反應設為公開" makeReactionsPublicDescription: "將您做過的反應設為公開可見。" -classic: "經典" +classic: "置中" muteThread: "將貼文串設為靜音" unmuteThread: "將貼文串的靜音解除" ffVisibility: "連接的公開範圍" -ffVisibilityDescription: "您可以設定您的關注/關注者資訊的公開範圍" +ffVisibilityDescription: "您可以設定您的關注/關注者資訊的公開範圍。" continueThread: "查看更多貼文" deleteAccountConfirm: "將要刪除帳戶。是否確定?" incorrectPassword: "密碼錯誤。" @@ -836,9 +839,9 @@ themeColor: "主題顏色" size: "大小" numberOfColumn: "列數" searchByGoogle: "搜尋" -instanceDefaultLightTheme: "實例預設的淺色主題" -instanceDefaultDarkTheme: "實例預設的深色主題" -instanceDefaultThemeDescription: "輸入物件形式的主题代碼" +instanceDefaultLightTheme: "伺服器預設的淺色主題" +instanceDefaultDarkTheme: "伺服器預設的深色主題" +instanceDefaultThemeDescription: "輸入物件形式的主題代碼。" mutePeriod: "靜音的期限" indefinitely: "無期限" tenMinutes: "10分鐘" @@ -860,8 +863,8 @@ check: "檢查" driveCapOverrideLabel: "更改這個使用者的雲端硬碟容量上限" driveCapOverrideCaption: "如果指定0以下的值,就會被取消。" requireAdminForView: "必須以管理者帳號登入才可以檢視。" -isSystemAccount: "由系統自動建立與管理的帳號。" -typeToConfirm: "要執行這項操作,請輸入 {x} " +isSystemAccount: "該帳號由系統自動創建並運行。 千千萬萬不要審核、編輯、刪除或以其他方式修改此帳戶,否則可能會破壞您的伺服器。" +typeToConfirm: "要執行這項操作,請輸入 {x}" deleteAccount: "刪除帳號" document: "文件" numberOfPageCache: "快取頁面數" @@ -872,7 +875,7 @@ statusbar: "狀態列" pleaseSelect: "請選擇" reverse: "翻轉" colored: "彩色" -refreshInterval: "更新間隔" +refreshInterval: "更新間隔 " label: "標籤" type: "類型" speed: "速度" @@ -886,12 +889,18 @@ cannotUploadBecauseInappropriate: "由於判定可能包含不適當的內容, cannotUploadBecauseNoFreeSpace: "由於雲端硬碟沒有可用空間,因此無法上傳。" beta: "Beta" enableAutoSensitive: "自動NSFW判定" -enableAutoSensitiveDescription: "如果可用,請利用機器學習在媒體上自動設置 NSFW 旗標。 即使關閉此功能,依實例而定也可能會自動設置。" +enableAutoSensitiveDescription: "如可用,請利用機器學習在媒體上自動設置 NSFW 旗標。 即使關閉此功能,依伺服器而定也可能會自動設置。" activeEmailValidationDescription: "積極地驗證用戶的電子郵件地址,判斷它是否為免洗地址,或者它是否可以通信。 若關閉,則只會檢查字元是否正確。" navbar: "導覽列" shuffle: "隨機" account: "帳戶" -move: "移動 " +move: "移動" +customKaTeXMacro: "自定義 KaTeX 宏" +customKaTeXMacroDescription: "使用宏來輕鬆的輸入數學表達式吧!宏的用法與 LaTeX 中的命令定義相同。你可以使用 \\newcommand{\\ + name}{content} 或 \\newcommand{\\name}[number of arguments]{content} 來輸入數學表達式。舉個例子,\\ + newcommand{\\add}[2]{#1 + #2} 會將 \\add{3}{foo} 展開為 3 + foo。此外,宏名稱外的花括號 {} 可以被替換為圓括號 + () 和方括號 [],這會影響用於參數的括號。每行只能夠定義一個宏,無法在中間換行,且無效的行將被忽略。只支持簡單字符串替換功能,不支持高級語法,如條件分支等。" +enableCustomKaTeXMacro: "啟用自定義 KaTeX 宏" _sensitiveMediaDetection: description: "您可以使用機器學習自動檢測敏感媒體並將其用於審核。 伺服器的負荷會稍微增加。" sensitivity: "檢測敏感度" @@ -923,11 +932,11 @@ _accountDelete: inProgress: "正在刪除" _ad: back: "返回" - reduceFrequencyOfThisAd: "降低此廣告的頻率 " + reduceFrequencyOfThisAd: "降低此廣告的頻率" _forgotPassword: enterEmail: "請輸入您的帳戶註冊的電子郵件地址。 密碼重置連結將被發送到該電子郵件地址。" - ifNoEmail: "如果您還沒有註冊您的電子郵件地址,請聯繫管理員。 " - contactAdmin: "此實例不支持電子郵件,請聯繫您的管理員重置您的密碼。 " + ifNoEmail: "如果您還沒有註冊您的電子郵件地址,請聯繫管理員。" + contactAdmin: "此伺服器不支援使用電郵,請聯繫您的管理員重置你的密碼。" _gallery: my: "我的貼文" liked: "喜歡的貼文" @@ -959,22 +968,28 @@ _preferencesBackups: createdAt: "建立日期:{date} {time}" updatedAt: "更新日期:{date} {time}" cannotLoad: "無法讀取" - invalidFile: "檔案形式錯誤。" + invalidFile: "無效的檔案格式" _registry: scope: "範圍" key: "機碼" keys: "機碼" domain: "域" createKey: "新增機碼" -_aboutMisskey: - about: "Misskey是由syuilo自2014年起開發的開源軟體。" +_aboutFirefish: + about: "Firefish是由ThatOneCalculator自2022年起開發的Misskey分支。" contributors: "主要貢獻者" allContributors: "全體貢獻人員" source: "原始碼" - translation: "翻譯Misskey" - donate: "贊助Misskey" + translation: "翻譯Firefish" + donate: "贊助Firefish" morePatrons: "還有許許多多幫助我們的其他人,非常感謝你們。 🥰" patrons: "贊助者" + patronsList: 按時間順序列出,而不是按贊助規模列出。使用上面的連結贊助,在這裡獲得顯示您名字的機會! + sponsors: Firefish 贊助者們 + donateTitle: 覺得 Firefish 棒嗎? + pleaseDonateToFirefish: 請考慮向 Firefish 贊助以支持其發展。 + pleaseDonateToHost: 還請考慮捐贈給您在使用的伺服器 {host},以支援龐大的運營成本。 + donateHost: 贊助給 {host} _nsfw: respect: "隱藏敏感內容" ignore: "不隱藏敏感內容" @@ -982,7 +997,7 @@ _nsfw: _mfm: cheatSheet: "MFM代碼小抄" intro: "MFM是Misskey專用的標記語言,可以在Misskey中的各個位置使用。 您可以這裏看到MFM可用語法列表。" - dummy: "Misskey拓展了Fediverse的世界" + dummy: "Firefish拓展了Fediverse的世界" mention: "提及" mentionDescription: "透過 @+用戶名 來標示特定使用者。" hashtag: "#tag" @@ -990,7 +1005,7 @@ _mfm: url: "URL" urlDescription: "可以展示URL位址。" link: "鏈接" - linkDescription: "您可以將特定範圍的文章與 URL 相關聯。 " + linkDescription: "您可以將特定範圍的文章與 URL 相關聯。" bold: "粗體" boldDescription: "可以將文字顯示为粗體来強調。" small: "縮小" @@ -1002,13 +1017,13 @@ _mfm: blockCode: "程式碼(區塊)" blockCodeDescription: "在區塊中用高亮度顯示,例如複數行的程式碼語法。" inlineMath: "數學公式(內嵌)" - inlineMathDescription: "顯示內嵌的KaTex數學公式。" + inlineMathDescription: "顯示內嵌的KaTeX數學公式" blockMath: "數學公式(方塊)" - blockMathDescription: "以區塊顯示複數行的KaTex數學式。" + blockMathDescription: "以區塊顯示KaTeX數學式" quote: "引用" quoteDescription: "可以用來表示引用的内容。" emoji: "自訂表情符號" - emojiDescription: "您可以通過將自定義表情符號名稱括在冒號中來顯示自定義表情符號。 " + emojiDescription: "您可以通過將自定義表情符號名稱括在冒號中來顯示自定義表情符號。" search: "搜尋" searchDescription: "您可以顯示所輸入的搜索框。" flip: "翻轉" @@ -1036,7 +1051,7 @@ _mfm: blur: "模糊" blurDescription: "產生模糊效果。将游標放在上面即可將内容顯示出來。" font: "字型" - fontDescription: "您可以設定顯示內容的字型" + fontDescription: "您可以設定顯示內容的字型。" rainbow: "彩虹" rainbowDescription: "用彩虹色來顯示內容。" sparkle: "閃閃發光" @@ -1045,6 +1060,14 @@ _mfm: rotateDescription: "以指定的角度旋轉。" plain: "簡潔" plainDescription: "停用全部的內部語法。" + play: 播放 MFM + stop: 暫停MFM + warn: MFM 可能包含快速移動或顯眼的動畫 + position: 位置 + alwaysPlay: 自動播放所有MFM動畫 + positionDescription: 按指定數量移動內容。 + advancedDescription: 如果禁用,則僅允許基本標記,除非正在播放 MFM 動畫 + advanced: 高級MFM _instanceTicker: none: "隱藏" remote: "向遠端使用者顯示" @@ -1053,6 +1076,7 @@ _serverDisconnectedBehavior: reload: "自動重載" dialog: "彈出式警告" quiet: "非侵入式警告" + nothing: 無 _channel: create: "建立頻道" edit: "編輯頻道" @@ -1063,6 +1087,8 @@ _channel: following: "關注中" usersCount: "有{n}人參與" notesCount: "有{n}個貼文" + nameAndDescription: "名稱與說明" + nameOnly: "僅名稱" _menuDisplay: sideFull: "側向" sideIcon: "側向(圖示)" @@ -1072,16 +1098,16 @@ _wordMute: muteWords: "加入靜音文字" muteWordsDescription: "用空格分隔指定AND,用換行分隔指定OR。" muteWordsDescription2: "將關鍵字用斜線括起來表示正規表達式。" - softDescription: "隱藏時間軸中指定條件的貼文。" - hardDescription: "具有指定條件的貼文將不添加到時間軸。 即使您更改條件,未被添加的貼文也會被排除在外。" + softDescription: "隱藏時間線中指定條件的貼文。" + hardDescription: "具有指定條件的貼文將不添加到時間線。 即使您更改條件,未被添加的貼文也會被排除在外。" soft: "軟性靜音" hard: "硬性靜音" mutedNotes: "已靜音的貼文" _instanceMute: - instanceMuteDescription: "包括對被靜音實例上的用戶的回覆,被設定的實例上所有貼文及轉發都會被靜音。" + instanceMuteDescription: "包括對被靜音伺服器上的用戶的回覆,被設定的伺服器上所有貼文及轉發都會被靜音。" instanceMuteDescription2: "設定時以換行進行分隔" - title: "被設定的實例,貼文將被隱藏。" - heading: "將實例靜音" + title: "被設定的伺服器,貼文將被隱藏。" + heading: "將會被靜音的伺服器" _theme: explore: "取得佈景主題" install: "安裝佈景主題" @@ -1099,13 +1125,13 @@ _theme: constant: "常數" defaultValue: "預設值" color: "顏色" - refProp: "查看屬性 " + refProp: "查看屬性" refConst: "查看常數" key: "按鍵" func: "函数" funcKind: "功能類型" argument: "參數" - basedProp: "要基於的屬性的名稱 " + basedProp: "要基於的屬性的名稱" alpha: "透明度" darken: "暗度" lighten: "亮度" @@ -1121,9 +1147,9 @@ _theme: panel: "面板" shadow: "陰影" header: "標題" - navBg: "側邊欄的背景 " + navBg: "側邊欄的背景" navFg: "側邊欄的文字" - navHoverFg: "側邊欄文字(懸停) " + navHoverFg: "側邊欄文字(懸停)" navActive: "側邊欄文本 (活動)" navIndicator: "側邊欄指示符" link: "鏈接" @@ -1180,30 +1206,30 @@ _time: hour: "小時" day: "日" _tutorial: - title: "如何使用Calckey" - step1_1: "欢迎!" - step1_2: "让我们把你安排好。你很快就会启动并运行!" - step2_1: "首先,请完成您的个人资料。" - step2_2: "通过提供一些关于你自己的信息,其他人会更容易了解他们是否想看到你的帖子或关注你。" - step3_1: "现在是时候跟随一些人了!" - step3_2: "你的主页和社交馈送是基于你所关注的人,所以试着先关注几个账户。{n点击个人资料右上角的加号圈就可以关注它。" - step4_1: "让我们出去找你。" - step4_2: "对于他们的第一条信息,有些人喜欢做{introduction}或一个简单的 \"hello world!\"" - step5_1: "时间限制,到处是时间限制!" - step5_2: "您的实例已启用各种时间线的{timelines}。" - step5_3: "主{icon}时间线是你可以看到你的订阅者的帖子的时间线。" - step5_4: "本地{icon}时间线是你可以看到实例中所有其他用户的信息的时间线。" - step5_5: "推荐的{icon}时间线 - 是时间轴,你可以看到管理员推荐的实例的信息" - step5_6: "社交{icon}时间线显示来自你的订阅者朋友的信息。" - step5_7: "全球{icon}时间线是你可以看到来自所有其他连接的实例的消息。" - step6_1: "那么,这里是什么地方?" - step6_2: "好吧,你不只是加入卡尔基。你已经加入了Fediverse的一个门户,这是一个由成千上万台服务器组成的互联网络,被称为 \"实例\"" - step6_3: "每个服务器的工作方式不同,并不是所有的服务器都运行Calckey。但这个人确实如此! 这有点复杂,但你很快就会明白的。" - step6_4: "现在去学习并享受乐趣!" + title: "如何使用Firefish" + step1_1: "歡迎!" + step1_2: "讓我們把你安排好。你很快就會啟動並運行!" + step2_1: "首先,請完成你的個人資料。" + step2_2: "通過提供一些關於你自己的資料,其他人會更容易了解他們是否想看到你的貼文或關注你。" + step3_1: "現在是時候追隨一些人了!" + step3_2: "你的主頁和社交時間線是基於你所追蹤的人,所以試著先追蹤幾個帳戶。\n點擊個人資料右上角的加號圈就可以關注它。" + step4_1: "讓我們出去找你。" + step4_2: "對於他們的第一條信息,有些人喜歡做 {introduction} 或一個簡單的 \"hello world!\"" + step5_1: "時間線,到處都是時間線!" + step5_2: "您的伺服器已啟用了{timelines}個時間線。" + step5_3: "首頁 {icon} 時間線是顯示你追蹤的帳號的貼文。" + step5_4: "本地 {icon} 時間線是你可以看到伺服器中所有其他用戶的貼文的時間線。" + step5_5: "社交 {icon} 時間線是你的 首頁時間線 和 本地時間線 的結合體。" + step5_6: "推薦 {icon} 時間線是顯示你的伺服器管理員推薦的貼文。" + step5_7: "全球 {icon} 時間線是顯示來自所有其他連接的伺服器的貼文。" + step6_1: "那麼,這裡是什麼地方?" + step6_2: "你不只是加入Firefish。你已經加入了Fediverse的一個門戶,這是一個由成千上萬台服務器組成的互聯網絡。" + step6_3: "每個服務器也有不同,而並不是所有的服務器都運行Firefish。但這個服務器確實是運行Firefish的! 你可能會覺得有點複雜,但你很快就會明白的。" + step6_4: "現在開始探索吧!" _2fa: - alreadyRegistered: "此設備已經被註冊過了" - registerDevice: "註冊裝置" - registerKey: "註冊鍵" + alreadyRegistered: "你已註冊過一個雙重認證的裝置。" + registerTOTP: "註冊裝置" + registerSecurityKey: "註冊鍵" step1: "首先,在您的設備上安裝二步驗證程式,例如{a}或{b}。" step2: "然後,掃描螢幕上的QR code。" step2Url: "在桌面版應用中,請輸入以下的URL:" @@ -1228,8 +1254,8 @@ _permissions: "write:notes": "撰寫或刪除貼文" "read:notifications": "查看通知" "write:notifications": "編輯通知" - "read:reactions": "查看情感" - "write:reactions": "編輯情感" + "read:reactions": "查看反應" + "write:reactions": "編輯反應" "write:votes": "投票" "read:pages": "顯示頁面" "write:pages": "編輯頁面" @@ -1267,7 +1293,7 @@ _weekday: _widgets: memo: "備忘錄" notifications: "通知" - timeline: "時間軸" + timeline: "時間線" calendar: "行事曆" trends: "發燒貼文" clock: "時鐘" @@ -1278,13 +1304,13 @@ _widgets: digitalClock: "電子時鐘" unixClock: "UNIX時間" federation: "聯邦宇宙" - instanceCloud: "實例雲" + instanceCloud: "伺服器雲端" postForm: "發佈窗口" slideshow: "幻燈片" button: "按鈕" onlineUsers: "線上的用戶" jobQueue: "佇列" - serverMetric: "服務器指標 " + serverMetric: "伺服器指標" aiscript: "AiScript控制台" aichan: "小藍" _cw: @@ -1293,14 +1319,14 @@ _cw: chars: "{count}字元" files: "{count} 個檔案" _poll: - noOnlyOneChoice: "至少需要兩個選項。" + noOnlyOneChoice: "至少需要兩個選項" choiceN: "選擇{n}" noMore: "沒辦法再添加選項了" canMultipleVote: "可以多次投票" expiration: "期限" infinite: "無期限" at: "結束時間" - after: "進度指定 " + after: "在指定時間後結束..." deadlineDate: "截止日期" deadlineTime: "小時" duration: "時長" @@ -1316,9 +1342,9 @@ _poll: remainingSeconds: "{s}秒後截止" _visibility: public: "公開" - publicDescription: "發布給所有用戶 " - home: "首頁" - homeDescription: "僅發送至首頁的時間軸" + publicDescription: "發布給所有用戶" + home: "不在主頁顯示" + homeDescription: "僅發送至首頁的時間線" followers: "追隨者" followersDescription: "僅發送至關注者" specified: "指定使用者" @@ -1328,7 +1354,7 @@ _visibility: _postForm: replyPlaceholder: "回覆此貼文..." quotePlaceholder: "引用此貼文..." - channelPlaceholder: "發佈到頻道" + channelPlaceholder: "發佈到頻道..." _placeholders: a: "今天過得如何?" b: "有什麼新鮮事嗎?" @@ -1340,14 +1366,15 @@ _profile: name: "名稱" username: "使用者名稱" description: "關於我" - youCanIncludeHashtags: "你也可以在「關於我」中加上 #tag" + youCanIncludeHashtags: "你也可以在「關於我」中加上 #tag。" metadata: "進階資訊" metadataEdit: "編輯進階資訊" - metadataDescription: "可以在個人資料中以表格形式顯示其他資訊。" + metadataDescription: "可以在個人資料中以表格形式顯示其他資訊。您可以添加帶有 {rel} 的 {a} 標籤或 {l} 標籤來驗證您個人資料上的鏈接!" metadataLabel: "標籤" metadataContent: "内容" changeAvatar: "更換大頭貼" changeBanner: "變更橫幅圖像" + locationDescription: 如果你先輸入你所在的城市,則會向其他用戶顯示你的當地時間。 _exportOrImport: allNotes: "所有貼文" followingList: "追隨中" @@ -1376,7 +1403,7 @@ _instanceCharts: usersTotal: "總計使用者" notes: "貼文増減" notesTotal: "累計貼文" - ff: "追隨/追隨者的増減" + ff: "追隨/追隨者的増減 " ffTotal: "追隨/追隨者累計" cacheSize: "增加或減少快取用量" cacheSizeTotal: "快取大小總計" @@ -1385,8 +1412,9 @@ _instanceCharts: _timelines: home: "首頁" local: "本地" - social: "社群" + social: "社交" global: "公開" + recommended: 推薦 _pages: newPage: "建立頁面" editPage: "編輯頁面" @@ -1439,7 +1467,7 @@ _pages: post: "發佈窗口" _post: text: "内容" - attachCanvasImage: "附加相簿圖像 " + attachCanvasImage: "附加相簿圖像" canvasId: "畫布ID" textInput: "插入字串" _textInput: @@ -1464,7 +1492,7 @@ _pages: note: "嵌式貼文" _note: id: "貼文ID" - idDescription: "您也可以粘貼筆記 URL 並進行設置。 " + idDescription: "您也可以粘貼筆記 URL 並進行設置。" detailed: "顯示詳細內容" switch: "開關" _switch: @@ -1481,14 +1509,14 @@ _pages: colored: "彩色" action: "按下按鈕後發生的行為" _action: - dialog: "顯示對話框 " + dialog: "顯示對話框" _dialog: content: "内容" resetRandom: "重設亂數" pushEvent: "發送事件" _pushEvent: event: "事件名稱" - message: "按下時顯示的消息 " + message: "按下時顯示的消息" variable: "要發送的變數" no-variable: "沒有" callAiScript: "調用AiScript" @@ -1507,7 +1535,7 @@ _pages: operation: "計算" comparison: "對比" random: "隨機" - value: "數值 " + value: "數值" fn: "函数" text: "文本操作" convert: "轉換" @@ -1517,7 +1545,7 @@ _pages: multiLineText: "字串(多行)" textList: "字串串列" _textList: - info: "請分開每個換行符 " + info: "請分開每個換行符" strLen: "字串長度" _strLen: arg1: "字串" @@ -1596,7 +1624,7 @@ _pages: _if: arg1: "如果" arg2: "如果" - arg3: "除此以外 " + arg3: "除此以外" not: "否" _not: arg1: "否" @@ -1607,7 +1635,7 @@ _pages: _rannum: arg1: "下限" arg2: "上限" - randomPick: "從列表中隨機選擇 " + randomPick: "從列表中隨機選擇" _randomPick: arg1: "清單" dailyRandom: "隨機(使用者每日變化 )" @@ -1617,7 +1645,7 @@ _pages: _dailyRannum: arg1: "下限" arg2: "上限" - dailyRandomPick: "從列表中隨機選擇(使用者每日變化 ) " + dailyRandomPick: "從列表中隨機選擇(使用者每日變化 )" _dailyRandomPick: arg1: "清單" seedRandom: "隨機抽選種子碼" @@ -1660,7 +1688,7 @@ _pages: slots: "欄位" slots-info: "用換行符分隔每個欄位" arg1: "輸出" - for: "重複 " + for: "重複" _for: arg1: "重複次數" arg2: "處理" @@ -1681,7 +1709,7 @@ _relayStatus: accepted: "已通過核准" rejected: "已拒絕" _notification: - fileUploaded: "上傳檔案成功。" + fileUploaded: "上傳檔案成功" youGotMention: "{name}提及到您" youGotReply: "{name}回覆了您" youGotQuote: "{name}引用了您" @@ -1696,7 +1724,7 @@ _notification: pollEnded: "問卷調查已產生結果" emptyPushNotificationMessage: "推送通知已更新" _types: - all: "全部 " + all: "全部" follow: "追隨中" mention: "提及" reply: "回覆" @@ -1707,7 +1735,7 @@ _notification: pollEnded: "問卷調查結束" receiveFollowRequest: "已收到追隨請求" followRequestAccepted: "追隨請求已接受" - groupInvited: "加入社群邀請" + groupInvited: "群組加入邀請" app: "應用程式通知" _actions: followBack: "回關" @@ -1724,18 +1752,150 @@ _deck: swapDown: "往下移動" stackLeft: "向左折疊" popRight: "向右彈出" - profile: "個人檔案" - newProfile: "新建個人檔案" - deleteProfile: "刪除個人檔案" + profile: "工作區" + newProfile: "新增工作區" + renameProfile: "重新命名工作區" + deleteProfile: "刪除工作區" + nameAlreadyExists: "該工作區名稱已經存在。" introduction: "組合欄位來製作屬於自己的介面吧!" introduction2: "您可以隨時透過按畫面右方的 + 來添加欄位。" - widgetsIntroduction: "請從欄位的選單中,選擇「編輯小工具」來添加小工具" + widgetsIntroduction: "請從欄位的選單中,選擇「編輯小工具」來添加小工具。" _columns: main: "主列" widgets: "小工具" notifications: "通知" - tl: "時間軸" + tl: "時間線" antenna: "天線" list: "清單" mentions: "提及" direct: "指定使用者" +secureMode: 安全模式(授權獲取) +instanceSecurity: 伺服器安全性 +privateMode: 私人模式 +allowedInstances: 列入白名單的伺服器 +secureModeInfo: 當從其他伺服器請求時,不要在沒有證據的情況下發回。 +_messaging: + dms: 私訊 + groups: 群組 +manageGroups: 管理群組 +replayTutorial: 重新播放教程 +moveFromLabel: '您想遷移的舊帳戶:' +customMOTDDescription: 每次用戶加載/重新加載頁面時,由換行符號分隔的 MOTD(啟動畫面)的自定信息將隨機顯示。 +privateModeInfo: 啟用後,只有列入白名單的伺服器才能與你的伺服器聯合。所有貼文都將對公眾隱藏。 +adminCustomCssWarn: 除非你知道它的作用,否則請不要使用此設定。 輸入不正確的值可能會導致每個人的客戶端無法正常運行。你可在你的的用戶設定中測試,確保你的 + CSS 正常工作。 +showUpdates: Firefish 更新時顯示彈出視窗 +recommendedInstances: 建議的伺服器 +caption: 自動字幕 +enterSendsMessage: 在 Messaging 中按 Return 發送消息 (如關閉則是 Ctrl + Return) +migrationConfirm: "您確定要將你的帳戶遷移到 {account} 嗎? 一旦這樣做,你將無法復原,而你將無法再次正常使用您的帳戶。\n另外,請確保你已將此當前帳戶設置為您要遷移的帳戶。" +customSplashIconsDescription: 每次用戶加載/重新加載頁面時,以換行符號分隔的自定啟動畫面圖標的網址將隨機顯示。請確保圖片位於靜態網址上,最好所有圖片解析度調整為 + 192x192。 +accountMoved: '該使用者已遷移至新帳戶:' +showAds: 顯示廣告 +noThankYou: 不用了,謝謝 +selectInstance: 選擇伺服器 +enableRecommendedTimeline: 啟用推薦時間線 +antennaInstancesDescription: 分行列出一個伺服器 +moveTo: 遷移此帳戶到新帳戶 +moveToLabel: '請輸入你將會遷移到的帳戶:' +moveAccount: 遷移帳戶! +moveAccountDescription: '這個過程是不可逆的。 在遷移前,請確保您已在新帳戶上為此帳戶設置了別名(Alias)。 請輸入帳戶標籤 (格式: + @person@server.com)' +moveFrom: 由舊帳戶移至此帳戶 +moveFromDescription: '這將為你的舊帳戶設置一個別名(Alias),以便你可以從該帳戶轉移到當前帳戶。 在你的舊帳戶移動之前請執行此操作。 請輸入帳戶標籤 + (格式: @person@server.com)' +enableEmojiReactions: 啟用表情符號反應 +breakFollowConfirm: 您確定要移除該關注者嗎? +socialTimeline: 社交時間軸 +cannotUploadBecauseExceedsFileSizeLimit: 因檔案太大而無法上傳。 +customMOTD: 自定義MOTD (網頁載入時顯示的信息) +customSplashIcons: 啟動畫面圖標 (網址) +splash: 啟動畫面 +updateAvailable: 可能有可用的更新! +showAdminUpdates: 表明新的 Firefish 版本可用(只限管理員) +migration: 遷移 +homeTimeline: 主頁時間軸 +swipeOnDesktop: 允許在桌面上進行手機式滑動 +logoImageUrl: 圖標網址 +addInstance: 增加一個伺服器 +noInstances: 沒有伺服器 +flagSpeakAsCat: 像貓一樣地說話 +silenceThisInstance: 靜音此伺服器 +silencedInstances: 已靜音的伺服器 +silenced: 已靜音 +_experiments: + title: 試驗功能 +findOtherInstance: 找找另一個伺服器 +noGraze: 瀏覽器擴展 "Graze for Mastodon" 會與Firefish發生衝突,請停用該擴展。 +userSaysSomethingReasonRenote: '{name} 轉傳了包含 {reason} 的貼文' +pushNotificationNotSupported: 你的瀏覽器或伺服器不支援推送通知 +accessibility: 輔助功能 +userSaysSomethingReasonReply: '{name} 回覆了包含 {reason} 的貼文' +hiddenTags: 隱藏主題標籤 +indexPosts: 索引貼文 +indexNotice: 現在開始索引。 這可能需要一段時間,請不要在一個小時內重啟你的伺服器。 +deleted: 已刪除 +editNote: 編輯筆記 +edited: '於 {date} {time} 編輯' +userSaysSomethingReason: '{name} 說了 {reason}' +allowedInstancesDescription: 要加入聯邦白名單的服務器,每台伺服器用新行分隔(僅適用於私有模式)。 +defaultReaction: 默認的表情符號反應 +license: 授權 +apps: 應用 +pushNotification: 推送通知 +subscribePushNotification: 啟用推送通知 +unsubscribePushNotification: 禁用推送通知 +pushNotificationAlreadySubscribed: 推送通知已經啟用 +recommendedInstancesDescription: 以每行分隔的推薦伺服器出現在推薦的時間線中。 +searchPlaceholder: 在聯邦網路上搜尋 +cw: 內容警告 +selectChannel: 選擇一個頻道 +newer: 較新 +older: 較舊 +jumpToPrevious: 跳到上一個 +removeReaction: 移除你的反應 +listsDesc: 清單可以創建一個只有您指定用戶的時間線。 可以從時間線頁面訪問它們。 +flagSpeakAsCatDescription: 在喵咪模式下你的貼文會被喵化ヾ(•ω•`)o +antennasDesc: "天線會顯示符合您設置條件的新貼文!\n 可以從時間線訪問它們。" +expandOnNoteClick: 點擊以打開貼文 +expandOnNoteClickDesc: 如果禁用,您仍然可以通過右鍵單擊菜單或單擊時間戳來打開貼文。 +hiddenTagsDescription: '列出您希望隱藏趨勢和探索的主題標籤(不帶 #)。 隱藏的主題標籤仍然可以通過其他方式發現。' +userSaysSomethingReasonQuote: '{name} 引用了一篇包含 {reason} 的貼文' +silencedInstancesDescription: 列出您想要靜音的伺服器的網址。 您列出的伺服器內的帳戶將被視為“沉默”,只能發出追隨請求,如果不追隨則不能提及本地帳戶。 + 這不會影響被阻止的伺服器。 +video: 影片 +audio: 音訊 +sendPushNotificationReadMessageCaption: 包含文本 “{emptyPushNotificationMessage}” 的通知將顯示一小段時間。 + 這可能會增加您設備的電池使用量(如果適用)。 +channelFederationWarn: 頻道功能尚未與聯邦宇宙連動 +swipeOnMobile: 允許以滑動在頁面之間切換 +sendPushNotificationReadMessage: 閱讀相關通知或消息後刪除推送通知 +image: 圖片 +seperateRenoteQuote: 分別獨立的轉傳及引用按鈕 +clipsDesc: 摘錄就像一個可以分享的書籤。 你可以從每個貼文的菜單創建新摘錄或將貼文加入已有的摘錄。 +noteId: 貼文 ID +sendModMail: 發送審核通知 +enableIdenticonGeneration: 啟用碎片生成 +enableServerMachineStats: 啟用伺服器硬體統計資訊 +reactionPickerSkinTone: 首選表情符號膚色 +indexFromDescription: 留空以索引每個貼文 +preventAiLearning: 防止 AI 機器人抓取 +preventAiLearningDescription: 請求第三方 AI 語言模型不要研究您上傳的內容,例如貼文和圖像。 +indexFrom: 從貼文 ID 開始的索引 +isLocked: 該帳戶已獲得以下批准 +isModerator: 板主 +isAdmin: 管理員 +isPatron: Firefish 項目贊助者 +silencedWarning: 顯示此頁面是因為這些使用者來自您伺服器管理員已靜音的伺服器,因此他們可能是垃圾訊息。 +signupsDisabled: 該伺服器上的註冊當前已被禁用,但您隨時可以在另一台伺服器上註冊!或是您有該伺服器的邀請碼,請在下面輸入。 +showPopup: 通過彈出式視窗通知用戶 +showWithSparkles: 閃閃發光的顯示 +youHaveUnreadAnnouncements: 您有未讀的公告 +donationLink: 連結到贊助頁面 +neverShow: 不再顯示 +remindMeLater: 可能之後 +removeQuote: 删除引用 +removeRecipient: 刪除收件者 +removeMember: 刪除成員 +isBot: 此帳戶是機器人 diff --git a/package.json b/package.json index 84749866d8..6c1e5c6743 100644 --- a/package.json +++ b/package.json @@ -1,16 +1,16 @@ { - "name": "calckey", - "version": "13.1.4.1", + "name": "firefish", + "version": "1.0.0", "codename": "aqua", "repository": { "type": "git", - "url": "https://codeberg.org/calckey/calckey.git" + "url": "https://gitlab.prometheus.systems/firefish/firefish.git" }, - "packageManager": "pnpm@7.27.0", + "packageManager": "pnpm@8.6.9", "private": true, "scripts": { - "rebuild": "pnpm run clean && pnpm -r run build && pnpm run gulp", - "build": "pnpm -r run build && pnpm run gulp", + "rebuild": "pnpm run clean && pnpm node ./scripts/build-greet.js && pnpm -r --parallel run build && pnpm run gulp", + "build": "pnpm node ./scripts/build-greet.js && pnpm -r --parallel run build && pnpm run gulp", "start": "pnpm --filter backend run start", "start:test": "pnpm --filter backend run start:test", "init": "pnpm run migrate", @@ -21,13 +21,13 @@ "watch": "pnpm run dev", "dev": "pnpm node ./scripts/dev.js", "dev:staging": "NODE_OPTIONS=--max_old_space_size=3072 NODE_ENV=development pnpm run build && pnpm run start", - "lint": "pnpm -r run lint", + "lint": "pnpm -r --parallel run lint", "cy:open": "cypress open --browser --e2e --config-file=cypress.config.ts", "cy:run": "cypress run", "e2e": "start-server-and-test start:test http://localhost:61812 cy:run", "mocha": "pnpm --filter backend run mocha", "test": "pnpm run mocha", - "format": "gulp format", + "format": "pnpm -r --parallel run format", "clean": "pnpm node ./scripts/clean.js", "clean-all": "pnpm node ./scripts/clean-all.js", "cleanall": "pnpm run clean-all" @@ -36,17 +36,18 @@ "chokidar": "^3.3.1" }, "dependencies": { - "@bull-board/api": "^4.10.2", - "@bull-board/ui": "^4.10.2", + "@bull-board/api": "5.6.0", + "@bull-board/ui": "5.6.0", + "@napi-rs/cli": "^2.16.1", "@tensorflow/tfjs": "^3.21.0", - "calckey-js": "^0.0.20", "js-yaml": "4.1.0", - "phosphor-icons": "^1.4.2", "seedrandom": "^3.0.5" }, "devDependencies": { - "@types/gulp": "4.0.10", - "@types/gulp-rename": "2.0.1", + "@types/gulp": "4.0.13", + "@types/gulp-rename": "2.0.2", + "@types/node": "20.4.1", + "chalk": "4.1.2", "cross-env": "7.0.3", "cypress": "10.11.0", "execa": "5.1.1", @@ -56,8 +57,8 @@ "gulp-replace": "1.1.4", "gulp-terser": "2.1.0", "install-peers": "^1.0.4", - "rome": "^11.0.0", + "rome": "^v12.1.3-nightly.f65b0d9", "start-server-and-test": "1.15.2", - "typescript": "4.9.4" + "typescript": "5.1.6" } } diff --git a/packages/README.md b/packages/README.md new file mode 100644 index 0000000000..ea04817f06 --- /dev/null +++ b/packages/README.md @@ -0,0 +1,10 @@ +# 📦 Packages + +This directory contains all of the packages Firefish uses. + +- `backend`: Main backend code written in TypeScript for NodeJS +- `backend/native-utils`: Backend code written in Rust, bound to NodeJS by [NAPI-RS](https://napi.rs/) +- `client`: Web interface written in Vue3 and TypeScript +- `sw`: Web [Service Worker](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API) written in TypeScript +- `firefish-js`: TypeScript SDK for both backend and client, also published on [NPM](https://www.npmjs.com/package/firefish-js) for public use +- `megalodon`: TypeScript library used for partial Mastodon API compatibility diff --git a/packages/backend/.swcrc b/packages/backend/.swcrc index 39e112ff7c..272d9f698c 100644 --- a/packages/backend/.swcrc +++ b/packages/backend/.swcrc @@ -1,15 +1,15 @@ { - "$schema": "https://json.schemastore.org/swcrc", - "jsc": { - "parser": { - "syntax": "typescript", - "dynamicImport": true, - "decorators": true - }, - "transform": { - "legacyDecorator": true, - "decoratorMetadata": true - }, + "$schema": "https://json.schemastore.org/swcrc", + "jsc": { + "parser": { + "syntax": "typescript", + "dynamicImport": true, + "decorators": true + }, + "transform": { + "legacyDecorator": true, + "decoratorMetadata": true + }, "experimental": { "keepImportAssertions": true }, @@ -20,6 +20,6 @@ ] }, "target": "es2022" - }, - "minify": false + }, + "minify": false } diff --git a/packages/backend/assets/LICENSE b/packages/backend/assets/LICENSE new file mode 100644 index 0000000000..342509dec1 --- /dev/null +++ b/packages/backend/assets/LICENSE @@ -0,0 +1,13 @@ +Copyright 2023 Calckey + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/packages/backend/assets/api-doc.png b/packages/backend/assets/api-doc.png index 95fe6977fd..c5594fff97 100644 Binary files a/packages/backend/assets/api-doc.png and b/packages/backend/assets/api-doc.png differ diff --git a/packages/backend/assets/apple-touch-icon.png b/packages/backend/assets/apple-touch-icon.png index 35963d8f08..e9e0ac4735 100644 Binary files a/packages/backend/assets/apple-touch-icon.png and b/packages/backend/assets/apple-touch-icon.png differ diff --git a/packages/backend/assets/avatar.png b/packages/backend/assets/avatar.png new file mode 100644 index 0000000000..ee22bdb3c8 Binary files /dev/null and b/packages/backend/assets/avatar.png differ diff --git a/packages/backend/assets/badges/CREDITS b/packages/backend/assets/badges/CREDITS deleted file mode 100644 index 678ee543a5..0000000000 --- a/packages/backend/assets/badges/CREDITS +++ /dev/null @@ -1,7 +0,0 @@ -All images om this directory are illustrated by Henki (https://www.youtube.com/c/Henkiwashere) -Please show them some love, they're an awesome artist! - -Character design by ThatOneCalculator (https://t1c.dev) - -The images in this directory are complete redraws based off of the original Misskey error images. -The artist and license of said original images is sadly unknown. diff --git a/packages/backend/assets/badges/LICENSE b/packages/backend/assets/badges/LICENSE deleted file mode 100644 index 795087c264..0000000000 --- a/packages/backend/assets/badges/LICENSE +++ /dev/null @@ -1,427 +0,0 @@ -Attribution-ShareAlike 4.0 International - -======================================================================= - -Creative Commons Corporation ("Creative Commons") is not a law firm and -does not provide legal services or legal advice. Distribution of -Creative Commons public licenses does not create a lawyer-client or -other relationship. Creative Commons makes its licenses and related -information available on an "as-is" basis. Creative Commons gives no -warranties regarding its licenses, any material licensed under their -terms and conditions, or any related information. Creative Commons -disclaims all liability for damages resulting from their use to the -fullest extent possible. - -Using Creative Commons Public Licenses - -Creative Commons public licenses provide a standard set of terms and -conditions that creators and other rights holders may use to share -original works of authorship and other material subject to copyright -and certain other rights specified in the public license below. The -following considerations are for informational purposes only, are not -exhaustive, and do not form part of our licenses. - - Considerations for licensors: Our public licenses are - intended for use by those authorized to give the public - permission to use material in ways otherwise restricted by - copyright and certain other rights. Our licenses are - irrevocable. Licensors should read and understand the terms - and conditions of the license they choose before applying it. - Licensors should also secure all rights necessary before - applying our licenses so that the public can reuse the - material as expected. Licensors should clearly mark any - material not subject to the license. This includes other CC- - licensed material, or material used under an exception or - limitation to copyright. More considerations for licensors: - wiki.creativecommons.org/Considerations_for_licensors - - Considerations for the public: By using one of our public - licenses, a licensor grants the public permission to use the - licensed material under specified terms and conditions. If - the licensor's permission is not necessary for any reason--for - example, because of any applicable exception or limitation to - copyright--then that use is not regulated by the license. Our - licenses grant only permissions under copyright and certain - other rights that a licensor has authority to grant. Use of - the licensed material may still be restricted for other - reasons, including because others have copyright or other - rights in the material. A licensor may make special requests, - such as asking that all changes be marked or described. - Although not required by our licenses, you are encouraged to - respect those requests where reasonable. More considerations - for the public: - wiki.creativecommons.org/Considerations_for_licensees - -======================================================================= - -Creative Commons Attribution-ShareAlike 4.0 International Public -License - -By exercising the Licensed Rights (defined below), You accept and agree -to be bound by the terms and conditions of this Creative Commons -Attribution-ShareAlike 4.0 International Public License ("Public -License"). To the extent this Public License may be interpreted as a -contract, You are granted the Licensed Rights in consideration of Your -acceptance of these terms and conditions, and the Licensor grants You -such rights in consideration of benefits the Licensor receives from -making the Licensed Material available under these terms and -conditions. - - -Section 1 -- Definitions. - - a. Adapted Material means material subject to Copyright and Similar - Rights that is derived from or based upon the Licensed Material - and in which the Licensed Material is translated, altered, - arranged, transformed, or otherwise modified in a manner requiring - permission under the Copyright and Similar Rights held by the - Licensor. For purposes of this Public License, where the Licensed - Material is a musical work, performance, or sound recording, - Adapted Material is always produced where the Licensed Material is - synched in timed relation with a moving image. - - b. Adapter's License means the license You apply to Your Copyright - and Similar Rights in Your contributions to Adapted Material in - accordance with the terms and conditions of this Public License. - - c. BY-SA Compatible License means a license listed at - creativecommons.org/compatiblelicenses, approved by Creative - Commons as essentially the equivalent of this Public License. - - d. Copyright and Similar Rights means copyright and/or similar rights - closely related to copyright including, without limitation, - performance, broadcast, sound recording, and Sui Generis Database - Rights, without regard to how the rights are labeled or - categorized. For purposes of this Public License, the rights - specified in Section 2(b)(1)-(2) are not Copyright and Similar - Rights. - - e. Effective Technological Measures means those measures that, in the - absence of proper authority, may not be circumvented under laws - fulfilling obligations under Article 11 of the WIPO Copyright - Treaty adopted on December 20, 1996, and/or similar international - agreements. - - f. Exceptions and Limitations means fair use, fair dealing, and/or - any other exception or limitation to Copyright and Similar Rights - that applies to Your use of the Licensed Material. - - g. License Elements means the license attributes listed in the name - of a Creative Commons Public License. The License Elements of this - Public License are Attribution and ShareAlike. - - h. Licensed Material means the artistic or literary work, database, - or other material to which the Licensor applied this Public - License. - - i. Licensed Rights means the rights granted to You subject to the - terms and conditions of this Public License, which are limited to - all Copyright and Similar Rights that apply to Your use of the - Licensed Material and that the Licensor has authority to license. - - j. Licensor means the individual(s) or entity(ies) granting rights - under this Public License. - - k. Share means to provide material to the public by any means or - process that requires permission under the Licensed Rights, such - as reproduction, public display, public performance, distribution, - dissemination, communication, or importation, and to make material - available to the public including in ways that members of the - public may access the material from a place and at a time - individually chosen by them. - - l. Sui Generis Database Rights means rights other than copyright - resulting from Directive 96/9/EC of the European Parliament and of - the Council of 11 March 1996 on the legal protection of databases, - as amended and/or succeeded, as well as other essentially - equivalent rights anywhere in the world. - - m. You means the individual or entity exercising the Licensed Rights - under this Public License. Your has a corresponding meaning. - - -Section 2 -- Scope. - - a. License grant. - - 1. Subject to the terms and conditions of this Public License, - the Licensor hereby grants You a worldwide, royalty-free, - non-sublicensable, non-exclusive, irrevocable license to - exercise the Licensed Rights in the Licensed Material to: - - a. reproduce and Share the Licensed Material, in whole or - in part; and - - b. produce, reproduce, and Share Adapted Material. - - 2. Exceptions and Limitations. For the avoidance of doubt, where - Exceptions and Limitations apply to Your use, this Public - License does not apply, and You do not need to comply with - its terms and conditions. - - 3. Term. The term of this Public License is specified in Section - 6(a). - - 4. Media and formats; technical modifications allowed. The - Licensor authorizes You to exercise the Licensed Rights in - all media and formats whether now known or hereafter created, - and to make technical modifications necessary to do so. The - Licensor waives and/or agrees not to assert any right or - authority to forbid You from making technical modifications - necessary to exercise the Licensed Rights, including - technical modifications necessary to circumvent Effective - Technological Measures. For purposes of this Public License, - simply making modifications authorized by this Section 2(a) - (4) never produces Adapted Material. - - 5. Downstream recipients. - - a. Offer from the Licensor -- Licensed Material. Every - recipient of the Licensed Material automatically - receives an offer from the Licensor to exercise the - Licensed Rights under the terms and conditions of this - Public License. - - b. Additional offer from the Licensor -- Adapted Material. - Every recipient of Adapted Material from You - automatically receives an offer from the Licensor to - exercise the Licensed Rights in the Adapted Material - under the conditions of the Adapter's License You apply. - - c. No downstream restrictions. You may not offer or impose - any additional or different terms or conditions on, or - apply any Effective Technological Measures to, the - Licensed Material if doing so restricts exercise of the - Licensed Rights by any recipient of the Licensed - Material. - - 6. No endorsement. Nothing in this Public License constitutes or - may be construed as permission to assert or imply that You - are, or that Your use of the Licensed Material is, connected - with, or sponsored, endorsed, or granted official status by, - the Licensor or others designated to receive attribution as - provided in Section 3(a)(1)(A)(i). - - b. Other rights. - - 1. Moral rights, such as the right of integrity, are not - licensed under this Public License, nor are publicity, - privacy, and/or other similar personality rights; however, to - the extent possible, the Licensor waives and/or agrees not to - assert any such rights held by the Licensor to the limited - extent necessary to allow You to exercise the Licensed - Rights, but not otherwise. - - 2. Patent and trademark rights are not licensed under this - Public License. - - 3. To the extent possible, the Licensor waives any right to - collect royalties from You for the exercise of the Licensed - Rights, whether directly or through a collecting society - under any voluntary or waivable statutory or compulsory - licensing scheme. In all other cases the Licensor expressly - reserves any right to collect such royalties. - - -Section 3 -- License Conditions. - -Your exercise of the Licensed Rights is expressly made subject to the -following conditions. - - a. Attribution. - - 1. If You Share the Licensed Material (including in modified - form), You must: - - a. retain the following if it is supplied by the Licensor - with the Licensed Material: - - i. identification of the creator(s) of the Licensed - Material and any others designated to receive - attribution, in any reasonable manner requested by - the Licensor (including by pseudonym if - designated); - - ii. a copyright notice; - - iii. a notice that refers to this Public License; - - iv. a notice that refers to the disclaimer of - warranties; - - v. a URI or hyperlink to the Licensed Material to the - extent reasonably practicable; - - b. indicate if You modified the Licensed Material and - retain an indication of any previous modifications; and - - c. indicate the Licensed Material is licensed under this - Public License, and include the text of, or the URI or - hyperlink to, this Public License. - - 2. You may satisfy the conditions in Section 3(a)(1) in any - reasonable manner based on the medium, means, and context in - which You Share the Licensed Material. For example, it may be - reasonable to satisfy the conditions by providing a URI or - hyperlink to a resource that includes the required - information. - - 3. If requested by the Licensor, You must remove any of the - information required by Section 3(a)(1)(A) to the extent - reasonably practicable. - - b. ShareAlike. - - In addition to the conditions in Section 3(a), if You Share - Adapted Material You produce, the following conditions also apply. - - 1. The Adapter's License You apply must be a Creative Commons - license with the same License Elements, this version or - later, or a BY-SA Compatible License. - - 2. You must include the text of, or the URI or hyperlink to, the - Adapter's License You apply. You may satisfy this condition - in any reasonable manner based on the medium, means, and - context in which You Share Adapted Material. - - 3. You may not offer or impose any additional or different terms - or conditions on, or apply any Effective Technological - Measures to, Adapted Material that restrict exercise of the - rights granted under the Adapter's License You apply. - - -Section 4 -- Sui Generis Database Rights. - -Where the Licensed Rights include Sui Generis Database Rights that -apply to Your use of the Licensed Material: - - a. for the avoidance of doubt, Section 2(a)(1) grants You the right - to extract, reuse, reproduce, and Share all or a substantial - portion of the contents of the database; - - b. if You include all or a substantial portion of the database - contents in a database in which You have Sui Generis Database - Rights, then the database in which You have Sui Generis Database - Rights (but not its individual contents) is Adapted Material, - including for purposes of Section 3(b); and - - c. You must comply with the conditions in Section 3(a) if You Share - all or a substantial portion of the contents of the database. - -For the avoidance of doubt, this Section 4 supplements and does not -replace Your obligations under this Public License where the Licensed -Rights include other Copyright and Similar Rights. - - -Section 5 -- Disclaimer of Warranties and Limitation of Liability. - - a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE - EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS - AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF - ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, - IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, - WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR - PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, - ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT - KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT - ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. - - b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE - TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, - NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, - INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, - COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR - USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN - ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR - DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR - IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. - - c. The disclaimer of warranties and limitation of liability provided - above shall be interpreted in a manner that, to the extent - possible, most closely approximates an absolute disclaimer and - waiver of all liability. - - -Section 6 -- Term and Termination. - - a. This Public License applies for the term of the Copyright and - Similar Rights licensed here. However, if You fail to comply with - this Public License, then Your rights under this Public License - terminate automatically. - - b. Where Your right to use the Licensed Material has terminated under - Section 6(a), it reinstates: - - 1. automatically as of the date the violation is cured, provided - it is cured within 30 days of Your discovery of the - violation; or - - 2. upon express reinstatement by the Licensor. - - For the avoidance of doubt, this Section 6(b) does not affect any - right the Licensor may have to seek remedies for Your violations - of this Public License. - - c. For the avoidance of doubt, the Licensor may also offer the - Licensed Material under separate terms or conditions or stop - distributing the Licensed Material at any time; however, doing so - will not terminate this Public License. - - d. Sections 1, 5, 6, 7, and 8 survive termination of this Public - License. - - -Section 7 -- Other Terms and Conditions. - - a. The Licensor shall not be bound by any additional or different - terms or conditions communicated by You unless expressly agreed. - - b. Any arrangements, understandings, or agreements regarding the - Licensed Material not stated herein are separate from and - independent of the terms and conditions of this Public License. - - -Section 8 -- Interpretation. - - a. For the avoidance of doubt, this Public License does not, and - shall not be interpreted to, reduce, limit, restrict, or impose - conditions on any use of the Licensed Material that could lawfully - be made without permission under this Public License. - - b. To the extent possible, if any provision of this Public License is - deemed unenforceable, it shall be automatically reformed to the - minimum extent necessary to make it enforceable. If the provision - cannot be reformed, it shall be severed from this Public License - without affecting the enforceability of the remaining terms and - conditions. - - c. No term or condition of this Public License will be waived and no - failure to comply consented to unless expressly agreed to by the - Licensor. - - d. Nothing in this Public License constitutes or may be interpreted - as a limitation upon, or waiver of, any privileges and immunities - that apply to the Licensor or You, including from the legal - processes of any jurisdiction or authority. - - -======================================================================= - -Creative Commons is not a party to its public licenses. -Notwithstanding, Creative Commons may elect to apply one of its public -licenses to material it publishes and in those instances will be -considered the “Licensor.” The text of the Creative Commons public -licenses is dedicated to the public domain under the CC0 Public Domain -Dedication. Except for the limited purpose of indicating that material -is shared under a Creative Commons public license or as otherwise -permitted by the Creative Commons policies published at -creativecommons.org/policies, Creative Commons does not authorize the -use of the trademark "Creative Commons" or any other trademark or logo -of Creative Commons without its prior written consent including, -without limitation, in connection with any unauthorized modifications -to any of its public licenses or any other arrangements, -understandings, or agreements concerning use of licensed material. For -the avoidance of doubt, this paragraph does not form part of the public -licenses. - -Creative Commons may be contacted at creativecommons.org. diff --git a/packages/backend/assets/badges/error.png b/packages/backend/assets/badges/error.png index e90912b405..046f18e149 100644 Binary files a/packages/backend/assets/badges/error.png and b/packages/backend/assets/badges/error.png differ diff --git a/packages/backend/assets/badges/error_original.png b/packages/backend/assets/badges/error_original.png deleted file mode 100644 index 3fef1053a2..0000000000 Binary files a/packages/backend/assets/badges/error_original.png and /dev/null differ diff --git a/packages/backend/assets/badges/info.png b/packages/backend/assets/badges/info.png index ac70544e4a..c6ab300a72 100644 Binary files a/packages/backend/assets/badges/info.png and b/packages/backend/assets/badges/info.png differ diff --git a/packages/backend/assets/badges/info_original.png b/packages/backend/assets/badges/info_original.png deleted file mode 100644 index 4f148ba615..0000000000 Binary files a/packages/backend/assets/badges/info_original.png and /dev/null differ diff --git a/packages/backend/assets/badges/not-found.png b/packages/backend/assets/badges/not-found.png index 73d611e0e1..63356530ce 100644 Binary files a/packages/backend/assets/badges/not-found.png and b/packages/backend/assets/badges/not-found.png differ diff --git a/packages/backend/assets/badges/not-found_original.png b/packages/backend/assets/badges/not-found_original.png deleted file mode 100644 index 3022473dcf..0000000000 Binary files a/packages/backend/assets/badges/not-found_original.png and /dev/null differ diff --git a/packages/backend/assets/favicon.ico b/packages/backend/assets/favicon.ico index f252ca8165..11a614ae72 100644 Binary files a/packages/backend/assets/favicon.ico and b/packages/backend/assets/favicon.ico differ diff --git a/packages/backend/assets/favicon.png b/packages/backend/assets/favicon.png index ee67787513..983e0d700e 100644 Binary files a/packages/backend/assets/favicon.png and b/packages/backend/assets/favicon.png differ diff --git a/packages/backend/assets/favicon.svg b/packages/backend/assets/favicon.svg new file mode 100644 index 0000000000..981fe2f8f9 --- /dev/null +++ b/packages/backend/assets/favicon.svg @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/backend/assets/icons/192.png b/packages/backend/assets/icons/192.png index 9cdb656a57..da83b75bca 100644 Binary files a/packages/backend/assets/icons/192.png and b/packages/backend/assets/icons/192.png differ diff --git a/packages/backend/assets/icons/512.png b/packages/backend/assets/icons/512.png index a830bfa8ad..8e74853b34 100644 Binary files a/packages/backend/assets/icons/512.png and b/packages/backend/assets/icons/512.png differ diff --git a/packages/backend/assets/icons/maskable.png b/packages/backend/assets/icons/maskable.png index 44b214ca7c..15828d749d 100644 Binary files a/packages/backend/assets/icons/maskable.png and b/packages/backend/assets/icons/maskable.png differ diff --git a/packages/backend/assets/icons/monochrome.png b/packages/backend/assets/icons/monochrome.png index 75894e7a4e..cce6436012 100644 Binary files a/packages/backend/assets/icons/monochrome.png and b/packages/backend/assets/icons/monochrome.png differ diff --git a/packages/backend/assets/mi-white.png b/packages/backend/assets/mi-white.png index 2a186a9bce..360db4fa78 100644 Binary files a/packages/backend/assets/mi-white.png and b/packages/backend/assets/mi-white.png differ diff --git a/packages/backend/assets/notification-badges/LICENSE b/packages/backend/assets/notification-badges/LICENSE index 841c4c682b..28b0b502d7 100644 --- a/packages/backend/assets/notification-badges/LICENSE +++ b/packages/backend/assets/notification-badges/LICENSE @@ -1,5 +1,24 @@ -Font Awesome Icons +Phosphor Icons ------------------------- -Ⓒ Font Awesome -CC BY 4.0 (https://creativecommons.org/licenses/by/4.0/) +MIT License + +Copyright (c) 2020 Phosphor Icons + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/backend/assets/notification-badges/at.png b/packages/backend/assets/notification-badges/at.png index d1492856de..4ffb1c30bd 100644 Binary files a/packages/backend/assets/notification-badges/at.png and b/packages/backend/assets/notification-badges/at.png differ diff --git a/packages/backend/assets/notification-badges/check.png b/packages/backend/assets/notification-badges/check.png index baeb76babf..e1f6d41d81 100644 Binary files a/packages/backend/assets/notification-badges/check.png and b/packages/backend/assets/notification-badges/check.png differ diff --git a/packages/backend/assets/notification-badges/clipboard-check-solid.png b/packages/backend/assets/notification-badges/clipboard-check-solid.png index d8cdfa9da4..65c62187ee 100644 Binary files a/packages/backend/assets/notification-badges/clipboard-check-solid.png and b/packages/backend/assets/notification-badges/clipboard-check-solid.png differ diff --git a/packages/backend/assets/notification-badges/clock.png b/packages/backend/assets/notification-badges/clock.png index 9323f8f307..1beea217e2 100644 Binary files a/packages/backend/assets/notification-badges/clock.png and b/packages/backend/assets/notification-badges/clock.png differ diff --git a/packages/backend/assets/notification-badges/comments.png b/packages/backend/assets/notification-badges/comments.png index bc8a1c35b4..515799c697 100644 Binary files a/packages/backend/assets/notification-badges/comments.png and b/packages/backend/assets/notification-badges/comments.png differ diff --git a/packages/backend/assets/notification-badges/id-card-alt.png b/packages/backend/assets/notification-badges/id-card-alt.png index 67e1410e34..9bcaf26ff9 100644 Binary files a/packages/backend/assets/notification-badges/id-card-alt.png and b/packages/backend/assets/notification-badges/id-card-alt.png differ diff --git a/packages/backend/assets/notification-badges/plus.png b/packages/backend/assets/notification-badges/plus.png index 05362c122b..3e114247fe 100644 Binary files a/packages/backend/assets/notification-badges/plus.png and b/packages/backend/assets/notification-badges/plus.png differ diff --git a/packages/backend/assets/notification-badges/poll-h.png b/packages/backend/assets/notification-badges/poll-h.png index 3b7ded6659..825b69c635 100644 Binary files a/packages/backend/assets/notification-badges/poll-h.png and b/packages/backend/assets/notification-badges/poll-h.png differ diff --git a/packages/backend/assets/notification-badges/quote-right.png b/packages/backend/assets/notification-badges/quote-right.png index 0fa4837654..a41a15b495 100644 Binary files a/packages/backend/assets/notification-badges/quote-right.png and b/packages/backend/assets/notification-badges/quote-right.png differ diff --git a/packages/backend/assets/notification-badges/reply.png b/packages/backend/assets/notification-badges/reply.png index 77021f71a7..53d7cf3a15 100644 Binary files a/packages/backend/assets/notification-badges/reply.png and b/packages/backend/assets/notification-badges/reply.png differ diff --git a/packages/backend/assets/notification-badges/user-plus.png b/packages/backend/assets/notification-badges/user-plus.png index 9d376d04d6..f47144576e 100644 Binary files a/packages/backend/assets/notification-badges/user-plus.png and b/packages/backend/assets/notification-badges/user-plus.png differ diff --git a/packages/backend/assets/redoc.html b/packages/backend/assets/redoc.html index 4013abe310..403af4bbfa 100644 --- a/packages/backend/assets/redoc.html +++ b/packages/backend/assets/redoc.html @@ -1,11 +1,10 @@ - Calckey API + Firefish API - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/backend/assets/transparent.png b/packages/backend/assets/transparent.png new file mode 100644 index 0000000000..240ca4f8d4 Binary files /dev/null and b/packages/backend/assets/transparent.png differ diff --git a/packages/backend/check_connect.js b/packages/backend/check_connect.js new file mode 100644 index 0000000000..7c1e716b3e --- /dev/null +++ b/packages/backend/check_connect.js @@ -0,0 +1,10 @@ +import { loadConfig } from "./built/config.js"; +import { createRedisConnection } from "./built/redis.js"; + +const config = loadConfig(); +const redis = createRedisConnection(config); + +redis.on("connect", () => redis.disconnect()); +redis.on("error", (e) => { + throw e; +}); diff --git a/packages/backend/jsconfig.json b/packages/backend/jsconfig.json index 1230aadd12..f3f4f9c772 100644 --- a/packages/backend/jsconfig.json +++ b/packages/backend/jsconfig.json @@ -4,10 +4,5 @@ "module": "commonjs", "allowSyntheticDefaultImports": true }, - "exclude": [ - "node_modules", - "jspm_packages", - "tmp", - "temp" - ] + "exclude": ["node_modules", "jspm_packages", "tmp", "temp"] } diff --git a/packages/backend/migration/1000000000000-Init.js b/packages/backend/migration/1000000000000-Init.js index 1140be7e84..bab5fae7a0 100644 --- a/packages/backend/migration/1000000000000-Init.js +++ b/packages/backend/migration/1000000000000-Init.js @@ -1,482 +1,1068 @@ - - export class Init1000000000000 { - async up(queryRunner) { - await queryRunner.query(`CREATE TYPE "log_level_enum" AS ENUM('error', 'warning', 'info', 'success', 'debug')`); - await queryRunner.query(`CREATE TABLE "log" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "domain" character varying(64) array NOT NULL DEFAULT '{}'::varchar[], "level" "log_level_enum" NOT NULL, "worker" character varying(8) NOT NULL, "machine" character varying(128) NOT NULL, "message" character varying(1024) NOT NULL, "data" jsonb NOT NULL DEFAULT '{}', CONSTRAINT "PK_350604cbdf991d5930d9e618fbd" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE INDEX "IDX_8e4eb51a35d81b64dda28eed0a" ON "log" ("createdAt") `); - await queryRunner.query(`CREATE INDEX "IDX_8cb40cfc8f3c28261e6f887b03" ON "log" ("domain") `); - await queryRunner.query(`CREATE INDEX "IDX_584b536b49e53ac81beb39a177" ON "log" ("level") `); - await queryRunner.query(`CREATE TABLE "drive_folder" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "name" character varying(128) NOT NULL, "userId" character varying(32), "parentId" character varying(32), CONSTRAINT "PK_7a0c089191f5ebdc214e0af808a" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE INDEX "IDX_02878d441ceae15ce060b73daf" ON "drive_folder" ("createdAt") `); - await queryRunner.query(`CREATE INDEX "IDX_f4fc06e49c0171c85f1c48060d" ON "drive_folder" ("userId") `); - await queryRunner.query(`CREATE INDEX "IDX_00ceffb0cdc238b3233294f08f" ON "drive_folder" ("parentId") `); - await queryRunner.query(`CREATE TABLE "drive_file" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "userId" character varying(32), "userHost" character varying(128), "md5" character varying(32) NOT NULL, "name" character varying(256) NOT NULL, "type" character varying(128) NOT NULL, "size" integer NOT NULL, "comment" character varying(512), "properties" jsonb NOT NULL DEFAULT '{}', "storedInternal" boolean NOT NULL, "url" character varying(512) NOT NULL, "thumbnailUrl" character varying(512), "webpublicUrl" character varying(512), "accessKey" character varying(256), "thumbnailAccessKey" character varying(256), "webpublicAccessKey" character varying(256), "uri" character varying(512), "src" character varying(512), "folderId" character varying(32), "isSensitive" boolean NOT NULL DEFAULT false, "isLink" boolean NOT NULL DEFAULT false, CONSTRAINT "PK_43ddaaaf18c9e68029b7cbb032e" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE INDEX "IDX_c8dfad3b72196dd1d6b5db168a" ON "drive_file" ("createdAt") `); - await queryRunner.query(`CREATE INDEX "IDX_860fa6f6c7df5bb887249fba22" ON "drive_file" ("userId") `); - await queryRunner.query(`CREATE INDEX "IDX_92779627994ac79277f070c91e" ON "drive_file" ("userHost") `); - await queryRunner.query(`CREATE INDEX "IDX_37bb9a1b4585f8a3beb24c62d6" ON "drive_file" ("md5") `); - await queryRunner.query(`CREATE INDEX "IDX_a40b8df8c989d7db937ea27cf6" ON "drive_file" ("type") `); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_d85a184c2540d2deba33daf642" ON "drive_file" ("accessKey") `); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_e74022ce9a074b3866f70e0d27" ON "drive_file" ("thumbnailAccessKey") `); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_c55b2b7c284d9fef98026fc88e" ON "drive_file" ("webpublicAccessKey") `); - await queryRunner.query(`CREATE INDEX "IDX_e5848eac4940934e23dbc17581" ON "drive_file" ("uri") `); - await queryRunner.query(`CREATE INDEX "IDX_bb90d1956dafc4068c28aa7560" ON "drive_file" ("folderId") `); - await queryRunner.query(`CREATE TABLE "user" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "updatedAt" TIMESTAMP WITH TIME ZONE, "lastFetchedAt" TIMESTAMP WITH TIME ZONE, "username" character varying(128) NOT NULL, "usernameLower" character varying(128) NOT NULL, "name" character varying(128), "followersCount" integer NOT NULL DEFAULT 0, "followingCount" integer NOT NULL DEFAULT 0, "notesCount" integer NOT NULL DEFAULT 0, "avatarId" character varying(32), "bannerId" character varying(32), "tags" character varying(128) array NOT NULL DEFAULT '{}'::varchar[], "avatarUrl" character varying(512), "bannerUrl" character varying(512), "avatarColor" character varying(32), "bannerColor" character varying(32), "isSuspended" boolean NOT NULL DEFAULT false, "isSilenced" boolean NOT NULL DEFAULT false, "isLocked" boolean NOT NULL DEFAULT false, "isBot" boolean NOT NULL DEFAULT false, "isCat" boolean NOT NULL DEFAULT false, "isAdmin" boolean NOT NULL DEFAULT false, "isModerator" boolean NOT NULL DEFAULT false, "isVerified" boolean NOT NULL DEFAULT false, "emojis" character varying(128) array NOT NULL DEFAULT '{}'::varchar[], "host" character varying(128), "inbox" character varying(512), "sharedInbox" character varying(512), "featured" character varying(512), "uri" character varying(512), "token" character(16), CONSTRAINT "UQ_a854e557b1b14814750c7c7b0c9" UNIQUE ("token"), CONSTRAINT "REL_58f5c71eaab331645112cf8cfa" UNIQUE ("avatarId"), CONSTRAINT "REL_afc64b53f8db3707ceb34eb28e" UNIQUE ("bannerId"), CONSTRAINT "PK_cace4a159ff9f2512dd42373760" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE INDEX "IDX_e11e649824a45d8ed01d597fd9" ON "user" ("createdAt") `); - await queryRunner.query(`CREATE INDEX "IDX_80ca6e6ef65fb9ef34ea8c90f4" ON "user" ("updatedAt") `); - await queryRunner.query(`CREATE INDEX "IDX_a27b942a0d6dcff90e3ee9b5e8" ON "user" ("usernameLower") `); - await queryRunner.query(`CREATE INDEX "IDX_fa99d777623947a5b05f394cae" ON "user" ("tags") `); - await queryRunner.query(`CREATE INDEX "IDX_3252a5df8d5bbd16b281f7799e" ON "user" ("host") `); - await queryRunner.query(`CREATE INDEX "IDX_be623adaa4c566baf5d29ce0c8" ON "user" ("uri") `); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_a854e557b1b14814750c7c7b0c" ON "user" ("token") `); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_5deb01ae162d1d70b80d064c27" ON "user" ("usernameLower", "host") `); - await queryRunner.query(`CREATE TABLE "app" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "userId" character varying(32), "secret" character varying(64) NOT NULL, "name" character varying(128) NOT NULL, "description" character varying(512) NOT NULL, "permission" character varying(64) array NOT NULL, "callbackUrl" character varying(512), CONSTRAINT "PK_9478629fc093d229df09e560aea" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE INDEX "IDX_048a757923ed8b157e9895da53" ON "app" ("createdAt") `); - await queryRunner.query(`CREATE INDEX "IDX_3f5b0899ef90527a3462d7c2cb" ON "app" ("userId") `); - await queryRunner.query(`CREATE INDEX "IDX_f49922d511d666848f250663c4" ON "app" ("secret") `); - await queryRunner.query(`CREATE TABLE "access_token" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "token" character varying(128) NOT NULL, "hash" character varying(128) NOT NULL, "userId" character varying(32) NOT NULL, "appId" character varying(32) NOT NULL, CONSTRAINT "PK_f20f028607b2603deabd8182d12" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE INDEX "IDX_70ba8f6af34bc924fc9e12adb8" ON "access_token" ("token") `); - await queryRunner.query(`CREATE INDEX "IDX_64c327441248bae40f7d92f34f" ON "access_token" ("hash") `); - await queryRunner.query(`CREATE INDEX "IDX_9949557d0e1b2c19e5344c171e" ON "access_token" ("userId") `); - await queryRunner.query(`CREATE TYPE "note_visibility_enum" AS ENUM('public', 'home', 'followers', 'specified')`); - await queryRunner.query(`CREATE TABLE "note" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "replyId" character varying(32), "renoteId" character varying(32), "text" text, "name" character varying(256), "cw" character varying(512), "appId" character varying(32), "userId" character varying(32) NOT NULL, "viaMobile" boolean NOT NULL DEFAULT false, "localOnly" boolean NOT NULL DEFAULT false, "renoteCount" smallint NOT NULL DEFAULT 0, "repliesCount" smallint NOT NULL DEFAULT 0, "reactions" jsonb NOT NULL DEFAULT '{}', "visibility" "note_visibility_enum" NOT NULL, "uri" character varying(512), "score" integer NOT NULL DEFAULT 0, "fileIds" character varying(32) array NOT NULL DEFAULT '{}'::varchar[], "attachedFileTypes" character varying(256) array NOT NULL DEFAULT '{}'::varchar[], "visibleUserIds" character varying(32) array NOT NULL DEFAULT '{}'::varchar[], "mentions" character varying(32) array NOT NULL DEFAULT '{}'::varchar[], "mentionedRemoteUsers" text NOT NULL DEFAULT '[]', "emojis" character varying(128) array NOT NULL DEFAULT '{}'::varchar[], "tags" character varying(128) array NOT NULL DEFAULT '{}'::varchar[], "hasPoll" boolean NOT NULL DEFAULT false, "geo" jsonb DEFAULT null, "userHost" character varying(128), "replyUserId" character varying(32), "replyUserHost" character varying(128), "renoteUserId" character varying(32), "renoteUserHost" character varying(128), CONSTRAINT "PK_96d0c172a4fba276b1bbed43058" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE INDEX "IDX_e7c0567f5261063592f022e9b5" ON "note" ("createdAt") `); - await queryRunner.query(`CREATE INDEX "IDX_17cb3553c700a4985dff5a30ff" ON "note" ("replyId") `); - await queryRunner.query(`CREATE INDEX "IDX_52ccc804d7c69037d558bac4c9" ON "note" ("renoteId") `); - await queryRunner.query(`CREATE INDEX "IDX_5b87d9d19127bd5d92026017a7" ON "note" ("userId") `); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_153536c67d05e9adb24e99fc2b" ON "note" ("uri") `); - await queryRunner.query(`CREATE INDEX "IDX_51c063b6a133a9cb87145450f5" ON "note" ("fileIds") `); - await queryRunner.query(`CREATE INDEX "IDX_25dfc71b0369b003a4cd434d0b" ON "note" ("attachedFileTypes") `); - await queryRunner.query(`CREATE INDEX "IDX_796a8c03959361f97dc2be1d5c" ON "note" ("visibleUserIds") `); - await queryRunner.query(`CREATE INDEX "IDX_54ebcb6d27222913b908d56fd8" ON "note" ("mentions") `); - await queryRunner.query(`CREATE INDEX "IDX_88937d94d7443d9a99a76fa5c0" ON "note" ("tags") `); - await queryRunner.query(`CREATE INDEX "IDX_7125a826ab192eb27e11d358a5" ON "note" ("userHost") `); - await queryRunner.query(`CREATE TABLE "poll_vote" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "userId" character varying(32) NOT NULL, "noteId" character varying(32) NOT NULL, "choice" integer NOT NULL, CONSTRAINT "PK_fd002d371201c472490ba89c6a0" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE INDEX "IDX_0fb627e1c2f753262a74f0562d" ON "poll_vote" ("createdAt") `); - await queryRunner.query(`CREATE INDEX "IDX_66d2bd2ee31d14bcc23069a89f" ON "poll_vote" ("userId") `); - await queryRunner.query(`CREATE INDEX "IDX_aecfbd5ef60374918e63ee95fa" ON "poll_vote" ("noteId") `); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_50bd7164c5b78f1f4a42c4d21f" ON "poll_vote" ("userId", "noteId", "choice") `); - await queryRunner.query(`CREATE TABLE "note_reaction" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "userId" character varying(32) NOT NULL, "noteId" character varying(32) NOT NULL, "reaction" character varying(128) NOT NULL, CONSTRAINT "PK_767ec729b108799b587a3fcc9cf" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE INDEX "IDX_01f4581f114e0ebd2bbb876f0b" ON "note_reaction" ("createdAt") `); - await queryRunner.query(`CREATE INDEX "IDX_13761f64257f40c5636d0ff95e" ON "note_reaction" ("userId") `); - await queryRunner.query(`CREATE INDEX "IDX_45145e4953780f3cd5656f0ea6" ON "note_reaction" ("noteId") `); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_ad0c221b25672daf2df320a817" ON "note_reaction" ("userId", "noteId") `); - await queryRunner.query(`CREATE TABLE "note_watching" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "userId" character varying(32) NOT NULL, "noteId" character varying(32) NOT NULL, "noteUserId" character varying(32) NOT NULL, CONSTRAINT "PK_49286fdb23725945a74aa27d757" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE INDEX "IDX_318cdf42a9cfc11f479bd802bb" ON "note_watching" ("createdAt") `); - await queryRunner.query(`CREATE INDEX "IDX_b0134ec406e8d09a540f818288" ON "note_watching" ("userId") `); - await queryRunner.query(`CREATE INDEX "IDX_03e7028ab8388a3f5e3ce2a861" ON "note_watching" ("noteId") `); - await queryRunner.query(`CREATE INDEX "IDX_44499765eec6b5489d72c4253b" ON "note_watching" ("noteUserId") `); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_a42c93c69989ce1d09959df4cf" ON "note_watching" ("userId", "noteId") `); - await queryRunner.query(`CREATE TABLE "note_unread" ("id" character varying(32) NOT NULL, "userId" character varying(32) NOT NULL, "noteId" character varying(32) NOT NULL, "noteUserId" character varying(32) NOT NULL, "isSpecified" boolean NOT NULL, CONSTRAINT "PK_1904eda61a784f57e6e51fa9c1f" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE INDEX "IDX_56b0166d34ddae49d8ef7610bb" ON "note_unread" ("userId") `); - await queryRunner.query(`CREATE INDEX "IDX_e637cba4dc4410218c4251260e" ON "note_unread" ("noteId") `); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_d908433a4953cc13216cd9c274" ON "note_unread" ("userId", "noteId") `); - await queryRunner.query(`CREATE TABLE "notification" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "notifieeId" character varying(32) NOT NULL, "notifierId" character varying(32) NOT NULL, "type" character varying(32) NOT NULL, "isRead" boolean NOT NULL DEFAULT false, "noteId" character varying(32), "reaction" character varying(128), "choice" integer, CONSTRAINT "PK_705b6c7cdf9b2c2ff7ac7872cb7" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE INDEX "IDX_b11a5e627c41d4dc3170f1d370" ON "notification" ("createdAt") `); - await queryRunner.query(`CREATE INDEX "IDX_3c601b70a1066d2c8b517094cb" ON "notification" ("notifieeId") `); - await queryRunner.query(`CREATE TABLE "meta" ("id" character varying(32) NOT NULL, "name" character varying(128), "description" character varying(1024), "maintainerName" character varying(128), "maintainerEmail" character varying(128), "announcements" jsonb NOT NULL DEFAULT '[]', "disableRegistration" boolean NOT NULL DEFAULT false, "disableLocalTimeline" boolean NOT NULL DEFAULT false, "disableGlobalTimeline" boolean NOT NULL DEFAULT false, "enableEmojiReaction" boolean NOT NULL DEFAULT true, "useStarForReactionFallback" boolean NOT NULL DEFAULT false, "langs" character varying(64) array NOT NULL DEFAULT '{}'::varchar[], "hiddenTags" character varying(256) array NOT NULL DEFAULT '{}'::varchar[], "blockedHosts" character varying(256) array NOT NULL DEFAULT '{}'::varchar[], "mascotImageUrl" character varying(512) DEFAULT '/assets/ai.png', "bannerUrl" character varying(512), "errorImageUrl" character varying(512) DEFAULT 'https://xn--931a.moe/aiart/yubitun.png', "iconUrl" character varying(512), "cacheRemoteFiles" boolean NOT NULL DEFAULT true, "proxyAccount" character varying(128), "enableRecaptcha" boolean NOT NULL DEFAULT false, "recaptchaSiteKey" character varying(64), "recaptchaSecretKey" character varying(64), "localDriveCapacityMb" integer NOT NULL DEFAULT 1024, "remoteDriveCapacityMb" integer NOT NULL DEFAULT 32, "maxNoteTextLength" integer NOT NULL DEFAULT 500, "summalyProxy" character varying(128), "enableEmail" boolean NOT NULL DEFAULT false, "email" character varying(128), "smtpSecure" boolean NOT NULL DEFAULT false, "smtpHost" character varying(128), "smtpPort" integer, "smtpUser" character varying(128), "smtpPass" character varying(128), "enableServiceWorker" boolean NOT NULL DEFAULT false, "swPublicKey" character varying(128), "swPrivateKey" character varying(128), "enableTwitterIntegration" boolean NOT NULL DEFAULT false, "twitterConsumerKey" character varying(128), "twitterConsumerSecret" character varying(128), "enableGithubIntegration" boolean NOT NULL DEFAULT false, "githubClientId" character varying(128), "githubClientSecret" character varying(128), "enableDiscordIntegration" boolean NOT NULL DEFAULT false, "discordClientId" character varying(128), "discordClientSecret" character varying(128), CONSTRAINT "PK_c4c17a6c2bd7651338b60fc590b" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE TABLE "following" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "followeeId" character varying(32) NOT NULL, "followerId" character varying(32) NOT NULL, "followerHost" character varying(128), "followerInbox" character varying(512), "followerSharedInbox" character varying(512), "followeeHost" character varying(128), "followeeInbox" character varying(512), "followeeSharedInbox" character varying(512), CONSTRAINT "PK_c76c6e044bdf76ecf8bfb82a645" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE INDEX "IDX_582f8fab771a9040a12961f3e7" ON "following" ("createdAt") `); - await queryRunner.query(`CREATE INDEX "IDX_24e0042143a18157b234df186c" ON "following" ("followeeId") `); - await queryRunner.query(`CREATE INDEX "IDX_6516c5a6f3c015b4eed39978be" ON "following" ("followerId") `); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_307be5f1d1252e0388662acb96" ON "following" ("followerId", "followeeId") `); - await queryRunner.query(`CREATE TABLE "instance" ("id" character varying(32) NOT NULL, "caughtAt" TIMESTAMP WITH TIME ZONE NOT NULL, "host" character varying(128) NOT NULL, "system" character varying(64), "usersCount" integer NOT NULL DEFAULT 0, "notesCount" integer NOT NULL DEFAULT 0, "followingCount" integer NOT NULL DEFAULT 0, "followersCount" integer NOT NULL DEFAULT 0, "driveUsage" integer NOT NULL DEFAULT 0, "driveFiles" integer NOT NULL DEFAULT 0, "latestRequestSentAt" TIMESTAMP WITH TIME ZONE, "latestStatus" integer, "latestRequestReceivedAt" TIMESTAMP WITH TIME ZONE, "lastCommunicatedAt" TIMESTAMP WITH TIME ZONE NOT NULL, "isNotResponding" boolean NOT NULL DEFAULT false, "isMarkedAsClosed" boolean NOT NULL DEFAULT false, CONSTRAINT "PK_eaf60e4a0c399c9935413e06474" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE INDEX "IDX_2cd3b2a6b4cf0b910b260afe08" ON "instance" ("caughtAt") `); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_8d5afc98982185799b160e10eb" ON "instance" ("host") `); - await queryRunner.query(`CREATE TABLE "muting" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "muteeId" character varying(32) NOT NULL, "muterId" character varying(32) NOT NULL, CONSTRAINT "PK_2e92d06c8b5c602eeb27ca9ba48" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE INDEX "IDX_f86d57fbca33c7a4e6897490cc" ON "muting" ("createdAt") `); - await queryRunner.query(`CREATE INDEX "IDX_ec96b4fed9dae517e0dbbe0675" ON "muting" ("muteeId") `); - await queryRunner.query(`CREATE INDEX "IDX_93060675b4a79a577f31d260c6" ON "muting" ("muterId") `); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_1eb9d9824a630321a29fd3b290" ON "muting" ("muterId", "muteeId") `); - await queryRunner.query(`CREATE TABLE "sw_subscription" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "userId" character varying(32) NOT NULL, "endpoint" character varying(512) NOT NULL, "auth" character varying(256) NOT NULL, "publickey" character varying(128) NOT NULL, CONSTRAINT "PK_e8f763631530051b95eb6279b91" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE INDEX "IDX_97754ca6f2baff9b4abb7f853d" ON "sw_subscription" ("userId") `); - await queryRunner.query(`CREATE TABLE "blocking" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "blockeeId" character varying(32) NOT NULL, "blockerId" character varying(32) NOT NULL, CONSTRAINT "PK_e5d9a541cc1965ee7e048ea09dd" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE INDEX "IDX_b9a354f7941c1e779f3b33aea6" ON "blocking" ("createdAt") `); - await queryRunner.query(`CREATE INDEX "IDX_2cd4a2743a99671308f5417759" ON "blocking" ("blockeeId") `); - await queryRunner.query(`CREATE INDEX "IDX_0627125f1a8a42c9a1929edb55" ON "blocking" ("blockerId") `); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_98a1bc5cb30dfd159de056549f" ON "blocking" ("blockerId", "blockeeId") `); - await queryRunner.query(`CREATE TABLE "user_list" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "userId" character varying(32) NOT NULL, "name" character varying(128) NOT NULL, CONSTRAINT "PK_87bab75775fd9b1ff822b656402" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE INDEX "IDX_b7fcefbdd1c18dce86687531f9" ON "user_list" ("userId") `); - await queryRunner.query(`CREATE TABLE "user_list_joining" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "userId" character varying(32) NOT NULL, "userListId" character varying(32) NOT NULL, CONSTRAINT "PK_11abb3768da1c5f8de101c9df45" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE INDEX "IDX_d844bfc6f3f523a05189076efa" ON "user_list_joining" ("userId") `); - await queryRunner.query(`CREATE INDEX "IDX_605472305f26818cc93d1baaa7" ON "user_list_joining" ("userListId") `); - await queryRunner.query(`CREATE TABLE "hashtag" ("id" character varying(32) NOT NULL, "name" character varying(128) NOT NULL, "mentionedUserIds" character varying(32) array NOT NULL, "mentionedUsersCount" integer NOT NULL DEFAULT 0, "mentionedLocalUserIds" character varying(32) array NOT NULL, "mentionedLocalUsersCount" integer NOT NULL DEFAULT 0, "mentionedRemoteUserIds" character varying(32) array NOT NULL, "mentionedRemoteUsersCount" integer NOT NULL DEFAULT 0, "attachedUserIds" character varying(32) array NOT NULL, "attachedUsersCount" integer NOT NULL DEFAULT 0, "attachedLocalUserIds" character varying(32) array NOT NULL, "attachedLocalUsersCount" integer NOT NULL DEFAULT 0, "attachedRemoteUserIds" character varying(32) array NOT NULL, "attachedRemoteUsersCount" integer NOT NULL DEFAULT 0, CONSTRAINT "PK_cb36eb8af8412bfa978f1165d78" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_347fec870eafea7b26c8a73bac" ON "hashtag" ("name") `); - await queryRunner.query(`CREATE INDEX "IDX_2710a55f826ee236ea1a62698f" ON "hashtag" ("mentionedUsersCount") `); - await queryRunner.query(`CREATE INDEX "IDX_0e206cec573f1edff4a3062923" ON "hashtag" ("mentionedLocalUsersCount") `); - await queryRunner.query(`CREATE INDEX "IDX_4c02d38a976c3ae132228c6fce" ON "hashtag" ("mentionedRemoteUsersCount") `); - await queryRunner.query(`CREATE INDEX "IDX_d57f9030cd3af7f63ffb1c267c" ON "hashtag" ("attachedUsersCount") `); - await queryRunner.query(`CREATE INDEX "IDX_0c44bf4f680964145f2a68a341" ON "hashtag" ("attachedLocalUsersCount") `); - await queryRunner.query(`CREATE INDEX "IDX_0b03cbcd7e6a7ce068efa8ecc2" ON "hashtag" ("attachedRemoteUsersCount") `); - await queryRunner.query(`CREATE TABLE "note_favorite" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "userId" character varying(32) NOT NULL, "noteId" character varying(32) NOT NULL, CONSTRAINT "PK_af0da35a60b9fa4463a62082b36" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE INDEX "IDX_47f4b1892f5d6ba8efb3057d81" ON "note_favorite" ("userId") `); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_0f4fb9ad355f3effff221ef245" ON "note_favorite" ("userId", "noteId") `); - await queryRunner.query(`CREATE TABLE "abuse_user_report" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "userId" character varying(32) NOT NULL, "reporterId" character varying(32) NOT NULL, "comment" character varying(512) NOT NULL, CONSTRAINT "PK_87873f5f5cc5c321a1306b2d18c" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE INDEX "IDX_db2098070b2b5a523c58181f74" ON "abuse_user_report" ("createdAt") `); - await queryRunner.query(`CREATE INDEX "IDX_d049123c413e68ca52abe73420" ON "abuse_user_report" ("userId") `); - await queryRunner.query(`CREATE INDEX "IDX_04cc96756f89d0b7f9473e8cdf" ON "abuse_user_report" ("reporterId") `); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_5cd442c3b2e74fdd99dae20243" ON "abuse_user_report" ("userId", "reporterId") `); - await queryRunner.query(`CREATE TABLE "registration_ticket" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "code" character varying(64) NOT NULL, CONSTRAINT "PK_f11696b6fafcf3662d4292734f8" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_0ff69e8dfa9fe31bb4a4660f59" ON "registration_ticket" ("code") `); - await queryRunner.query(`CREATE TABLE "messaging_message" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "userId" character varying(32) NOT NULL, "recipientId" character varying(32) NOT NULL, "text" character varying(4096), "isRead" boolean NOT NULL DEFAULT false, "fileId" character varying(32), CONSTRAINT "PK_db398fd79dc95d0eb8c30456eaa" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE INDEX "IDX_e21cd3646e52ef9c94aaf17c2e" ON "messaging_message" ("createdAt") `); - await queryRunner.query(`CREATE INDEX "IDX_5377c307783fce2b6d352e1203" ON "messaging_message" ("userId") `); - await queryRunner.query(`CREATE INDEX "IDX_cac14a4e3944454a5ce7daa514" ON "messaging_message" ("recipientId") `); - await queryRunner.query(`CREATE TABLE "signin" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "userId" character varying(32) NOT NULL, "ip" character varying(128) NOT NULL, "headers" jsonb NOT NULL, "success" boolean NOT NULL, CONSTRAINT "PK_9e96ddc025712616fc492b3b588" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE INDEX "IDX_2c308dbdc50d94dc625670055f" ON "signin" ("userId") `); - await queryRunner.query(`CREATE TABLE "auth_session" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "token" character varying(128) NOT NULL, "userId" character varying(32), "appId" character varying(32) NOT NULL, CONSTRAINT "PK_19354ed146424a728c1112a8cbf" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE INDEX "IDX_62cb09e1129f6ec024ef66e183" ON "auth_session" ("token") `); - await queryRunner.query(`CREATE TABLE "follow_request" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "followeeId" character varying(32) NOT NULL, "followerId" character varying(32) NOT NULL, "requestId" character varying(128), "followerHost" character varying(128), "followerInbox" character varying(512), "followerSharedInbox" character varying(512), "followeeHost" character varying(128), "followeeInbox" character varying(512), "followeeSharedInbox" character varying(512), CONSTRAINT "PK_53a9aa3725f7a3deb150b39dbfc" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE INDEX "IDX_12c01c0d1a79f77d9f6c15fadd" ON "follow_request" ("followeeId") `); - await queryRunner.query(`CREATE INDEX "IDX_a7fd92dd6dc519e6fb435dd108" ON "follow_request" ("followerId") `); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_d54a512b822fac7ed52800f6b4" ON "follow_request" ("followerId", "followeeId") `); - await queryRunner.query(`CREATE TABLE "emoji" ("id" character varying(32) NOT NULL, "updatedAt" TIMESTAMP WITH TIME ZONE, "name" character varying(128) NOT NULL, "host" character varying(128), "url" character varying(512) NOT NULL, "uri" character varying(512), "type" character varying(64), "aliases" character varying(128) array NOT NULL DEFAULT '{}'::varchar[], CONSTRAINT "PK_df74ce05e24999ee01ea0bc50a3" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE INDEX "IDX_b37dafc86e9af007e3295c2781" ON "emoji" ("name") `); - await queryRunner.query(`CREATE INDEX "IDX_5900e907bb46516ddf2871327c" ON "emoji" ("host") `); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_4f4d35e1256c84ae3d1f0eab10" ON "emoji" ("name", "host") `); - await queryRunner.query(`CREATE TABLE "reversi_game" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "startedAt" TIMESTAMP WITH TIME ZONE, "user1Id" character varying(32) NOT NULL, "user2Id" character varying(32) NOT NULL, "user1Accepted" boolean NOT NULL DEFAULT false, "user2Accepted" boolean NOT NULL DEFAULT false, "black" integer, "isStarted" boolean NOT NULL DEFAULT false, "isEnded" boolean NOT NULL DEFAULT false, "winnerId" character varying(32), "surrendered" character varying(32), "logs" jsonb NOT NULL DEFAULT '[]', "map" character varying(64) array NOT NULL, "bw" character varying(32) NOT NULL, "isLlotheo" boolean NOT NULL DEFAULT false, "canPutEverywhere" boolean NOT NULL DEFAULT false, "loopedBoard" boolean NOT NULL DEFAULT false, "form1" jsonb DEFAULT null, "form2" jsonb DEFAULT null, "crc32" character varying(32), CONSTRAINT "PK_76b30eeba71b1193ad7c5311c3f" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE INDEX "IDX_b46ec40746efceac604142be1c" ON "reversi_game" ("createdAt") `); - await queryRunner.query(`CREATE TABLE "reversi_matching" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "parentId" character varying(32) NOT NULL, "childId" character varying(32) NOT NULL, CONSTRAINT "PK_880bd0afbab232f21c8b9d146cf" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE INDEX "IDX_b604d92d6c7aec38627f6eaf16" ON "reversi_matching" ("createdAt") `); - await queryRunner.query(`CREATE INDEX "IDX_3b25402709dd9882048c2bbade" ON "reversi_matching" ("parentId") `); - await queryRunner.query(`CREATE INDEX "IDX_e247b23a3c9b45f89ec1299d06" ON "reversi_matching" ("childId") `); - await queryRunner.query(`CREATE TABLE "user_note_pining" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "userId" character varying(32) NOT NULL, "noteId" character varying(32) NOT NULL, CONSTRAINT "PK_a6a2dad4ae000abce2ea9d9b103" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE INDEX "IDX_bfbc6f79ba4007b4ce5097f08d" ON "user_note_pining" ("userId") `); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_410cd649884b501c02d6e72738" ON "user_note_pining" ("userId", "noteId") `); - await queryRunner.query(`CREATE TYPE "poll_notevisibility_enum" AS ENUM('public', 'home', 'followers', 'specified')`); - await queryRunner.query(`CREATE TABLE "poll" ("noteId" character varying(32) NOT NULL, "expiresAt" TIMESTAMP WITH TIME ZONE, "multiple" boolean NOT NULL, "choices" character varying(128) array NOT NULL DEFAULT '{}'::varchar[], "votes" integer array NOT NULL, "noteVisibility" "poll_notevisibility_enum" NOT NULL, "userId" character varying(32) NOT NULL, "userHost" character varying(128), CONSTRAINT "REL_da851e06d0dfe2ef397d8b1bf1" UNIQUE ("noteId"), CONSTRAINT "PK_da851e06d0dfe2ef397d8b1bf1b" PRIMARY KEY ("noteId"))`); - await queryRunner.query(`CREATE INDEX "IDX_0610ebcfcfb4a18441a9bcdab2" ON "poll" ("userId") `); - await queryRunner.query(`CREATE INDEX "IDX_7fa20a12319c7f6dc3aed98c0a" ON "poll" ("userHost") `); - await queryRunner.query(`CREATE TABLE "user_keypair" ("userId" character varying(32) NOT NULL, "publicKey" character varying(4096) NOT NULL, "privateKey" character varying(4096) NOT NULL, CONSTRAINT "REL_f4853eb41ab722fe05f81cedeb" UNIQUE ("userId"), CONSTRAINT "PK_f4853eb41ab722fe05f81cedeb6" PRIMARY KEY ("userId"))`); - await queryRunner.query(`CREATE TABLE "user_publickey" ("userId" character varying(32) NOT NULL, "keyId" character varying(256) NOT NULL, "keyPem" character varying(4096) NOT NULL, CONSTRAINT "REL_10c146e4b39b443ede016f6736" UNIQUE ("userId"), CONSTRAINT "PK_10c146e4b39b443ede016f6736d" PRIMARY KEY ("userId"))`); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_171e64971c780ebd23fae140bb" ON "user_publickey" ("keyId") `); - await queryRunner.query(`CREATE TABLE "user_profile" ("userId" character varying(32) NOT NULL, "location" character varying(128), "birthday" character(10), "description" character varying(1024), "fields" jsonb NOT NULL DEFAULT '[]', "url" character varying(512), "email" character varying(128), "emailVerifyCode" character varying(128), "emailVerified" boolean NOT NULL DEFAULT false, "twoFactorTempSecret" character varying(128), "twoFactorSecret" character varying(128), "twoFactorEnabled" boolean NOT NULL DEFAULT false, "password" character varying(128), "clientData" jsonb NOT NULL DEFAULT '{}', "autoWatch" boolean NOT NULL DEFAULT false, "autoAcceptFollowed" boolean NOT NULL DEFAULT false, "alwaysMarkNsfw" boolean NOT NULL DEFAULT false, "carefulBot" boolean NOT NULL DEFAULT false, "twitter" boolean NOT NULL DEFAULT false, "twitterAccessToken" character varying(64) DEFAULT null, "twitterAccessTokenSecret" character varying(64) DEFAULT null, "twitterUserId" character varying(64) DEFAULT null, "twitterScreenName" character varying(64) DEFAULT null, "github" boolean NOT NULL DEFAULT false, "githubAccessToken" character varying(64) DEFAULT null, "githubId" integer DEFAULT null, "githubLogin" character varying(64) DEFAULT null, "discord" boolean NOT NULL DEFAULT false, "discordAccessToken" character varying(64) DEFAULT null, "discordRefreshToken" character varying(64) DEFAULT null, "discordExpiresDate" integer DEFAULT null, "discordId" character varying(64) DEFAULT null, "discordUsername" character varying(64) DEFAULT null, "discordDiscriminator" character varying(64) DEFAULT null, "userHost" character varying(128), CONSTRAINT "REL_51cb79b5555effaf7d69ba1cff" UNIQUE ("userId"), CONSTRAINT "PK_51cb79b5555effaf7d69ba1cff9" PRIMARY KEY ("userId"))`); - await queryRunner.query(`CREATE INDEX "IDX_dce530b98e454793dac5ec2f5a" ON "user_profile" ("userHost") `); - await queryRunner.query(`CREATE TYPE "__chart__active_users_span_enum" AS ENUM('hour', 'day')`); - await queryRunner.query(`CREATE TABLE "__chart__active_users" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "group" character varying(128), "span" "__chart__active_users_span_enum" NOT NULL, "unique" jsonb NOT NULL DEFAULT '{}', "___local_count" bigint NOT NULL, "___remote_count" bigint NOT NULL, CONSTRAINT "PK_317237a9f733b970604a11e314f" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE TYPE "__chart__drive_span_enum" AS ENUM('hour', 'day')`); - await queryRunner.query(`CREATE TABLE "__chart__drive" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "group" character varying(128), "span" "__chart__drive_span_enum" NOT NULL, "unique" jsonb NOT NULL DEFAULT '{}', "___local_totalCount" bigint NOT NULL, "___local_totalSize" bigint NOT NULL, "___local_incCount" bigint NOT NULL, "___local_incSize" bigint NOT NULL, "___local_decCount" bigint NOT NULL, "___local_decSize" bigint NOT NULL, "___remote_totalCount" bigint NOT NULL, "___remote_totalSize" bigint NOT NULL, "___remote_incCount" bigint NOT NULL, "___remote_incSize" bigint NOT NULL, "___remote_decCount" bigint NOT NULL, "___remote_decSize" bigint NOT NULL, CONSTRAINT "PK_f96bc548a765cd4b3b354221ce7" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE TYPE "__chart__federation_span_enum" AS ENUM('hour', 'day')`); - await queryRunner.query(`CREATE TABLE "__chart__federation" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "group" character varying(128), "span" "__chart__federation_span_enum" NOT NULL, "unique" jsonb NOT NULL DEFAULT '{}', "___instance_total" bigint NOT NULL, "___instance_inc" bigint NOT NULL, "___instance_dec" bigint NOT NULL, CONSTRAINT "PK_b39dcd31a0fe1a7757e348e85fd" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE TYPE "__chart__hashtag_span_enum" AS ENUM('hour', 'day')`); - await queryRunner.query(`CREATE TABLE "__chart__hashtag" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "group" character varying(128), "span" "__chart__hashtag_span_enum" NOT NULL, "unique" jsonb NOT NULL DEFAULT '{}', "___local_count" bigint NOT NULL, "___remote_count" bigint NOT NULL, CONSTRAINT "PK_c32f1ea2b44a5d2f7881e37f8f9" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE TYPE "__chart__instance_span_enum" AS ENUM('hour', 'day')`); - await queryRunner.query(`CREATE TABLE "__chart__instance" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "group" character varying(128), "span" "__chart__instance_span_enum" NOT NULL, "unique" jsonb NOT NULL DEFAULT '{}', "___requests_failed" bigint NOT NULL, "___requests_succeeded" bigint NOT NULL, "___requests_received" bigint NOT NULL, "___notes_total" bigint NOT NULL, "___notes_inc" bigint NOT NULL, "___notes_dec" bigint NOT NULL, "___notes_diffs_normal" bigint NOT NULL, "___notes_diffs_reply" bigint NOT NULL, "___notes_diffs_renote" bigint NOT NULL, "___users_total" bigint NOT NULL, "___users_inc" bigint NOT NULL, "___users_dec" bigint NOT NULL, "___following_total" bigint NOT NULL, "___following_inc" bigint NOT NULL, "___following_dec" bigint NOT NULL, "___followers_total" bigint NOT NULL, "___followers_inc" bigint NOT NULL, "___followers_dec" bigint NOT NULL, "___drive_totalFiles" bigint NOT NULL, "___drive_totalUsage" bigint NOT NULL, "___drive_incFiles" bigint NOT NULL, "___drive_incUsage" bigint NOT NULL, "___drive_decFiles" bigint NOT NULL, "___drive_decUsage" bigint NOT NULL, CONSTRAINT "PK_1267c67c7c2d47b4903975f2c00" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE TYPE "__chart__network_span_enum" AS ENUM('hour', 'day')`); - await queryRunner.query(`CREATE TABLE "__chart__network" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "group" character varying(128), "span" "__chart__network_span_enum" NOT NULL, "unique" jsonb NOT NULL DEFAULT '{}', "___incomingRequests" bigint NOT NULL, "___outgoingRequests" bigint NOT NULL, "___totalTime" bigint NOT NULL, "___incomingBytes" bigint NOT NULL, "___outgoingBytes" bigint NOT NULL, CONSTRAINT "PK_bc4290c2e27fad14ef0c1ca93f3" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE TYPE "__chart__notes_span_enum" AS ENUM('hour', 'day')`); - await queryRunner.query(`CREATE TABLE "__chart__notes" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "group" character varying(128), "span" "__chart__notes_span_enum" NOT NULL, "unique" jsonb NOT NULL DEFAULT '{}', "___local_total" bigint NOT NULL, "___local_inc" bigint NOT NULL, "___local_dec" bigint NOT NULL, "___local_diffs_normal" bigint NOT NULL, "___local_diffs_reply" bigint NOT NULL, "___local_diffs_renote" bigint NOT NULL, "___remote_total" bigint NOT NULL, "___remote_inc" bigint NOT NULL, "___remote_dec" bigint NOT NULL, "___remote_diffs_normal" bigint NOT NULL, "___remote_diffs_reply" bigint NOT NULL, "___remote_diffs_renote" bigint NOT NULL, CONSTRAINT "PK_0aec823fa85c7f901bdb3863b14" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE TYPE "__chart__per_user_drive_span_enum" AS ENUM('hour', 'day')`); - await queryRunner.query(`CREATE TABLE "__chart__per_user_drive" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "group" character varying(128), "span" "__chart__per_user_drive_span_enum" NOT NULL, "unique" jsonb NOT NULL DEFAULT '{}', "___totalCount" bigint NOT NULL, "___totalSize" bigint NOT NULL, "___incCount" bigint NOT NULL, "___incSize" bigint NOT NULL, "___decCount" bigint NOT NULL, "___decSize" bigint NOT NULL, CONSTRAINT "PK_d0ef23d24d666e1a44a0cd3d208" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE TYPE "__chart__per_user_following_span_enum" AS ENUM('hour', 'day')`); - await queryRunner.query(`CREATE TABLE "__chart__per_user_following" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "group" character varying(128), "span" "__chart__per_user_following_span_enum" NOT NULL, "unique" jsonb NOT NULL DEFAULT '{}', "___local_followings_total" bigint NOT NULL, "___local_followings_inc" bigint NOT NULL, "___local_followings_dec" bigint NOT NULL, "___local_followers_total" bigint NOT NULL, "___local_followers_inc" bigint NOT NULL, "___local_followers_dec" bigint NOT NULL, "___remote_followings_total" bigint NOT NULL, "___remote_followings_inc" bigint NOT NULL, "___remote_followings_dec" bigint NOT NULL, "___remote_followers_total" bigint NOT NULL, "___remote_followers_inc" bigint NOT NULL, "___remote_followers_dec" bigint NOT NULL, CONSTRAINT "PK_85bb1b540363a29c2fec83bd907" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE TYPE "__chart__per_user_notes_span_enum" AS ENUM('hour', 'day')`); - await queryRunner.query(`CREATE TABLE "__chart__per_user_notes" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "group" character varying(128), "span" "__chart__per_user_notes_span_enum" NOT NULL, "unique" jsonb NOT NULL DEFAULT '{}', "___total" bigint NOT NULL, "___inc" bigint NOT NULL, "___dec" bigint NOT NULL, "___diffs_normal" bigint NOT NULL, "___diffs_reply" bigint NOT NULL, "___diffs_renote" bigint NOT NULL, CONSTRAINT "PK_334acf6e915af2f29edc11b8e50" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE TYPE "__chart__per_user_reaction_span_enum" AS ENUM('hour', 'day')`); - await queryRunner.query(`CREATE TABLE "__chart__per_user_reaction" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "group" character varying(128), "span" "__chart__per_user_reaction_span_enum" NOT NULL, "unique" jsonb NOT NULL DEFAULT '{}', "___local_count" bigint NOT NULL, "___remote_count" bigint NOT NULL, CONSTRAINT "PK_984f54dae441e65b633e8d27a7f" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE TYPE "__chart__test_grouped_span_enum" AS ENUM('hour', 'day')`); - await queryRunner.query(`CREATE TABLE "__chart__test_grouped" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "group" character varying(128), "span" "__chart__test_grouped_span_enum" NOT NULL, "unique" jsonb NOT NULL DEFAULT '{}', "___foo_total" bigint NOT NULL, "___foo_inc" bigint NOT NULL, "___foo_dec" bigint NOT NULL, CONSTRAINT "PK_f4a2b175d308695af30d4293272" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE TYPE "__chart__test_unique_span_enum" AS ENUM('hour', 'day')`); - await queryRunner.query(`CREATE TABLE "__chart__test_unique" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "group" character varying(128), "span" "__chart__test_unique_span_enum" NOT NULL, "unique" jsonb NOT NULL DEFAULT '{}', "___foo" bigint NOT NULL, CONSTRAINT "PK_409bac9c97cc612d8500012319d" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE TYPE "__chart__test_span_enum" AS ENUM('hour', 'day')`); - await queryRunner.query(`CREATE TABLE "__chart__test" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "group" character varying(128), "span" "__chart__test_span_enum" NOT NULL, "unique" jsonb NOT NULL DEFAULT '{}', "___foo_total" bigint NOT NULL, "___foo_inc" bigint NOT NULL, "___foo_dec" bigint NOT NULL, CONSTRAINT "PK_b4bc31dffbd1b785276a3ecfc1e" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE TYPE "__chart__users_span_enum" AS ENUM('hour', 'day')`); - await queryRunner.query(`CREATE TABLE "__chart__users" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "group" character varying(128), "span" "__chart__users_span_enum" NOT NULL, "unique" jsonb NOT NULL DEFAULT '{}', "___local_total" bigint NOT NULL, "___local_inc" bigint NOT NULL, "___local_dec" bigint NOT NULL, "___remote_total" bigint NOT NULL, "___remote_inc" bigint NOT NULL, "___remote_dec" bigint NOT NULL, CONSTRAINT "PK_4dfcf2c78d03524b9eb2c99d328" PRIMARY KEY ("id"))`); - await queryRunner.query(`ALTER TABLE "drive_folder" ADD CONSTRAINT "FK_f4fc06e49c0171c85f1c48060d2" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "drive_folder" ADD CONSTRAINT "FK_00ceffb0cdc238b3233294f08f2" FOREIGN KEY ("parentId") REFERENCES "drive_folder"("id") ON DELETE SET NULL ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "drive_file" ADD CONSTRAINT "FK_860fa6f6c7df5bb887249fba22e" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE SET NULL ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "drive_file" ADD CONSTRAINT "FK_bb90d1956dafc4068c28aa7560a" FOREIGN KEY ("folderId") REFERENCES "drive_folder"("id") ON DELETE SET NULL ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "user" ADD CONSTRAINT "FK_58f5c71eaab331645112cf8cfa5" FOREIGN KEY ("avatarId") REFERENCES "drive_file"("id") ON DELETE SET NULL ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "user" ADD CONSTRAINT "FK_afc64b53f8db3707ceb34eb28e2" FOREIGN KEY ("bannerId") REFERENCES "drive_file"("id") ON DELETE SET NULL ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "app" ADD CONSTRAINT "FK_3f5b0899ef90527a3462d7c2cb3" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE SET NULL ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "access_token" ADD CONSTRAINT "FK_9949557d0e1b2c19e5344c171e9" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "access_token" ADD CONSTRAINT "FK_a3ff16c90cc87a82a0b5959e560" FOREIGN KEY ("appId") REFERENCES "app"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "note" ADD CONSTRAINT "FK_17cb3553c700a4985dff5a30ff5" FOREIGN KEY ("replyId") REFERENCES "note"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "note" ADD CONSTRAINT "FK_52ccc804d7c69037d558bac4c96" FOREIGN KEY ("renoteId") REFERENCES "note"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "note" ADD CONSTRAINT "FK_ec5c201576192ba8904c345c5cc" FOREIGN KEY ("appId") REFERENCES "app"("id") ON DELETE SET NULL ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "note" ADD CONSTRAINT "FK_5b87d9d19127bd5d92026017a7b" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "poll_vote" ADD CONSTRAINT "FK_66d2bd2ee31d14bcc23069a89f8" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "poll_vote" ADD CONSTRAINT "FK_aecfbd5ef60374918e63ee95fa7" FOREIGN KEY ("noteId") REFERENCES "note"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "note_reaction" ADD CONSTRAINT "FK_13761f64257f40c5636d0ff95ee" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "note_reaction" ADD CONSTRAINT "FK_45145e4953780f3cd5656f0ea6a" FOREIGN KEY ("noteId") REFERENCES "note"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "note_watching" ADD CONSTRAINT "FK_b0134ec406e8d09a540f8182888" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "note_watching" ADD CONSTRAINT "FK_03e7028ab8388a3f5e3ce2a8619" FOREIGN KEY ("noteId") REFERENCES "note"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "note_unread" ADD CONSTRAINT "FK_56b0166d34ddae49d8ef7610bb9" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "note_unread" ADD CONSTRAINT "FK_e637cba4dc4410218c4251260e4" FOREIGN KEY ("noteId") REFERENCES "note"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "notification" ADD CONSTRAINT "FK_3c601b70a1066d2c8b517094cb9" FOREIGN KEY ("notifieeId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "notification" ADD CONSTRAINT "FK_3b4e96eec8d36a8bbb9d02aa710" FOREIGN KEY ("notifierId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "notification" ADD CONSTRAINT "FK_769cb6b73a1efe22ddf733ac453" FOREIGN KEY ("noteId") REFERENCES "note"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "following" ADD CONSTRAINT "FK_24e0042143a18157b234df186c3" FOREIGN KEY ("followeeId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "following" ADD CONSTRAINT "FK_6516c5a6f3c015b4eed39978be5" FOREIGN KEY ("followerId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "muting" ADD CONSTRAINT "FK_ec96b4fed9dae517e0dbbe0675c" FOREIGN KEY ("muteeId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "muting" ADD CONSTRAINT "FK_93060675b4a79a577f31d260c67" FOREIGN KEY ("muterId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "sw_subscription" ADD CONSTRAINT "FK_97754ca6f2baff9b4abb7f853dd" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "blocking" ADD CONSTRAINT "FK_2cd4a2743a99671308f5417759e" FOREIGN KEY ("blockeeId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "blocking" ADD CONSTRAINT "FK_0627125f1a8a42c9a1929edb552" FOREIGN KEY ("blockerId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "user_list" ADD CONSTRAINT "FK_b7fcefbdd1c18dce86687531f99" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "user_list_joining" ADD CONSTRAINT "FK_d844bfc6f3f523a05189076efaa" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "user_list_joining" ADD CONSTRAINT "FK_605472305f26818cc93d1baaa74" FOREIGN KEY ("userListId") REFERENCES "user_list"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "note_favorite" ADD CONSTRAINT "FK_47f4b1892f5d6ba8efb3057d81a" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "note_favorite" ADD CONSTRAINT "FK_0e00498f180193423c992bc4370" FOREIGN KEY ("noteId") REFERENCES "note"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "abuse_user_report" ADD CONSTRAINT "FK_d049123c413e68ca52abe734203" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "abuse_user_report" ADD CONSTRAINT "FK_04cc96756f89d0b7f9473e8cdf3" FOREIGN KEY ("reporterId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "messaging_message" ADD CONSTRAINT "FK_5377c307783fce2b6d352e1203b" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "messaging_message" ADD CONSTRAINT "FK_cac14a4e3944454a5ce7daa5142" FOREIGN KEY ("recipientId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "messaging_message" ADD CONSTRAINT "FK_535def119223ac05ad3fa9ef64b" FOREIGN KEY ("fileId") REFERENCES "drive_file"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "signin" ADD CONSTRAINT "FK_2c308dbdc50d94dc625670055f7" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "auth_session" ADD CONSTRAINT "FK_c072b729d71697f959bde66ade0" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "auth_session" ADD CONSTRAINT "FK_dbe037d4bddd17b03a1dc778dee" FOREIGN KEY ("appId") REFERENCES "app"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "follow_request" ADD CONSTRAINT "FK_12c01c0d1a79f77d9f6c15fadd2" FOREIGN KEY ("followeeId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "follow_request" ADD CONSTRAINT "FK_a7fd92dd6dc519e6fb435dd108f" FOREIGN KEY ("followerId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "reversi_game" ADD CONSTRAINT "FK_f7467510c60a45ce5aca6292743" FOREIGN KEY ("user1Id") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "reversi_game" ADD CONSTRAINT "FK_6649a4e8c5d5cf32fb03b5da9f6" FOREIGN KEY ("user2Id") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "reversi_matching" ADD CONSTRAINT "FK_3b25402709dd9882048c2bbade0" FOREIGN KEY ("parentId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "reversi_matching" ADD CONSTRAINT "FK_e247b23a3c9b45f89ec1299d066" FOREIGN KEY ("childId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "user_note_pining" ADD CONSTRAINT "FK_bfbc6f79ba4007b4ce5097f08d6" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "user_note_pining" ADD CONSTRAINT "FK_68881008f7c3588ad7ecae471cf" FOREIGN KEY ("noteId") REFERENCES "note"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "poll" ADD CONSTRAINT "FK_da851e06d0dfe2ef397d8b1bf1b" FOREIGN KEY ("noteId") REFERENCES "note"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "user_keypair" ADD CONSTRAINT "FK_f4853eb41ab722fe05f81cedeb6" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "user_publickey" ADD CONSTRAINT "FK_10c146e4b39b443ede016f6736d" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "user_profile" ADD CONSTRAINT "FK_51cb79b5555effaf7d69ba1cff9" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "user_profile" DROP CONSTRAINT "FK_51cb79b5555effaf7d69ba1cff9"`); - await queryRunner.query(`ALTER TABLE "user_publickey" DROP CONSTRAINT "FK_10c146e4b39b443ede016f6736d"`); - await queryRunner.query(`ALTER TABLE "user_keypair" DROP CONSTRAINT "FK_f4853eb41ab722fe05f81cedeb6"`); - await queryRunner.query(`ALTER TABLE "poll" DROP CONSTRAINT "FK_da851e06d0dfe2ef397d8b1bf1b"`); - await queryRunner.query(`ALTER TABLE "user_note_pining" DROP CONSTRAINT "FK_68881008f7c3588ad7ecae471cf"`); - await queryRunner.query(`ALTER TABLE "user_note_pining" DROP CONSTRAINT "FK_bfbc6f79ba4007b4ce5097f08d6"`); - await queryRunner.query(`ALTER TABLE "reversi_matching" DROP CONSTRAINT "FK_e247b23a3c9b45f89ec1299d066"`); - await queryRunner.query(`ALTER TABLE "reversi_matching" DROP CONSTRAINT "FK_3b25402709dd9882048c2bbade0"`); - await queryRunner.query(`ALTER TABLE "reversi_game" DROP CONSTRAINT "FK_6649a4e8c5d5cf32fb03b5da9f6"`); - await queryRunner.query(`ALTER TABLE "reversi_game" DROP CONSTRAINT "FK_f7467510c60a45ce5aca6292743"`); - await queryRunner.query(`ALTER TABLE "follow_request" DROP CONSTRAINT "FK_a7fd92dd6dc519e6fb435dd108f"`); - await queryRunner.query(`ALTER TABLE "follow_request" DROP CONSTRAINT "FK_12c01c0d1a79f77d9f6c15fadd2"`); - await queryRunner.query(`ALTER TABLE "auth_session" DROP CONSTRAINT "FK_dbe037d4bddd17b03a1dc778dee"`); - await queryRunner.query(`ALTER TABLE "auth_session" DROP CONSTRAINT "FK_c072b729d71697f959bde66ade0"`); - await queryRunner.query(`ALTER TABLE "signin" DROP CONSTRAINT "FK_2c308dbdc50d94dc625670055f7"`); - await queryRunner.query(`ALTER TABLE "messaging_message" DROP CONSTRAINT "FK_535def119223ac05ad3fa9ef64b"`); - await queryRunner.query(`ALTER TABLE "messaging_message" DROP CONSTRAINT "FK_cac14a4e3944454a5ce7daa5142"`); - await queryRunner.query(`ALTER TABLE "messaging_message" DROP CONSTRAINT "FK_5377c307783fce2b6d352e1203b"`); - await queryRunner.query(`ALTER TABLE "abuse_user_report" DROP CONSTRAINT "FK_04cc96756f89d0b7f9473e8cdf3"`); - await queryRunner.query(`ALTER TABLE "abuse_user_report" DROP CONSTRAINT "FK_d049123c413e68ca52abe734203"`); - await queryRunner.query(`ALTER TABLE "note_favorite" DROP CONSTRAINT "FK_0e00498f180193423c992bc4370"`); - await queryRunner.query(`ALTER TABLE "note_favorite" DROP CONSTRAINT "FK_47f4b1892f5d6ba8efb3057d81a"`); - await queryRunner.query(`ALTER TABLE "user_list_joining" DROP CONSTRAINT "FK_605472305f26818cc93d1baaa74"`); - await queryRunner.query(`ALTER TABLE "user_list_joining" DROP CONSTRAINT "FK_d844bfc6f3f523a05189076efaa"`); - await queryRunner.query(`ALTER TABLE "user_list" DROP CONSTRAINT "FK_b7fcefbdd1c18dce86687531f99"`); - await queryRunner.query(`ALTER TABLE "blocking" DROP CONSTRAINT "FK_0627125f1a8a42c9a1929edb552"`); - await queryRunner.query(`ALTER TABLE "blocking" DROP CONSTRAINT "FK_2cd4a2743a99671308f5417759e"`); - await queryRunner.query(`ALTER TABLE "sw_subscription" DROP CONSTRAINT "FK_97754ca6f2baff9b4abb7f853dd"`); - await queryRunner.query(`ALTER TABLE "muting" DROP CONSTRAINT "FK_93060675b4a79a577f31d260c67"`); - await queryRunner.query(`ALTER TABLE "muting" DROP CONSTRAINT "FK_ec96b4fed9dae517e0dbbe0675c"`); - await queryRunner.query(`ALTER TABLE "following" DROP CONSTRAINT "FK_6516c5a6f3c015b4eed39978be5"`); - await queryRunner.query(`ALTER TABLE "following" DROP CONSTRAINT "FK_24e0042143a18157b234df186c3"`); - await queryRunner.query(`ALTER TABLE "notification" DROP CONSTRAINT "FK_769cb6b73a1efe22ddf733ac453"`); - await queryRunner.query(`ALTER TABLE "notification" DROP CONSTRAINT "FK_3b4e96eec8d36a8bbb9d02aa710"`); - await queryRunner.query(`ALTER TABLE "notification" DROP CONSTRAINT "FK_3c601b70a1066d2c8b517094cb9"`); - await queryRunner.query(`ALTER TABLE "note_unread" DROP CONSTRAINT "FK_e637cba4dc4410218c4251260e4"`); - await queryRunner.query(`ALTER TABLE "note_unread" DROP CONSTRAINT "FK_56b0166d34ddae49d8ef7610bb9"`); - await queryRunner.query(`ALTER TABLE "note_watching" DROP CONSTRAINT "FK_03e7028ab8388a3f5e3ce2a8619"`); - await queryRunner.query(`ALTER TABLE "note_watching" DROP CONSTRAINT "FK_b0134ec406e8d09a540f8182888"`); - await queryRunner.query(`ALTER TABLE "note_reaction" DROP CONSTRAINT "FK_45145e4953780f3cd5656f0ea6a"`); - await queryRunner.query(`ALTER TABLE "note_reaction" DROP CONSTRAINT "FK_13761f64257f40c5636d0ff95ee"`); - await queryRunner.query(`ALTER TABLE "poll_vote" DROP CONSTRAINT "FK_aecfbd5ef60374918e63ee95fa7"`); - await queryRunner.query(`ALTER TABLE "poll_vote" DROP CONSTRAINT "FK_66d2bd2ee31d14bcc23069a89f8"`); - await queryRunner.query(`ALTER TABLE "note" DROP CONSTRAINT "FK_5b87d9d19127bd5d92026017a7b"`); - await queryRunner.query(`ALTER TABLE "note" DROP CONSTRAINT "FK_ec5c201576192ba8904c345c5cc"`); - await queryRunner.query(`ALTER TABLE "note" DROP CONSTRAINT "FK_52ccc804d7c69037d558bac4c96"`); - await queryRunner.query(`ALTER TABLE "note" DROP CONSTRAINT "FK_17cb3553c700a4985dff5a30ff5"`); - await queryRunner.query(`ALTER TABLE "access_token" DROP CONSTRAINT "FK_a3ff16c90cc87a82a0b5959e560"`); - await queryRunner.query(`ALTER TABLE "access_token" DROP CONSTRAINT "FK_9949557d0e1b2c19e5344c171e9"`); - await queryRunner.query(`ALTER TABLE "app" DROP CONSTRAINT "FK_3f5b0899ef90527a3462d7c2cb3"`); - await queryRunner.query(`ALTER TABLE "user" DROP CONSTRAINT "FK_afc64b53f8db3707ceb34eb28e2"`); - await queryRunner.query(`ALTER TABLE "user" DROP CONSTRAINT "FK_58f5c71eaab331645112cf8cfa5"`); - await queryRunner.query(`ALTER TABLE "drive_file" DROP CONSTRAINT "FK_bb90d1956dafc4068c28aa7560a"`); - await queryRunner.query(`ALTER TABLE "drive_file" DROP CONSTRAINT "FK_860fa6f6c7df5bb887249fba22e"`); - await queryRunner.query(`ALTER TABLE "drive_folder" DROP CONSTRAINT "FK_00ceffb0cdc238b3233294f08f2"`); - await queryRunner.query(`ALTER TABLE "drive_folder" DROP CONSTRAINT "FK_f4fc06e49c0171c85f1c48060d2"`); - await queryRunner.query(`DROP TABLE "__chart__users"`); - await queryRunner.query(`DROP TYPE "__chart__users_span_enum"`); - await queryRunner.query(`DROP TABLE "__chart__test"`); - await queryRunner.query(`DROP TYPE "__chart__test_span_enum"`); - await queryRunner.query(`DROP TABLE "__chart__test_unique"`); - await queryRunner.query(`DROP TYPE "__chart__test_unique_span_enum"`); - await queryRunner.query(`DROP TABLE "__chart__test_grouped"`); - await queryRunner.query(`DROP TYPE "__chart__test_grouped_span_enum"`); - await queryRunner.query(`DROP TABLE "__chart__per_user_reaction"`); - await queryRunner.query(`DROP TYPE "__chart__per_user_reaction_span_enum"`); - await queryRunner.query(`DROP TABLE "__chart__per_user_notes"`); - await queryRunner.query(`DROP TYPE "__chart__per_user_notes_span_enum"`); - await queryRunner.query(`DROP TABLE "__chart__per_user_following"`); - await queryRunner.query(`DROP TYPE "__chart__per_user_following_span_enum"`); - await queryRunner.query(`DROP TABLE "__chart__per_user_drive"`); - await queryRunner.query(`DROP TYPE "__chart__per_user_drive_span_enum"`); - await queryRunner.query(`DROP TABLE "__chart__notes"`); - await queryRunner.query(`DROP TYPE "__chart__notes_span_enum"`); - await queryRunner.query(`DROP TABLE "__chart__network"`); - await queryRunner.query(`DROP TYPE "__chart__network_span_enum"`); - await queryRunner.query(`DROP TABLE "__chart__instance"`); - await queryRunner.query(`DROP TYPE "__chart__instance_span_enum"`); - await queryRunner.query(`DROP TABLE "__chart__hashtag"`); - await queryRunner.query(`DROP TYPE "__chart__hashtag_span_enum"`); - await queryRunner.query(`DROP TABLE "__chart__federation"`); - await queryRunner.query(`DROP TYPE "__chart__federation_span_enum"`); - await queryRunner.query(`DROP TABLE "__chart__drive"`); - await queryRunner.query(`DROP TYPE "__chart__drive_span_enum"`); - await queryRunner.query(`DROP TABLE "__chart__active_users"`); - await queryRunner.query(`DROP TYPE "__chart__active_users_span_enum"`); - await queryRunner.query(`DROP INDEX "IDX_dce530b98e454793dac5ec2f5a"`); - await queryRunner.query(`DROP TABLE "user_profile"`); - await queryRunner.query(`DROP INDEX "IDX_171e64971c780ebd23fae140bb"`); - await queryRunner.query(`DROP TABLE "user_publickey"`); - await queryRunner.query(`DROP TABLE "user_keypair"`); - await queryRunner.query(`DROP INDEX "IDX_7fa20a12319c7f6dc3aed98c0a"`); - await queryRunner.query(`DROP INDEX "IDX_0610ebcfcfb4a18441a9bcdab2"`); - await queryRunner.query(`DROP TABLE "poll"`); - await queryRunner.query(`DROP TYPE "poll_notevisibility_enum"`); - await queryRunner.query(`DROP INDEX "IDX_410cd649884b501c02d6e72738"`); - await queryRunner.query(`DROP INDEX "IDX_bfbc6f79ba4007b4ce5097f08d"`); - await queryRunner.query(`DROP TABLE "user_note_pining"`); - await queryRunner.query(`DROP INDEX "IDX_e247b23a3c9b45f89ec1299d06"`); - await queryRunner.query(`DROP INDEX "IDX_3b25402709dd9882048c2bbade"`); - await queryRunner.query(`DROP INDEX "IDX_b604d92d6c7aec38627f6eaf16"`); - await queryRunner.query(`DROP TABLE "reversi_matching"`); - await queryRunner.query(`DROP INDEX "IDX_b46ec40746efceac604142be1c"`); - await queryRunner.query(`DROP TABLE "reversi_game"`); - await queryRunner.query(`DROP INDEX "IDX_4f4d35e1256c84ae3d1f0eab10"`); - await queryRunner.query(`DROP INDEX "IDX_5900e907bb46516ddf2871327c"`); - await queryRunner.query(`DROP INDEX "IDX_b37dafc86e9af007e3295c2781"`); - await queryRunner.query(`DROP TABLE "emoji"`); - await queryRunner.query(`DROP INDEX "IDX_d54a512b822fac7ed52800f6b4"`); - await queryRunner.query(`DROP INDEX "IDX_a7fd92dd6dc519e6fb435dd108"`); - await queryRunner.query(`DROP INDEX "IDX_12c01c0d1a79f77d9f6c15fadd"`); - await queryRunner.query(`DROP TABLE "follow_request"`); - await queryRunner.query(`DROP INDEX "IDX_62cb09e1129f6ec024ef66e183"`); - await queryRunner.query(`DROP TABLE "auth_session"`); - await queryRunner.query(`DROP INDEX "IDX_2c308dbdc50d94dc625670055f"`); - await queryRunner.query(`DROP TABLE "signin"`); - await queryRunner.query(`DROP INDEX "IDX_cac14a4e3944454a5ce7daa514"`); - await queryRunner.query(`DROP INDEX "IDX_5377c307783fce2b6d352e1203"`); - await queryRunner.query(`DROP INDEX "IDX_e21cd3646e52ef9c94aaf17c2e"`); - await queryRunner.query(`DROP TABLE "messaging_message"`); - await queryRunner.query(`DROP INDEX "IDX_0ff69e8dfa9fe31bb4a4660f59"`); - await queryRunner.query(`DROP TABLE "registration_ticket"`); - await queryRunner.query(`DROP INDEX "IDX_5cd442c3b2e74fdd99dae20243"`); - await queryRunner.query(`DROP INDEX "IDX_04cc96756f89d0b7f9473e8cdf"`); - await queryRunner.query(`DROP INDEX "IDX_d049123c413e68ca52abe73420"`); - await queryRunner.query(`DROP INDEX "IDX_db2098070b2b5a523c58181f74"`); - await queryRunner.query(`DROP TABLE "abuse_user_report"`); - await queryRunner.query(`DROP INDEX "IDX_0f4fb9ad355f3effff221ef245"`); - await queryRunner.query(`DROP INDEX "IDX_47f4b1892f5d6ba8efb3057d81"`); - await queryRunner.query(`DROP TABLE "note_favorite"`); - await queryRunner.query(`DROP INDEX "IDX_0b03cbcd7e6a7ce068efa8ecc2"`); - await queryRunner.query(`DROP INDEX "IDX_0c44bf4f680964145f2a68a341"`); - await queryRunner.query(`DROP INDEX "IDX_d57f9030cd3af7f63ffb1c267c"`); - await queryRunner.query(`DROP INDEX "IDX_4c02d38a976c3ae132228c6fce"`); - await queryRunner.query(`DROP INDEX "IDX_0e206cec573f1edff4a3062923"`); - await queryRunner.query(`DROP INDEX "IDX_2710a55f826ee236ea1a62698f"`); - await queryRunner.query(`DROP INDEX "IDX_347fec870eafea7b26c8a73bac"`); - await queryRunner.query(`DROP TABLE "hashtag"`); - await queryRunner.query(`DROP INDEX "IDX_605472305f26818cc93d1baaa7"`); - await queryRunner.query(`DROP INDEX "IDX_d844bfc6f3f523a05189076efa"`); - await queryRunner.query(`DROP TABLE "user_list_joining"`); - await queryRunner.query(`DROP INDEX "IDX_b7fcefbdd1c18dce86687531f9"`); - await queryRunner.query(`DROP TABLE "user_list"`); - await queryRunner.query(`DROP INDEX "IDX_98a1bc5cb30dfd159de056549f"`); - await queryRunner.query(`DROP INDEX "IDX_0627125f1a8a42c9a1929edb55"`); - await queryRunner.query(`DROP INDEX "IDX_2cd4a2743a99671308f5417759"`); - await queryRunner.query(`DROP INDEX "IDX_b9a354f7941c1e779f3b33aea6"`); - await queryRunner.query(`DROP TABLE "blocking"`); - await queryRunner.query(`DROP INDEX "IDX_97754ca6f2baff9b4abb7f853d"`); - await queryRunner.query(`DROP TABLE "sw_subscription"`); - await queryRunner.query(`DROP INDEX "IDX_1eb9d9824a630321a29fd3b290"`); - await queryRunner.query(`DROP INDEX "IDX_93060675b4a79a577f31d260c6"`); - await queryRunner.query(`DROP INDEX "IDX_ec96b4fed9dae517e0dbbe0675"`); - await queryRunner.query(`DROP INDEX "IDX_f86d57fbca33c7a4e6897490cc"`); - await queryRunner.query(`DROP TABLE "muting"`); - await queryRunner.query(`DROP INDEX "IDX_8d5afc98982185799b160e10eb"`); - await queryRunner.query(`DROP INDEX "IDX_2cd3b2a6b4cf0b910b260afe08"`); - await queryRunner.query(`DROP TABLE "instance"`); - await queryRunner.query(`DROP INDEX "IDX_307be5f1d1252e0388662acb96"`); - await queryRunner.query(`DROP INDEX "IDX_6516c5a6f3c015b4eed39978be"`); - await queryRunner.query(`DROP INDEX "IDX_24e0042143a18157b234df186c"`); - await queryRunner.query(`DROP INDEX "IDX_582f8fab771a9040a12961f3e7"`); - await queryRunner.query(`DROP TABLE "following"`); - await queryRunner.query(`DROP TABLE "meta"`); - await queryRunner.query(`DROP INDEX "IDX_3c601b70a1066d2c8b517094cb"`); - await queryRunner.query(`DROP INDEX "IDX_b11a5e627c41d4dc3170f1d370"`); - await queryRunner.query(`DROP TABLE "notification"`); - await queryRunner.query(`DROP INDEX "IDX_d908433a4953cc13216cd9c274"`); - await queryRunner.query(`DROP INDEX "IDX_e637cba4dc4410218c4251260e"`); - await queryRunner.query(`DROP INDEX "IDX_56b0166d34ddae49d8ef7610bb"`); - await queryRunner.query(`DROP TABLE "note_unread"`); - await queryRunner.query(`DROP INDEX "IDX_a42c93c69989ce1d09959df4cf"`); - await queryRunner.query(`DROP INDEX "IDX_44499765eec6b5489d72c4253b"`); - await queryRunner.query(`DROP INDEX "IDX_03e7028ab8388a3f5e3ce2a861"`); - await queryRunner.query(`DROP INDEX "IDX_b0134ec406e8d09a540f818288"`); - await queryRunner.query(`DROP INDEX "IDX_318cdf42a9cfc11f479bd802bb"`); - await queryRunner.query(`DROP TABLE "note_watching"`); - await queryRunner.query(`DROP INDEX "IDX_ad0c221b25672daf2df320a817"`); - await queryRunner.query(`DROP INDEX "IDX_45145e4953780f3cd5656f0ea6"`); - await queryRunner.query(`DROP INDEX "IDX_13761f64257f40c5636d0ff95e"`); - await queryRunner.query(`DROP INDEX "IDX_01f4581f114e0ebd2bbb876f0b"`); - await queryRunner.query(`DROP TABLE "note_reaction"`); - await queryRunner.query(`DROP INDEX "IDX_50bd7164c5b78f1f4a42c4d21f"`); - await queryRunner.query(`DROP INDEX "IDX_aecfbd5ef60374918e63ee95fa"`); - await queryRunner.query(`DROP INDEX "IDX_66d2bd2ee31d14bcc23069a89f"`); - await queryRunner.query(`DROP INDEX "IDX_0fb627e1c2f753262a74f0562d"`); - await queryRunner.query(`DROP TABLE "poll_vote"`); - await queryRunner.query(`DROP INDEX "IDX_7125a826ab192eb27e11d358a5"`); - await queryRunner.query(`DROP INDEX "IDX_88937d94d7443d9a99a76fa5c0"`); - await queryRunner.query(`DROP INDEX "IDX_54ebcb6d27222913b908d56fd8"`); - await queryRunner.query(`DROP INDEX "IDX_796a8c03959361f97dc2be1d5c"`); - await queryRunner.query(`DROP INDEX "IDX_25dfc71b0369b003a4cd434d0b"`); - await queryRunner.query(`DROP INDEX "IDX_51c063b6a133a9cb87145450f5"`); - await queryRunner.query(`DROP INDEX "IDX_153536c67d05e9adb24e99fc2b"`); - await queryRunner.query(`DROP INDEX "IDX_5b87d9d19127bd5d92026017a7"`); - await queryRunner.query(`DROP INDEX "IDX_52ccc804d7c69037d558bac4c9"`); - await queryRunner.query(`DROP INDEX "IDX_17cb3553c700a4985dff5a30ff"`); - await queryRunner.query(`DROP INDEX "IDX_e7c0567f5261063592f022e9b5"`); - await queryRunner.query(`DROP TABLE "note"`); - await queryRunner.query(`DROP TYPE "note_visibility_enum"`); - await queryRunner.query(`DROP INDEX "IDX_9949557d0e1b2c19e5344c171e"`); - await queryRunner.query(`DROP INDEX "IDX_64c327441248bae40f7d92f34f"`); - await queryRunner.query(`DROP INDEX "IDX_70ba8f6af34bc924fc9e12adb8"`); - await queryRunner.query(`DROP TABLE "access_token"`); - await queryRunner.query(`DROP INDEX "IDX_f49922d511d666848f250663c4"`); - await queryRunner.query(`DROP INDEX "IDX_3f5b0899ef90527a3462d7c2cb"`); - await queryRunner.query(`DROP INDEX "IDX_048a757923ed8b157e9895da53"`); - await queryRunner.query(`DROP TABLE "app"`); - await queryRunner.query(`DROP INDEX "IDX_5deb01ae162d1d70b80d064c27"`); - await queryRunner.query(`DROP INDEX "IDX_a854e557b1b14814750c7c7b0c"`); - await queryRunner.query(`DROP INDEX "IDX_be623adaa4c566baf5d29ce0c8"`); - await queryRunner.query(`DROP INDEX "IDX_3252a5df8d5bbd16b281f7799e"`); - await queryRunner.query(`DROP INDEX "IDX_fa99d777623947a5b05f394cae"`); - await queryRunner.query(`DROP INDEX "IDX_a27b942a0d6dcff90e3ee9b5e8"`); - await queryRunner.query(`DROP INDEX "IDX_80ca6e6ef65fb9ef34ea8c90f4"`); - await queryRunner.query(`DROP INDEX "IDX_e11e649824a45d8ed01d597fd9"`); - await queryRunner.query(`DROP TABLE "user"`); - await queryRunner.query(`DROP INDEX "IDX_bb90d1956dafc4068c28aa7560"`); - await queryRunner.query(`DROP INDEX "IDX_e5848eac4940934e23dbc17581"`); - await queryRunner.query(`DROP INDEX "IDX_c55b2b7c284d9fef98026fc88e"`); - await queryRunner.query(`DROP INDEX "IDX_e74022ce9a074b3866f70e0d27"`); - await queryRunner.query(`DROP INDEX "IDX_d85a184c2540d2deba33daf642"`); - await queryRunner.query(`DROP INDEX "IDX_a40b8df8c989d7db937ea27cf6"`); - await queryRunner.query(`DROP INDEX "IDX_37bb9a1b4585f8a3beb24c62d6"`); - await queryRunner.query(`DROP INDEX "IDX_92779627994ac79277f070c91e"`); - await queryRunner.query(`DROP INDEX "IDX_860fa6f6c7df5bb887249fba22"`); - await queryRunner.query(`DROP INDEX "IDX_c8dfad3b72196dd1d6b5db168a"`); - await queryRunner.query(`DROP TABLE "drive_file"`); - await queryRunner.query(`DROP INDEX "IDX_00ceffb0cdc238b3233294f08f"`); - await queryRunner.query(`DROP INDEX "IDX_f4fc06e49c0171c85f1c48060d"`); - await queryRunner.query(`DROP INDEX "IDX_02878d441ceae15ce060b73daf"`); - await queryRunner.query(`DROP TABLE "drive_folder"`); - await queryRunner.query(`DROP INDEX "IDX_584b536b49e53ac81beb39a177"`); - await queryRunner.query(`DROP INDEX "IDX_8cb40cfc8f3c28261e6f887b03"`); - await queryRunner.query(`DROP INDEX "IDX_8e4eb51a35d81b64dda28eed0a"`); - await queryRunner.query(`DROP TABLE "log"`); - await queryRunner.query(`DROP TYPE "log_level_enum"`); - } + async up(queryRunner) { + await queryRunner.query( + `CREATE TYPE "log_level_enum" AS ENUM('error', 'warning', 'info', 'success', 'debug')`, + ); + await queryRunner.query( + `CREATE TABLE "log" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "domain" character varying(64) array NOT NULL DEFAULT '{}'::varchar[], "level" "log_level_enum" NOT NULL, "worker" character varying(8) NOT NULL, "machine" character varying(128) NOT NULL, "message" character varying(1024) NOT NULL, "data" jsonb NOT NULL DEFAULT '{}', CONSTRAINT "PK_350604cbdf991d5930d9e618fbd" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE INDEX "IDX_8e4eb51a35d81b64dda28eed0a" ON "log" ("createdAt") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_8cb40cfc8f3c28261e6f887b03" ON "log" ("domain") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_584b536b49e53ac81beb39a177" ON "log" ("level") `, + ); + await queryRunner.query( + `CREATE TABLE "drive_folder" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "name" character varying(128) NOT NULL, "userId" character varying(32), "parentId" character varying(32), CONSTRAINT "PK_7a0c089191f5ebdc214e0af808a" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE INDEX "IDX_02878d441ceae15ce060b73daf" ON "drive_folder" ("createdAt") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_f4fc06e49c0171c85f1c48060d" ON "drive_folder" ("userId") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_00ceffb0cdc238b3233294f08f" ON "drive_folder" ("parentId") `, + ); + await queryRunner.query( + `CREATE TABLE "drive_file" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "userId" character varying(32), "userHost" character varying(128), "md5" character varying(32) NOT NULL, "name" character varying(256) NOT NULL, "type" character varying(128) NOT NULL, "size" integer NOT NULL, "comment" character varying(512), "properties" jsonb NOT NULL DEFAULT '{}', "storedInternal" boolean NOT NULL, "url" character varying(512) NOT NULL, "thumbnailUrl" character varying(512), "webpublicUrl" character varying(512), "accessKey" character varying(256), "thumbnailAccessKey" character varying(256), "webpublicAccessKey" character varying(256), "uri" character varying(512), "src" character varying(512), "folderId" character varying(32), "isSensitive" boolean NOT NULL DEFAULT false, "isLink" boolean NOT NULL DEFAULT false, CONSTRAINT "PK_43ddaaaf18c9e68029b7cbb032e" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE INDEX "IDX_c8dfad3b72196dd1d6b5db168a" ON "drive_file" ("createdAt") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_860fa6f6c7df5bb887249fba22" ON "drive_file" ("userId") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_92779627994ac79277f070c91e" ON "drive_file" ("userHost") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_37bb9a1b4585f8a3beb24c62d6" ON "drive_file" ("md5") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_a40b8df8c989d7db937ea27cf6" ON "drive_file" ("type") `, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_d85a184c2540d2deba33daf642" ON "drive_file" ("accessKey") `, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_e74022ce9a074b3866f70e0d27" ON "drive_file" ("thumbnailAccessKey") `, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_c55b2b7c284d9fef98026fc88e" ON "drive_file" ("webpublicAccessKey") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_e5848eac4940934e23dbc17581" ON "drive_file" ("uri") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_bb90d1956dafc4068c28aa7560" ON "drive_file" ("folderId") `, + ); + await queryRunner.query( + `CREATE TABLE "user" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "updatedAt" TIMESTAMP WITH TIME ZONE, "lastFetchedAt" TIMESTAMP WITH TIME ZONE, "username" character varying(128) NOT NULL, "usernameLower" character varying(128) NOT NULL, "name" character varying(128), "followersCount" integer NOT NULL DEFAULT 0, "followingCount" integer NOT NULL DEFAULT 0, "notesCount" integer NOT NULL DEFAULT 0, "avatarId" character varying(32), "bannerId" character varying(32), "tags" character varying(128) array NOT NULL DEFAULT '{}'::varchar[], "avatarUrl" character varying(512), "bannerUrl" character varying(512), "avatarColor" character varying(32), "bannerColor" character varying(32), "isSuspended" boolean NOT NULL DEFAULT false, "isSilenced" boolean NOT NULL DEFAULT false, "isLocked" boolean NOT NULL DEFAULT false, "isBot" boolean NOT NULL DEFAULT false, "isCat" boolean NOT NULL DEFAULT false, "isAdmin" boolean NOT NULL DEFAULT false, "isModerator" boolean NOT NULL DEFAULT false, "isVerified" boolean NOT NULL DEFAULT false, "emojis" character varying(128) array NOT NULL DEFAULT '{}'::varchar[], "host" character varying(128), "inbox" character varying(512), "sharedInbox" character varying(512), "featured" character varying(512), "uri" character varying(512), "token" character(16), CONSTRAINT "UQ_a854e557b1b14814750c7c7b0c9" UNIQUE ("token"), CONSTRAINT "REL_58f5c71eaab331645112cf8cfa" UNIQUE ("avatarId"), CONSTRAINT "REL_afc64b53f8db3707ceb34eb28e" UNIQUE ("bannerId"), CONSTRAINT "PK_cace4a159ff9f2512dd42373760" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE INDEX "IDX_e11e649824a45d8ed01d597fd9" ON "user" ("createdAt") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_80ca6e6ef65fb9ef34ea8c90f4" ON "user" ("updatedAt") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_a27b942a0d6dcff90e3ee9b5e8" ON "user" ("usernameLower") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_fa99d777623947a5b05f394cae" ON "user" ("tags") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_3252a5df8d5bbd16b281f7799e" ON "user" ("host") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_be623adaa4c566baf5d29ce0c8" ON "user" ("uri") `, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_a854e557b1b14814750c7c7b0c" ON "user" ("token") `, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_5deb01ae162d1d70b80d064c27" ON "user" ("usernameLower", "host") `, + ); + await queryRunner.query( + `CREATE TABLE "app" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "userId" character varying(32), "secret" character varying(64) NOT NULL, "name" character varying(128) NOT NULL, "description" character varying(512) NOT NULL, "permission" character varying(64) array NOT NULL, "callbackUrl" character varying(512), CONSTRAINT "PK_9478629fc093d229df09e560aea" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE INDEX "IDX_048a757923ed8b157e9895da53" ON "app" ("createdAt") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_3f5b0899ef90527a3462d7c2cb" ON "app" ("userId") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_f49922d511d666848f250663c4" ON "app" ("secret") `, + ); + await queryRunner.query( + `CREATE TABLE "access_token" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "token" character varying(128) NOT NULL, "hash" character varying(128) NOT NULL, "userId" character varying(32) NOT NULL, "appId" character varying(32) NOT NULL, CONSTRAINT "PK_f20f028607b2603deabd8182d12" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE INDEX "IDX_70ba8f6af34bc924fc9e12adb8" ON "access_token" ("token") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_64c327441248bae40f7d92f34f" ON "access_token" ("hash") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_9949557d0e1b2c19e5344c171e" ON "access_token" ("userId") `, + ); + await queryRunner.query( + `CREATE TYPE "note_visibility_enum" AS ENUM('public', 'home', 'followers', 'specified')`, + ); + await queryRunner.query( + `CREATE TABLE "note" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "replyId" character varying(32), "renoteId" character varying(32), "text" text, "name" character varying(256), "cw" character varying(512), "appId" character varying(32), "userId" character varying(32) NOT NULL, "viaMobile" boolean NOT NULL DEFAULT false, "localOnly" boolean NOT NULL DEFAULT false, "renoteCount" smallint NOT NULL DEFAULT 0, "repliesCount" smallint NOT NULL DEFAULT 0, "reactions" jsonb NOT NULL DEFAULT '{}', "visibility" "note_visibility_enum" NOT NULL, "uri" character varying(512), "score" integer NOT NULL DEFAULT 0, "fileIds" character varying(32) array NOT NULL DEFAULT '{}'::varchar[], "attachedFileTypes" character varying(256) array NOT NULL DEFAULT '{}'::varchar[], "visibleUserIds" character varying(32) array NOT NULL DEFAULT '{}'::varchar[], "mentions" character varying(32) array NOT NULL DEFAULT '{}'::varchar[], "mentionedRemoteUsers" text NOT NULL DEFAULT '[]', "emojis" character varying(128) array NOT NULL DEFAULT '{}'::varchar[], "tags" character varying(128) array NOT NULL DEFAULT '{}'::varchar[], "hasPoll" boolean NOT NULL DEFAULT false, "geo" jsonb DEFAULT null, "userHost" character varying(128), "replyUserId" character varying(32), "replyUserHost" character varying(128), "renoteUserId" character varying(32), "renoteUserHost" character varying(128), CONSTRAINT "PK_96d0c172a4fba276b1bbed43058" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE INDEX "IDX_e7c0567f5261063592f022e9b5" ON "note" ("createdAt") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_17cb3553c700a4985dff5a30ff" ON "note" ("replyId") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_52ccc804d7c69037d558bac4c9" ON "note" ("renoteId") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_5b87d9d19127bd5d92026017a7" ON "note" ("userId") `, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_153536c67d05e9adb24e99fc2b" ON "note" ("uri") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_51c063b6a133a9cb87145450f5" ON "note" ("fileIds") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_25dfc71b0369b003a4cd434d0b" ON "note" ("attachedFileTypes") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_796a8c03959361f97dc2be1d5c" ON "note" ("visibleUserIds") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_54ebcb6d27222913b908d56fd8" ON "note" ("mentions") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_88937d94d7443d9a99a76fa5c0" ON "note" ("tags") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_7125a826ab192eb27e11d358a5" ON "note" ("userHost") `, + ); + await queryRunner.query( + `CREATE TABLE "poll_vote" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "userId" character varying(32) NOT NULL, "noteId" character varying(32) NOT NULL, "choice" integer NOT NULL, CONSTRAINT "PK_fd002d371201c472490ba89c6a0" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE INDEX "IDX_0fb627e1c2f753262a74f0562d" ON "poll_vote" ("createdAt") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_66d2bd2ee31d14bcc23069a89f" ON "poll_vote" ("userId") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_aecfbd5ef60374918e63ee95fa" ON "poll_vote" ("noteId") `, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_50bd7164c5b78f1f4a42c4d21f" ON "poll_vote" ("userId", "noteId", "choice") `, + ); + await queryRunner.query( + `CREATE TABLE "note_reaction" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "userId" character varying(32) NOT NULL, "noteId" character varying(32) NOT NULL, "reaction" character varying(128) NOT NULL, CONSTRAINT "PK_767ec729b108799b587a3fcc9cf" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE INDEX "IDX_01f4581f114e0ebd2bbb876f0b" ON "note_reaction" ("createdAt") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_13761f64257f40c5636d0ff95e" ON "note_reaction" ("userId") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_45145e4953780f3cd5656f0ea6" ON "note_reaction" ("noteId") `, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_ad0c221b25672daf2df320a817" ON "note_reaction" ("userId", "noteId") `, + ); + await queryRunner.query( + `CREATE TABLE "note_watching" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "userId" character varying(32) NOT NULL, "noteId" character varying(32) NOT NULL, "noteUserId" character varying(32) NOT NULL, CONSTRAINT "PK_49286fdb23725945a74aa27d757" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE INDEX "IDX_318cdf42a9cfc11f479bd802bb" ON "note_watching" ("createdAt") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_b0134ec406e8d09a540f818288" ON "note_watching" ("userId") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_03e7028ab8388a3f5e3ce2a861" ON "note_watching" ("noteId") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_44499765eec6b5489d72c4253b" ON "note_watching" ("noteUserId") `, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_a42c93c69989ce1d09959df4cf" ON "note_watching" ("userId", "noteId") `, + ); + await queryRunner.query( + `CREATE TABLE "note_unread" ("id" character varying(32) NOT NULL, "userId" character varying(32) NOT NULL, "noteId" character varying(32) NOT NULL, "noteUserId" character varying(32) NOT NULL, "isSpecified" boolean NOT NULL, CONSTRAINT "PK_1904eda61a784f57e6e51fa9c1f" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE INDEX "IDX_56b0166d34ddae49d8ef7610bb" ON "note_unread" ("userId") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_e637cba4dc4410218c4251260e" ON "note_unread" ("noteId") `, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_d908433a4953cc13216cd9c274" ON "note_unread" ("userId", "noteId") `, + ); + await queryRunner.query( + `CREATE TABLE "notification" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "notifieeId" character varying(32) NOT NULL, "notifierId" character varying(32) NOT NULL, "type" character varying(32) NOT NULL, "isRead" boolean NOT NULL DEFAULT false, "noteId" character varying(32), "reaction" character varying(128), "choice" integer, CONSTRAINT "PK_705b6c7cdf9b2c2ff7ac7872cb7" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE INDEX "IDX_b11a5e627c41d4dc3170f1d370" ON "notification" ("createdAt") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_3c601b70a1066d2c8b517094cb" ON "notification" ("notifieeId") `, + ); + await queryRunner.query( + `CREATE TABLE "meta" ("id" character varying(32) NOT NULL, "name" character varying(128), "description" character varying(1024), "maintainerName" character varying(128), "maintainerEmail" character varying(128), "announcements" jsonb NOT NULL DEFAULT '[]', "disableRegistration" boolean NOT NULL DEFAULT false, "disableLocalTimeline" boolean NOT NULL DEFAULT false, "disableGlobalTimeline" boolean NOT NULL DEFAULT false, "enableEmojiReaction" boolean NOT NULL DEFAULT true, "useStarForReactionFallback" boolean NOT NULL DEFAULT false, "langs" character varying(64) array NOT NULL DEFAULT '{}'::varchar[], "hiddenTags" character varying(256) array NOT NULL DEFAULT '{}'::varchar[], "blockedHosts" character varying(256) array NOT NULL DEFAULT '{}'::varchar[], "mascotImageUrl" character varying(512) DEFAULT '/static-assets/badges/info.png', "bannerUrl" character varying(512), "errorImageUrl" character varying(512) DEFAULT '/static-assets/badges/error.png', "iconUrl" character varying(512), "cacheRemoteFiles" boolean NOT NULL DEFAULT false, "proxyAccount" character varying(128), "enableRecaptcha" boolean NOT NULL DEFAULT false, "recaptchaSiteKey" character varying(64), "recaptchaSecretKey" character varying(64), "localDriveCapacityMb" integer NOT NULL DEFAULT 1024, "remoteDriveCapacityMb" integer NOT NULL DEFAULT 32, "maxNoteTextLength" integer NOT NULL DEFAULT 500, "summalyProxy" character varying(128), "enableEmail" boolean NOT NULL DEFAULT false, "email" character varying(128), "smtpSecure" boolean NOT NULL DEFAULT false, "smtpHost" character varying(128), "smtpPort" integer, "smtpUser" character varying(128), "smtpPass" character varying(128), "enableServiceWorker" boolean NOT NULL DEFAULT false, "swPublicKey" character varying(128), "swPrivateKey" character varying(128), "enableTwitterIntegration" boolean NOT NULL DEFAULT false, "twitterConsumerKey" character varying(128), "twitterConsumerSecret" character varying(128), "enableGithubIntegration" boolean NOT NULL DEFAULT false, "githubClientId" character varying(128), "githubClientSecret" character varying(128), "enableDiscordIntegration" boolean NOT NULL DEFAULT false, "discordClientId" character varying(128), "discordClientSecret" character varying(128), CONSTRAINT "PK_c4c17a6c2bd7651338b60fc590b" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE TABLE "following" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "followeeId" character varying(32) NOT NULL, "followerId" character varying(32) NOT NULL, "followerHost" character varying(128), "followerInbox" character varying(512), "followerSharedInbox" character varying(512), "followeeHost" character varying(128), "followeeInbox" character varying(512), "followeeSharedInbox" character varying(512), CONSTRAINT "PK_c76c6e044bdf76ecf8bfb82a645" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE INDEX "IDX_582f8fab771a9040a12961f3e7" ON "following" ("createdAt") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_24e0042143a18157b234df186c" ON "following" ("followeeId") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_6516c5a6f3c015b4eed39978be" ON "following" ("followerId") `, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_307be5f1d1252e0388662acb96" ON "following" ("followerId", "followeeId") `, + ); + await queryRunner.query( + `CREATE TABLE "instance" ("id" character varying(32) NOT NULL, "caughtAt" TIMESTAMP WITH TIME ZONE NOT NULL, "host" character varying(128) NOT NULL, "system" character varying(64), "usersCount" integer NOT NULL DEFAULT 0, "notesCount" integer NOT NULL DEFAULT 0, "followingCount" integer NOT NULL DEFAULT 0, "followersCount" integer NOT NULL DEFAULT 0, "driveUsage" integer NOT NULL DEFAULT 0, "driveFiles" integer NOT NULL DEFAULT 0, "latestRequestSentAt" TIMESTAMP WITH TIME ZONE, "latestStatus" integer, "latestRequestReceivedAt" TIMESTAMP WITH TIME ZONE, "lastCommunicatedAt" TIMESTAMP WITH TIME ZONE NOT NULL, "isNotResponding" boolean NOT NULL DEFAULT false, "isMarkedAsClosed" boolean NOT NULL DEFAULT false, CONSTRAINT "PK_eaf60e4a0c399c9935413e06474" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE INDEX "IDX_2cd3b2a6b4cf0b910b260afe08" ON "instance" ("caughtAt") `, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_8d5afc98982185799b160e10eb" ON "instance" ("host") `, + ); + await queryRunner.query( + `CREATE TABLE "muting" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "muteeId" character varying(32) NOT NULL, "muterId" character varying(32) NOT NULL, CONSTRAINT "PK_2e92d06c8b5c602eeb27ca9ba48" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE INDEX "IDX_f86d57fbca33c7a4e6897490cc" ON "muting" ("createdAt") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_ec96b4fed9dae517e0dbbe0675" ON "muting" ("muteeId") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_93060675b4a79a577f31d260c6" ON "muting" ("muterId") `, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_1eb9d9824a630321a29fd3b290" ON "muting" ("muterId", "muteeId") `, + ); + await queryRunner.query( + `CREATE TABLE "sw_subscription" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "userId" character varying(32) NOT NULL, "endpoint" character varying(512) NOT NULL, "auth" character varying(256) NOT NULL, "publickey" character varying(128) NOT NULL, CONSTRAINT "PK_e8f763631530051b95eb6279b91" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE INDEX "IDX_97754ca6f2baff9b4abb7f853d" ON "sw_subscription" ("userId") `, + ); + await queryRunner.query( + `CREATE TABLE "blocking" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "blockeeId" character varying(32) NOT NULL, "blockerId" character varying(32) NOT NULL, CONSTRAINT "PK_e5d9a541cc1965ee7e048ea09dd" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE INDEX "IDX_b9a354f7941c1e779f3b33aea6" ON "blocking" ("createdAt") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_2cd4a2743a99671308f5417759" ON "blocking" ("blockeeId") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_0627125f1a8a42c9a1929edb55" ON "blocking" ("blockerId") `, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_98a1bc5cb30dfd159de056549f" ON "blocking" ("blockerId", "blockeeId") `, + ); + await queryRunner.query( + `CREATE TABLE "user_list" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "userId" character varying(32) NOT NULL, "name" character varying(128) NOT NULL, CONSTRAINT "PK_87bab75775fd9b1ff822b656402" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE INDEX "IDX_b7fcefbdd1c18dce86687531f9" ON "user_list" ("userId") `, + ); + await queryRunner.query( + `CREATE TABLE "user_list_joining" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "userId" character varying(32) NOT NULL, "userListId" character varying(32) NOT NULL, CONSTRAINT "PK_11abb3768da1c5f8de101c9df45" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE INDEX "IDX_d844bfc6f3f523a05189076efa" ON "user_list_joining" ("userId") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_605472305f26818cc93d1baaa7" ON "user_list_joining" ("userListId") `, + ); + await queryRunner.query( + `CREATE TABLE "hashtag" ("id" character varying(32) NOT NULL, "name" character varying(128) NOT NULL, "mentionedUserIds" character varying(32) array NOT NULL, "mentionedUsersCount" integer NOT NULL DEFAULT 0, "mentionedLocalUserIds" character varying(32) array NOT NULL, "mentionedLocalUsersCount" integer NOT NULL DEFAULT 0, "mentionedRemoteUserIds" character varying(32) array NOT NULL, "mentionedRemoteUsersCount" integer NOT NULL DEFAULT 0, "attachedUserIds" character varying(32) array NOT NULL, "attachedUsersCount" integer NOT NULL DEFAULT 0, "attachedLocalUserIds" character varying(32) array NOT NULL, "attachedLocalUsersCount" integer NOT NULL DEFAULT 0, "attachedRemoteUserIds" character varying(32) array NOT NULL, "attachedRemoteUsersCount" integer NOT NULL DEFAULT 0, CONSTRAINT "PK_cb36eb8af8412bfa978f1165d78" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_347fec870eafea7b26c8a73bac" ON "hashtag" ("name") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_2710a55f826ee236ea1a62698f" ON "hashtag" ("mentionedUsersCount") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_0e206cec573f1edff4a3062923" ON "hashtag" ("mentionedLocalUsersCount") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_4c02d38a976c3ae132228c6fce" ON "hashtag" ("mentionedRemoteUsersCount") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_d57f9030cd3af7f63ffb1c267c" ON "hashtag" ("attachedUsersCount") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_0c44bf4f680964145f2a68a341" ON "hashtag" ("attachedLocalUsersCount") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_0b03cbcd7e6a7ce068efa8ecc2" ON "hashtag" ("attachedRemoteUsersCount") `, + ); + await queryRunner.query( + `CREATE TABLE "note_favorite" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "userId" character varying(32) NOT NULL, "noteId" character varying(32) NOT NULL, CONSTRAINT "PK_af0da35a60b9fa4463a62082b36" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE INDEX "IDX_47f4b1892f5d6ba8efb3057d81" ON "note_favorite" ("userId") `, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_0f4fb9ad355f3effff221ef245" ON "note_favorite" ("userId", "noteId") `, + ); + await queryRunner.query( + `CREATE TABLE "abuse_user_report" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "userId" character varying(32) NOT NULL, "reporterId" character varying(32) NOT NULL, "comment" character varying(512) NOT NULL, CONSTRAINT "PK_87873f5f5cc5c321a1306b2d18c" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE INDEX "IDX_db2098070b2b5a523c58181f74" ON "abuse_user_report" ("createdAt") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_d049123c413e68ca52abe73420" ON "abuse_user_report" ("userId") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_04cc96756f89d0b7f9473e8cdf" ON "abuse_user_report" ("reporterId") `, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_5cd442c3b2e74fdd99dae20243" ON "abuse_user_report" ("userId", "reporterId") `, + ); + await queryRunner.query( + `CREATE TABLE "registration_ticket" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "code" character varying(64) NOT NULL, CONSTRAINT "PK_f11696b6fafcf3662d4292734f8" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_0ff69e8dfa9fe31bb4a4660f59" ON "registration_ticket" ("code") `, + ); + await queryRunner.query( + `CREATE TABLE "messaging_message" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "userId" character varying(32) NOT NULL, "recipientId" character varying(32) NOT NULL, "text" character varying(4096), "isRead" boolean NOT NULL DEFAULT false, "fileId" character varying(32), CONSTRAINT "PK_db398fd79dc95d0eb8c30456eaa" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE INDEX "IDX_e21cd3646e52ef9c94aaf17c2e" ON "messaging_message" ("createdAt") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_5377c307783fce2b6d352e1203" ON "messaging_message" ("userId") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_cac14a4e3944454a5ce7daa514" ON "messaging_message" ("recipientId") `, + ); + await queryRunner.query( + `CREATE TABLE "signin" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "userId" character varying(32) NOT NULL, "ip" character varying(128) NOT NULL, "headers" jsonb NOT NULL, "success" boolean NOT NULL, CONSTRAINT "PK_9e96ddc025712616fc492b3b588" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE INDEX "IDX_2c308dbdc50d94dc625670055f" ON "signin" ("userId") `, + ); + await queryRunner.query( + `CREATE TABLE "auth_session" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "token" character varying(128) NOT NULL, "userId" character varying(32), "appId" character varying(32) NOT NULL, CONSTRAINT "PK_19354ed146424a728c1112a8cbf" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE INDEX "IDX_62cb09e1129f6ec024ef66e183" ON "auth_session" ("token") `, + ); + await queryRunner.query( + `CREATE TABLE "follow_request" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "followeeId" character varying(32) NOT NULL, "followerId" character varying(32) NOT NULL, "requestId" character varying(128), "followerHost" character varying(128), "followerInbox" character varying(512), "followerSharedInbox" character varying(512), "followeeHost" character varying(128), "followeeInbox" character varying(512), "followeeSharedInbox" character varying(512), CONSTRAINT "PK_53a9aa3725f7a3deb150b39dbfc" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE INDEX "IDX_12c01c0d1a79f77d9f6c15fadd" ON "follow_request" ("followeeId") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_a7fd92dd6dc519e6fb435dd108" ON "follow_request" ("followerId") `, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_d54a512b822fac7ed52800f6b4" ON "follow_request" ("followerId", "followeeId") `, + ); + await queryRunner.query( + `CREATE TABLE "emoji" ("id" character varying(32) NOT NULL, "updatedAt" TIMESTAMP WITH TIME ZONE, "name" character varying(128) NOT NULL, "host" character varying(128), "url" character varying(512) NOT NULL, "uri" character varying(512), "type" character varying(64), "aliases" character varying(128) array NOT NULL DEFAULT '{}'::varchar[], CONSTRAINT "PK_df74ce05e24999ee01ea0bc50a3" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE INDEX "IDX_b37dafc86e9af007e3295c2781" ON "emoji" ("name") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_5900e907bb46516ddf2871327c" ON "emoji" ("host") `, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_4f4d35e1256c84ae3d1f0eab10" ON "emoji" ("name", "host") `, + ); + await queryRunner.query( + `CREATE TABLE "reversi_game" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "startedAt" TIMESTAMP WITH TIME ZONE, "user1Id" character varying(32) NOT NULL, "user2Id" character varying(32) NOT NULL, "user1Accepted" boolean NOT NULL DEFAULT false, "user2Accepted" boolean NOT NULL DEFAULT false, "black" integer, "isStarted" boolean NOT NULL DEFAULT false, "isEnded" boolean NOT NULL DEFAULT false, "winnerId" character varying(32), "surrendered" character varying(32), "logs" jsonb NOT NULL DEFAULT '[]', "map" character varying(64) array NOT NULL, "bw" character varying(32) NOT NULL, "isLlotheo" boolean NOT NULL DEFAULT false, "canPutEverywhere" boolean NOT NULL DEFAULT false, "loopedBoard" boolean NOT NULL DEFAULT false, "form1" jsonb DEFAULT null, "form2" jsonb DEFAULT null, "crc32" character varying(32), CONSTRAINT "PK_76b30eeba71b1193ad7c5311c3f" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE INDEX "IDX_b46ec40746efceac604142be1c" ON "reversi_game" ("createdAt") `, + ); + await queryRunner.query( + `CREATE TABLE "reversi_matching" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "parentId" character varying(32) NOT NULL, "childId" character varying(32) NOT NULL, CONSTRAINT "PK_880bd0afbab232f21c8b9d146cf" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE INDEX "IDX_b604d92d6c7aec38627f6eaf16" ON "reversi_matching" ("createdAt") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_3b25402709dd9882048c2bbade" ON "reversi_matching" ("parentId") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_e247b23a3c9b45f89ec1299d06" ON "reversi_matching" ("childId") `, + ); + await queryRunner.query( + `CREATE TABLE "user_note_pining" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "userId" character varying(32) NOT NULL, "noteId" character varying(32) NOT NULL, CONSTRAINT "PK_a6a2dad4ae000abce2ea9d9b103" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE INDEX "IDX_bfbc6f79ba4007b4ce5097f08d" ON "user_note_pining" ("userId") `, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_410cd649884b501c02d6e72738" ON "user_note_pining" ("userId", "noteId") `, + ); + await queryRunner.query( + `CREATE TYPE "poll_notevisibility_enum" AS ENUM('public', 'home', 'followers', 'specified')`, + ); + await queryRunner.query( + `CREATE TABLE "poll" ("noteId" character varying(32) NOT NULL, "expiresAt" TIMESTAMP WITH TIME ZONE, "multiple" boolean NOT NULL, "choices" character varying(128) array NOT NULL DEFAULT '{}'::varchar[], "votes" integer array NOT NULL, "noteVisibility" "poll_notevisibility_enum" NOT NULL, "userId" character varying(32) NOT NULL, "userHost" character varying(128), CONSTRAINT "REL_da851e06d0dfe2ef397d8b1bf1" UNIQUE ("noteId"), CONSTRAINT "PK_da851e06d0dfe2ef397d8b1bf1b" PRIMARY KEY ("noteId"))`, + ); + await queryRunner.query( + `CREATE INDEX "IDX_0610ebcfcfb4a18441a9bcdab2" ON "poll" ("userId") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_7fa20a12319c7f6dc3aed98c0a" ON "poll" ("userHost") `, + ); + await queryRunner.query( + `CREATE TABLE "user_keypair" ("userId" character varying(32) NOT NULL, "publicKey" character varying(4096) NOT NULL, "privateKey" character varying(4096) NOT NULL, CONSTRAINT "REL_f4853eb41ab722fe05f81cedeb" UNIQUE ("userId"), CONSTRAINT "PK_f4853eb41ab722fe05f81cedeb6" PRIMARY KEY ("userId"))`, + ); + await queryRunner.query( + `CREATE TABLE "user_publickey" ("userId" character varying(32) NOT NULL, "keyId" character varying(256) NOT NULL, "keyPem" character varying(4096) NOT NULL, CONSTRAINT "REL_10c146e4b39b443ede016f6736" UNIQUE ("userId"), CONSTRAINT "PK_10c146e4b39b443ede016f6736d" PRIMARY KEY ("userId"))`, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_171e64971c780ebd23fae140bb" ON "user_publickey" ("keyId") `, + ); + await queryRunner.query( + `CREATE TABLE "user_profile" ("userId" character varying(32) NOT NULL, "location" character varying(128), "birthday" character(10), "description" character varying(1024), "fields" jsonb NOT NULL DEFAULT '[]', "url" character varying(512), "email" character varying(128), "emailVerifyCode" character varying(128), "emailVerified" boolean NOT NULL DEFAULT false, "twoFactorTempSecret" character varying(128), "twoFactorSecret" character varying(128), "twoFactorEnabled" boolean NOT NULL DEFAULT false, "password" character varying(128), "clientData" jsonb NOT NULL DEFAULT '{}', "autoWatch" boolean NOT NULL DEFAULT false, "autoAcceptFollowed" boolean NOT NULL DEFAULT false, "alwaysMarkNsfw" boolean NOT NULL DEFAULT false, "carefulBot" boolean NOT NULL DEFAULT false, "twitter" boolean NOT NULL DEFAULT false, "twitterAccessToken" character varying(64) DEFAULT null, "twitterAccessTokenSecret" character varying(64) DEFAULT null, "twitterUserId" character varying(64) DEFAULT null, "twitterScreenName" character varying(64) DEFAULT null, "github" boolean NOT NULL DEFAULT false, "githubAccessToken" character varying(64) DEFAULT null, "githubId" integer DEFAULT null, "githubLogin" character varying(64) DEFAULT null, "discord" boolean NOT NULL DEFAULT false, "discordAccessToken" character varying(64) DEFAULT null, "discordRefreshToken" character varying(64) DEFAULT null, "discordExpiresDate" integer DEFAULT null, "discordId" character varying(64) DEFAULT null, "discordUsername" character varying(64) DEFAULT null, "discordDiscriminator" character varying(64) DEFAULT null, "userHost" character varying(128), CONSTRAINT "REL_51cb79b5555effaf7d69ba1cff" UNIQUE ("userId"), CONSTRAINT "PK_51cb79b5555effaf7d69ba1cff9" PRIMARY KEY ("userId"))`, + ); + await queryRunner.query( + `CREATE INDEX "IDX_dce530b98e454793dac5ec2f5a" ON "user_profile" ("userHost") `, + ); + await queryRunner.query( + `CREATE TYPE "__chart__active_users_span_enum" AS ENUM('hour', 'day')`, + ); + await queryRunner.query( + `CREATE TABLE "__chart__active_users" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "group" character varying(128), "span" "__chart__active_users_span_enum" NOT NULL, "unique" jsonb NOT NULL DEFAULT '{}', "___local_count" bigint NOT NULL, "___remote_count" bigint NOT NULL, CONSTRAINT "PK_317237a9f733b970604a11e314f" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE TYPE "__chart__drive_span_enum" AS ENUM('hour', 'day')`, + ); + await queryRunner.query( + `CREATE TABLE "__chart__drive" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "group" character varying(128), "span" "__chart__drive_span_enum" NOT NULL, "unique" jsonb NOT NULL DEFAULT '{}', "___local_totalCount" bigint NOT NULL, "___local_totalSize" bigint NOT NULL, "___local_incCount" bigint NOT NULL, "___local_incSize" bigint NOT NULL, "___local_decCount" bigint NOT NULL, "___local_decSize" bigint NOT NULL, "___remote_totalCount" bigint NOT NULL, "___remote_totalSize" bigint NOT NULL, "___remote_incCount" bigint NOT NULL, "___remote_incSize" bigint NOT NULL, "___remote_decCount" bigint NOT NULL, "___remote_decSize" bigint NOT NULL, CONSTRAINT "PK_f96bc548a765cd4b3b354221ce7" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE TYPE "__chart__federation_span_enum" AS ENUM('hour', 'day')`, + ); + await queryRunner.query( + `CREATE TABLE "__chart__federation" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "group" character varying(128), "span" "__chart__federation_span_enum" NOT NULL, "unique" jsonb NOT NULL DEFAULT '{}', "___instance_total" bigint NOT NULL, "___instance_inc" bigint NOT NULL, "___instance_dec" bigint NOT NULL, CONSTRAINT "PK_b39dcd31a0fe1a7757e348e85fd" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE TYPE "__chart__hashtag_span_enum" AS ENUM('hour', 'day')`, + ); + await queryRunner.query( + `CREATE TABLE "__chart__hashtag" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "group" character varying(128), "span" "__chart__hashtag_span_enum" NOT NULL, "unique" jsonb NOT NULL DEFAULT '{}', "___local_count" bigint NOT NULL, "___remote_count" bigint NOT NULL, CONSTRAINT "PK_c32f1ea2b44a5d2f7881e37f8f9" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE TYPE "__chart__instance_span_enum" AS ENUM('hour', 'day')`, + ); + await queryRunner.query( + `CREATE TABLE "__chart__instance" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "group" character varying(128), "span" "__chart__instance_span_enum" NOT NULL, "unique" jsonb NOT NULL DEFAULT '{}', "___requests_failed" bigint NOT NULL, "___requests_succeeded" bigint NOT NULL, "___requests_received" bigint NOT NULL, "___notes_total" bigint NOT NULL, "___notes_inc" bigint NOT NULL, "___notes_dec" bigint NOT NULL, "___notes_diffs_normal" bigint NOT NULL, "___notes_diffs_reply" bigint NOT NULL, "___notes_diffs_renote" bigint NOT NULL, "___users_total" bigint NOT NULL, "___users_inc" bigint NOT NULL, "___users_dec" bigint NOT NULL, "___following_total" bigint NOT NULL, "___following_inc" bigint NOT NULL, "___following_dec" bigint NOT NULL, "___followers_total" bigint NOT NULL, "___followers_inc" bigint NOT NULL, "___followers_dec" bigint NOT NULL, "___drive_totalFiles" bigint NOT NULL, "___drive_totalUsage" bigint NOT NULL, "___drive_incFiles" bigint NOT NULL, "___drive_incUsage" bigint NOT NULL, "___drive_decFiles" bigint NOT NULL, "___drive_decUsage" bigint NOT NULL, CONSTRAINT "PK_1267c67c7c2d47b4903975f2c00" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE TYPE "__chart__network_span_enum" AS ENUM('hour', 'day')`, + ); + await queryRunner.query( + `CREATE TABLE "__chart__network" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "group" character varying(128), "span" "__chart__network_span_enum" NOT NULL, "unique" jsonb NOT NULL DEFAULT '{}', "___incomingRequests" bigint NOT NULL, "___outgoingRequests" bigint NOT NULL, "___totalTime" bigint NOT NULL, "___incomingBytes" bigint NOT NULL, "___outgoingBytes" bigint NOT NULL, CONSTRAINT "PK_bc4290c2e27fad14ef0c1ca93f3" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE TYPE "__chart__notes_span_enum" AS ENUM('hour', 'day')`, + ); + await queryRunner.query( + `CREATE TABLE "__chart__notes" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "group" character varying(128), "span" "__chart__notes_span_enum" NOT NULL, "unique" jsonb NOT NULL DEFAULT '{}', "___local_total" bigint NOT NULL, "___local_inc" bigint NOT NULL, "___local_dec" bigint NOT NULL, "___local_diffs_normal" bigint NOT NULL, "___local_diffs_reply" bigint NOT NULL, "___local_diffs_renote" bigint NOT NULL, "___remote_total" bigint NOT NULL, "___remote_inc" bigint NOT NULL, "___remote_dec" bigint NOT NULL, "___remote_diffs_normal" bigint NOT NULL, "___remote_diffs_reply" bigint NOT NULL, "___remote_diffs_renote" bigint NOT NULL, CONSTRAINT "PK_0aec823fa85c7f901bdb3863b14" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE TYPE "__chart__per_user_drive_span_enum" AS ENUM('hour', 'day')`, + ); + await queryRunner.query( + `CREATE TABLE "__chart__per_user_drive" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "group" character varying(128), "span" "__chart__per_user_drive_span_enum" NOT NULL, "unique" jsonb NOT NULL DEFAULT '{}', "___totalCount" bigint NOT NULL, "___totalSize" bigint NOT NULL, "___incCount" bigint NOT NULL, "___incSize" bigint NOT NULL, "___decCount" bigint NOT NULL, "___decSize" bigint NOT NULL, CONSTRAINT "PK_d0ef23d24d666e1a44a0cd3d208" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE TYPE "__chart__per_user_following_span_enum" AS ENUM('hour', 'day')`, + ); + await queryRunner.query( + `CREATE TABLE "__chart__per_user_following" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "group" character varying(128), "span" "__chart__per_user_following_span_enum" NOT NULL, "unique" jsonb NOT NULL DEFAULT '{}', "___local_followings_total" bigint NOT NULL, "___local_followings_inc" bigint NOT NULL, "___local_followings_dec" bigint NOT NULL, "___local_followers_total" bigint NOT NULL, "___local_followers_inc" bigint NOT NULL, "___local_followers_dec" bigint NOT NULL, "___remote_followings_total" bigint NOT NULL, "___remote_followings_inc" bigint NOT NULL, "___remote_followings_dec" bigint NOT NULL, "___remote_followers_total" bigint NOT NULL, "___remote_followers_inc" bigint NOT NULL, "___remote_followers_dec" bigint NOT NULL, CONSTRAINT "PK_85bb1b540363a29c2fec83bd907" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE TYPE "__chart__per_user_notes_span_enum" AS ENUM('hour', 'day')`, + ); + await queryRunner.query( + `CREATE TABLE "__chart__per_user_notes" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "group" character varying(128), "span" "__chart__per_user_notes_span_enum" NOT NULL, "unique" jsonb NOT NULL DEFAULT '{}', "___total" bigint NOT NULL, "___inc" bigint NOT NULL, "___dec" bigint NOT NULL, "___diffs_normal" bigint NOT NULL, "___diffs_reply" bigint NOT NULL, "___diffs_renote" bigint NOT NULL, CONSTRAINT "PK_334acf6e915af2f29edc11b8e50" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE TYPE "__chart__per_user_reaction_span_enum" AS ENUM('hour', 'day')`, + ); + await queryRunner.query( + `CREATE TABLE "__chart__per_user_reaction" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "group" character varying(128), "span" "__chart__per_user_reaction_span_enum" NOT NULL, "unique" jsonb NOT NULL DEFAULT '{}', "___local_count" bigint NOT NULL, "___remote_count" bigint NOT NULL, CONSTRAINT "PK_984f54dae441e65b633e8d27a7f" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE TYPE "__chart__test_grouped_span_enum" AS ENUM('hour', 'day')`, + ); + await queryRunner.query( + `CREATE TABLE "__chart__test_grouped" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "group" character varying(128), "span" "__chart__test_grouped_span_enum" NOT NULL, "unique" jsonb NOT NULL DEFAULT '{}', "___foo_total" bigint NOT NULL, "___foo_inc" bigint NOT NULL, "___foo_dec" bigint NOT NULL, CONSTRAINT "PK_f4a2b175d308695af30d4293272" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE TYPE "__chart__test_unique_span_enum" AS ENUM('hour', 'day')`, + ); + await queryRunner.query( + `CREATE TABLE "__chart__test_unique" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "group" character varying(128), "span" "__chart__test_unique_span_enum" NOT NULL, "unique" jsonb NOT NULL DEFAULT '{}', "___foo" bigint NOT NULL, CONSTRAINT "PK_409bac9c97cc612d8500012319d" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE TYPE "__chart__test_span_enum" AS ENUM('hour', 'day')`, + ); + await queryRunner.query( + `CREATE TABLE "__chart__test" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "group" character varying(128), "span" "__chart__test_span_enum" NOT NULL, "unique" jsonb NOT NULL DEFAULT '{}', "___foo_total" bigint NOT NULL, "___foo_inc" bigint NOT NULL, "___foo_dec" bigint NOT NULL, CONSTRAINT "PK_b4bc31dffbd1b785276a3ecfc1e" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE TYPE "__chart__users_span_enum" AS ENUM('hour', 'day')`, + ); + await queryRunner.query( + `CREATE TABLE "__chart__users" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "group" character varying(128), "span" "__chart__users_span_enum" NOT NULL, "unique" jsonb NOT NULL DEFAULT '{}', "___local_total" bigint NOT NULL, "___local_inc" bigint NOT NULL, "___local_dec" bigint NOT NULL, "___remote_total" bigint NOT NULL, "___remote_inc" bigint NOT NULL, "___remote_dec" bigint NOT NULL, CONSTRAINT "PK_4dfcf2c78d03524b9eb2c99d328" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `ALTER TABLE "drive_folder" ADD CONSTRAINT "FK_f4fc06e49c0171c85f1c48060d2" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "drive_folder" ADD CONSTRAINT "FK_00ceffb0cdc238b3233294f08f2" FOREIGN KEY ("parentId") REFERENCES "drive_folder"("id") ON DELETE SET NULL ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "drive_file" ADD CONSTRAINT "FK_860fa6f6c7df5bb887249fba22e" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE SET NULL ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "drive_file" ADD CONSTRAINT "FK_bb90d1956dafc4068c28aa7560a" FOREIGN KEY ("folderId") REFERENCES "drive_folder"("id") ON DELETE SET NULL ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "user" ADD CONSTRAINT "FK_58f5c71eaab331645112cf8cfa5" FOREIGN KEY ("avatarId") REFERENCES "drive_file"("id") ON DELETE SET NULL ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "user" ADD CONSTRAINT "FK_afc64b53f8db3707ceb34eb28e2" FOREIGN KEY ("bannerId") REFERENCES "drive_file"("id") ON DELETE SET NULL ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "app" ADD CONSTRAINT "FK_3f5b0899ef90527a3462d7c2cb3" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE SET NULL ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "access_token" ADD CONSTRAINT "FK_9949557d0e1b2c19e5344c171e9" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "access_token" ADD CONSTRAINT "FK_a3ff16c90cc87a82a0b5959e560" FOREIGN KEY ("appId") REFERENCES "app"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "note" ADD CONSTRAINT "FK_17cb3553c700a4985dff5a30ff5" FOREIGN KEY ("replyId") REFERENCES "note"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "note" ADD CONSTRAINT "FK_52ccc804d7c69037d558bac4c96" FOREIGN KEY ("renoteId") REFERENCES "note"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "note" ADD CONSTRAINT "FK_ec5c201576192ba8904c345c5cc" FOREIGN KEY ("appId") REFERENCES "app"("id") ON DELETE SET NULL ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "note" ADD CONSTRAINT "FK_5b87d9d19127bd5d92026017a7b" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "poll_vote" ADD CONSTRAINT "FK_66d2bd2ee31d14bcc23069a89f8" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "poll_vote" ADD CONSTRAINT "FK_aecfbd5ef60374918e63ee95fa7" FOREIGN KEY ("noteId") REFERENCES "note"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "note_reaction" ADD CONSTRAINT "FK_13761f64257f40c5636d0ff95ee" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "note_reaction" ADD CONSTRAINT "FK_45145e4953780f3cd5656f0ea6a" FOREIGN KEY ("noteId") REFERENCES "note"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "note_watching" ADD CONSTRAINT "FK_b0134ec406e8d09a540f8182888" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "note_watching" ADD CONSTRAINT "FK_03e7028ab8388a3f5e3ce2a8619" FOREIGN KEY ("noteId") REFERENCES "note"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "note_unread" ADD CONSTRAINT "FK_56b0166d34ddae49d8ef7610bb9" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "note_unread" ADD CONSTRAINT "FK_e637cba4dc4410218c4251260e4" FOREIGN KEY ("noteId") REFERENCES "note"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "notification" ADD CONSTRAINT "FK_3c601b70a1066d2c8b517094cb9" FOREIGN KEY ("notifieeId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "notification" ADD CONSTRAINT "FK_3b4e96eec8d36a8bbb9d02aa710" FOREIGN KEY ("notifierId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "notification" ADD CONSTRAINT "FK_769cb6b73a1efe22ddf733ac453" FOREIGN KEY ("noteId") REFERENCES "note"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "following" ADD CONSTRAINT "FK_24e0042143a18157b234df186c3" FOREIGN KEY ("followeeId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "following" ADD CONSTRAINT "FK_6516c5a6f3c015b4eed39978be5" FOREIGN KEY ("followerId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "muting" ADD CONSTRAINT "FK_ec96b4fed9dae517e0dbbe0675c" FOREIGN KEY ("muteeId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "muting" ADD CONSTRAINT "FK_93060675b4a79a577f31d260c67" FOREIGN KEY ("muterId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "sw_subscription" ADD CONSTRAINT "FK_97754ca6f2baff9b4abb7f853dd" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "blocking" ADD CONSTRAINT "FK_2cd4a2743a99671308f5417759e" FOREIGN KEY ("blockeeId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "blocking" ADD CONSTRAINT "FK_0627125f1a8a42c9a1929edb552" FOREIGN KEY ("blockerId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "user_list" ADD CONSTRAINT "FK_b7fcefbdd1c18dce86687531f99" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "user_list_joining" ADD CONSTRAINT "FK_d844bfc6f3f523a05189076efaa" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "user_list_joining" ADD CONSTRAINT "FK_605472305f26818cc93d1baaa74" FOREIGN KEY ("userListId") REFERENCES "user_list"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "note_favorite" ADD CONSTRAINT "FK_47f4b1892f5d6ba8efb3057d81a" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "note_favorite" ADD CONSTRAINT "FK_0e00498f180193423c992bc4370" FOREIGN KEY ("noteId") REFERENCES "note"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "abuse_user_report" ADD CONSTRAINT "FK_d049123c413e68ca52abe734203" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "abuse_user_report" ADD CONSTRAINT "FK_04cc96756f89d0b7f9473e8cdf3" FOREIGN KEY ("reporterId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "messaging_message" ADD CONSTRAINT "FK_5377c307783fce2b6d352e1203b" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "messaging_message" ADD CONSTRAINT "FK_cac14a4e3944454a5ce7daa5142" FOREIGN KEY ("recipientId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "messaging_message" ADD CONSTRAINT "FK_535def119223ac05ad3fa9ef64b" FOREIGN KEY ("fileId") REFERENCES "drive_file"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "signin" ADD CONSTRAINT "FK_2c308dbdc50d94dc625670055f7" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "auth_session" ADD CONSTRAINT "FK_c072b729d71697f959bde66ade0" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "auth_session" ADD CONSTRAINT "FK_dbe037d4bddd17b03a1dc778dee" FOREIGN KEY ("appId") REFERENCES "app"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "follow_request" ADD CONSTRAINT "FK_12c01c0d1a79f77d9f6c15fadd2" FOREIGN KEY ("followeeId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "follow_request" ADD CONSTRAINT "FK_a7fd92dd6dc519e6fb435dd108f" FOREIGN KEY ("followerId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "reversi_game" ADD CONSTRAINT "FK_f7467510c60a45ce5aca6292743" FOREIGN KEY ("user1Id") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "reversi_game" ADD CONSTRAINT "FK_6649a4e8c5d5cf32fb03b5da9f6" FOREIGN KEY ("user2Id") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "reversi_matching" ADD CONSTRAINT "FK_3b25402709dd9882048c2bbade0" FOREIGN KEY ("parentId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "reversi_matching" ADD CONSTRAINT "FK_e247b23a3c9b45f89ec1299d066" FOREIGN KEY ("childId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "user_note_pining" ADD CONSTRAINT "FK_bfbc6f79ba4007b4ce5097f08d6" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "user_note_pining" ADD CONSTRAINT "FK_68881008f7c3588ad7ecae471cf" FOREIGN KEY ("noteId") REFERENCES "note"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "poll" ADD CONSTRAINT "FK_da851e06d0dfe2ef397d8b1bf1b" FOREIGN KEY ("noteId") REFERENCES "note"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "user_keypair" ADD CONSTRAINT "FK_f4853eb41ab722fe05f81cedeb6" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "user_publickey" ADD CONSTRAINT "FK_10c146e4b39b443ede016f6736d" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "user_profile" ADD CONSTRAINT "FK_51cb79b5555effaf7d69ba1cff9" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + } + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "user_profile" DROP CONSTRAINT "FK_51cb79b5555effaf7d69ba1cff9"`, + ); + await queryRunner.query( + `ALTER TABLE "user_publickey" DROP CONSTRAINT "FK_10c146e4b39b443ede016f6736d"`, + ); + await queryRunner.query( + `ALTER TABLE "user_keypair" DROP CONSTRAINT "FK_f4853eb41ab722fe05f81cedeb6"`, + ); + await queryRunner.query( + `ALTER TABLE "poll" DROP CONSTRAINT "FK_da851e06d0dfe2ef397d8b1bf1b"`, + ); + await queryRunner.query( + `ALTER TABLE "user_note_pining" DROP CONSTRAINT "FK_68881008f7c3588ad7ecae471cf"`, + ); + await queryRunner.query( + `ALTER TABLE "user_note_pining" DROP CONSTRAINT "FK_bfbc6f79ba4007b4ce5097f08d6"`, + ); + await queryRunner.query( + `ALTER TABLE "reversi_matching" DROP CONSTRAINT "FK_e247b23a3c9b45f89ec1299d066"`, + ); + await queryRunner.query( + `ALTER TABLE "reversi_matching" DROP CONSTRAINT "FK_3b25402709dd9882048c2bbade0"`, + ); + await queryRunner.query( + `ALTER TABLE "reversi_game" DROP CONSTRAINT "FK_6649a4e8c5d5cf32fb03b5da9f6"`, + ); + await queryRunner.query( + `ALTER TABLE "reversi_game" DROP CONSTRAINT "FK_f7467510c60a45ce5aca6292743"`, + ); + await queryRunner.query( + `ALTER TABLE "follow_request" DROP CONSTRAINT "FK_a7fd92dd6dc519e6fb435dd108f"`, + ); + await queryRunner.query( + `ALTER TABLE "follow_request" DROP CONSTRAINT "FK_12c01c0d1a79f77d9f6c15fadd2"`, + ); + await queryRunner.query( + `ALTER TABLE "auth_session" DROP CONSTRAINT "FK_dbe037d4bddd17b03a1dc778dee"`, + ); + await queryRunner.query( + `ALTER TABLE "auth_session" DROP CONSTRAINT "FK_c072b729d71697f959bde66ade0"`, + ); + await queryRunner.query( + `ALTER TABLE "signin" DROP CONSTRAINT "FK_2c308dbdc50d94dc625670055f7"`, + ); + await queryRunner.query( + `ALTER TABLE "messaging_message" DROP CONSTRAINT "FK_535def119223ac05ad3fa9ef64b"`, + ); + await queryRunner.query( + `ALTER TABLE "messaging_message" DROP CONSTRAINT "FK_cac14a4e3944454a5ce7daa5142"`, + ); + await queryRunner.query( + `ALTER TABLE "messaging_message" DROP CONSTRAINT "FK_5377c307783fce2b6d352e1203b"`, + ); + await queryRunner.query( + `ALTER TABLE "abuse_user_report" DROP CONSTRAINT "FK_04cc96756f89d0b7f9473e8cdf3"`, + ); + await queryRunner.query( + `ALTER TABLE "abuse_user_report" DROP CONSTRAINT "FK_d049123c413e68ca52abe734203"`, + ); + await queryRunner.query( + `ALTER TABLE "note_favorite" DROP CONSTRAINT "FK_0e00498f180193423c992bc4370"`, + ); + await queryRunner.query( + `ALTER TABLE "note_favorite" DROP CONSTRAINT "FK_47f4b1892f5d6ba8efb3057d81a"`, + ); + await queryRunner.query( + `ALTER TABLE "user_list_joining" DROP CONSTRAINT "FK_605472305f26818cc93d1baaa74"`, + ); + await queryRunner.query( + `ALTER TABLE "user_list_joining" DROP CONSTRAINT "FK_d844bfc6f3f523a05189076efaa"`, + ); + await queryRunner.query( + `ALTER TABLE "user_list" DROP CONSTRAINT "FK_b7fcefbdd1c18dce86687531f99"`, + ); + await queryRunner.query( + `ALTER TABLE "blocking" DROP CONSTRAINT "FK_0627125f1a8a42c9a1929edb552"`, + ); + await queryRunner.query( + `ALTER TABLE "blocking" DROP CONSTRAINT "FK_2cd4a2743a99671308f5417759e"`, + ); + await queryRunner.query( + `ALTER TABLE "sw_subscription" DROP CONSTRAINT "FK_97754ca6f2baff9b4abb7f853dd"`, + ); + await queryRunner.query( + `ALTER TABLE "muting" DROP CONSTRAINT "FK_93060675b4a79a577f31d260c67"`, + ); + await queryRunner.query( + `ALTER TABLE "muting" DROP CONSTRAINT "FK_ec96b4fed9dae517e0dbbe0675c"`, + ); + await queryRunner.query( + `ALTER TABLE "following" DROP CONSTRAINT "FK_6516c5a6f3c015b4eed39978be5"`, + ); + await queryRunner.query( + `ALTER TABLE "following" DROP CONSTRAINT "FK_24e0042143a18157b234df186c3"`, + ); + await queryRunner.query( + `ALTER TABLE "notification" DROP CONSTRAINT "FK_769cb6b73a1efe22ddf733ac453"`, + ); + await queryRunner.query( + `ALTER TABLE "notification" DROP CONSTRAINT "FK_3b4e96eec8d36a8bbb9d02aa710"`, + ); + await queryRunner.query( + `ALTER TABLE "notification" DROP CONSTRAINT "FK_3c601b70a1066d2c8b517094cb9"`, + ); + await queryRunner.query( + `ALTER TABLE "note_unread" DROP CONSTRAINT "FK_e637cba4dc4410218c4251260e4"`, + ); + await queryRunner.query( + `ALTER TABLE "note_unread" DROP CONSTRAINT "FK_56b0166d34ddae49d8ef7610bb9"`, + ); + await queryRunner.query( + `ALTER TABLE "note_watching" DROP CONSTRAINT "FK_03e7028ab8388a3f5e3ce2a8619"`, + ); + await queryRunner.query( + `ALTER TABLE "note_watching" DROP CONSTRAINT "FK_b0134ec406e8d09a540f8182888"`, + ); + await queryRunner.query( + `ALTER TABLE "note_reaction" DROP CONSTRAINT "FK_45145e4953780f3cd5656f0ea6a"`, + ); + await queryRunner.query( + `ALTER TABLE "note_reaction" DROP CONSTRAINT "FK_13761f64257f40c5636d0ff95ee"`, + ); + await queryRunner.query( + `ALTER TABLE "poll_vote" DROP CONSTRAINT "FK_aecfbd5ef60374918e63ee95fa7"`, + ); + await queryRunner.query( + `ALTER TABLE "poll_vote" DROP CONSTRAINT "FK_66d2bd2ee31d14bcc23069a89f8"`, + ); + await queryRunner.query( + `ALTER TABLE "note" DROP CONSTRAINT "FK_5b87d9d19127bd5d92026017a7b"`, + ); + await queryRunner.query( + `ALTER TABLE "note" DROP CONSTRAINT "FK_ec5c201576192ba8904c345c5cc"`, + ); + await queryRunner.query( + `ALTER TABLE "note" DROP CONSTRAINT "FK_52ccc804d7c69037d558bac4c96"`, + ); + await queryRunner.query( + `ALTER TABLE "note" DROP CONSTRAINT "FK_17cb3553c700a4985dff5a30ff5"`, + ); + await queryRunner.query( + `ALTER TABLE "access_token" DROP CONSTRAINT "FK_a3ff16c90cc87a82a0b5959e560"`, + ); + await queryRunner.query( + `ALTER TABLE "access_token" DROP CONSTRAINT "FK_9949557d0e1b2c19e5344c171e9"`, + ); + await queryRunner.query( + `ALTER TABLE "app" DROP CONSTRAINT "FK_3f5b0899ef90527a3462d7c2cb3"`, + ); + await queryRunner.query( + `ALTER TABLE "user" DROP CONSTRAINT "FK_afc64b53f8db3707ceb34eb28e2"`, + ); + await queryRunner.query( + `ALTER TABLE "user" DROP CONSTRAINT "FK_58f5c71eaab331645112cf8cfa5"`, + ); + await queryRunner.query( + `ALTER TABLE "drive_file" DROP CONSTRAINT "FK_bb90d1956dafc4068c28aa7560a"`, + ); + await queryRunner.query( + `ALTER TABLE "drive_file" DROP CONSTRAINT "FK_860fa6f6c7df5bb887249fba22e"`, + ); + await queryRunner.query( + `ALTER TABLE "drive_folder" DROP CONSTRAINT "FK_00ceffb0cdc238b3233294f08f2"`, + ); + await queryRunner.query( + `ALTER TABLE "drive_folder" DROP CONSTRAINT "FK_f4fc06e49c0171c85f1c48060d2"`, + ); + await queryRunner.query(`DROP TABLE "__chart__users"`); + await queryRunner.query(`DROP TYPE "__chart__users_span_enum"`); + await queryRunner.query(`DROP TABLE "__chart__test"`); + await queryRunner.query(`DROP TYPE "__chart__test_span_enum"`); + await queryRunner.query(`DROP TABLE "__chart__test_unique"`); + await queryRunner.query(`DROP TYPE "__chart__test_unique_span_enum"`); + await queryRunner.query(`DROP TABLE "__chart__test_grouped"`); + await queryRunner.query(`DROP TYPE "__chart__test_grouped_span_enum"`); + await queryRunner.query(`DROP TABLE "__chart__per_user_reaction"`); + await queryRunner.query(`DROP TYPE "__chart__per_user_reaction_span_enum"`); + await queryRunner.query(`DROP TABLE "__chart__per_user_notes"`); + await queryRunner.query(`DROP TYPE "__chart__per_user_notes_span_enum"`); + await queryRunner.query(`DROP TABLE "__chart__per_user_following"`); + await queryRunner.query( + `DROP TYPE "__chart__per_user_following_span_enum"`, + ); + await queryRunner.query(`DROP TABLE "__chart__per_user_drive"`); + await queryRunner.query(`DROP TYPE "__chart__per_user_drive_span_enum"`); + await queryRunner.query(`DROP TABLE "__chart__notes"`); + await queryRunner.query(`DROP TYPE "__chart__notes_span_enum"`); + await queryRunner.query(`DROP TABLE "__chart__network"`); + await queryRunner.query(`DROP TYPE "__chart__network_span_enum"`); + await queryRunner.query(`DROP TABLE "__chart__instance"`); + await queryRunner.query(`DROP TYPE "__chart__instance_span_enum"`); + await queryRunner.query(`DROP TABLE "__chart__hashtag"`); + await queryRunner.query(`DROP TYPE "__chart__hashtag_span_enum"`); + await queryRunner.query(`DROP TABLE "__chart__federation"`); + await queryRunner.query(`DROP TYPE "__chart__federation_span_enum"`); + await queryRunner.query(`DROP TABLE "__chart__drive"`); + await queryRunner.query(`DROP TYPE "__chart__drive_span_enum"`); + await queryRunner.query(`DROP TABLE "__chart__active_users"`); + await queryRunner.query(`DROP TYPE "__chart__active_users_span_enum"`); + await queryRunner.query(`DROP INDEX "IDX_dce530b98e454793dac5ec2f5a"`); + await queryRunner.query(`DROP TABLE "user_profile"`); + await queryRunner.query(`DROP INDEX "IDX_171e64971c780ebd23fae140bb"`); + await queryRunner.query(`DROP TABLE "user_publickey"`); + await queryRunner.query(`DROP TABLE "user_keypair"`); + await queryRunner.query(`DROP INDEX "IDX_7fa20a12319c7f6dc3aed98c0a"`); + await queryRunner.query(`DROP INDEX "IDX_0610ebcfcfb4a18441a9bcdab2"`); + await queryRunner.query(`DROP TABLE "poll"`); + await queryRunner.query(`DROP TYPE "poll_notevisibility_enum"`); + await queryRunner.query(`DROP INDEX "IDX_410cd649884b501c02d6e72738"`); + await queryRunner.query(`DROP INDEX "IDX_bfbc6f79ba4007b4ce5097f08d"`); + await queryRunner.query(`DROP TABLE "user_note_pining"`); + await queryRunner.query(`DROP INDEX "IDX_e247b23a3c9b45f89ec1299d06"`); + await queryRunner.query(`DROP INDEX "IDX_3b25402709dd9882048c2bbade"`); + await queryRunner.query(`DROP INDEX "IDX_b604d92d6c7aec38627f6eaf16"`); + await queryRunner.query(`DROP TABLE "reversi_matching"`); + await queryRunner.query(`DROP INDEX "IDX_b46ec40746efceac604142be1c"`); + await queryRunner.query(`DROP TABLE "reversi_game"`); + await queryRunner.query(`DROP INDEX "IDX_4f4d35e1256c84ae3d1f0eab10"`); + await queryRunner.query(`DROP INDEX "IDX_5900e907bb46516ddf2871327c"`); + await queryRunner.query(`DROP INDEX "IDX_b37dafc86e9af007e3295c2781"`); + await queryRunner.query(`DROP TABLE "emoji"`); + await queryRunner.query(`DROP INDEX "IDX_d54a512b822fac7ed52800f6b4"`); + await queryRunner.query(`DROP INDEX "IDX_a7fd92dd6dc519e6fb435dd108"`); + await queryRunner.query(`DROP INDEX "IDX_12c01c0d1a79f77d9f6c15fadd"`); + await queryRunner.query(`DROP TABLE "follow_request"`); + await queryRunner.query(`DROP INDEX "IDX_62cb09e1129f6ec024ef66e183"`); + await queryRunner.query(`DROP TABLE "auth_session"`); + await queryRunner.query(`DROP INDEX "IDX_2c308dbdc50d94dc625670055f"`); + await queryRunner.query(`DROP TABLE "signin"`); + await queryRunner.query(`DROP INDEX "IDX_cac14a4e3944454a5ce7daa514"`); + await queryRunner.query(`DROP INDEX "IDX_5377c307783fce2b6d352e1203"`); + await queryRunner.query(`DROP INDEX "IDX_e21cd3646e52ef9c94aaf17c2e"`); + await queryRunner.query(`DROP TABLE "messaging_message"`); + await queryRunner.query(`DROP INDEX "IDX_0ff69e8dfa9fe31bb4a4660f59"`); + await queryRunner.query(`DROP TABLE "registration_ticket"`); + await queryRunner.query(`DROP INDEX "IDX_5cd442c3b2e74fdd99dae20243"`); + await queryRunner.query(`DROP INDEX "IDX_04cc96756f89d0b7f9473e8cdf"`); + await queryRunner.query(`DROP INDEX "IDX_d049123c413e68ca52abe73420"`); + await queryRunner.query(`DROP INDEX "IDX_db2098070b2b5a523c58181f74"`); + await queryRunner.query(`DROP TABLE "abuse_user_report"`); + await queryRunner.query(`DROP INDEX "IDX_0f4fb9ad355f3effff221ef245"`); + await queryRunner.query(`DROP INDEX "IDX_47f4b1892f5d6ba8efb3057d81"`); + await queryRunner.query(`DROP TABLE "note_favorite"`); + await queryRunner.query(`DROP INDEX "IDX_0b03cbcd7e6a7ce068efa8ecc2"`); + await queryRunner.query(`DROP INDEX "IDX_0c44bf4f680964145f2a68a341"`); + await queryRunner.query(`DROP INDEX "IDX_d57f9030cd3af7f63ffb1c267c"`); + await queryRunner.query(`DROP INDEX "IDX_4c02d38a976c3ae132228c6fce"`); + await queryRunner.query(`DROP INDEX "IDX_0e206cec573f1edff4a3062923"`); + await queryRunner.query(`DROP INDEX "IDX_2710a55f826ee236ea1a62698f"`); + await queryRunner.query(`DROP INDEX "IDX_347fec870eafea7b26c8a73bac"`); + await queryRunner.query(`DROP TABLE "hashtag"`); + await queryRunner.query(`DROP INDEX "IDX_605472305f26818cc93d1baaa7"`); + await queryRunner.query(`DROP INDEX "IDX_d844bfc6f3f523a05189076efa"`); + await queryRunner.query(`DROP TABLE "user_list_joining"`); + await queryRunner.query(`DROP INDEX "IDX_b7fcefbdd1c18dce86687531f9"`); + await queryRunner.query(`DROP TABLE "user_list"`); + await queryRunner.query(`DROP INDEX "IDX_98a1bc5cb30dfd159de056549f"`); + await queryRunner.query(`DROP INDEX "IDX_0627125f1a8a42c9a1929edb55"`); + await queryRunner.query(`DROP INDEX "IDX_2cd4a2743a99671308f5417759"`); + await queryRunner.query(`DROP INDEX "IDX_b9a354f7941c1e779f3b33aea6"`); + await queryRunner.query(`DROP TABLE "blocking"`); + await queryRunner.query(`DROP INDEX "IDX_97754ca6f2baff9b4abb7f853d"`); + await queryRunner.query(`DROP TABLE "sw_subscription"`); + await queryRunner.query(`DROP INDEX "IDX_1eb9d9824a630321a29fd3b290"`); + await queryRunner.query(`DROP INDEX "IDX_93060675b4a79a577f31d260c6"`); + await queryRunner.query(`DROP INDEX "IDX_ec96b4fed9dae517e0dbbe0675"`); + await queryRunner.query(`DROP INDEX "IDX_f86d57fbca33c7a4e6897490cc"`); + await queryRunner.query(`DROP TABLE "muting"`); + await queryRunner.query(`DROP INDEX "IDX_8d5afc98982185799b160e10eb"`); + await queryRunner.query(`DROP INDEX "IDX_2cd3b2a6b4cf0b910b260afe08"`); + await queryRunner.query(`DROP TABLE "instance"`); + await queryRunner.query(`DROP INDEX "IDX_307be5f1d1252e0388662acb96"`); + await queryRunner.query(`DROP INDEX "IDX_6516c5a6f3c015b4eed39978be"`); + await queryRunner.query(`DROP INDEX "IDX_24e0042143a18157b234df186c"`); + await queryRunner.query(`DROP INDEX "IDX_582f8fab771a9040a12961f3e7"`); + await queryRunner.query(`DROP TABLE "following"`); + await queryRunner.query(`DROP TABLE "meta"`); + await queryRunner.query(`DROP INDEX "IDX_3c601b70a1066d2c8b517094cb"`); + await queryRunner.query(`DROP INDEX "IDX_b11a5e627c41d4dc3170f1d370"`); + await queryRunner.query(`DROP TABLE "notification"`); + await queryRunner.query(`DROP INDEX "IDX_d908433a4953cc13216cd9c274"`); + await queryRunner.query(`DROP INDEX "IDX_e637cba4dc4410218c4251260e"`); + await queryRunner.query(`DROP INDEX "IDX_56b0166d34ddae49d8ef7610bb"`); + await queryRunner.query(`DROP TABLE "note_unread"`); + await queryRunner.query(`DROP INDEX "IDX_a42c93c69989ce1d09959df4cf"`); + await queryRunner.query(`DROP INDEX "IDX_44499765eec6b5489d72c4253b"`); + await queryRunner.query(`DROP INDEX "IDX_03e7028ab8388a3f5e3ce2a861"`); + await queryRunner.query(`DROP INDEX "IDX_b0134ec406e8d09a540f818288"`); + await queryRunner.query(`DROP INDEX "IDX_318cdf42a9cfc11f479bd802bb"`); + await queryRunner.query(`DROP TABLE "note_watching"`); + await queryRunner.query(`DROP INDEX "IDX_ad0c221b25672daf2df320a817"`); + await queryRunner.query(`DROP INDEX "IDX_45145e4953780f3cd5656f0ea6"`); + await queryRunner.query(`DROP INDEX "IDX_13761f64257f40c5636d0ff95e"`); + await queryRunner.query(`DROP INDEX "IDX_01f4581f114e0ebd2bbb876f0b"`); + await queryRunner.query(`DROP TABLE "note_reaction"`); + await queryRunner.query(`DROP INDEX "IDX_50bd7164c5b78f1f4a42c4d21f"`); + await queryRunner.query(`DROP INDEX "IDX_aecfbd5ef60374918e63ee95fa"`); + await queryRunner.query(`DROP INDEX "IDX_66d2bd2ee31d14bcc23069a89f"`); + await queryRunner.query(`DROP INDEX "IDX_0fb627e1c2f753262a74f0562d"`); + await queryRunner.query(`DROP TABLE "poll_vote"`); + await queryRunner.query(`DROP INDEX "IDX_7125a826ab192eb27e11d358a5"`); + await queryRunner.query(`DROP INDEX "IDX_88937d94d7443d9a99a76fa5c0"`); + await queryRunner.query(`DROP INDEX "IDX_54ebcb6d27222913b908d56fd8"`); + await queryRunner.query(`DROP INDEX "IDX_796a8c03959361f97dc2be1d5c"`); + await queryRunner.query(`DROP INDEX "IDX_25dfc71b0369b003a4cd434d0b"`); + await queryRunner.query(`DROP INDEX "IDX_51c063b6a133a9cb87145450f5"`); + await queryRunner.query(`DROP INDEX "IDX_153536c67d05e9adb24e99fc2b"`); + await queryRunner.query(`DROP INDEX "IDX_5b87d9d19127bd5d92026017a7"`); + await queryRunner.query(`DROP INDEX "IDX_52ccc804d7c69037d558bac4c9"`); + await queryRunner.query(`DROP INDEX "IDX_17cb3553c700a4985dff5a30ff"`); + await queryRunner.query(`DROP INDEX "IDX_e7c0567f5261063592f022e9b5"`); + await queryRunner.query(`DROP TABLE "note"`); + await queryRunner.query(`DROP TYPE "note_visibility_enum"`); + await queryRunner.query(`DROP INDEX "IDX_9949557d0e1b2c19e5344c171e"`); + await queryRunner.query(`DROP INDEX "IDX_64c327441248bae40f7d92f34f"`); + await queryRunner.query(`DROP INDEX "IDX_70ba8f6af34bc924fc9e12adb8"`); + await queryRunner.query(`DROP TABLE "access_token"`); + await queryRunner.query(`DROP INDEX "IDX_f49922d511d666848f250663c4"`); + await queryRunner.query(`DROP INDEX "IDX_3f5b0899ef90527a3462d7c2cb"`); + await queryRunner.query(`DROP INDEX "IDX_048a757923ed8b157e9895da53"`); + await queryRunner.query(`DROP TABLE "app"`); + await queryRunner.query(`DROP INDEX "IDX_5deb01ae162d1d70b80d064c27"`); + await queryRunner.query(`DROP INDEX "IDX_a854e557b1b14814750c7c7b0c"`); + await queryRunner.query(`DROP INDEX "IDX_be623adaa4c566baf5d29ce0c8"`); + await queryRunner.query(`DROP INDEX "IDX_3252a5df8d5bbd16b281f7799e"`); + await queryRunner.query(`DROP INDEX "IDX_fa99d777623947a5b05f394cae"`); + await queryRunner.query(`DROP INDEX "IDX_a27b942a0d6dcff90e3ee9b5e8"`); + await queryRunner.query(`DROP INDEX "IDX_80ca6e6ef65fb9ef34ea8c90f4"`); + await queryRunner.query(`DROP INDEX "IDX_e11e649824a45d8ed01d597fd9"`); + await queryRunner.query(`DROP TABLE "user"`); + await queryRunner.query(`DROP INDEX "IDX_bb90d1956dafc4068c28aa7560"`); + await queryRunner.query(`DROP INDEX "IDX_e5848eac4940934e23dbc17581"`); + await queryRunner.query(`DROP INDEX "IDX_c55b2b7c284d9fef98026fc88e"`); + await queryRunner.query(`DROP INDEX "IDX_e74022ce9a074b3866f70e0d27"`); + await queryRunner.query(`DROP INDEX "IDX_d85a184c2540d2deba33daf642"`); + await queryRunner.query(`DROP INDEX "IDX_a40b8df8c989d7db937ea27cf6"`); + await queryRunner.query(`DROP INDEX "IDX_37bb9a1b4585f8a3beb24c62d6"`); + await queryRunner.query(`DROP INDEX "IDX_92779627994ac79277f070c91e"`); + await queryRunner.query(`DROP INDEX "IDX_860fa6f6c7df5bb887249fba22"`); + await queryRunner.query(`DROP INDEX "IDX_c8dfad3b72196dd1d6b5db168a"`); + await queryRunner.query(`DROP TABLE "drive_file"`); + await queryRunner.query(`DROP INDEX "IDX_00ceffb0cdc238b3233294f08f"`); + await queryRunner.query(`DROP INDEX "IDX_f4fc06e49c0171c85f1c48060d"`); + await queryRunner.query(`DROP INDEX "IDX_02878d441ceae15ce060b73daf"`); + await queryRunner.query(`DROP TABLE "drive_folder"`); + await queryRunner.query(`DROP INDEX "IDX_584b536b49e53ac81beb39a177"`); + await queryRunner.query(`DROP INDEX "IDX_8cb40cfc8f3c28261e6f887b03"`); + await queryRunner.query(`DROP INDEX "IDX_8e4eb51a35d81b64dda28eed0a"`); + await queryRunner.query(`DROP TABLE "log"`); + await queryRunner.query(`DROP TYPE "log_level_enum"`); + } } diff --git a/packages/backend/migration/1556348509290-Pages.js b/packages/backend/migration/1556348509290-Pages.js index 50caa2ce91..696b2acbec 100644 --- a/packages/backend/migration/1556348509290-Pages.js +++ b/packages/backend/migration/1556348509290-Pages.js @@ -1,28 +1,50 @@ - - export class Pages1556348509290 { - async up(queryRunner) { - await queryRunner.query(`CREATE TYPE "page_visibility_enum" AS ENUM('public', 'followers', 'specified')`); - await queryRunner.query(`CREATE TABLE "page" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL, "title" character varying(256) NOT NULL, "name" character varying(256) NOT NULL, "summary" character varying(256), "alignCenter" boolean NOT NULL, "font" character varying(32) NOT NULL, "userId" character varying(32) NOT NULL, "eyeCatchingImageId" character varying(32), "content" jsonb NOT NULL DEFAULT '[]', "variables" jsonb NOT NULL DEFAULT '[]', "visibility" "page_visibility_enum" NOT NULL, "visibleUserIds" character varying(32) array NOT NULL DEFAULT '{}'::varchar[], CONSTRAINT "PK_742f4117e065c5b6ad21b37ba1f" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE INDEX "IDX_fbb4297c927a9b85e9cefa2eb1" ON "page" ("createdAt") `); - await queryRunner.query(`CREATE INDEX "IDX_af639b066dfbca78b01a920f8a" ON "page" ("updatedAt") `); - await queryRunner.query(`CREATE INDEX "IDX_b82c19c08afb292de4600d99e4" ON "page" ("name") `); - await queryRunner.query(`CREATE INDEX "IDX_ae1d917992dd0c9d9bbdad06c4" ON "page" ("userId") `); - await queryRunner.query(`CREATE INDEX "IDX_90148bbc2bf0854428786bfc15" ON "page" ("visibleUserIds") `); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_2133ef8317e4bdb839c0dcbf13" ON "page" ("userId", "name") `); - await queryRunner.query(`ALTER TABLE "page" ADD CONSTRAINT "FK_ae1d917992dd0c9d9bbdad06c4a" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "page" ADD CONSTRAINT "FK_3126dd7c502c9e4d7597ef7ef10" FOREIGN KEY ("eyeCatchingImageId") REFERENCES "drive_file"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "page" DROP CONSTRAINT "FK_3126dd7c502c9e4d7597ef7ef10"`); - await queryRunner.query(`ALTER TABLE "page" DROP CONSTRAINT "FK_ae1d917992dd0c9d9bbdad06c4a"`); - await queryRunner.query(`DROP INDEX "IDX_2133ef8317e4bdb839c0dcbf13"`); - await queryRunner.query(`DROP INDEX "IDX_90148bbc2bf0854428786bfc15"`); - await queryRunner.query(`DROP INDEX "IDX_ae1d917992dd0c9d9bbdad06c4"`); - await queryRunner.query(`DROP INDEX "IDX_b82c19c08afb292de4600d99e4"`); - await queryRunner.query(`DROP INDEX "IDX_af639b066dfbca78b01a920f8a"`); - await queryRunner.query(`DROP INDEX "IDX_fbb4297c927a9b85e9cefa2eb1"`); - await queryRunner.query(`DROP TABLE "page"`); - await queryRunner.query(`DROP TYPE "page_visibility_enum"`); - } + async up(queryRunner) { + await queryRunner.query( + `CREATE TYPE "page_visibility_enum" AS ENUM('public', 'followers', 'specified')`, + ); + await queryRunner.query( + `CREATE TABLE "page" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL, "title" character varying(256) NOT NULL, "name" character varying(256) NOT NULL, "summary" character varying(256), "alignCenter" boolean NOT NULL, "font" character varying(32) NOT NULL, "userId" character varying(32) NOT NULL, "eyeCatchingImageId" character varying(32), "content" jsonb NOT NULL DEFAULT '[]', "variables" jsonb NOT NULL DEFAULT '[]', "visibility" "page_visibility_enum" NOT NULL, "visibleUserIds" character varying(32) array NOT NULL DEFAULT '{}'::varchar[], CONSTRAINT "PK_742f4117e065c5b6ad21b37ba1f" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE INDEX "IDX_fbb4297c927a9b85e9cefa2eb1" ON "page" ("createdAt") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_af639b066dfbca78b01a920f8a" ON "page" ("updatedAt") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_b82c19c08afb292de4600d99e4" ON "page" ("name") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_ae1d917992dd0c9d9bbdad06c4" ON "page" ("userId") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_90148bbc2bf0854428786bfc15" ON "page" ("visibleUserIds") `, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_2133ef8317e4bdb839c0dcbf13" ON "page" ("userId", "name") `, + ); + await queryRunner.query( + `ALTER TABLE "page" ADD CONSTRAINT "FK_ae1d917992dd0c9d9bbdad06c4a" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "page" ADD CONSTRAINT "FK_3126dd7c502c9e4d7597ef7ef10" FOREIGN KEY ("eyeCatchingImageId") REFERENCES "drive_file"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + } + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "page" DROP CONSTRAINT "FK_3126dd7c502c9e4d7597ef7ef10"`, + ); + await queryRunner.query( + `ALTER TABLE "page" DROP CONSTRAINT "FK_ae1d917992dd0c9d9bbdad06c4a"`, + ); + await queryRunner.query(`DROP INDEX "IDX_2133ef8317e4bdb839c0dcbf13"`); + await queryRunner.query(`DROP INDEX "IDX_90148bbc2bf0854428786bfc15"`); + await queryRunner.query(`DROP INDEX "IDX_ae1d917992dd0c9d9bbdad06c4"`); + await queryRunner.query(`DROP INDEX "IDX_b82c19c08afb292de4600d99e4"`); + await queryRunner.query(`DROP INDEX "IDX_af639b066dfbca78b01a920f8a"`); + await queryRunner.query(`DROP INDEX "IDX_fbb4297c927a9b85e9cefa2eb1"`); + await queryRunner.query(`DROP TABLE "page"`); + await queryRunner.query(`DROP TYPE "page_visibility_enum"`); + } } diff --git a/packages/backend/migration/1556746559567-UserProfile.js b/packages/backend/migration/1556746559567-UserProfile.js index 50a9d1a8be..d229ec519f 100644 --- a/packages/backend/migration/1556746559567-UserProfile.js +++ b/packages/backend/migration/1556746559567-UserProfile.js @@ -1,13 +1,21 @@ - - export class UserProfile1556746559567 { - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "user_profile" ALTER COLUMN "githubId" TYPE VARCHAR(64) USING "githubId"::VARCHAR(64)`); - await queryRunner.query(`ALTER TABLE "user_profile" ALTER COLUMN "discordExpiresDate" TYPE VARCHAR(64) USING "discordExpiresDate"::VARCHAR(64)`); - } - async down(queryRunner) { - await queryRunner.query(`UPDATE "user_profile" SET github = FALSE, discord = FALSE`); - await queryRunner.query(`ALTER TABLE "user_profile" ALTER COLUMN "githubId" TYPE INTEGER USING NULL`); - await queryRunner.query(`ALTER TABLE "user_profile" ALTER COLUMN "discordExpiresDate" TYPE INTEGER USING NULL`); - } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "user_profile" ALTER COLUMN "githubId" TYPE VARCHAR(64) USING "githubId"::VARCHAR(64)`, + ); + await queryRunner.query( + `ALTER TABLE "user_profile" ALTER COLUMN "discordExpiresDate" TYPE VARCHAR(64) USING "discordExpiresDate"::VARCHAR(64)`, + ); + } + async down(queryRunner) { + await queryRunner.query( + `UPDATE "user_profile" SET github = FALSE, discord = FALSE`, + ); + await queryRunner.query( + `ALTER TABLE "user_profile" ALTER COLUMN "githubId" TYPE INTEGER USING NULL`, + ); + await queryRunner.query( + `ALTER TABLE "user_profile" ALTER COLUMN "discordExpiresDate" TYPE INTEGER USING NULL`, + ); + } } diff --git a/packages/backend/migration/1557476068003-PinnedUsers.js b/packages/backend/migration/1557476068003-PinnedUsers.js index d9cce25435..6f348836ad 100644 --- a/packages/backend/migration/1557476068003-PinnedUsers.js +++ b/packages/backend/migration/1557476068003-PinnedUsers.js @@ -1,10 +1,10 @@ - - export class PinnedUsers1557476068003 { - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "meta" ADD "pinnedUsers" character varying(256) array NOT NULL DEFAULT '{}'::varchar[]`); - } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "pinnedUsers"`); - } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "meta" ADD "pinnedUsers" character varying(256) array NOT NULL DEFAULT '{}'::varchar[]`, + ); + } + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "pinnedUsers"`); + } } diff --git a/packages/backend/migration/1557761316509-AddSomeUrls.js b/packages/backend/migration/1557761316509-AddSomeUrls.js index ab8736f7cc..9839fe010b 100644 --- a/packages/backend/migration/1557761316509-AddSomeUrls.js +++ b/packages/backend/migration/1557761316509-AddSomeUrls.js @@ -1,14 +1,18 @@ - - export class AddSomeUrls1557761316509 { - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "meta" ADD "ToSUrl" character varying(512)`); - await queryRunner.query(`ALTER TABLE "meta" ADD "repositoryUrl" character varying(512) NOT NULL DEFAULT 'https://github.com/misskey-dev/misskey'`); - await queryRunner.query(`ALTER TABLE "meta" ADD "feedbackUrl" character varying(512) DEFAULT 'https://github.com/misskey-dev/misskey/issues/new'`); - } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "feedbackUrl"`); - await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "repositoryUrl"`); - await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "ToSUrl"`); - } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "meta" ADD "ToSUrl" character varying(512)`, + ); + await queryRunner.query( + `ALTER TABLE "meta" ADD "repositoryUrl" character varying(512) NOT NULL DEFAULT 'https://codeberg.org/firefish/firefish'`, + ); + await queryRunner.query( + `ALTER TABLE "meta" ADD "feedbackUrl" character varying(512) DEFAULT 'https://codeberg.org/firefish/firefish/issues'`, + ); + } + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "feedbackUrl"`); + await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "repositoryUrl"`); + await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "ToSUrl"`); + } } diff --git a/packages/backend/migration/1557932705754-ObjectStorageSetting.js b/packages/backend/migration/1557932705754-ObjectStorageSetting.js index 19a0b9d5cd..5fcc98fe15 100644 --- a/packages/backend/migration/1557932705754-ObjectStorageSetting.js +++ b/packages/backend/migration/1557932705754-ObjectStorageSetting.js @@ -1,28 +1,66 @@ - - export class ObjectStorageSetting1557932705754 { - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "meta" ADD "useObjectStorage" boolean NOT NULL DEFAULT false`); - await queryRunner.query(`ALTER TABLE "meta" ADD "objectStorageBucket" character varying(512)`); - await queryRunner.query(`ALTER TABLE "meta" ADD "objectStoragePrefix" character varying(512)`); - await queryRunner.query(`ALTER TABLE "meta" ADD "objectStorageBaseUrl" character varying(512)`); - await queryRunner.query(`ALTER TABLE "meta" ADD "objectStorageEndpoint" character varying(512)`); - await queryRunner.query(`ALTER TABLE "meta" ADD "objectStorageRegion" character varying(512)`); - await queryRunner.query(`ALTER TABLE "meta" ADD "objectStorageAccessKey" character varying(512)`); - await queryRunner.query(`ALTER TABLE "meta" ADD "objectStorageSecretKey" character varying(512)`); - await queryRunner.query(`ALTER TABLE "meta" ADD "objectStoragePort" integer`); - await queryRunner.query(`ALTER TABLE "meta" ADD "objectStorageUseSSL" boolean NOT NULL DEFAULT true`); - } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "objectStorageUseSSL"`); - await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "objectStoragePort"`); - await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "objectStorageSecretKey"`); - await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "objectStorageAccessKey"`); - await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "objectStorageRegion"`); - await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "objectStorageEndpoint"`); - await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "objectStorageBaseUrl"`); - await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "objectStoragePrefix"`); - await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "objectStorageBucket"`); - await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "useObjectStorage"`); - } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "meta" ADD "useObjectStorage" boolean NOT NULL DEFAULT false`, + ); + await queryRunner.query( + `ALTER TABLE "meta" ADD "objectStorageBucket" character varying(512)`, + ); + await queryRunner.query( + `ALTER TABLE "meta" ADD "objectStoragePrefix" character varying(512)`, + ); + await queryRunner.query( + `ALTER TABLE "meta" ADD "objectStorageBaseUrl" character varying(512)`, + ); + await queryRunner.query( + `ALTER TABLE "meta" ADD "objectStorageEndpoint" character varying(512)`, + ); + await queryRunner.query( + `ALTER TABLE "meta" ADD "objectStorageRegion" character varying(512)`, + ); + await queryRunner.query( + `ALTER TABLE "meta" ADD "objectStorageAccessKey" character varying(512)`, + ); + await queryRunner.query( + `ALTER TABLE "meta" ADD "objectStorageSecretKey" character varying(512)`, + ); + await queryRunner.query( + `ALTER TABLE "meta" ADD "objectStoragePort" integer`, + ); + await queryRunner.query( + `ALTER TABLE "meta" ADD "objectStorageUseSSL" boolean NOT NULL DEFAULT true`, + ); + } + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "meta" DROP COLUMN "objectStorageUseSSL"`, + ); + await queryRunner.query( + `ALTER TABLE "meta" DROP COLUMN "objectStoragePort"`, + ); + await queryRunner.query( + `ALTER TABLE "meta" DROP COLUMN "objectStorageSecretKey"`, + ); + await queryRunner.query( + `ALTER TABLE "meta" DROP COLUMN "objectStorageAccessKey"`, + ); + await queryRunner.query( + `ALTER TABLE "meta" DROP COLUMN "objectStorageRegion"`, + ); + await queryRunner.query( + `ALTER TABLE "meta" DROP COLUMN "objectStorageEndpoint"`, + ); + await queryRunner.query( + `ALTER TABLE "meta" DROP COLUMN "objectStorageBaseUrl"`, + ); + await queryRunner.query( + `ALTER TABLE "meta" DROP COLUMN "objectStoragePrefix"`, + ); + await queryRunner.query( + `ALTER TABLE "meta" DROP COLUMN "objectStorageBucket"`, + ); + await queryRunner.query( + `ALTER TABLE "meta" DROP COLUMN "useObjectStorage"`, + ); + } } diff --git a/packages/backend/migration/1558072954435-PageLike.js b/packages/backend/migration/1558072954435-PageLike.js index 31b08418a9..e919e02ddb 100644 --- a/packages/backend/migration/1558072954435-PageLike.js +++ b/packages/backend/migration/1558072954435-PageLike.js @@ -1,20 +1,34 @@ - - export class PageLike1558072954435 { - async up(queryRunner) { - await queryRunner.query(`CREATE TABLE "page_like" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "userId" character varying(32) NOT NULL, "pageId" character varying(32) NOT NULL, CONSTRAINT "PK_813f034843af992d3ae0f43c64c" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE INDEX "IDX_0e61efab7f88dbb79c9166dbb4" ON "page_like" ("userId") `); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_4ce6fb9c70529b4c8ac46c9bfa" ON "page_like" ("userId", "pageId") `); - await queryRunner.query(`ALTER TABLE "page" ADD "likedCount" integer NOT NULL DEFAULT 0`); - await queryRunner.query(`ALTER TABLE "page_like" ADD CONSTRAINT "FK_0e61efab7f88dbb79c9166dbb48" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "page_like" ADD CONSTRAINT "FK_cf8782626dced3176038176a847" FOREIGN KEY ("pageId") REFERENCES "page"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "page_like" DROP CONSTRAINT "FK_cf8782626dced3176038176a847"`); - await queryRunner.query(`ALTER TABLE "page_like" DROP CONSTRAINT "FK_0e61efab7f88dbb79c9166dbb48"`); - await queryRunner.query(`ALTER TABLE "page" DROP COLUMN "likedCount"`); - await queryRunner.query(`DROP INDEX "IDX_4ce6fb9c70529b4c8ac46c9bfa"`); - await queryRunner.query(`DROP INDEX "IDX_0e61efab7f88dbb79c9166dbb4"`); - await queryRunner.query(`DROP TABLE "page_like"`); - } + async up(queryRunner) { + await queryRunner.query( + `CREATE TABLE "page_like" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "userId" character varying(32) NOT NULL, "pageId" character varying(32) NOT NULL, CONSTRAINT "PK_813f034843af992d3ae0f43c64c" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE INDEX "IDX_0e61efab7f88dbb79c9166dbb4" ON "page_like" ("userId") `, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_4ce6fb9c70529b4c8ac46c9bfa" ON "page_like" ("userId", "pageId") `, + ); + await queryRunner.query( + `ALTER TABLE "page" ADD "likedCount" integer NOT NULL DEFAULT 0`, + ); + await queryRunner.query( + `ALTER TABLE "page_like" ADD CONSTRAINT "FK_0e61efab7f88dbb79c9166dbb48" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "page_like" ADD CONSTRAINT "FK_cf8782626dced3176038176a847" FOREIGN KEY ("pageId") REFERENCES "page"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + } + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "page_like" DROP CONSTRAINT "FK_cf8782626dced3176038176a847"`, + ); + await queryRunner.query( + `ALTER TABLE "page_like" DROP CONSTRAINT "FK_0e61efab7f88dbb79c9166dbb48"`, + ); + await queryRunner.query(`ALTER TABLE "page" DROP COLUMN "likedCount"`); + await queryRunner.query(`DROP INDEX "IDX_4ce6fb9c70529b4c8ac46c9bfa"`); + await queryRunner.query(`DROP INDEX "IDX_0e61efab7f88dbb79c9166dbb4"`); + await queryRunner.query(`DROP TABLE "page_like"`); + } } diff --git a/packages/backend/migration/1558103093633-UserGroup.js b/packages/backend/migration/1558103093633-UserGroup.js index b670b31c3d..dc581a24ef 100644 --- a/packages/backend/migration/1558103093633-UserGroup.js +++ b/packages/backend/migration/1558103093633-UserGroup.js @@ -1,38 +1,82 @@ - - export class UserGroup1558103093633 { - async up(queryRunner) { - await queryRunner.query(`CREATE TABLE "user_group" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "name" character varying(256) NOT NULL, "userId" character varying(32) NOT NULL, "isPrivate" boolean NOT NULL DEFAULT false, CONSTRAINT "PK_3c29fba6fe013ec8724378ce7c9" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE INDEX "IDX_20e30aa35180e317e133d75316" ON "user_group" ("createdAt") `); - await queryRunner.query(`CREATE INDEX "IDX_3d6b372788ab01be58853003c9" ON "user_group" ("userId") `); - await queryRunner.query(`CREATE TABLE "user_group_joining" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "userId" character varying(32) NOT NULL, "userGroupId" character varying(32) NOT NULL, CONSTRAINT "PK_15f2425885253c5507e1599cfe7" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE INDEX "IDX_f3a1b4bd0c7cabba958a0c0b23" ON "user_group_joining" ("userId") `); - await queryRunner.query(`CREATE INDEX "IDX_67dc758bc0566985d1b3d39986" ON "user_group_joining" ("userGroupId") `); - await queryRunner.query(`ALTER TABLE "messaging_message" ADD "groupId" character varying(32)`); - await queryRunner.query(`ALTER TABLE "messaging_message" ADD "reads" character varying(32) array NOT NULL DEFAULT '{}'::varchar[]`); - await queryRunner.query(`ALTER TABLE "messaging_message" ALTER COLUMN "recipientId" DROP NOT NULL`); - await queryRunner.query(`COMMENT ON COLUMN "messaging_message"."recipientId" IS 'The recipient user ID.'`); - await queryRunner.query(`CREATE INDEX "IDX_2c4be03b446884f9e9c502135b" ON "messaging_message" ("groupId") `); - await queryRunner.query(`ALTER TABLE "messaging_message" ADD CONSTRAINT "FK_2c4be03b446884f9e9c502135be" FOREIGN KEY ("groupId") REFERENCES "user_group"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "user_group" ADD CONSTRAINT "FK_3d6b372788ab01be58853003c93" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "user_group_joining" ADD CONSTRAINT "FK_f3a1b4bd0c7cabba958a0c0b231" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "user_group_joining" ADD CONSTRAINT "FK_67dc758bc0566985d1b3d399865" FOREIGN KEY ("userGroupId") REFERENCES "user_group"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "user_group_joining" DROP CONSTRAINT "FK_67dc758bc0566985d1b3d399865"`); - await queryRunner.query(`ALTER TABLE "user_group_joining" DROP CONSTRAINT "FK_f3a1b4bd0c7cabba958a0c0b231"`); - await queryRunner.query(`ALTER TABLE "user_group" DROP CONSTRAINT "FK_3d6b372788ab01be58853003c93"`); - await queryRunner.query(`ALTER TABLE "messaging_message" DROP CONSTRAINT "FK_2c4be03b446884f9e9c502135be"`); - await queryRunner.query(`DROP INDEX "IDX_2c4be03b446884f9e9c502135b"`); - await queryRunner.query(`COMMENT ON COLUMN "messaging_message"."recipientId" IS ''`); - await queryRunner.query(`ALTER TABLE "messaging_message" ALTER COLUMN "recipientId" SET NOT NULL`); - await queryRunner.query(`ALTER TABLE "messaging_message" DROP COLUMN "reads"`); - await queryRunner.query(`ALTER TABLE "messaging_message" DROP COLUMN "groupId"`); - await queryRunner.query(`DROP INDEX "IDX_67dc758bc0566985d1b3d39986"`); - await queryRunner.query(`DROP INDEX "IDX_f3a1b4bd0c7cabba958a0c0b23"`); - await queryRunner.query(`DROP TABLE "user_group_joining"`); - await queryRunner.query(`DROP INDEX "IDX_3d6b372788ab01be58853003c9"`); - await queryRunner.query(`DROP INDEX "IDX_20e30aa35180e317e133d75316"`); - await queryRunner.query(`DROP TABLE "user_group"`); - } + async up(queryRunner) { + await queryRunner.query( + `CREATE TABLE "user_group" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "name" character varying(256) NOT NULL, "userId" character varying(32) NOT NULL, "isPrivate" boolean NOT NULL DEFAULT false, CONSTRAINT "PK_3c29fba6fe013ec8724378ce7c9" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE INDEX "IDX_20e30aa35180e317e133d75316" ON "user_group" ("createdAt") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_3d6b372788ab01be58853003c9" ON "user_group" ("userId") `, + ); + await queryRunner.query( + `CREATE TABLE "user_group_joining" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "userId" character varying(32) NOT NULL, "userGroupId" character varying(32) NOT NULL, CONSTRAINT "PK_15f2425885253c5507e1599cfe7" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE INDEX "IDX_f3a1b4bd0c7cabba958a0c0b23" ON "user_group_joining" ("userId") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_67dc758bc0566985d1b3d39986" ON "user_group_joining" ("userGroupId") `, + ); + await queryRunner.query( + `ALTER TABLE "messaging_message" ADD "groupId" character varying(32)`, + ); + await queryRunner.query( + `ALTER TABLE "messaging_message" ADD "reads" character varying(32) array NOT NULL DEFAULT '{}'::varchar[]`, + ); + await queryRunner.query( + `ALTER TABLE "messaging_message" ALTER COLUMN "recipientId" DROP NOT NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "messaging_message"."recipientId" IS 'The recipient user ID.'`, + ); + await queryRunner.query( + `CREATE INDEX "IDX_2c4be03b446884f9e9c502135b" ON "messaging_message" ("groupId") `, + ); + await queryRunner.query( + `ALTER TABLE "messaging_message" ADD CONSTRAINT "FK_2c4be03b446884f9e9c502135be" FOREIGN KEY ("groupId") REFERENCES "user_group"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "user_group" ADD CONSTRAINT "FK_3d6b372788ab01be58853003c93" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "user_group_joining" ADD CONSTRAINT "FK_f3a1b4bd0c7cabba958a0c0b231" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "user_group_joining" ADD CONSTRAINT "FK_67dc758bc0566985d1b3d399865" FOREIGN KEY ("userGroupId") REFERENCES "user_group"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + } + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "user_group_joining" DROP CONSTRAINT "FK_67dc758bc0566985d1b3d399865"`, + ); + await queryRunner.query( + `ALTER TABLE "user_group_joining" DROP CONSTRAINT "FK_f3a1b4bd0c7cabba958a0c0b231"`, + ); + await queryRunner.query( + `ALTER TABLE "user_group" DROP CONSTRAINT "FK_3d6b372788ab01be58853003c93"`, + ); + await queryRunner.query( + `ALTER TABLE "messaging_message" DROP CONSTRAINT "FK_2c4be03b446884f9e9c502135be"`, + ); + await queryRunner.query(`DROP INDEX "IDX_2c4be03b446884f9e9c502135b"`); + await queryRunner.query( + `COMMENT ON COLUMN "messaging_message"."recipientId" IS ''`, + ); + await queryRunner.query( + `ALTER TABLE "messaging_message" ALTER COLUMN "recipientId" SET NOT NULL`, + ); + await queryRunner.query( + `ALTER TABLE "messaging_message" DROP COLUMN "reads"`, + ); + await queryRunner.query( + `ALTER TABLE "messaging_message" DROP COLUMN "groupId"`, + ); + await queryRunner.query(`DROP INDEX "IDX_67dc758bc0566985d1b3d39986"`); + await queryRunner.query(`DROP INDEX "IDX_f3a1b4bd0c7cabba958a0c0b23"`); + await queryRunner.query(`DROP TABLE "user_group_joining"`); + await queryRunner.query(`DROP INDEX "IDX_3d6b372788ab01be58853003c9"`); + await queryRunner.query(`DROP INDEX "IDX_20e30aa35180e317e133d75316"`); + await queryRunner.query(`DROP TABLE "user_group"`); + } } diff --git a/packages/backend/migration/1558257926829-UserGroupInvite.js b/packages/backend/migration/1558257926829-UserGroupInvite.js index e48bd3a7ff..ed137fdb2e 100644 --- a/packages/backend/migration/1558257926829-UserGroupInvite.js +++ b/packages/backend/migration/1558257926829-UserGroupInvite.js @@ -1,22 +1,38 @@ - - export class UserGroupInvite1558257926829 { - async up(queryRunner) { - await queryRunner.query(`CREATE TABLE "user_group_invite" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "userId" character varying(32) NOT NULL, "userGroupId" character varying(32) NOT NULL, CONSTRAINT "PK_3893884af0d3a5f4d01e7921a97" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE INDEX "IDX_1039988afa3bf991185b277fe0" ON "user_group_invite" ("userId") `); - await queryRunner.query(`CREATE INDEX "IDX_e10924607d058004304611a436" ON "user_group_invite" ("userGroupId") `); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_78787741f9010886796f2320a4" ON "user_group_invite" ("userId", "userGroupId") `); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_d9ecaed8c6dc43f3592c229282" ON "user_group_joining" ("userId", "userGroupId") `); - await queryRunner.query(`ALTER TABLE "user_group_invite" ADD CONSTRAINT "FK_1039988afa3bf991185b277fe03" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "user_group_invite" ADD CONSTRAINT "FK_e10924607d058004304611a436a" FOREIGN KEY ("userGroupId") REFERENCES "user_group"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "user_group_invite" DROP CONSTRAINT "FK_e10924607d058004304611a436a"`); - await queryRunner.query(`ALTER TABLE "user_group_invite" DROP CONSTRAINT "FK_1039988afa3bf991185b277fe03"`); - await queryRunner.query(`DROP INDEX "IDX_d9ecaed8c6dc43f3592c229282"`); - await queryRunner.query(`DROP INDEX "IDX_78787741f9010886796f2320a4"`); - await queryRunner.query(`DROP INDEX "IDX_e10924607d058004304611a436"`); - await queryRunner.query(`DROP INDEX "IDX_1039988afa3bf991185b277fe0"`); - await queryRunner.query(`DROP TABLE "user_group_invite"`); - } + async up(queryRunner) { + await queryRunner.query( + `CREATE TABLE "user_group_invite" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "userId" character varying(32) NOT NULL, "userGroupId" character varying(32) NOT NULL, CONSTRAINT "PK_3893884af0d3a5f4d01e7921a97" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE INDEX "IDX_1039988afa3bf991185b277fe0" ON "user_group_invite" ("userId") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_e10924607d058004304611a436" ON "user_group_invite" ("userGroupId") `, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_78787741f9010886796f2320a4" ON "user_group_invite" ("userId", "userGroupId") `, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_d9ecaed8c6dc43f3592c229282" ON "user_group_joining" ("userId", "userGroupId") `, + ); + await queryRunner.query( + `ALTER TABLE "user_group_invite" ADD CONSTRAINT "FK_1039988afa3bf991185b277fe03" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "user_group_invite" ADD CONSTRAINT "FK_e10924607d058004304611a436a" FOREIGN KEY ("userGroupId") REFERENCES "user_group"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + } + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "user_group_invite" DROP CONSTRAINT "FK_e10924607d058004304611a436a"`, + ); + await queryRunner.query( + `ALTER TABLE "user_group_invite" DROP CONSTRAINT "FK_1039988afa3bf991185b277fe03"`, + ); + await queryRunner.query(`DROP INDEX "IDX_d9ecaed8c6dc43f3592c229282"`); + await queryRunner.query(`DROP INDEX "IDX_78787741f9010886796f2320a4"`); + await queryRunner.query(`DROP INDEX "IDX_e10924607d058004304611a436"`); + await queryRunner.query(`DROP INDEX "IDX_1039988afa3bf991185b277fe0"`); + await queryRunner.query(`DROP TABLE "user_group_invite"`); + } } diff --git a/packages/backend/migration/1558266512381-UserListJoining.js b/packages/backend/migration/1558266512381-UserListJoining.js index 3398aed139..3597e51f76 100644 --- a/packages/backend/migration/1558266512381-UserListJoining.js +++ b/packages/backend/migration/1558266512381-UserListJoining.js @@ -1,10 +1,10 @@ - - export class UserListJoining1558266512381 { - async up(queryRunner) { - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_90f7da835e4c10aca6853621e1" ON "user_list_joining" ("userId", "userListId") `); - } - async down(queryRunner) { - await queryRunner.query(`DROP INDEX "IDX_90f7da835e4c10aca6853621e1"`); - } + async up(queryRunner) { + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_90f7da835e4c10aca6853621e1" ON "user_list_joining" ("userId", "userListId") `, + ); + } + async down(queryRunner) { + await queryRunner.query(`DROP INDEX "IDX_90f7da835e4c10aca6853621e1"`); + } } diff --git a/packages/backend/migration/1561706992953-webauthn.js b/packages/backend/migration/1561706992953-webauthn.js index b007ffef14..f860d2c11b 100644 --- a/packages/backend/migration/1561706992953-webauthn.js +++ b/packages/backend/migration/1561706992953-webauthn.js @@ -1,26 +1,48 @@ - - export class webauthn1561706992953 { - async up(queryRunner) { - await queryRunner.query(`CREATE TABLE "attestation_challenge" ("id" character varying(32) NOT NULL, "userId" character varying(32) NOT NULL, "challenge" character varying(64) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "registrationChallenge" boolean NOT NULL DEFAULT false, CONSTRAINT "PK_d0ba6786e093f1bcb497572a6b5" PRIMARY KEY ("id", "userId"))`); - await queryRunner.query(`CREATE INDEX "IDX_f1a461a618fa1755692d0e0d59" ON "attestation_challenge" ("userId") `); - await queryRunner.query(`CREATE INDEX "IDX_47efb914aed1f72dd39a306c7b" ON "attestation_challenge" ("challenge") `); - await queryRunner.query(`CREATE TABLE "user_security_key" ("id" character varying NOT NULL, "userId" character varying(32) NOT NULL, "publicKey" character varying NOT NULL, "lastUsed" TIMESTAMP WITH TIME ZONE NOT NULL, "name" character varying(30) NOT NULL, CONSTRAINT "PK_3e508571121ab39c5f85d10c166" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE INDEX "IDX_ff9ca3b5f3ee3d0681367a9b44" ON "user_security_key" ("userId") `); - await queryRunner.query(`CREATE INDEX "IDX_0d7718e562dcedd0aa5cf2c9f7" ON "user_security_key" ("publicKey") `); - await queryRunner.query(`ALTER TABLE "user_profile" ADD "securityKeysAvailable" boolean NOT NULL DEFAULT false`); - await queryRunner.query(`ALTER TABLE "attestation_challenge" ADD CONSTRAINT "FK_f1a461a618fa1755692d0e0d592" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "user_security_key" ADD CONSTRAINT "FK_ff9ca3b5f3ee3d0681367a9b447" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "user_security_key" DROP CONSTRAINT "FK_ff9ca3b5f3ee3d0681367a9b447"`); - await queryRunner.query(`ALTER TABLE "attestation_challenge" DROP CONSTRAINT "FK_f1a461a618fa1755692d0e0d592"`); - await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "securityKeysAvailable"`); - await queryRunner.query(`DROP INDEX "IDX_0d7718e562dcedd0aa5cf2c9f7"`); - await queryRunner.query(`DROP INDEX "IDX_ff9ca3b5f3ee3d0681367a9b44"`); - await queryRunner.query(`DROP TABLE "user_security_key"`); - await queryRunner.query(`DROP INDEX "IDX_47efb914aed1f72dd39a306c7b"`); - await queryRunner.query(`DROP INDEX "IDX_f1a461a618fa1755692d0e0d59"`); - await queryRunner.query(`DROP TABLE "attestation_challenge"`); - } + async up(queryRunner) { + await queryRunner.query( + `CREATE TABLE "attestation_challenge" ("id" character varying(32) NOT NULL, "userId" character varying(32) NOT NULL, "challenge" character varying(64) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "registrationChallenge" boolean NOT NULL DEFAULT false, CONSTRAINT "PK_d0ba6786e093f1bcb497572a6b5" PRIMARY KEY ("id", "userId"))`, + ); + await queryRunner.query( + `CREATE INDEX "IDX_f1a461a618fa1755692d0e0d59" ON "attestation_challenge" ("userId") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_47efb914aed1f72dd39a306c7b" ON "attestation_challenge" ("challenge") `, + ); + await queryRunner.query( + `CREATE TABLE "user_security_key" ("id" character varying NOT NULL, "userId" character varying(32) NOT NULL, "publicKey" character varying NOT NULL, "lastUsed" TIMESTAMP WITH TIME ZONE NOT NULL, "name" character varying(30) NOT NULL, CONSTRAINT "PK_3e508571121ab39c5f85d10c166" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE INDEX "IDX_ff9ca3b5f3ee3d0681367a9b44" ON "user_security_key" ("userId") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_0d7718e562dcedd0aa5cf2c9f7" ON "user_security_key" ("publicKey") `, + ); + await queryRunner.query( + `ALTER TABLE "user_profile" ADD "securityKeysAvailable" boolean NOT NULL DEFAULT false`, + ); + await queryRunner.query( + `ALTER TABLE "attestation_challenge" ADD CONSTRAINT "FK_f1a461a618fa1755692d0e0d592" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "user_security_key" ADD CONSTRAINT "FK_ff9ca3b5f3ee3d0681367a9b447" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + } + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "user_security_key" DROP CONSTRAINT "FK_ff9ca3b5f3ee3d0681367a9b447"`, + ); + await queryRunner.query( + `ALTER TABLE "attestation_challenge" DROP CONSTRAINT "FK_f1a461a618fa1755692d0e0d592"`, + ); + await queryRunner.query( + `ALTER TABLE "user_profile" DROP COLUMN "securityKeysAvailable"`, + ); + await queryRunner.query(`DROP INDEX "IDX_0d7718e562dcedd0aa5cf2c9f7"`); + await queryRunner.query(`DROP INDEX "IDX_ff9ca3b5f3ee3d0681367a9b44"`); + await queryRunner.query(`DROP TABLE "user_security_key"`); + await queryRunner.query(`DROP INDEX "IDX_47efb914aed1f72dd39a306c7b"`); + await queryRunner.query(`DROP INDEX "IDX_f1a461a618fa1755692d0e0d59"`); + await queryRunner.query(`DROP TABLE "attestation_challenge"`); + } } diff --git a/packages/backend/migration/1561873850023-ChartIndexes.js b/packages/backend/migration/1561873850023-ChartIndexes.js index 3ce53567fc..ba46da1e41 100644 --- a/packages/backend/migration/1561873850023-ChartIndexes.js +++ b/packages/backend/migration/1561873850023-ChartIndexes.js @@ -1,198 +1,376 @@ - - export class ChartIndexes1561873850023 { - async up(queryRunner) { - await queryRunner.query(`CREATE INDEX "IDX_0ad37b7ef50f4ddc84363d7ccc" ON "__chart__active_users" ("date") `); - await queryRunner.query(`CREATE INDEX "IDX_15e91a03aeeac9dbccdf43fc06" ON "__chart__active_users" ("span") `); - await queryRunner.query(`CREATE INDEX "IDX_00ed5f86db1f7efafb1978bf21" ON "__chart__active_users" ("group") `); - await queryRunner.query(`CREATE INDEX "IDX_20f57cc8f142c131340ee16742" ON "__chart__active_users" ("span", "date") `); - await queryRunner.query(`CREATE INDEX "IDX_9a3ed15a30ab7e3a37702e6e08" ON "__chart__active_users" ("date", "group") `); - await queryRunner.query(`CREATE INDEX "IDX_c26e2c1cbb6e911e0554b27416" ON "__chart__active_users" ("span", "date", "group") `); - await queryRunner.query(`CREATE INDEX "IDX_13565815f618a1ff53886c5b28" ON "__chart__drive" ("date") `); - await queryRunner.query(`CREATE INDEX "IDX_3fa0d0f17ca72e3dc80999a032" ON "__chart__drive" ("span") `); - await queryRunner.query(`CREATE INDEX "IDX_7a170f67425e62a8fabb76c872" ON "__chart__drive" ("group") `); - await queryRunner.query(`CREATE INDEX "IDX_6e1df243476e20cbf86572ecc0" ON "__chart__drive" ("span", "date") `); - await queryRunner.query(`CREATE INDEX "IDX_3313d7288855ec105b5bbf6c21" ON "__chart__drive" ("date", "group") `); - await queryRunner.query(`CREATE INDEX "IDX_06690fc959f1c9fdaf21928222" ON "__chart__drive" ("span", "date", "group") `); - await queryRunner.query(`CREATE INDEX "IDX_36cb699c49580d4e6c2e6159f9" ON "__chart__federation" ("date") `); - await queryRunner.query(`CREATE INDEX "IDX_e447064455928cf627590ef527" ON "__chart__federation" ("span") `); - await queryRunner.query(`CREATE INDEX "IDX_76e87c7bfc5d925fcbba405d84" ON "__chart__federation" ("group") `); - await queryRunner.query(`CREATE INDEX "IDX_2d416e6af791a82e338c79d480" ON "__chart__federation" ("span", "date") `); - await queryRunner.query(`CREATE INDEX "IDX_dd907becf76104e4b656659e6b" ON "__chart__federation" ("date", "group") `); - await queryRunner.query(`CREATE INDEX "IDX_e9cd07672b37d8966cf3709283" ON "__chart__federation" ("span", "date", "group") `); - await queryRunner.query(`CREATE INDEX "IDX_07747a1038c05f532a718fe1de" ON "__chart__hashtag" ("date") `); - await queryRunner.query(`CREATE INDEX "IDX_fcc181fb8283009c61cc4083ef" ON "__chart__hashtag" ("span") `); - await queryRunner.query(`CREATE INDEX "IDX_99a7d2faaef84a6f728d714ad6" ON "__chart__hashtag" ("group") `); - await queryRunner.query(`CREATE INDEX "IDX_49975586f50ed7b800fdd88fbd" ON "__chart__hashtag" ("span", "date") `); - await queryRunner.query(`CREATE INDEX "IDX_25a97c02003338124b2b75fdbc" ON "__chart__hashtag" ("date", "group") `); - await queryRunner.query(`CREATE INDEX "IDX_6d6f156ceefc6bc5f273a0e370" ON "__chart__hashtag" ("span", "date", "group") `); - await queryRunner.query(`CREATE INDEX "IDX_6b8f34a1a64b06014b6fb66824" ON "__chart__instance" ("date") `); - await queryRunner.query(`CREATE INDEX "IDX_c12f0af4a66cdd30c2287ce8aa" ON "__chart__instance" ("span") `); - await queryRunner.query(`CREATE INDEX "IDX_da8a46ba84ca1d8bb5a29bfb63" ON "__chart__instance" ("group") `); - await queryRunner.query(`CREATE INDEX "IDX_d0a4f79af5a97b08f37b547197" ON "__chart__instance" ("span", "date") `); - await queryRunner.query(`CREATE INDEX "IDX_39ee857ab2f23493037c6b6631" ON "__chart__instance" ("date", "group") `); - await queryRunner.query(`CREATE INDEX "IDX_f5448d9633cff74208d850aabe" ON "__chart__instance" ("span", "date", "group") `); - await queryRunner.query(`CREATE INDEX "IDX_a1efd3e0048a5f2793a47360dc" ON "__chart__network" ("date") `); - await queryRunner.query(`CREATE INDEX "IDX_f8dd01baeded2ffa833e0a610a" ON "__chart__network" ("span") `); - await queryRunner.query(`CREATE INDEX "IDX_7b5da130992ec9df96712d4290" ON "__chart__network" ("group") `); - await queryRunner.query(`CREATE INDEX "IDX_08fac0eb3b11f04c200c0b40dd" ON "__chart__network" ("span", "date") `); - await queryRunner.query(`CREATE INDEX "IDX_0a905b992fecd2b5c3fb98759e" ON "__chart__network" ("date", "group") `); - await queryRunner.query(`CREATE INDEX "IDX_9ff6944f01acb756fdc92d7563" ON "__chart__network" ("span", "date", "group") `); - await queryRunner.query(`CREATE INDEX "IDX_42eb716a37d381cdf566192b2b" ON "__chart__notes" ("date") `); - await queryRunner.query(`CREATE INDEX "IDX_e69096589f11e3baa98ddd64d0" ON "__chart__notes" ("span") `); - await queryRunner.query(`CREATE INDEX "IDX_7036f2957151588b813185c794" ON "__chart__notes" ("group") `); - await queryRunner.query(`CREATE INDEX "IDX_0c9a159c5082cbeef3ca6706b5" ON "__chart__notes" ("span", "date") `); - await queryRunner.query(`CREATE INDEX "IDX_f09d543e3acb16c5976bdb31fa" ON "__chart__notes" ("date", "group") `); - await queryRunner.query(`CREATE INDEX "IDX_924fc196c80ca24bae01dd37e4" ON "__chart__notes" ("span", "date", "group") `); - await queryRunner.query(`CREATE INDEX "IDX_5f86db6492274e07c1a3cdf286" ON "__chart__per_user_drive" ("date") `); - await queryRunner.query(`CREATE INDEX "IDX_328f259961e60c4fa0bfcf55ca" ON "__chart__per_user_drive" ("span") `); - await queryRunner.query(`CREATE INDEX "IDX_e496ca8096d28f6b9b509264dc" ON "__chart__per_user_drive" ("group") `); - await queryRunner.query(`CREATE INDEX "IDX_42ea9381f0fda8dfe0fa1c8b53" ON "__chart__per_user_drive" ("span", "date") `); - await queryRunner.query(`CREATE INDEX "IDX_30bf67687f483ace115c5ca642" ON "__chart__per_user_drive" ("date", "group") `); - await queryRunner.query(`CREATE INDEX "IDX_f2aeafde2ae6fbad38e857631b" ON "__chart__per_user_drive" ("span", "date", "group") `); - await queryRunner.query(`CREATE INDEX "IDX_7af07790712aa3438ff6773f3b" ON "__chart__per_user_following" ("date") `); - await queryRunner.query(`CREATE INDEX "IDX_f92dd6d03f8d994f29987f6214" ON "__chart__per_user_following" ("span") `); - await queryRunner.query(`CREATE INDEX "IDX_4b3593098b6edc9c5afe36b18b" ON "__chart__per_user_following" ("group") `); - await queryRunner.query(`CREATE INDEX "IDX_57b5458d0d3d6d1e7f13d4e57f" ON "__chart__per_user_following" ("span", "date") `); - await queryRunner.query(`CREATE INDEX "IDX_b77d4dd9562c3a899d9a286fcd" ON "__chart__per_user_following" ("date", "group") `); - await queryRunner.query(`CREATE INDEX "IDX_4db3b84c7be0d3464714f3e0b1" ON "__chart__per_user_following" ("span", "date", "group") `); - await queryRunner.query(`CREATE INDEX "IDX_84234bd1abb873f07329681c83" ON "__chart__per_user_notes" ("date") `); - await queryRunner.query(`CREATE INDEX "IDX_8d2cbbc8114d90d19b44d626b6" ON "__chart__per_user_notes" ("span") `); - await queryRunner.query(`CREATE INDEX "IDX_55bf20f366979f2436de99206b" ON "__chart__per_user_notes" ("group") `); - await queryRunner.query(`CREATE INDEX "IDX_046feeb12e9ef5f783f409866a" ON "__chart__per_user_notes" ("span", "date") `); - await queryRunner.query(`CREATE INDEX "IDX_5048e9daccbbbc6d567bb142d3" ON "__chart__per_user_notes" ("date", "group") `); - await queryRunner.query(`CREATE INDEX "IDX_f68a5ab958f9f5fa17a32ac23b" ON "__chart__per_user_notes" ("span", "date", "group") `); - await queryRunner.query(`CREATE INDEX "IDX_f7bf4c62059764c2c2bb40fdab" ON "__chart__per_user_reaction" ("date") `); - await queryRunner.query(`CREATE INDEX "IDX_65633a106bce43fc7c5c30a5c7" ON "__chart__per_user_reaction" ("span") `); - await queryRunner.query(`CREATE INDEX "IDX_8cf3156fd7a6b15c43459c6e3b" ON "__chart__per_user_reaction" ("group") `); - await queryRunner.query(`CREATE INDEX "IDX_edeb73c09c3143a81bcb34d569" ON "__chart__per_user_reaction" ("span", "date") `); - await queryRunner.query(`CREATE INDEX "IDX_229a41ad465f9205f1f5703291" ON "__chart__per_user_reaction" ("date", "group") `); - await queryRunner.query(`CREATE INDEX "IDX_e316f01a6d24eb31db27f88262" ON "__chart__per_user_reaction" ("span", "date", "group") `); - await queryRunner.query(`CREATE INDEX "IDX_0c641990ecf47d2545df4edb75" ON "__chart__test_grouped" ("date") `); - await queryRunner.query(`CREATE INDEX "IDX_2be7ec6cebddc14dc11e206686" ON "__chart__test_grouped" ("span") `); - await queryRunner.query(`CREATE INDEX "IDX_234dff3c0b56a6150b95431ab9" ON "__chart__test_grouped" ("group") `); - await queryRunner.query(`CREATE INDEX "IDX_a5133470f4825902e170328ca5" ON "__chart__test_grouped" ("span", "date") `); - await queryRunner.query(`CREATE INDEX "IDX_b14489029e4b3aaf4bba5fb524" ON "__chart__test_grouped" ("date", "group") `); - await queryRunner.query(`CREATE INDEX "IDX_84e661abb7bd1e51b690d4b017" ON "__chart__test_grouped" ("span", "date", "group") `); - await queryRunner.query(`CREATE INDEX "IDX_437bab3c6061d90f6bb65fd2cc" ON "__chart__test_unique" ("date") `); - await queryRunner.query(`CREATE INDEX "IDX_5c73bf61da4f6e6f15bae88ed1" ON "__chart__test_unique" ("span") `); - await queryRunner.query(`CREATE INDEX "IDX_bbfa573a8181018851ed0b6357" ON "__chart__test_unique" ("group") `); - await queryRunner.query(`CREATE INDEX "IDX_d70c86baedc68326be11f9c0ce" ON "__chart__test_unique" ("span", "date") `); - await queryRunner.query(`CREATE INDEX "IDX_a0cd75442dd10d0643a17c4a49" ON "__chart__test_unique" ("date", "group") `); - await queryRunner.query(`CREATE INDEX "IDX_66e1e1ecd2f29e57778af35b59" ON "__chart__test_unique" ("span", "date", "group") `); - await queryRunner.query(`CREATE INDEX "IDX_b070a906db04b44c67c6c2144d" ON "__chart__test" ("date") `); - await queryRunner.query(`CREATE INDEX "IDX_92255988735563f0fe4aba1f05" ON "__chart__test" ("span") `); - await queryRunner.query(`CREATE INDEX "IDX_d41cce6aee1a50bfc062038f9b" ON "__chart__test" ("group") `); - await queryRunner.query(`CREATE INDEX "IDX_c5870993e25c3d5771f91f5003" ON "__chart__test" ("span", "date") `); - await queryRunner.query(`CREATE INDEX "IDX_a319e5dbf47e8a17497623beae" ON "__chart__test" ("date", "group") `); - await queryRunner.query(`CREATE INDEX "IDX_f170de677ea75ad4533de2723e" ON "__chart__test" ("span", "date", "group") `); - await queryRunner.query(`CREATE INDEX "IDX_845254b3eaf708ae8a6cac3026" ON "__chart__users" ("date") `); - await queryRunner.query(`CREATE INDEX "IDX_7c184198ecf66a8d3ecb253ab3" ON "__chart__users" ("span") `); - await queryRunner.query(`CREATE INDEX "IDX_ed9b95919c672a13008e9487ee" ON "__chart__users" ("group") `); - await queryRunner.query(`CREATE INDEX "IDX_f091abb24193d50c653c6b77fc" ON "__chart__users" ("span", "date") `); - await queryRunner.query(`CREATE INDEX "IDX_337e9599f278bd7537fe30876f" ON "__chart__users" ("date", "group") `); - await queryRunner.query(`CREATE INDEX "IDX_a770a57c70e668cc61590c9161" ON "__chart__users" ("span", "date", "group") `); - } - async down(queryRunner) { - await queryRunner.query(`DROP INDEX "IDX_a770a57c70e668cc61590c9161"`); - await queryRunner.query(`DROP INDEX "IDX_337e9599f278bd7537fe30876f"`); - await queryRunner.query(`DROP INDEX "IDX_f091abb24193d50c653c6b77fc"`); - await queryRunner.query(`DROP INDEX "IDX_ed9b95919c672a13008e9487ee"`); - await queryRunner.query(`DROP INDEX "IDX_7c184198ecf66a8d3ecb253ab3"`); - await queryRunner.query(`DROP INDEX "IDX_845254b3eaf708ae8a6cac3026"`); - await queryRunner.query(`DROP INDEX "IDX_f170de677ea75ad4533de2723e"`); - await queryRunner.query(`DROP INDEX "IDX_a319e5dbf47e8a17497623beae"`); - await queryRunner.query(`DROP INDEX "IDX_c5870993e25c3d5771f91f5003"`); - await queryRunner.query(`DROP INDEX "IDX_d41cce6aee1a50bfc062038f9b"`); - await queryRunner.query(`DROP INDEX "IDX_92255988735563f0fe4aba1f05"`); - await queryRunner.query(`DROP INDEX "IDX_b070a906db04b44c67c6c2144d"`); - await queryRunner.query(`DROP INDEX "IDX_66e1e1ecd2f29e57778af35b59"`); - await queryRunner.query(`DROP INDEX "IDX_a0cd75442dd10d0643a17c4a49"`); - await queryRunner.query(`DROP INDEX "IDX_d70c86baedc68326be11f9c0ce"`); - await queryRunner.query(`DROP INDEX "IDX_bbfa573a8181018851ed0b6357"`); - await queryRunner.query(`DROP INDEX "IDX_5c73bf61da4f6e6f15bae88ed1"`); - await queryRunner.query(`DROP INDEX "IDX_437bab3c6061d90f6bb65fd2cc"`); - await queryRunner.query(`DROP INDEX "IDX_84e661abb7bd1e51b690d4b017"`); - await queryRunner.query(`DROP INDEX "IDX_b14489029e4b3aaf4bba5fb524"`); - await queryRunner.query(`DROP INDEX "IDX_a5133470f4825902e170328ca5"`); - await queryRunner.query(`DROP INDEX "IDX_234dff3c0b56a6150b95431ab9"`); - await queryRunner.query(`DROP INDEX "IDX_2be7ec6cebddc14dc11e206686"`); - await queryRunner.query(`DROP INDEX "IDX_0c641990ecf47d2545df4edb75"`); - await queryRunner.query(`DROP INDEX "IDX_e316f01a6d24eb31db27f88262"`); - await queryRunner.query(`DROP INDEX "IDX_229a41ad465f9205f1f5703291"`); - await queryRunner.query(`DROP INDEX "IDX_edeb73c09c3143a81bcb34d569"`); - await queryRunner.query(`DROP INDEX "IDX_8cf3156fd7a6b15c43459c6e3b"`); - await queryRunner.query(`DROP INDEX "IDX_65633a106bce43fc7c5c30a5c7"`); - await queryRunner.query(`DROP INDEX "IDX_f7bf4c62059764c2c2bb40fdab"`); - await queryRunner.query(`DROP INDEX "IDX_f68a5ab958f9f5fa17a32ac23b"`); - await queryRunner.query(`DROP INDEX "IDX_5048e9daccbbbc6d567bb142d3"`); - await queryRunner.query(`DROP INDEX "IDX_046feeb12e9ef5f783f409866a"`); - await queryRunner.query(`DROP INDEX "IDX_55bf20f366979f2436de99206b"`); - await queryRunner.query(`DROP INDEX "IDX_8d2cbbc8114d90d19b44d626b6"`); - await queryRunner.query(`DROP INDEX "IDX_84234bd1abb873f07329681c83"`); - await queryRunner.query(`DROP INDEX "IDX_4db3b84c7be0d3464714f3e0b1"`); - await queryRunner.query(`DROP INDEX "IDX_b77d4dd9562c3a899d9a286fcd"`); - await queryRunner.query(`DROP INDEX "IDX_57b5458d0d3d6d1e7f13d4e57f"`); - await queryRunner.query(`DROP INDEX "IDX_4b3593098b6edc9c5afe36b18b"`); - await queryRunner.query(`DROP INDEX "IDX_f92dd6d03f8d994f29987f6214"`); - await queryRunner.query(`DROP INDEX "IDX_7af07790712aa3438ff6773f3b"`); - await queryRunner.query(`DROP INDEX "IDX_f2aeafde2ae6fbad38e857631b"`); - await queryRunner.query(`DROP INDEX "IDX_30bf67687f483ace115c5ca642"`); - await queryRunner.query(`DROP INDEX "IDX_42ea9381f0fda8dfe0fa1c8b53"`); - await queryRunner.query(`DROP INDEX "IDX_e496ca8096d28f6b9b509264dc"`); - await queryRunner.query(`DROP INDEX "IDX_328f259961e60c4fa0bfcf55ca"`); - await queryRunner.query(`DROP INDEX "IDX_5f86db6492274e07c1a3cdf286"`); - await queryRunner.query(`DROP INDEX "IDX_924fc196c80ca24bae01dd37e4"`); - await queryRunner.query(`DROP INDEX "IDX_f09d543e3acb16c5976bdb31fa"`); - await queryRunner.query(`DROP INDEX "IDX_0c9a159c5082cbeef3ca6706b5"`); - await queryRunner.query(`DROP INDEX "IDX_7036f2957151588b813185c794"`); - await queryRunner.query(`DROP INDEX "IDX_e69096589f11e3baa98ddd64d0"`); - await queryRunner.query(`DROP INDEX "IDX_42eb716a37d381cdf566192b2b"`); - await queryRunner.query(`DROP INDEX "IDX_9ff6944f01acb756fdc92d7563"`); - await queryRunner.query(`DROP INDEX "IDX_0a905b992fecd2b5c3fb98759e"`); - await queryRunner.query(`DROP INDEX "IDX_08fac0eb3b11f04c200c0b40dd"`); - await queryRunner.query(`DROP INDEX "IDX_7b5da130992ec9df96712d4290"`); - await queryRunner.query(`DROP INDEX "IDX_f8dd01baeded2ffa833e0a610a"`); - await queryRunner.query(`DROP INDEX "IDX_a1efd3e0048a5f2793a47360dc"`); - await queryRunner.query(`DROP INDEX "IDX_f5448d9633cff74208d850aabe"`); - await queryRunner.query(`DROP INDEX "IDX_39ee857ab2f23493037c6b6631"`); - await queryRunner.query(`DROP INDEX "IDX_d0a4f79af5a97b08f37b547197"`); - await queryRunner.query(`DROP INDEX "IDX_da8a46ba84ca1d8bb5a29bfb63"`); - await queryRunner.query(`DROP INDEX "IDX_c12f0af4a66cdd30c2287ce8aa"`); - await queryRunner.query(`DROP INDEX "IDX_6b8f34a1a64b06014b6fb66824"`); - await queryRunner.query(`DROP INDEX "IDX_6d6f156ceefc6bc5f273a0e370"`); - await queryRunner.query(`DROP INDEX "IDX_25a97c02003338124b2b75fdbc"`); - await queryRunner.query(`DROP INDEX "IDX_49975586f50ed7b800fdd88fbd"`); - await queryRunner.query(`DROP INDEX "IDX_99a7d2faaef84a6f728d714ad6"`); - await queryRunner.query(`DROP INDEX "IDX_fcc181fb8283009c61cc4083ef"`); - await queryRunner.query(`DROP INDEX "IDX_07747a1038c05f532a718fe1de"`); - await queryRunner.query(`DROP INDEX "IDX_e9cd07672b37d8966cf3709283"`); - await queryRunner.query(`DROP INDEX "IDX_dd907becf76104e4b656659e6b"`); - await queryRunner.query(`DROP INDEX "IDX_2d416e6af791a82e338c79d480"`); - await queryRunner.query(`DROP INDEX "IDX_76e87c7bfc5d925fcbba405d84"`); - await queryRunner.query(`DROP INDEX "IDX_e447064455928cf627590ef527"`); - await queryRunner.query(`DROP INDEX "IDX_36cb699c49580d4e6c2e6159f9"`); - await queryRunner.query(`DROP INDEX "IDX_06690fc959f1c9fdaf21928222"`); - await queryRunner.query(`DROP INDEX "IDX_3313d7288855ec105b5bbf6c21"`); - await queryRunner.query(`DROP INDEX "IDX_6e1df243476e20cbf86572ecc0"`); - await queryRunner.query(`DROP INDEX "IDX_7a170f67425e62a8fabb76c872"`); - await queryRunner.query(`DROP INDEX "IDX_3fa0d0f17ca72e3dc80999a032"`); - await queryRunner.query(`DROP INDEX "IDX_13565815f618a1ff53886c5b28"`); - await queryRunner.query(`DROP INDEX "IDX_c26e2c1cbb6e911e0554b27416"`); - await queryRunner.query(`DROP INDEX "IDX_9a3ed15a30ab7e3a37702e6e08"`); - await queryRunner.query(`DROP INDEX "IDX_20f57cc8f142c131340ee16742"`); - await queryRunner.query(`DROP INDEX "IDX_00ed5f86db1f7efafb1978bf21"`); - await queryRunner.query(`DROP INDEX "IDX_15e91a03aeeac9dbccdf43fc06"`); - await queryRunner.query(`DROP INDEX "IDX_0ad37b7ef50f4ddc84363d7ccc"`); - await queryRunner.query(`DROP INDEX "IDX_90148bbc2bf0854428786bfc15"`); - await queryRunner.query(`DROP INDEX "IDX_88937d94d7443d9a99a76fa5c0"`); - await queryRunner.query(`DROP INDEX "IDX_54ebcb6d27222913b908d56fd8"`); - await queryRunner.query(`DROP INDEX "IDX_796a8c03959361f97dc2be1d5c"`); - await queryRunner.query(`DROP INDEX "IDX_25dfc71b0369b003a4cd434d0b"`); - await queryRunner.query(`DROP INDEX "IDX_51c063b6a133a9cb87145450f5"`); - await queryRunner.query(`DROP INDEX "IDX_fa99d777623947a5b05f394cae"`); - await queryRunner.query(`DROP INDEX "IDX_315c779174fe8247ab324f036e"`); - await queryRunner.query(`DROP INDEX "IDX_c5d46cbfda48b1c33ed852e21b"`); - await queryRunner.query(`DROP INDEX "IDX_8cb40cfc8f3c28261e6f887b03"`); - } + async up(queryRunner) { + await queryRunner.query( + `CREATE INDEX "IDX_0ad37b7ef50f4ddc84363d7ccc" ON "__chart__active_users" ("date") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_15e91a03aeeac9dbccdf43fc06" ON "__chart__active_users" ("span") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_00ed5f86db1f7efafb1978bf21" ON "__chart__active_users" ("group") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_20f57cc8f142c131340ee16742" ON "__chart__active_users" ("span", "date") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_9a3ed15a30ab7e3a37702e6e08" ON "__chart__active_users" ("date", "group") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_c26e2c1cbb6e911e0554b27416" ON "__chart__active_users" ("span", "date", "group") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_13565815f618a1ff53886c5b28" ON "__chart__drive" ("date") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_3fa0d0f17ca72e3dc80999a032" ON "__chart__drive" ("span") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_7a170f67425e62a8fabb76c872" ON "__chart__drive" ("group") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_6e1df243476e20cbf86572ecc0" ON "__chart__drive" ("span", "date") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_3313d7288855ec105b5bbf6c21" ON "__chart__drive" ("date", "group") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_06690fc959f1c9fdaf21928222" ON "__chart__drive" ("span", "date", "group") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_36cb699c49580d4e6c2e6159f9" ON "__chart__federation" ("date") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_e447064455928cf627590ef527" ON "__chart__federation" ("span") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_76e87c7bfc5d925fcbba405d84" ON "__chart__federation" ("group") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_2d416e6af791a82e338c79d480" ON "__chart__federation" ("span", "date") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_dd907becf76104e4b656659e6b" ON "__chart__federation" ("date", "group") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_e9cd07672b37d8966cf3709283" ON "__chart__federation" ("span", "date", "group") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_07747a1038c05f532a718fe1de" ON "__chart__hashtag" ("date") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_fcc181fb8283009c61cc4083ef" ON "__chart__hashtag" ("span") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_99a7d2faaef84a6f728d714ad6" ON "__chart__hashtag" ("group") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_49975586f50ed7b800fdd88fbd" ON "__chart__hashtag" ("span", "date") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_25a97c02003338124b2b75fdbc" ON "__chart__hashtag" ("date", "group") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_6d6f156ceefc6bc5f273a0e370" ON "__chart__hashtag" ("span", "date", "group") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_6b8f34a1a64b06014b6fb66824" ON "__chart__instance" ("date") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_c12f0af4a66cdd30c2287ce8aa" ON "__chart__instance" ("span") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_da8a46ba84ca1d8bb5a29bfb63" ON "__chart__instance" ("group") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_d0a4f79af5a97b08f37b547197" ON "__chart__instance" ("span", "date") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_39ee857ab2f23493037c6b6631" ON "__chart__instance" ("date", "group") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_f5448d9633cff74208d850aabe" ON "__chart__instance" ("span", "date", "group") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_a1efd3e0048a5f2793a47360dc" ON "__chart__network" ("date") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_f8dd01baeded2ffa833e0a610a" ON "__chart__network" ("span") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_7b5da130992ec9df96712d4290" ON "__chart__network" ("group") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_08fac0eb3b11f04c200c0b40dd" ON "__chart__network" ("span", "date") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_0a905b992fecd2b5c3fb98759e" ON "__chart__network" ("date", "group") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_9ff6944f01acb756fdc92d7563" ON "__chart__network" ("span", "date", "group") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_42eb716a37d381cdf566192b2b" ON "__chart__notes" ("date") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_e69096589f11e3baa98ddd64d0" ON "__chart__notes" ("span") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_7036f2957151588b813185c794" ON "__chart__notes" ("group") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_0c9a159c5082cbeef3ca6706b5" ON "__chart__notes" ("span", "date") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_f09d543e3acb16c5976bdb31fa" ON "__chart__notes" ("date", "group") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_924fc196c80ca24bae01dd37e4" ON "__chart__notes" ("span", "date", "group") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_5f86db6492274e07c1a3cdf286" ON "__chart__per_user_drive" ("date") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_328f259961e60c4fa0bfcf55ca" ON "__chart__per_user_drive" ("span") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_e496ca8096d28f6b9b509264dc" ON "__chart__per_user_drive" ("group") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_42ea9381f0fda8dfe0fa1c8b53" ON "__chart__per_user_drive" ("span", "date") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_30bf67687f483ace115c5ca642" ON "__chart__per_user_drive" ("date", "group") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_f2aeafde2ae6fbad38e857631b" ON "__chart__per_user_drive" ("span", "date", "group") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_7af07790712aa3438ff6773f3b" ON "__chart__per_user_following" ("date") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_f92dd6d03f8d994f29987f6214" ON "__chart__per_user_following" ("span") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_4b3593098b6edc9c5afe36b18b" ON "__chart__per_user_following" ("group") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_57b5458d0d3d6d1e7f13d4e57f" ON "__chart__per_user_following" ("span", "date") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_b77d4dd9562c3a899d9a286fcd" ON "__chart__per_user_following" ("date", "group") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_4db3b84c7be0d3464714f3e0b1" ON "__chart__per_user_following" ("span", "date", "group") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_84234bd1abb873f07329681c83" ON "__chart__per_user_notes" ("date") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_8d2cbbc8114d90d19b44d626b6" ON "__chart__per_user_notes" ("span") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_55bf20f366979f2436de99206b" ON "__chart__per_user_notes" ("group") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_046feeb12e9ef5f783f409866a" ON "__chart__per_user_notes" ("span", "date") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_5048e9daccbbbc6d567bb142d3" ON "__chart__per_user_notes" ("date", "group") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_f68a5ab958f9f5fa17a32ac23b" ON "__chart__per_user_notes" ("span", "date", "group") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_f7bf4c62059764c2c2bb40fdab" ON "__chart__per_user_reaction" ("date") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_65633a106bce43fc7c5c30a5c7" ON "__chart__per_user_reaction" ("span") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_8cf3156fd7a6b15c43459c6e3b" ON "__chart__per_user_reaction" ("group") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_edeb73c09c3143a81bcb34d569" ON "__chart__per_user_reaction" ("span", "date") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_229a41ad465f9205f1f5703291" ON "__chart__per_user_reaction" ("date", "group") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_e316f01a6d24eb31db27f88262" ON "__chart__per_user_reaction" ("span", "date", "group") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_0c641990ecf47d2545df4edb75" ON "__chart__test_grouped" ("date") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_2be7ec6cebddc14dc11e206686" ON "__chart__test_grouped" ("span") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_234dff3c0b56a6150b95431ab9" ON "__chart__test_grouped" ("group") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_a5133470f4825902e170328ca5" ON "__chart__test_grouped" ("span", "date") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_b14489029e4b3aaf4bba5fb524" ON "__chart__test_grouped" ("date", "group") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_84e661abb7bd1e51b690d4b017" ON "__chart__test_grouped" ("span", "date", "group") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_437bab3c6061d90f6bb65fd2cc" ON "__chart__test_unique" ("date") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_5c73bf61da4f6e6f15bae88ed1" ON "__chart__test_unique" ("span") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_bbfa573a8181018851ed0b6357" ON "__chart__test_unique" ("group") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_d70c86baedc68326be11f9c0ce" ON "__chart__test_unique" ("span", "date") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_a0cd75442dd10d0643a17c4a49" ON "__chart__test_unique" ("date", "group") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_66e1e1ecd2f29e57778af35b59" ON "__chart__test_unique" ("span", "date", "group") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_b070a906db04b44c67c6c2144d" ON "__chart__test" ("date") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_92255988735563f0fe4aba1f05" ON "__chart__test" ("span") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_d41cce6aee1a50bfc062038f9b" ON "__chart__test" ("group") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_c5870993e25c3d5771f91f5003" ON "__chart__test" ("span", "date") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_a319e5dbf47e8a17497623beae" ON "__chart__test" ("date", "group") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_f170de677ea75ad4533de2723e" ON "__chart__test" ("span", "date", "group") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_845254b3eaf708ae8a6cac3026" ON "__chart__users" ("date") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_7c184198ecf66a8d3ecb253ab3" ON "__chart__users" ("span") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_ed9b95919c672a13008e9487ee" ON "__chart__users" ("group") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_f091abb24193d50c653c6b77fc" ON "__chart__users" ("span", "date") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_337e9599f278bd7537fe30876f" ON "__chart__users" ("date", "group") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_a770a57c70e668cc61590c9161" ON "__chart__users" ("span", "date", "group") `, + ); + } + async down(queryRunner) { + await queryRunner.query(`DROP INDEX "IDX_a770a57c70e668cc61590c9161"`); + await queryRunner.query(`DROP INDEX "IDX_337e9599f278bd7537fe30876f"`); + await queryRunner.query(`DROP INDEX "IDX_f091abb24193d50c653c6b77fc"`); + await queryRunner.query(`DROP INDEX "IDX_ed9b95919c672a13008e9487ee"`); + await queryRunner.query(`DROP INDEX "IDX_7c184198ecf66a8d3ecb253ab3"`); + await queryRunner.query(`DROP INDEX "IDX_845254b3eaf708ae8a6cac3026"`); + await queryRunner.query(`DROP INDEX "IDX_f170de677ea75ad4533de2723e"`); + await queryRunner.query(`DROP INDEX "IDX_a319e5dbf47e8a17497623beae"`); + await queryRunner.query(`DROP INDEX "IDX_c5870993e25c3d5771f91f5003"`); + await queryRunner.query(`DROP INDEX "IDX_d41cce6aee1a50bfc062038f9b"`); + await queryRunner.query(`DROP INDEX "IDX_92255988735563f0fe4aba1f05"`); + await queryRunner.query(`DROP INDEX "IDX_b070a906db04b44c67c6c2144d"`); + await queryRunner.query(`DROP INDEX "IDX_66e1e1ecd2f29e57778af35b59"`); + await queryRunner.query(`DROP INDEX "IDX_a0cd75442dd10d0643a17c4a49"`); + await queryRunner.query(`DROP INDEX "IDX_d70c86baedc68326be11f9c0ce"`); + await queryRunner.query(`DROP INDEX "IDX_bbfa573a8181018851ed0b6357"`); + await queryRunner.query(`DROP INDEX "IDX_5c73bf61da4f6e6f15bae88ed1"`); + await queryRunner.query(`DROP INDEX "IDX_437bab3c6061d90f6bb65fd2cc"`); + await queryRunner.query(`DROP INDEX "IDX_84e661abb7bd1e51b690d4b017"`); + await queryRunner.query(`DROP INDEX "IDX_b14489029e4b3aaf4bba5fb524"`); + await queryRunner.query(`DROP INDEX "IDX_a5133470f4825902e170328ca5"`); + await queryRunner.query(`DROP INDEX "IDX_234dff3c0b56a6150b95431ab9"`); + await queryRunner.query(`DROP INDEX "IDX_2be7ec6cebddc14dc11e206686"`); + await queryRunner.query(`DROP INDEX "IDX_0c641990ecf47d2545df4edb75"`); + await queryRunner.query(`DROP INDEX "IDX_e316f01a6d24eb31db27f88262"`); + await queryRunner.query(`DROP INDEX "IDX_229a41ad465f9205f1f5703291"`); + await queryRunner.query(`DROP INDEX "IDX_edeb73c09c3143a81bcb34d569"`); + await queryRunner.query(`DROP INDEX "IDX_8cf3156fd7a6b15c43459c6e3b"`); + await queryRunner.query(`DROP INDEX "IDX_65633a106bce43fc7c5c30a5c7"`); + await queryRunner.query(`DROP INDEX "IDX_f7bf4c62059764c2c2bb40fdab"`); + await queryRunner.query(`DROP INDEX "IDX_f68a5ab958f9f5fa17a32ac23b"`); + await queryRunner.query(`DROP INDEX "IDX_5048e9daccbbbc6d567bb142d3"`); + await queryRunner.query(`DROP INDEX "IDX_046feeb12e9ef5f783f409866a"`); + await queryRunner.query(`DROP INDEX "IDX_55bf20f366979f2436de99206b"`); + await queryRunner.query(`DROP INDEX "IDX_8d2cbbc8114d90d19b44d626b6"`); + await queryRunner.query(`DROP INDEX "IDX_84234bd1abb873f07329681c83"`); + await queryRunner.query(`DROP INDEX "IDX_4db3b84c7be0d3464714f3e0b1"`); + await queryRunner.query(`DROP INDEX "IDX_b77d4dd9562c3a899d9a286fcd"`); + await queryRunner.query(`DROP INDEX "IDX_57b5458d0d3d6d1e7f13d4e57f"`); + await queryRunner.query(`DROP INDEX "IDX_4b3593098b6edc9c5afe36b18b"`); + await queryRunner.query(`DROP INDEX "IDX_f92dd6d03f8d994f29987f6214"`); + await queryRunner.query(`DROP INDEX "IDX_7af07790712aa3438ff6773f3b"`); + await queryRunner.query(`DROP INDEX "IDX_f2aeafde2ae6fbad38e857631b"`); + await queryRunner.query(`DROP INDEX "IDX_30bf67687f483ace115c5ca642"`); + await queryRunner.query(`DROP INDEX "IDX_42ea9381f0fda8dfe0fa1c8b53"`); + await queryRunner.query(`DROP INDEX "IDX_e496ca8096d28f6b9b509264dc"`); + await queryRunner.query(`DROP INDEX "IDX_328f259961e60c4fa0bfcf55ca"`); + await queryRunner.query(`DROP INDEX "IDX_5f86db6492274e07c1a3cdf286"`); + await queryRunner.query(`DROP INDEX "IDX_924fc196c80ca24bae01dd37e4"`); + await queryRunner.query(`DROP INDEX "IDX_f09d543e3acb16c5976bdb31fa"`); + await queryRunner.query(`DROP INDEX "IDX_0c9a159c5082cbeef3ca6706b5"`); + await queryRunner.query(`DROP INDEX "IDX_7036f2957151588b813185c794"`); + await queryRunner.query(`DROP INDEX "IDX_e69096589f11e3baa98ddd64d0"`); + await queryRunner.query(`DROP INDEX "IDX_42eb716a37d381cdf566192b2b"`); + await queryRunner.query(`DROP INDEX "IDX_9ff6944f01acb756fdc92d7563"`); + await queryRunner.query(`DROP INDEX "IDX_0a905b992fecd2b5c3fb98759e"`); + await queryRunner.query(`DROP INDEX "IDX_08fac0eb3b11f04c200c0b40dd"`); + await queryRunner.query(`DROP INDEX "IDX_7b5da130992ec9df96712d4290"`); + await queryRunner.query(`DROP INDEX "IDX_f8dd01baeded2ffa833e0a610a"`); + await queryRunner.query(`DROP INDEX "IDX_a1efd3e0048a5f2793a47360dc"`); + await queryRunner.query(`DROP INDEX "IDX_f5448d9633cff74208d850aabe"`); + await queryRunner.query(`DROP INDEX "IDX_39ee857ab2f23493037c6b6631"`); + await queryRunner.query(`DROP INDEX "IDX_d0a4f79af5a97b08f37b547197"`); + await queryRunner.query(`DROP INDEX "IDX_da8a46ba84ca1d8bb5a29bfb63"`); + await queryRunner.query(`DROP INDEX "IDX_c12f0af4a66cdd30c2287ce8aa"`); + await queryRunner.query(`DROP INDEX "IDX_6b8f34a1a64b06014b6fb66824"`); + await queryRunner.query(`DROP INDEX "IDX_6d6f156ceefc6bc5f273a0e370"`); + await queryRunner.query(`DROP INDEX "IDX_25a97c02003338124b2b75fdbc"`); + await queryRunner.query(`DROP INDEX "IDX_49975586f50ed7b800fdd88fbd"`); + await queryRunner.query(`DROP INDEX "IDX_99a7d2faaef84a6f728d714ad6"`); + await queryRunner.query(`DROP INDEX "IDX_fcc181fb8283009c61cc4083ef"`); + await queryRunner.query(`DROP INDEX "IDX_07747a1038c05f532a718fe1de"`); + await queryRunner.query(`DROP INDEX "IDX_e9cd07672b37d8966cf3709283"`); + await queryRunner.query(`DROP INDEX "IDX_dd907becf76104e4b656659e6b"`); + await queryRunner.query(`DROP INDEX "IDX_2d416e6af791a82e338c79d480"`); + await queryRunner.query(`DROP INDEX "IDX_76e87c7bfc5d925fcbba405d84"`); + await queryRunner.query(`DROP INDEX "IDX_e447064455928cf627590ef527"`); + await queryRunner.query(`DROP INDEX "IDX_36cb699c49580d4e6c2e6159f9"`); + await queryRunner.query(`DROP INDEX "IDX_06690fc959f1c9fdaf21928222"`); + await queryRunner.query(`DROP INDEX "IDX_3313d7288855ec105b5bbf6c21"`); + await queryRunner.query(`DROP INDEX "IDX_6e1df243476e20cbf86572ecc0"`); + await queryRunner.query(`DROP INDEX "IDX_7a170f67425e62a8fabb76c872"`); + await queryRunner.query(`DROP INDEX "IDX_3fa0d0f17ca72e3dc80999a032"`); + await queryRunner.query(`DROP INDEX "IDX_13565815f618a1ff53886c5b28"`); + await queryRunner.query(`DROP INDEX "IDX_c26e2c1cbb6e911e0554b27416"`); + await queryRunner.query(`DROP INDEX "IDX_9a3ed15a30ab7e3a37702e6e08"`); + await queryRunner.query(`DROP INDEX "IDX_20f57cc8f142c131340ee16742"`); + await queryRunner.query(`DROP INDEX "IDX_00ed5f86db1f7efafb1978bf21"`); + await queryRunner.query(`DROP INDEX "IDX_15e91a03aeeac9dbccdf43fc06"`); + await queryRunner.query(`DROP INDEX "IDX_0ad37b7ef50f4ddc84363d7ccc"`); + await queryRunner.query(`DROP INDEX "IDX_90148bbc2bf0854428786bfc15"`); + await queryRunner.query(`DROP INDEX "IDX_88937d94d7443d9a99a76fa5c0"`); + await queryRunner.query(`DROP INDEX "IDX_54ebcb6d27222913b908d56fd8"`); + await queryRunner.query(`DROP INDEX "IDX_796a8c03959361f97dc2be1d5c"`); + await queryRunner.query(`DROP INDEX "IDX_25dfc71b0369b003a4cd434d0b"`); + await queryRunner.query(`DROP INDEX "IDX_51c063b6a133a9cb87145450f5"`); + await queryRunner.query(`DROP INDEX "IDX_fa99d777623947a5b05f394cae"`); + await queryRunner.query(`DROP INDEX "IDX_315c779174fe8247ab324f036e"`); + await queryRunner.query(`DROP INDEX "IDX_c5d46cbfda48b1c33ed852e21b"`); + await queryRunner.query(`DROP INDEX "IDX_8cb40cfc8f3c28261e6f887b03"`); + } } diff --git a/packages/backend/migration/1562422242907-PasswordLessLogin.js b/packages/backend/migration/1562422242907-PasswordLessLogin.js index b73c7db4d3..8e1280830b 100644 --- a/packages/backend/migration/1562422242907-PasswordLessLogin.js +++ b/packages/backend/migration/1562422242907-PasswordLessLogin.js @@ -1,10 +1,12 @@ - - export class PasswordLessLogin1562422242907 { - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "user_profile" ADD COLUMN "usePasswordLessLogin" boolean DEFAULT false NOT NULL`); - } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "usePasswordLessLogin"`); - } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "user_profile" ADD COLUMN "usePasswordLessLogin" boolean DEFAULT false NOT NULL`, + ); + } + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "user_profile" DROP COLUMN "usePasswordLessLogin"`, + ); + } } diff --git a/packages/backend/migration/1562444565093-PinnedPage.js b/packages/backend/migration/1562444565093-PinnedPage.js index 9a999a9150..dcb342d3fc 100644 --- a/packages/backend/migration/1562444565093-PinnedPage.js +++ b/packages/backend/migration/1562444565093-PinnedPage.js @@ -1,14 +1,24 @@ - - export class PinnedPage1562444565093 { - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "user_profile" ADD "pinnedPageId" character varying(32)`); - await queryRunner.query(`ALTER TABLE "user_profile" ADD CONSTRAINT "UQ_6dc44f1ceb65b1e72bacef2ca27" UNIQUE ("pinnedPageId")`); - await queryRunner.query(`ALTER TABLE "user_profile" ADD CONSTRAINT "FK_6dc44f1ceb65b1e72bacef2ca27" FOREIGN KEY ("pinnedPageId") REFERENCES "page"("id") ON DELETE SET NULL ON UPDATE NO ACTION`); - } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "user_profile" DROP CONSTRAINT "FK_6dc44f1ceb65b1e72bacef2ca27"`); - await queryRunner.query(`ALTER TABLE "user_profile" DROP CONSTRAINT "UQ_6dc44f1ceb65b1e72bacef2ca27"`); - await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "pinnedPageId"`); - } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "user_profile" ADD "pinnedPageId" character varying(32)`, + ); + await queryRunner.query( + `ALTER TABLE "user_profile" ADD CONSTRAINT "UQ_6dc44f1ceb65b1e72bacef2ca27" UNIQUE ("pinnedPageId")`, + ); + await queryRunner.query( + `ALTER TABLE "user_profile" ADD CONSTRAINT "FK_6dc44f1ceb65b1e72bacef2ca27" FOREIGN KEY ("pinnedPageId") REFERENCES "page"("id") ON DELETE SET NULL ON UPDATE NO ACTION`, + ); + } + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "user_profile" DROP CONSTRAINT "FK_6dc44f1ceb65b1e72bacef2ca27"`, + ); + await queryRunner.query( + `ALTER TABLE "user_profile" DROP CONSTRAINT "UQ_6dc44f1ceb65b1e72bacef2ca27"`, + ); + await queryRunner.query( + `ALTER TABLE "user_profile" DROP COLUMN "pinnedPageId"`, + ); + } } diff --git a/packages/backend/migration/1562448332510-PageTitleHideOption.js b/packages/backend/migration/1562448332510-PageTitleHideOption.js index 8fc78d202f..9a1b42f4bb 100644 --- a/packages/backend/migration/1562448332510-PageTitleHideOption.js +++ b/packages/backend/migration/1562448332510-PageTitleHideOption.js @@ -1,10 +1,12 @@ - - export class PageTitleHideOption1562448332510 { - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "page" ADD "hideTitleWhenPinned" boolean NOT NULL DEFAULT false`); - } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "page" DROP COLUMN "hideTitleWhenPinned"`); - } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "page" ADD "hideTitleWhenPinned" boolean NOT NULL DEFAULT false`, + ); + } + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "page" DROP COLUMN "hideTitleWhenPinned"`, + ); + } } diff --git a/packages/backend/migration/1562869971568-ModerationLog.js b/packages/backend/migration/1562869971568-ModerationLog.js index dd66d16eec..f652b067b4 100644 --- a/packages/backend/migration/1562869971568-ModerationLog.js +++ b/packages/backend/migration/1562869971568-ModerationLog.js @@ -1,14 +1,20 @@ - - export class ModerationLog1562869971568 { - async up(queryRunner) { - await queryRunner.query(`CREATE TABLE "moderation_log" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "userId" character varying(32) NOT NULL, "type" character varying(128) NOT NULL, "info" jsonb NOT NULL, CONSTRAINT "PK_d0adca6ecfd068db83e4526cc26" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE INDEX "IDX_a08ad074601d204e0f69da9a95" ON "moderation_log" ("userId") `); - await queryRunner.query(`ALTER TABLE "moderation_log" ADD CONSTRAINT "FK_a08ad074601d204e0f69da9a954" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "moderation_log" DROP CONSTRAINT "FK_a08ad074601d204e0f69da9a954"`); - await queryRunner.query(`DROP INDEX "IDX_a08ad074601d204e0f69da9a95"`); - await queryRunner.query(`DROP TABLE "moderation_log"`); - } + async up(queryRunner) { + await queryRunner.query( + `CREATE TABLE "moderation_log" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "userId" character varying(32) NOT NULL, "type" character varying(128) NOT NULL, "info" jsonb NOT NULL, CONSTRAINT "PK_d0adca6ecfd068db83e4526cc26" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE INDEX "IDX_a08ad074601d204e0f69da9a95" ON "moderation_log" ("userId") `, + ); + await queryRunner.query( + `ALTER TABLE "moderation_log" ADD CONSTRAINT "FK_a08ad074601d204e0f69da9a954" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + } + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "moderation_log" DROP CONSTRAINT "FK_a08ad074601d204e0f69da9a954"`, + ); + await queryRunner.query(`DROP INDEX "IDX_a08ad074601d204e0f69da9a95"`); + await queryRunner.query(`DROP TABLE "moderation_log"`); + } } diff --git a/packages/backend/migration/1563757595828-UsedUsername.js b/packages/backend/migration/1563757595828-UsedUsername.js index 8972df297d..2cf5d8c740 100644 --- a/packages/backend/migration/1563757595828-UsedUsername.js +++ b/packages/backend/migration/1563757595828-UsedUsername.js @@ -1,10 +1,10 @@ - - export class UsedUsername1563757595828 { - async up(queryRunner) { - await queryRunner.query(`CREATE TABLE "used_username" ("username" character varying(128) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, CONSTRAINT "PK_78fd79d2d24c6ac2f4cc9a31a5d" PRIMARY KEY ("username"))`); - } - async down(queryRunner) { - await queryRunner.query(`DROP TABLE "used_username"`); - } + async up(queryRunner) { + await queryRunner.query( + `CREATE TABLE "used_username" ("username" character varying(128) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, CONSTRAINT "PK_78fd79d2d24c6ac2f4cc9a31a5d" PRIMARY KEY ("username"))`, + ); + } + async down(queryRunner) { + await queryRunner.query(`DROP TABLE "used_username"`); + } } diff --git a/packages/backend/migration/1565634203341-room.js b/packages/backend/migration/1565634203341-room.js index 679940f244..1023c0a31a 100644 --- a/packages/backend/migration/1565634203341-room.js +++ b/packages/backend/migration/1565634203341-room.js @@ -1,10 +1,10 @@ - - export class room1565634203341 { - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "user_profile" ADD "room" jsonb NOT NULL DEFAULT '{}'`); - } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "room"`); - } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "user_profile" ADD "room" jsonb NOT NULL DEFAULT '{}'`, + ); + } + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "room"`); + } } diff --git a/packages/backend/migration/1571220798684-CustomEmojiCategory.js b/packages/backend/migration/1571220798684-CustomEmojiCategory.js index 37c07366e1..4d75313328 100644 --- a/packages/backend/migration/1571220798684-CustomEmojiCategory.js +++ b/packages/backend/migration/1571220798684-CustomEmojiCategory.js @@ -1,10 +1,14 @@ - - export class CustomEmojiCategory1571220798684 { - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "emoji" ADD "category" character varying(128)`, undefined); - } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "emoji" DROP COLUMN "category"`, undefined); - } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "emoji" ADD "category" character varying(128)`, + undefined, + ); + } + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "emoji" DROP COLUMN "category"`, + undefined, + ); + } } diff --git a/packages/backend/migration/1572760203493-nodeinfo.js b/packages/backend/migration/1572760203493-nodeinfo.js index 54d5f914a4..eea87c82c8 100644 --- a/packages/backend/migration/1572760203493-nodeinfo.js +++ b/packages/backend/migration/1572760203493-nodeinfo.js @@ -1,26 +1,78 @@ - - export class nodeinfo1572760203493 { - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "instance" DROP COLUMN "system"`, undefined); - await queryRunner.query(`ALTER TABLE "instance" ADD "softwareName" character varying(64) DEFAULT null`, undefined); - await queryRunner.query(`ALTER TABLE "instance" ADD "softwareVersion" character varying(64) DEFAULT null`, undefined); - await queryRunner.query(`ALTER TABLE "instance" ADD "openRegistrations" boolean DEFAULT null`, undefined); - await queryRunner.query(`ALTER TABLE "instance" ADD "name" character varying(256) DEFAULT null`, undefined); - await queryRunner.query(`ALTER TABLE "instance" ADD "description" character varying(4096) DEFAULT null`, undefined); - await queryRunner.query(`ALTER TABLE "instance" ADD "maintainerName" character varying(128) DEFAULT null`, undefined); - await queryRunner.query(`ALTER TABLE "instance" ADD "maintainerEmail" character varying(256) DEFAULT null`, undefined); - await queryRunner.query(`ALTER TABLE "instance" ADD "infoUpdatedAt" TIMESTAMP WITH TIME ZONE`, undefined); - } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "instance" DROP COLUMN "infoUpdatedAt"`, undefined); - await queryRunner.query(`ALTER TABLE "instance" DROP COLUMN "maintainerEmail"`, undefined); - await queryRunner.query(`ALTER TABLE "instance" DROP COLUMN "maintainerName"`, undefined); - await queryRunner.query(`ALTER TABLE "instance" DROP COLUMN "description"`, undefined); - await queryRunner.query(`ALTER TABLE "instance" DROP COLUMN "name"`, undefined); - await queryRunner.query(`ALTER TABLE "instance" DROP COLUMN "openRegistrations"`, undefined); - await queryRunner.query(`ALTER TABLE "instance" DROP COLUMN "softwareVersion"`, undefined); - await queryRunner.query(`ALTER TABLE "instance" DROP COLUMN "softwareName"`, undefined); - await queryRunner.query(`ALTER TABLE "instance" ADD "system" character varying(64)`, undefined); - } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "instance" DROP COLUMN "system"`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "instance" ADD "softwareName" character varying(64) DEFAULT null`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "instance" ADD "softwareVersion" character varying(64) DEFAULT null`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "instance" ADD "openRegistrations" boolean DEFAULT null`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "instance" ADD "name" character varying(256) DEFAULT null`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "instance" ADD "description" character varying(4096) DEFAULT null`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "instance" ADD "maintainerName" character varying(128) DEFAULT null`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "instance" ADD "maintainerEmail" character varying(256) DEFAULT null`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "instance" ADD "infoUpdatedAt" TIMESTAMP WITH TIME ZONE`, + undefined, + ); + } + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "instance" DROP COLUMN "infoUpdatedAt"`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "instance" DROP COLUMN "maintainerEmail"`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "instance" DROP COLUMN "maintainerName"`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "instance" DROP COLUMN "description"`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "instance" DROP COLUMN "name"`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "instance" DROP COLUMN "openRegistrations"`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "instance" DROP COLUMN "softwareVersion"`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "instance" DROP COLUMN "softwareName"`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "instance" ADD "system" character varying(64)`, + undefined, + ); + } } diff --git a/packages/backend/migration/1576269851876-TalkFederationId.js b/packages/backend/migration/1576269851876-TalkFederationId.js index 35861d571f..f0aed7dd83 100644 --- a/packages/backend/migration/1576269851876-TalkFederationId.js +++ b/packages/backend/migration/1576269851876-TalkFederationId.js @@ -1,13 +1,17 @@ - - export class TalkFederationId1576269851876 { - constructor() { - this.name = 'TalkFederationId1576269851876'; - } - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "messaging_message" ADD "uri" character varying(512)`, undefined); - } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "messaging_message" DROP COLUMN "uri"`, undefined); - } + constructor() { + this.name = "TalkFederationId1576269851876"; + } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "messaging_message" ADD "uri" character varying(512)`, + undefined, + ); + } + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "messaging_message" DROP COLUMN "uri"`, + undefined, + ); + } } diff --git a/packages/backend/migration/1576869585998-ProxyRemoteFiles.js b/packages/backend/migration/1576869585998-ProxyRemoteFiles.js index d6d134be40..5565f64b6d 100644 --- a/packages/backend/migration/1576869585998-ProxyRemoteFiles.js +++ b/packages/backend/migration/1576869585998-ProxyRemoteFiles.js @@ -1,13 +1,17 @@ - - export class ProxyRemoteFiles1576869585998 { - constructor() { - this.name = 'ProxyRemoteFiles1576869585998'; - } - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "meta" ADD "proxyRemoteFiles" boolean NOT NULL DEFAULT false`, undefined); - } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "proxyRemoteFiles"`, undefined); - } + constructor() { + this.name = "ProxyRemoteFiles1576869585998"; + } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "meta" ADD "proxyRemoteFiles" boolean NOT NULL DEFAULT false`, + undefined, + ); + } + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "meta" DROP COLUMN "proxyRemoteFiles"`, + undefined, + ); + } } diff --git a/packages/backend/migration/1579267006611-v12.js b/packages/backend/migration/1579267006611-v12.js index 7f6318a192..f1d906465e 100644 --- a/packages/backend/migration/1579267006611-v12.js +++ b/packages/backend/migration/1579267006611-v12.js @@ -1,33 +1,91 @@ - - export class v121579267006611 { - constructor() { - this.name = 'v121579267006611'; - } - async up(queryRunner) { - await queryRunner.query(`CREATE TABLE "announcement" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "text" character varying(8192) NOT NULL, "title" character varying(256) NOT NULL, "imageUrl" character varying(1024), CONSTRAINT "PK_e0ef0550174fd1099a308fd18a0" PRIMARY KEY ("id"))`, undefined); - await queryRunner.query(`CREATE INDEX "IDX_118ec703e596086fc4515acb39" ON "announcement" ("createdAt") `, undefined); - await queryRunner.query(`CREATE TABLE "announcement_read" ("id" character varying(32) NOT NULL, "userId" character varying(32) NOT NULL, "announcementId" character varying(32) NOT NULL, CONSTRAINT "PK_4b90ad1f42681d97b2683890c5e" PRIMARY KEY ("id"))`, undefined); - await queryRunner.query(`CREATE INDEX "IDX_8288151386172b8109f7239ab2" ON "announcement_read" ("userId") `, undefined); - await queryRunner.query(`CREATE INDEX "IDX_603a7b1e7aa0533c6c88e9bfaf" ON "announcement_read" ("announcementId") `, undefined); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_924fa71815cfa3941d003702a0" ON "announcement_read" ("userId", "announcementId") `, undefined); - await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "isVerified"`, undefined); - await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "announcements"`, undefined); - await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "enableEmojiReaction"`, undefined); - await queryRunner.query(`ALTER TABLE "announcement_read" ADD CONSTRAINT "FK_8288151386172b8109f7239ab28" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, undefined); - await queryRunner.query(`ALTER TABLE "announcement_read" ADD CONSTRAINT "FK_603a7b1e7aa0533c6c88e9bfafe" FOREIGN KEY ("announcementId") REFERENCES "announcement"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, undefined); - } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "announcement_read" DROP CONSTRAINT "FK_603a7b1e7aa0533c6c88e9bfafe"`, undefined); - await queryRunner.query(`ALTER TABLE "announcement_read" DROP CONSTRAINT "FK_8288151386172b8109f7239ab28"`, undefined); - await queryRunner.query(`ALTER TABLE "meta" ADD "enableEmojiReaction" boolean NOT NULL DEFAULT true`, undefined); - await queryRunner.query(`ALTER TABLE "meta" ADD "announcements" jsonb NOT NULL DEFAULT '[]'`, undefined); - await queryRunner.query(`ALTER TABLE "user" ADD "isVerified" boolean NOT NULL DEFAULT false`, undefined); - await queryRunner.query(`DROP INDEX "IDX_924fa71815cfa3941d003702a0"`, undefined); - await queryRunner.query(`DROP INDEX "IDX_603a7b1e7aa0533c6c88e9bfaf"`, undefined); - await queryRunner.query(`DROP INDEX "IDX_8288151386172b8109f7239ab2"`, undefined); - await queryRunner.query(`DROP TABLE "announcement_read"`, undefined); - await queryRunner.query(`DROP INDEX "IDX_118ec703e596086fc4515acb39"`, undefined); - await queryRunner.query(`DROP TABLE "announcement"`, undefined); - } + constructor() { + this.name = "v121579267006611"; + } + async up(queryRunner) { + await queryRunner.query( + `CREATE TABLE "announcement" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "text" character varying(8192) NOT NULL, "title" character varying(256) NOT NULL, "imageUrl" character varying(1024), CONSTRAINT "PK_e0ef0550174fd1099a308fd18a0" PRIMARY KEY ("id"))`, + undefined, + ); + await queryRunner.query( + `CREATE INDEX "IDX_118ec703e596086fc4515acb39" ON "announcement" ("createdAt") `, + undefined, + ); + await queryRunner.query( + `CREATE TABLE "announcement_read" ("id" character varying(32) NOT NULL, "userId" character varying(32) NOT NULL, "announcementId" character varying(32) NOT NULL, CONSTRAINT "PK_4b90ad1f42681d97b2683890c5e" PRIMARY KEY ("id"))`, + undefined, + ); + await queryRunner.query( + `CREATE INDEX "IDX_8288151386172b8109f7239ab2" ON "announcement_read" ("userId") `, + undefined, + ); + await queryRunner.query( + `CREATE INDEX "IDX_603a7b1e7aa0533c6c88e9bfaf" ON "announcement_read" ("announcementId") `, + undefined, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_924fa71815cfa3941d003702a0" ON "announcement_read" ("userId", "announcementId") `, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "user" DROP COLUMN "isVerified"`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "meta" DROP COLUMN "announcements"`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "meta" DROP COLUMN "enableEmojiReaction"`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "announcement_read" ADD CONSTRAINT "FK_8288151386172b8109f7239ab28" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "announcement_read" ADD CONSTRAINT "FK_603a7b1e7aa0533c6c88e9bfafe" FOREIGN KEY ("announcementId") REFERENCES "announcement"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + undefined, + ); + } + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "announcement_read" DROP CONSTRAINT "FK_603a7b1e7aa0533c6c88e9bfafe"`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "announcement_read" DROP CONSTRAINT "FK_8288151386172b8109f7239ab28"`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "meta" ADD "enableEmojiReaction" boolean NOT NULL DEFAULT true`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "meta" ADD "announcements" jsonb NOT NULL DEFAULT '[]'`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "user" ADD "isVerified" boolean NOT NULL DEFAULT false`, + undefined, + ); + await queryRunner.query( + `DROP INDEX "IDX_924fa71815cfa3941d003702a0"`, + undefined, + ); + await queryRunner.query( + `DROP INDEX "IDX_603a7b1e7aa0533c6c88e9bfaf"`, + undefined, + ); + await queryRunner.query( + `DROP INDEX "IDX_8288151386172b8109f7239ab2"`, + undefined, + ); + await queryRunner.query(`DROP TABLE "announcement_read"`, undefined); + await queryRunner.query( + `DROP INDEX "IDX_118ec703e596086fc4515acb39"`, + undefined, + ); + await queryRunner.query(`DROP TABLE "announcement"`, undefined); + } } diff --git a/packages/backend/migration/1579270193251-v12-2.js b/packages/backend/migration/1579270193251-v12-2.js index c51ce63066..6bbcdcc152 100644 --- a/packages/backend/migration/1579270193251-v12-2.js +++ b/packages/backend/migration/1579270193251-v12-2.js @@ -1,13 +1,17 @@ - - export class v1221579270193251 { - constructor() { - this.name = 'v1221579270193251'; - } - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "announcement_read" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`, undefined); - } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "announcement_read" DROP COLUMN "createdAt"`, undefined); - } + constructor() { + this.name = "v1221579270193251"; + } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "announcement_read" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`, + undefined, + ); + } + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "announcement_read" DROP COLUMN "createdAt"`, + undefined, + ); + } } diff --git a/packages/backend/migration/1579282808087-v12-3.js b/packages/backend/migration/1579282808087-v12-3.js index aeb4f5a873..d5a9a86b52 100644 --- a/packages/backend/migration/1579282808087-v12-3.js +++ b/packages/backend/migration/1579282808087-v12-3.js @@ -1,13 +1,17 @@ - - export class v1231579282808087 { - constructor() { - this.name = 'v1231579282808087'; - } - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "announcement" ADD "updatedAt" TIMESTAMP WITH TIME ZONE`, undefined); - } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "announcement" DROP COLUMN "updatedAt"`, undefined); - } + constructor() { + this.name = "v1231579282808087"; + } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "announcement" ADD "updatedAt" TIMESTAMP WITH TIME ZONE`, + undefined, + ); + } + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "announcement" DROP COLUMN "updatedAt"`, + undefined, + ); + } } diff --git a/packages/backend/migration/1579544426412-v12-4.js b/packages/backend/migration/1579544426412-v12-4.js index f1e093413e..efd6ee61bb 100644 --- a/packages/backend/migration/1579544426412-v12-4.js +++ b/packages/backend/migration/1579544426412-v12-4.js @@ -1,15 +1,25 @@ - - export class v1241579544426412 { - constructor() { - this.name = 'v1241579544426412'; - } - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "notification" ADD "followRequestId" character varying(32)`, undefined); - await queryRunner.query(`ALTER TABLE "notification" ADD CONSTRAINT "FK_bd7fab507621e635b32cd31892c" FOREIGN KEY ("followRequestId") REFERENCES "follow_request"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, undefined); - } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "notification" DROP CONSTRAINT "FK_bd7fab507621e635b32cd31892c"`, undefined); - await queryRunner.query(`ALTER TABLE "notification" DROP COLUMN "followRequestId"`, undefined); - } + constructor() { + this.name = "v1241579544426412"; + } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "notification" ADD "followRequestId" character varying(32)`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "notification" ADD CONSTRAINT "FK_bd7fab507621e635b32cd31892c" FOREIGN KEY ("followRequestId") REFERENCES "follow_request"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + undefined, + ); + } + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "notification" DROP CONSTRAINT "FK_bd7fab507621e635b32cd31892c"`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "notification" DROP COLUMN "followRequestId"`, + undefined, + ); + } } diff --git a/packages/backend/migration/1579977526288-v12-5.js b/packages/backend/migration/1579977526288-v12-5.js index 6d2b5c584a..f71c287551 100644 --- a/packages/backend/migration/1579977526288-v12-5.js +++ b/packages/backend/migration/1579977526288-v12-5.js @@ -1,53 +1,156 @@ - - export class v1251579977526288 { - constructor() { - this.name = 'v1251579977526288'; - } - async up(queryRunner) { - await queryRunner.query(`CREATE TABLE "clip" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "userId" character varying(32) NOT NULL, "name" character varying(128) NOT NULL, "isPublic" boolean NOT NULL DEFAULT false, CONSTRAINT "PK_f0685dac8d4dd056d7255670b75" PRIMARY KEY ("id"))`, undefined); - await queryRunner.query(`CREATE INDEX "IDX_2b5ec6c574d6802c94c80313fb" ON "clip" ("userId") `, undefined); - await queryRunner.query(`CREATE TABLE "clip_note" ("id" character varying(32) NOT NULL, "noteId" character varying(32) NOT NULL, "clipId" character varying(32) NOT NULL, CONSTRAINT "PK_e94cda2f40a99b57e032a1a738b" PRIMARY KEY ("id"))`, undefined); - await queryRunner.query(`CREATE INDEX "IDX_a012eaf5c87c65da1deb5fdbfa" ON "clip_note" ("noteId") `, undefined); - await queryRunner.query(`CREATE INDEX "IDX_ebe99317bbbe9968a0c6f579ad" ON "clip_note" ("clipId") `, undefined); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_6fc0ec357d55a18646262fdfff" ON "clip_note" ("noteId", "clipId") `, undefined); - await queryRunner.query(`CREATE TYPE "antenna_src_enum" AS ENUM('home', 'all', 'list')`, undefined); - await queryRunner.query(`CREATE TABLE "antenna" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "userId" character varying(32) NOT NULL, "name" character varying(128) NOT NULL, "src" "antenna_src_enum" NOT NULL, "userListId" character varying(32), "keywords" jsonb NOT NULL DEFAULT '[]', "withFile" boolean NOT NULL, "expression" character varying(2048), "notify" boolean NOT NULL, "hasNewNote" boolean NOT NULL DEFAULT false, CONSTRAINT "PK_c170b99775e1dccca947c9f2d5f" PRIMARY KEY ("id"))`, undefined); - await queryRunner.query(`CREATE INDEX "IDX_6446c571a0e8d0f05f01c78909" ON "antenna" ("userId") `, undefined); - await queryRunner.query(`CREATE TABLE "antenna_note" ("id" character varying(32) NOT NULL, "noteId" character varying(32) NOT NULL, "antennaId" character varying(32) NOT NULL, CONSTRAINT "PK_fb28d94d0989a3872df19fd6ef8" PRIMARY KEY ("id"))`, undefined); - await queryRunner.query(`CREATE INDEX "IDX_bd0397be22147e17210940e125" ON "antenna_note" ("noteId") `, undefined); - await queryRunner.query(`CREATE INDEX "IDX_0d775946662d2575dfd2068a5f" ON "antenna_note" ("antennaId") `, undefined); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_335a0bf3f904406f9ef3dd51c2" ON "antenna_note" ("noteId", "antennaId") `, undefined); - await queryRunner.query(`ALTER TABLE "note" DROP COLUMN "geo"`, undefined); - await queryRunner.query(`ALTER TABLE "clip" ADD CONSTRAINT "FK_2b5ec6c574d6802c94c80313fb2" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, undefined); - await queryRunner.query(`ALTER TABLE "clip_note" ADD CONSTRAINT "FK_a012eaf5c87c65da1deb5fdbfa3" FOREIGN KEY ("noteId") REFERENCES "note"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, undefined); - await queryRunner.query(`ALTER TABLE "clip_note" ADD CONSTRAINT "FK_ebe99317bbbe9968a0c6f579adf" FOREIGN KEY ("clipId") REFERENCES "clip"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, undefined); - await queryRunner.query(`ALTER TABLE "antenna" ADD CONSTRAINT "FK_6446c571a0e8d0f05f01c789096" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, undefined); - await queryRunner.query(`ALTER TABLE "antenna" ADD CONSTRAINT "FK_709d7d32053d0dd7620f678eeb9" FOREIGN KEY ("userListId") REFERENCES "user_list"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, undefined); - await queryRunner.query(`ALTER TABLE "antenna_note" ADD CONSTRAINT "FK_bd0397be22147e17210940e125b" FOREIGN KEY ("noteId") REFERENCES "note"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, undefined); - await queryRunner.query(`ALTER TABLE "antenna_note" ADD CONSTRAINT "FK_0d775946662d2575dfd2068a5f5" FOREIGN KEY ("antennaId") REFERENCES "antenna"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, undefined); - } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "antenna_note" DROP CONSTRAINT "FK_0d775946662d2575dfd2068a5f5"`, undefined); - await queryRunner.query(`ALTER TABLE "antenna_note" DROP CONSTRAINT "FK_bd0397be22147e17210940e125b"`, undefined); - await queryRunner.query(`ALTER TABLE "antenna" DROP CONSTRAINT "FK_709d7d32053d0dd7620f678eeb9"`, undefined); - await queryRunner.query(`ALTER TABLE "antenna" DROP CONSTRAINT "FK_6446c571a0e8d0f05f01c789096"`, undefined); - await queryRunner.query(`ALTER TABLE "clip_note" DROP CONSTRAINT "FK_ebe99317bbbe9968a0c6f579adf"`, undefined); - await queryRunner.query(`ALTER TABLE "clip_note" DROP CONSTRAINT "FK_a012eaf5c87c65da1deb5fdbfa3"`, undefined); - await queryRunner.query(`ALTER TABLE "clip" DROP CONSTRAINT "FK_2b5ec6c574d6802c94c80313fb2"`, undefined); - await queryRunner.query(`ALTER TABLE "note" ADD "geo" jsonb`, undefined); - await queryRunner.query(`DROP INDEX "IDX_335a0bf3f904406f9ef3dd51c2"`, undefined); - await queryRunner.query(`DROP INDEX "IDX_0d775946662d2575dfd2068a5f"`, undefined); - await queryRunner.query(`DROP INDEX "IDX_bd0397be22147e17210940e125"`, undefined); - await queryRunner.query(`DROP TABLE "antenna_note"`, undefined); - await queryRunner.query(`DROP INDEX "IDX_6446c571a0e8d0f05f01c78909"`, undefined); - await queryRunner.query(`DROP TABLE "antenna"`, undefined); - await queryRunner.query(`DROP TYPE "antenna_src_enum"`, undefined); - await queryRunner.query(`DROP INDEX "IDX_6fc0ec357d55a18646262fdfff"`, undefined); - await queryRunner.query(`DROP INDEX "IDX_ebe99317bbbe9968a0c6f579ad"`, undefined); - await queryRunner.query(`DROP INDEX "IDX_a012eaf5c87c65da1deb5fdbfa"`, undefined); - await queryRunner.query(`DROP TABLE "clip_note"`, undefined); - await queryRunner.query(`DROP INDEX "IDX_2b5ec6c574d6802c94c80313fb"`, undefined); - await queryRunner.query(`DROP TABLE "clip"`, undefined); - } + constructor() { + this.name = "v1251579977526288"; + } + async up(queryRunner) { + await queryRunner.query( + `CREATE TABLE "clip" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "userId" character varying(32) NOT NULL, "name" character varying(128) NOT NULL, "isPublic" boolean NOT NULL DEFAULT false, CONSTRAINT "PK_f0685dac8d4dd056d7255670b75" PRIMARY KEY ("id"))`, + undefined, + ); + await queryRunner.query( + `CREATE INDEX "IDX_2b5ec6c574d6802c94c80313fb" ON "clip" ("userId") `, + undefined, + ); + await queryRunner.query( + `CREATE TABLE "clip_note" ("id" character varying(32) NOT NULL, "noteId" character varying(32) NOT NULL, "clipId" character varying(32) NOT NULL, CONSTRAINT "PK_e94cda2f40a99b57e032a1a738b" PRIMARY KEY ("id"))`, + undefined, + ); + await queryRunner.query( + `CREATE INDEX "IDX_a012eaf5c87c65da1deb5fdbfa" ON "clip_note" ("noteId") `, + undefined, + ); + await queryRunner.query( + `CREATE INDEX "IDX_ebe99317bbbe9968a0c6f579ad" ON "clip_note" ("clipId") `, + undefined, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_6fc0ec357d55a18646262fdfff" ON "clip_note" ("noteId", "clipId") `, + undefined, + ); + await queryRunner.query( + `CREATE TYPE "antenna_src_enum" AS ENUM('home', 'all', 'list')`, + undefined, + ); + await queryRunner.query( + `CREATE TABLE "antenna" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "userId" character varying(32) NOT NULL, "name" character varying(128) NOT NULL, "src" "antenna_src_enum" NOT NULL, "userListId" character varying(32), "keywords" jsonb NOT NULL DEFAULT '[]', "withFile" boolean NOT NULL, "expression" character varying(2048), "notify" boolean NOT NULL, "hasNewNote" boolean NOT NULL DEFAULT false, CONSTRAINT "PK_c170b99775e1dccca947c9f2d5f" PRIMARY KEY ("id"))`, + undefined, + ); + await queryRunner.query( + `CREATE INDEX "IDX_6446c571a0e8d0f05f01c78909" ON "antenna" ("userId") `, + undefined, + ); + await queryRunner.query( + `CREATE TABLE "antenna_note" ("id" character varying(32) NOT NULL, "noteId" character varying(32) NOT NULL, "antennaId" character varying(32) NOT NULL, CONSTRAINT "PK_fb28d94d0989a3872df19fd6ef8" PRIMARY KEY ("id"))`, + undefined, + ); + await queryRunner.query( + `CREATE INDEX "IDX_bd0397be22147e17210940e125" ON "antenna_note" ("noteId") `, + undefined, + ); + await queryRunner.query( + `CREATE INDEX "IDX_0d775946662d2575dfd2068a5f" ON "antenna_note" ("antennaId") `, + undefined, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_335a0bf3f904406f9ef3dd51c2" ON "antenna_note" ("noteId", "antennaId") `, + undefined, + ); + await queryRunner.query(`ALTER TABLE "note" DROP COLUMN "geo"`, undefined); + await queryRunner.query( + `ALTER TABLE "clip" ADD CONSTRAINT "FK_2b5ec6c574d6802c94c80313fb2" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "clip_note" ADD CONSTRAINT "FK_a012eaf5c87c65da1deb5fdbfa3" FOREIGN KEY ("noteId") REFERENCES "note"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "clip_note" ADD CONSTRAINT "FK_ebe99317bbbe9968a0c6f579adf" FOREIGN KEY ("clipId") REFERENCES "clip"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "antenna" ADD CONSTRAINT "FK_6446c571a0e8d0f05f01c789096" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "antenna" ADD CONSTRAINT "FK_709d7d32053d0dd7620f678eeb9" FOREIGN KEY ("userListId") REFERENCES "user_list"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "antenna_note" ADD CONSTRAINT "FK_bd0397be22147e17210940e125b" FOREIGN KEY ("noteId") REFERENCES "note"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "antenna_note" ADD CONSTRAINT "FK_0d775946662d2575dfd2068a5f5" FOREIGN KEY ("antennaId") REFERENCES "antenna"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + undefined, + ); + } + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "antenna_note" DROP CONSTRAINT "FK_0d775946662d2575dfd2068a5f5"`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "antenna_note" DROP CONSTRAINT "FK_bd0397be22147e17210940e125b"`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "antenna" DROP CONSTRAINT "FK_709d7d32053d0dd7620f678eeb9"`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "antenna" DROP CONSTRAINT "FK_6446c571a0e8d0f05f01c789096"`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "clip_note" DROP CONSTRAINT "FK_ebe99317bbbe9968a0c6f579adf"`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "clip_note" DROP CONSTRAINT "FK_a012eaf5c87c65da1deb5fdbfa3"`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "clip" DROP CONSTRAINT "FK_2b5ec6c574d6802c94c80313fb2"`, + undefined, + ); + await queryRunner.query(`ALTER TABLE "note" ADD "geo" jsonb`, undefined); + await queryRunner.query( + `DROP INDEX "IDX_335a0bf3f904406f9ef3dd51c2"`, + undefined, + ); + await queryRunner.query( + `DROP INDEX "IDX_0d775946662d2575dfd2068a5f"`, + undefined, + ); + await queryRunner.query( + `DROP INDEX "IDX_bd0397be22147e17210940e125"`, + undefined, + ); + await queryRunner.query(`DROP TABLE "antenna_note"`, undefined); + await queryRunner.query( + `DROP INDEX "IDX_6446c571a0e8d0f05f01c78909"`, + undefined, + ); + await queryRunner.query(`DROP TABLE "antenna"`, undefined); + await queryRunner.query(`DROP TYPE "antenna_src_enum"`, undefined); + await queryRunner.query( + `DROP INDEX "IDX_6fc0ec357d55a18646262fdfff"`, + undefined, + ); + await queryRunner.query( + `DROP INDEX "IDX_ebe99317bbbe9968a0c6f579ad"`, + undefined, + ); + await queryRunner.query( + `DROP INDEX "IDX_a012eaf5c87c65da1deb5fdbfa"`, + undefined, + ); + await queryRunner.query(`DROP TABLE "clip_note"`, undefined); + await queryRunner.query( + `DROP INDEX "IDX_2b5ec6c574d6802c94c80313fb"`, + undefined, + ); + await queryRunner.query(`DROP TABLE "clip"`, undefined); + } } diff --git a/packages/backend/migration/1579993013959-v12-6.js b/packages/backend/migration/1579993013959-v12-6.js index 3941c1391d..fc954c3612 100644 --- a/packages/backend/migration/1579993013959-v12-6.js +++ b/packages/backend/migration/1579993013959-v12-6.js @@ -1,17 +1,33 @@ - - export class v1261579993013959 { - constructor() { - this.name = 'v1261579993013959'; - } - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "antenna" DROP COLUMN "hasNewNote"`, undefined); - await queryRunner.query(`ALTER TABLE "antenna_note" ADD "read" boolean NOT NULL DEFAULT false`, undefined); - await queryRunner.query(`CREATE INDEX "IDX_9937ea48d7ae97ffb4f3f063a4" ON "antenna_note" ("read") `, undefined); - } - async down(queryRunner) { - await queryRunner.query(`DROP INDEX "IDX_9937ea48d7ae97ffb4f3f063a4"`, undefined); - await queryRunner.query(`ALTER TABLE "antenna_note" DROP COLUMN "read"`, undefined); - await queryRunner.query(`ALTER TABLE "antenna" ADD "hasNewNote" boolean NOT NULL DEFAULT false`, undefined); - } + constructor() { + this.name = "v1261579993013959"; + } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "antenna" DROP COLUMN "hasNewNote"`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "antenna_note" ADD "read" boolean NOT NULL DEFAULT false`, + undefined, + ); + await queryRunner.query( + `CREATE INDEX "IDX_9937ea48d7ae97ffb4f3f063a4" ON "antenna_note" ("read") `, + undefined, + ); + } + async down(queryRunner) { + await queryRunner.query( + `DROP INDEX "IDX_9937ea48d7ae97ffb4f3f063a4"`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "antenna_note" DROP COLUMN "read"`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "antenna" ADD "hasNewNote" boolean NOT NULL DEFAULT false`, + undefined, + ); + } } diff --git a/packages/backend/migration/1580069531114-v12-7.js b/packages/backend/migration/1580069531114-v12-7.js index 4b4790cb7d..c4214ddf13 100644 --- a/packages/backend/migration/1580069531114-v12-7.js +++ b/packages/backend/migration/1580069531114-v12-7.js @@ -1,23 +1,51 @@ - - export class v1271580069531114 { - constructor() { - this.name = 'v1271580069531114'; - } - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "antenna" ADD "users" character varying(1024) array NOT NULL DEFAULT '{}'::varchar[]`, undefined); - await queryRunner.query(`ALTER TABLE "antenna" ADD "caseSensitive" boolean NOT NULL DEFAULT false`, undefined); - await queryRunner.query(`ALTER TYPE "public"."antenna_src_enum" RENAME TO "antenna_src_enum_old"`, undefined); - await queryRunner.query(`CREATE TYPE "antenna_src_enum" AS ENUM('home', 'all', 'users', 'list')`, undefined); - await queryRunner.query(`ALTER TABLE "antenna" ALTER COLUMN "src" TYPE "antenna_src_enum" USING "src"::"text"::"antenna_src_enum"`, undefined); - await queryRunner.query(`DROP TYPE "antenna_src_enum_old"`, undefined); - } - async down(queryRunner) { - await queryRunner.query(`CREATE TYPE "antenna_src_enum_old" AS ENUM('home', 'all', 'list')`, undefined); - await queryRunner.query(`ALTER TABLE "antenna" ALTER COLUMN "src" TYPE "antenna_src_enum_old" USING "src"::"text"::"antenna_src_enum_old"`, undefined); - await queryRunner.query(`DROP TYPE "antenna_src_enum"`, undefined); - await queryRunner.query(`ALTER TYPE "antenna_src_enum_old" RENAME TO "antenna_src_enum"`, undefined); - await queryRunner.query(`ALTER TABLE "antenna" DROP COLUMN "caseSensitive"`, undefined); - await queryRunner.query(`ALTER TABLE "antenna" DROP COLUMN "users"`, undefined); - } + constructor() { + this.name = "v1271580069531114"; + } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "antenna" ADD "users" character varying(1024) array NOT NULL DEFAULT '{}'::varchar[]`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "antenna" ADD "caseSensitive" boolean NOT NULL DEFAULT false`, + undefined, + ); + await queryRunner.query( + `ALTER TYPE "public"."antenna_src_enum" RENAME TO "antenna_src_enum_old"`, + undefined, + ); + await queryRunner.query( + `CREATE TYPE "antenna_src_enum" AS ENUM('home', 'all', 'users', 'list')`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "antenna" ALTER COLUMN "src" TYPE "antenna_src_enum" USING "src"::"text"::"antenna_src_enum"`, + undefined, + ); + await queryRunner.query(`DROP TYPE "antenna_src_enum_old"`, undefined); + } + async down(queryRunner) { + await queryRunner.query( + `CREATE TYPE "antenna_src_enum_old" AS ENUM('home', 'all', 'list')`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "antenna" ALTER COLUMN "src" TYPE "antenna_src_enum_old" USING "src"::"text"::"antenna_src_enum_old"`, + undefined, + ); + await queryRunner.query(`DROP TYPE "antenna_src_enum"`, undefined); + await queryRunner.query( + `ALTER TYPE "antenna_src_enum_old" RENAME TO "antenna_src_enum"`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "antenna" DROP COLUMN "caseSensitive"`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "antenna" DROP COLUMN "users"`, + undefined, + ); + } } diff --git a/packages/backend/migration/1580148575182-v12-8.js b/packages/backend/migration/1580148575182-v12-8.js index cc30200c14..11b792b7ea 100644 --- a/packages/backend/migration/1580148575182-v12-8.js +++ b/packages/backend/migration/1580148575182-v12-8.js @@ -1,15 +1,25 @@ - - export class v1281580148575182 { - constructor() { - this.name = 'v1281580148575182'; - } - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "note" DROP CONSTRAINT "FK_ec5c201576192ba8904c345c5cc"`, undefined); - await queryRunner.query(`ALTER TABLE "note" DROP COLUMN "appId"`, undefined); - } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "note" ADD "appId" character varying(32)`, undefined); - await queryRunner.query(`ALTER TABLE "note" ADD CONSTRAINT "FK_ec5c201576192ba8904c345c5cc" FOREIGN KEY ("appId") REFERENCES "app"("id") ON DELETE SET NULL ON UPDATE NO ACTION`, undefined); - } + constructor() { + this.name = "v1281580148575182"; + } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "note" DROP CONSTRAINT "FK_ec5c201576192ba8904c345c5cc"`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "note" DROP COLUMN "appId"`, + undefined, + ); + } + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "note" ADD "appId" character varying(32)`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "note" ADD CONSTRAINT "FK_ec5c201576192ba8904c345c5cc" FOREIGN KEY ("appId") REFERENCES "app"("id") ON DELETE SET NULL ON UPDATE NO ACTION`, + undefined, + ); + } } diff --git a/packages/backend/migration/1580154400017-v12-9.js b/packages/backend/migration/1580154400017-v12-9.js index 3715798f19..19f8be0958 100644 --- a/packages/backend/migration/1580154400017-v12-9.js +++ b/packages/backend/migration/1580154400017-v12-9.js @@ -1,13 +1,17 @@ - - export class v1291580154400017 { - constructor() { - this.name = 'v1291580154400017'; - } - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "antenna" ADD "withReplies" boolean NOT NULL DEFAULT false`, undefined); - } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "antenna" DROP COLUMN "withReplies"`, undefined); - } + constructor() { + this.name = "v1291580154400017"; + } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "antenna" ADD "withReplies" boolean NOT NULL DEFAULT false`, + undefined, + ); + } + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "antenna" DROP COLUMN "withReplies"`, + undefined, + ); + } } diff --git a/packages/backend/migration/1580276619901-v12-10.js b/packages/backend/migration/1580276619901-v12-10.js index d5decb882e..e5dd1db5e5 100644 --- a/packages/backend/migration/1580276619901-v12-10.js +++ b/packages/backend/migration/1580276619901-v12-10.js @@ -1,18 +1,31 @@ - - export class v12101580276619901 { - constructor() { - this.name = 'v12101580276619901'; - } - async up(queryRunner) { - await queryRunner.query(`TRUNCATE TABLE "notification"`, undefined); - await queryRunner.query(`ALTER TABLE "notification" DROP COLUMN "type"`, undefined); - await queryRunner.query(`CREATE TYPE "notification_type_enum" AS ENUM('follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'receiveFollowRequest', 'followRequestAccepted')`, undefined); - await queryRunner.query(`ALTER TABLE "notification" ADD "type" "notification_type_enum" NOT NULL`, undefined); - } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "notification" DROP COLUMN "type"`, undefined); - await queryRunner.query(`DROP TYPE "notification_type_enum"`, undefined); - await queryRunner.query(`ALTER TABLE "notification" ADD "type" character varying(32) NOT NULL`, undefined); - } + constructor() { + this.name = "v12101580276619901"; + } + async up(queryRunner) { + await queryRunner.query(`TRUNCATE TABLE "notification"`, undefined); + await queryRunner.query( + `ALTER TABLE "notification" DROP COLUMN "type"`, + undefined, + ); + await queryRunner.query( + `CREATE TYPE "notification_type_enum" AS ENUM('follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'receiveFollowRequest', 'followRequestAccepted')`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "notification" ADD "type" "notification_type_enum" NOT NULL`, + undefined, + ); + } + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "notification" DROP COLUMN "type"`, + undefined, + ); + await queryRunner.query(`DROP TYPE "notification_type_enum"`, undefined); + await queryRunner.query( + `ALTER TABLE "notification" ADD "type" character varying(32) NOT NULL`, + undefined, + ); + } } diff --git a/packages/backend/migration/1580331224276-v12-11.js b/packages/backend/migration/1580331224276-v12-11.js index 129720adbf..c31dca1758 100644 --- a/packages/backend/migration/1580331224276-v12-11.js +++ b/packages/backend/migration/1580331224276-v12-11.js @@ -1,17 +1,33 @@ - - export class v12111580331224276 { - constructor() { - this.name = 'v12111580331224276'; - } - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "instance" DROP COLUMN "isMarkedAsClosed"`, undefined); - await queryRunner.query(`ALTER TABLE "instance" ADD "isSuspended" boolean NOT NULL DEFAULT false`, undefined); - await queryRunner.query(`CREATE INDEX "IDX_34500da2e38ac393f7bb6b299c" ON "instance" ("isSuspended") `, undefined); - } - async down(queryRunner) { - await queryRunner.query(`DROP INDEX "IDX_34500da2e38ac393f7bb6b299c"`, undefined); - await queryRunner.query(`ALTER TABLE "instance" DROP COLUMN "isSuspended"`, undefined); - await queryRunner.query(`ALTER TABLE "instance" ADD "isMarkedAsClosed" boolean NOT NULL DEFAULT false`, undefined); - } + constructor() { + this.name = "v12111580331224276"; + } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "instance" DROP COLUMN "isMarkedAsClosed"`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "instance" ADD "isSuspended" boolean NOT NULL DEFAULT false`, + undefined, + ); + await queryRunner.query( + `CREATE INDEX "IDX_34500da2e38ac393f7bb6b299c" ON "instance" ("isSuspended") `, + undefined, + ); + } + async down(queryRunner) { + await queryRunner.query( + `DROP INDEX "IDX_34500da2e38ac393f7bb6b299c"`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "instance" DROP COLUMN "isSuspended"`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "instance" ADD "isMarkedAsClosed" boolean NOT NULL DEFAULT false`, + undefined, + ); + } } diff --git a/packages/backend/migration/1580508795118-v12-12.js b/packages/backend/migration/1580508795118-v12-12.js index c5cec23a36..6df1b8f8a9 100644 --- a/packages/backend/migration/1580508795118-v12-12.js +++ b/packages/backend/migration/1580508795118-v12-12.js @@ -1,45 +1,145 @@ - - export class v12121580508795118 { - constructor() { - this.name = 'v12121580508795118'; - } - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "twitter"`, undefined); - await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "twitterAccessToken"`, undefined); - await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "twitterAccessTokenSecret"`, undefined); - await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "twitterUserId"`, undefined); - await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "twitterScreenName"`, undefined); - await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "github"`, undefined); - await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "githubAccessToken"`, undefined); - await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "githubId"`, undefined); - await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "githubLogin"`, undefined); - await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "discord"`, undefined); - await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "discordAccessToken"`, undefined); - await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "discordRefreshToken"`, undefined); - await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "discordExpiresDate"`, undefined); - await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "discordId"`, undefined); - await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "discordUsername"`, undefined); - await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "discordDiscriminator"`, undefined); - await queryRunner.query(`ALTER TABLE "user_profile" ADD "integrations" jsonb NOT NULL DEFAULT '{}'`, undefined); - } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "integrations"`, undefined); - await queryRunner.query(`ALTER TABLE "user_profile" ADD "discordDiscriminator" character varying(64) DEFAULT NULL`, undefined); - await queryRunner.query(`ALTER TABLE "user_profile" ADD "discordUsername" character varying(64) DEFAULT NULL`, undefined); - await queryRunner.query(`ALTER TABLE "user_profile" ADD "discordId" character varying(64) DEFAULT NULL`, undefined); - await queryRunner.query(`ALTER TABLE "user_profile" ADD "discordExpiresDate" character varying(64)`, undefined); - await queryRunner.query(`ALTER TABLE "user_profile" ADD "discordRefreshToken" character varying(64) DEFAULT NULL`, undefined); - await queryRunner.query(`ALTER TABLE "user_profile" ADD "discordAccessToken" character varying(64) DEFAULT NULL`, undefined); - await queryRunner.query(`ALTER TABLE "user_profile" ADD "discord" boolean NOT NULL DEFAULT false`, undefined); - await queryRunner.query(`ALTER TABLE "user_profile" ADD "githubLogin" character varying(64) DEFAULT NULL`, undefined); - await queryRunner.query(`ALTER TABLE "user_profile" ADD "githubId" character varying(64)`, undefined); - await queryRunner.query(`ALTER TABLE "user_profile" ADD "githubAccessToken" character varying(64) DEFAULT NULL`, undefined); - await queryRunner.query(`ALTER TABLE "user_profile" ADD "github" boolean NOT NULL DEFAULT false`, undefined); - await queryRunner.query(`ALTER TABLE "user_profile" ADD "twitterScreenName" character varying(64) DEFAULT NULL`, undefined); - await queryRunner.query(`ALTER TABLE "user_profile" ADD "twitterUserId" character varying(64) DEFAULT NULL`, undefined); - await queryRunner.query(`ALTER TABLE "user_profile" ADD "twitterAccessTokenSecret" character varying(64) DEFAULT NULL`, undefined); - await queryRunner.query(`ALTER TABLE "user_profile" ADD "twitterAccessToken" character varying(64) DEFAULT NULL`, undefined); - await queryRunner.query(`ALTER TABLE "user_profile" ADD "twitter" boolean NOT NULL DEFAULT false`, undefined); - } + constructor() { + this.name = "v12121580508795118"; + } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "user_profile" DROP COLUMN "twitter"`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "user_profile" DROP COLUMN "twitterAccessToken"`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "user_profile" DROP COLUMN "twitterAccessTokenSecret"`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "user_profile" DROP COLUMN "twitterUserId"`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "user_profile" DROP COLUMN "twitterScreenName"`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "user_profile" DROP COLUMN "github"`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "user_profile" DROP COLUMN "githubAccessToken"`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "user_profile" DROP COLUMN "githubId"`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "user_profile" DROP COLUMN "githubLogin"`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "user_profile" DROP COLUMN "discord"`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "user_profile" DROP COLUMN "discordAccessToken"`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "user_profile" DROP COLUMN "discordRefreshToken"`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "user_profile" DROP COLUMN "discordExpiresDate"`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "user_profile" DROP COLUMN "discordId"`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "user_profile" DROP COLUMN "discordUsername"`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "user_profile" DROP COLUMN "discordDiscriminator"`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "user_profile" ADD "integrations" jsonb NOT NULL DEFAULT '{}'`, + undefined, + ); + } + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "user_profile" DROP COLUMN "integrations"`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "user_profile" ADD "discordDiscriminator" character varying(64) DEFAULT NULL`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "user_profile" ADD "discordUsername" character varying(64) DEFAULT NULL`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "user_profile" ADD "discordId" character varying(64) DEFAULT NULL`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "user_profile" ADD "discordExpiresDate" character varying(64)`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "user_profile" ADD "discordRefreshToken" character varying(64) DEFAULT NULL`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "user_profile" ADD "discordAccessToken" character varying(64) DEFAULT NULL`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "user_profile" ADD "discord" boolean NOT NULL DEFAULT false`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "user_profile" ADD "githubLogin" character varying(64) DEFAULT NULL`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "user_profile" ADD "githubId" character varying(64)`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "user_profile" ADD "githubAccessToken" character varying(64) DEFAULT NULL`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "user_profile" ADD "github" boolean NOT NULL DEFAULT false`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "user_profile" ADD "twitterScreenName" character varying(64) DEFAULT NULL`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "user_profile" ADD "twitterUserId" character varying(64) DEFAULT NULL`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "user_profile" ADD "twitterAccessTokenSecret" character varying(64) DEFAULT NULL`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "user_profile" ADD "twitterAccessToken" character varying(64) DEFAULT NULL`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "user_profile" ADD "twitter" boolean NOT NULL DEFAULT false`, + undefined, + ); + } } diff --git a/packages/backend/migration/1580543501339-v12-13.js b/packages/backend/migration/1580543501339-v12-13.js index 2fa490392d..fd4d131518 100644 --- a/packages/backend/migration/1580543501339-v12-13.js +++ b/packages/backend/migration/1580543501339-v12-13.js @@ -1,13 +1,14 @@ - - export class v12131580543501339 { - constructor() { - this.name = 'v12131580543501339'; - } - async up(queryRunner) { - await queryRunner.query(`CREATE INDEX "IDX_NOTE_TAGS" ON "note" USING gin ("tags")`, undefined); - } - async down(queryRunner) { - await queryRunner.query(`DROP INDEX "IDX_NOTE_TAGS"`, undefined); - } + constructor() { + this.name = "v12131580543501339"; + } + async up(queryRunner) { + await queryRunner.query( + `CREATE INDEX "IDX_NOTE_TAGS" ON "note" USING gin ("tags")`, + undefined, + ); + } + async down(queryRunner) { + await queryRunner.query(`DROP INDEX "IDX_NOTE_TAGS"`, undefined); + } } diff --git a/packages/backend/migration/1580864313253-v12-14.js b/packages/backend/migration/1580864313253-v12-14.js index a3756ad029..47f778e1d7 100644 --- a/packages/backend/migration/1580864313253-v12-14.js +++ b/packages/backend/migration/1580864313253-v12-14.js @@ -1,19 +1,41 @@ - - export class v12141580864313253 { - constructor() { - this.name = 'v12141580864313253'; - } - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "meta" RENAME COLUMN "proxyAccount" TO "proxyAccountId"`, undefined); - await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "proxyAccountId"`, undefined); - await queryRunner.query(`ALTER TABLE "meta" ADD "proxyAccountId" character varying(32)`, undefined); - await queryRunner.query(`ALTER TABLE "meta" ADD CONSTRAINT "FK_ab1bc0c1e209daa77b8e8d212ad" FOREIGN KEY ("proxyAccountId") REFERENCES "user"("id") ON DELETE SET NULL ON UPDATE NO ACTION`, undefined); - } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "meta" DROP CONSTRAINT "FK_ab1bc0c1e209daa77b8e8d212ad"`, undefined); - await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "proxyAccountId"`, undefined); - await queryRunner.query(`ALTER TABLE "meta" ADD "proxyAccountId" character varying(128)`, undefined); - await queryRunner.query(`ALTER TABLE "meta" RENAME COLUMN "proxyAccountId" TO "proxyAccount"`, undefined); - } + constructor() { + this.name = "v12141580864313253"; + } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "meta" RENAME COLUMN "proxyAccount" TO "proxyAccountId"`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "meta" DROP COLUMN "proxyAccountId"`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "meta" ADD "proxyAccountId" character varying(32)`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "meta" ADD CONSTRAINT "FK_ab1bc0c1e209daa77b8e8d212ad" FOREIGN KEY ("proxyAccountId") REFERENCES "user"("id") ON DELETE SET NULL ON UPDATE NO ACTION`, + undefined, + ); + } + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "meta" DROP CONSTRAINT "FK_ab1bc0c1e209daa77b8e8d212ad"`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "meta" DROP COLUMN "proxyAccountId"`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "meta" ADD "proxyAccountId" character varying(128)`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "meta" RENAME COLUMN "proxyAccountId" TO "proxyAccount"`, + undefined, + ); + } } diff --git a/packages/backend/migration/1581526429287-user-group-invitation.js b/packages/backend/migration/1581526429287-user-group-invitation.js index 181b0aba86..e327816ea7 100644 --- a/packages/backend/migration/1581526429287-user-group-invitation.js +++ b/packages/backend/migration/1581526429287-user-group-invitation.js @@ -1,37 +1,107 @@ - - export class userGroupInvitation1581526429287 { - constructor() { - this.name = 'userGroupInvitation1581526429287'; - } - async up(queryRunner) { - await queryRunner.query(`CREATE TABLE "user_group_invitation" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "userId" character varying(32) NOT NULL, "userGroupId" character varying(32) NOT NULL, CONSTRAINT "PK_160c63ec02bf23f6a5c5e8140d6" PRIMARY KEY ("id"))`, undefined); - await queryRunner.query(`CREATE INDEX "IDX_bfbc6305547539369fe73eb144" ON "user_group_invitation" ("userId") `, undefined); - await queryRunner.query(`CREATE INDEX "IDX_5cc8c468090e129857e9fecce5" ON "user_group_invitation" ("userGroupId") `, undefined); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_e9793f65f504e5a31fbaedbf2f" ON "user_group_invitation" ("userId", "userGroupId") `, undefined); - await queryRunner.query(`ALTER TABLE "notification" ADD "userGroupInvitationId" character varying(32)`, undefined); - await queryRunner.query(`ALTER TYPE "public"."notification_type_enum" RENAME TO "notification_type_enum_old"`, undefined); - await queryRunner.query(`CREATE TYPE "notification_type_enum" AS ENUM('follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'receiveFollowRequest', 'followRequestAccepted', 'groupInvited')`, undefined); - await queryRunner.query(`ALTER TABLE "notification" ALTER COLUMN "type" TYPE "notification_type_enum" USING "type"::"text"::"notification_type_enum"`, undefined); - await queryRunner.query(`DROP TYPE "notification_type_enum_old"`, undefined); - await queryRunner.query(`COMMENT ON COLUMN "notification"."type" IS 'The type of the Notification.'`, undefined); - await queryRunner.query(`ALTER TABLE "user_group_invitation" ADD CONSTRAINT "FK_bfbc6305547539369fe73eb144a" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, undefined); - await queryRunner.query(`ALTER TABLE "user_group_invitation" ADD CONSTRAINT "FK_5cc8c468090e129857e9fecce5a" FOREIGN KEY ("userGroupId") REFERENCES "user_group"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, undefined); - await queryRunner.query(`ALTER TABLE "notification" ADD CONSTRAINT "FK_8fe87814e978053a53b1beb7e98" FOREIGN KEY ("userGroupInvitationId") REFERENCES "user_group_invitation"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, undefined); - } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "notification" DROP CONSTRAINT "FK_8fe87814e978053a53b1beb7e98"`, undefined); - await queryRunner.query(`ALTER TABLE "user_group_invitation" DROP CONSTRAINT "FK_5cc8c468090e129857e9fecce5a"`, undefined); - await queryRunner.query(`ALTER TABLE "user_group_invitation" DROP CONSTRAINT "FK_bfbc6305547539369fe73eb144a"`, undefined); - await queryRunner.query(`COMMENT ON COLUMN "notification"."type" IS ''`, undefined); - await queryRunner.query(`CREATE TYPE "notification_type_enum_old" AS ENUM('follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'receiveFollowRequest', 'followRequestAccepted')`, undefined); - await queryRunner.query(`ALTER TABLE "notification" ALTER COLUMN "type" TYPE "notification_type_enum_old" USING "type"::"text"::"notification_type_enum_old"`, undefined); - await queryRunner.query(`DROP TYPE "notification_type_enum"`, undefined); - await queryRunner.query(`ALTER TYPE "notification_type_enum_old" RENAME TO "notification_type_enum"`, undefined); - await queryRunner.query(`ALTER TABLE "notification" DROP COLUMN "userGroupInvitationId"`, undefined); - await queryRunner.query(`DROP INDEX "IDX_e9793f65f504e5a31fbaedbf2f"`, undefined); - await queryRunner.query(`DROP INDEX "IDX_5cc8c468090e129857e9fecce5"`, undefined); - await queryRunner.query(`DROP INDEX "IDX_bfbc6305547539369fe73eb144"`, undefined); - await queryRunner.query(`DROP TABLE "user_group_invitation"`, undefined); - } + constructor() { + this.name = "userGroupInvitation1581526429287"; + } + async up(queryRunner) { + await queryRunner.query( + `CREATE TABLE "user_group_invitation" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "userId" character varying(32) NOT NULL, "userGroupId" character varying(32) NOT NULL, CONSTRAINT "PK_160c63ec02bf23f6a5c5e8140d6" PRIMARY KEY ("id"))`, + undefined, + ); + await queryRunner.query( + `CREATE INDEX "IDX_bfbc6305547539369fe73eb144" ON "user_group_invitation" ("userId") `, + undefined, + ); + await queryRunner.query( + `CREATE INDEX "IDX_5cc8c468090e129857e9fecce5" ON "user_group_invitation" ("userGroupId") `, + undefined, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_e9793f65f504e5a31fbaedbf2f" ON "user_group_invitation" ("userId", "userGroupId") `, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "notification" ADD "userGroupInvitationId" character varying(32)`, + undefined, + ); + await queryRunner.query( + `ALTER TYPE "public"."notification_type_enum" RENAME TO "notification_type_enum_old"`, + undefined, + ); + await queryRunner.query( + `CREATE TYPE "notification_type_enum" AS ENUM('follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'receiveFollowRequest', 'followRequestAccepted', 'groupInvited')`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "notification" ALTER COLUMN "type" TYPE "notification_type_enum" USING "type"::"text"::"notification_type_enum"`, + undefined, + ); + await queryRunner.query( + `DROP TYPE "notification_type_enum_old"`, + undefined, + ); + await queryRunner.query( + `COMMENT ON COLUMN "notification"."type" IS 'The type of the Notification.'`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "user_group_invitation" ADD CONSTRAINT "FK_bfbc6305547539369fe73eb144a" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "user_group_invitation" ADD CONSTRAINT "FK_5cc8c468090e129857e9fecce5a" FOREIGN KEY ("userGroupId") REFERENCES "user_group"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "notification" ADD CONSTRAINT "FK_8fe87814e978053a53b1beb7e98" FOREIGN KEY ("userGroupInvitationId") REFERENCES "user_group_invitation"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + undefined, + ); + } + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "notification" DROP CONSTRAINT "FK_8fe87814e978053a53b1beb7e98"`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "user_group_invitation" DROP CONSTRAINT "FK_5cc8c468090e129857e9fecce5a"`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "user_group_invitation" DROP CONSTRAINT "FK_bfbc6305547539369fe73eb144a"`, + undefined, + ); + await queryRunner.query( + `COMMENT ON COLUMN "notification"."type" IS ''`, + undefined, + ); + await queryRunner.query( + `CREATE TYPE "notification_type_enum_old" AS ENUM('follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'receiveFollowRequest', 'followRequestAccepted')`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "notification" ALTER COLUMN "type" TYPE "notification_type_enum_old" USING "type"::"text"::"notification_type_enum_old"`, + undefined, + ); + await queryRunner.query(`DROP TYPE "notification_type_enum"`, undefined); + await queryRunner.query( + `ALTER TYPE "notification_type_enum_old" RENAME TO "notification_type_enum"`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "notification" DROP COLUMN "userGroupInvitationId"`, + undefined, + ); + await queryRunner.query( + `DROP INDEX "IDX_e9793f65f504e5a31fbaedbf2f"`, + undefined, + ); + await queryRunner.query( + `DROP INDEX "IDX_5cc8c468090e129857e9fecce5"`, + undefined, + ); + await queryRunner.query( + `DROP INDEX "IDX_bfbc6305547539369fe73eb144"`, + undefined, + ); + await queryRunner.query(`DROP TABLE "user_group_invitation"`, undefined); + } } diff --git a/packages/backend/migration/1581695816408-user-group-antenna.js b/packages/backend/migration/1581695816408-user-group-antenna.js index 267b58cd9b..28112767e7 100644 --- a/packages/backend/migration/1581695816408-user-group-antenna.js +++ b/packages/backend/migration/1581695816408-user-group-antenna.js @@ -1,27 +1,67 @@ - - export class userGroupAntenna1581695816408 { - constructor() { - this.name = 'userGroupAntenna1581695816408'; - } - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "antenna" ADD "userGroupJoiningId" character varying(32)`, undefined); - await queryRunner.query(`ALTER TYPE "public"."antenna_src_enum" RENAME TO "antenna_src_enum_old"`, undefined); - await queryRunner.query(`CREATE TYPE "antenna_src_enum" AS ENUM('home', 'all', 'users', 'list', 'group')`, undefined); - await queryRunner.query(`ALTER TABLE "antenna" ALTER COLUMN "src" TYPE "antenna_src_enum" USING "src"::"text"::"antenna_src_enum"`, undefined); - await queryRunner.query(`DROP TYPE "antenna_src_enum_old"`, undefined); - await queryRunner.query(`ALTER TABLE "antenna" DROP COLUMN "users"`, undefined); - await queryRunner.query(`ALTER TABLE "antenna" ADD "users" character varying(1024) array NOT NULL DEFAULT '{}'::varchar[]`, undefined); - await queryRunner.query(`ALTER TABLE "antenna" ADD CONSTRAINT "FK_ccbf5a8c0be4511133dcc50ddeb" FOREIGN KEY ("userGroupJoiningId") REFERENCES "user_group_joining"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, undefined); - } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "antenna" DROP CONSTRAINT "FK_ccbf5a8c0be4511133dcc50ddeb"`, undefined); - await queryRunner.query(`ALTER TABLE "antenna" DROP COLUMN "users"`, undefined); - await queryRunner.query(`ALTER TABLE "antenna" ADD "users" character varying array NOT NULL DEFAULT '{}'`, undefined); - await queryRunner.query(`CREATE TYPE "antenna_src_enum_old" AS ENUM('home', 'all', 'users', 'list')`, undefined); - await queryRunner.query(`ALTER TABLE "antenna" ALTER COLUMN "src" TYPE "antenna_src_enum_old" USING "src"::"text"::"antenna_src_enum_old"`, undefined); - await queryRunner.query(`DROP TYPE "antenna_src_enum"`, undefined); - await queryRunner.query(`ALTER TYPE "antenna_src_enum_old" RENAME TO "antenna_src_enum"`, undefined); - await queryRunner.query(`ALTER TABLE "antenna" DROP COLUMN "userGroupJoiningId"`, undefined); - } + constructor() { + this.name = "userGroupAntenna1581695816408"; + } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "antenna" ADD "userGroupJoiningId" character varying(32)`, + undefined, + ); + await queryRunner.query( + `ALTER TYPE "public"."antenna_src_enum" RENAME TO "antenna_src_enum_old"`, + undefined, + ); + await queryRunner.query( + `CREATE TYPE "antenna_src_enum" AS ENUM('home', 'all', 'users', 'list', 'group')`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "antenna" ALTER COLUMN "src" TYPE "antenna_src_enum" USING "src"::"text"::"antenna_src_enum"`, + undefined, + ); + await queryRunner.query(`DROP TYPE "antenna_src_enum_old"`, undefined); + await queryRunner.query( + `ALTER TABLE "antenna" DROP COLUMN "users"`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "antenna" ADD "users" character varying(1024) array NOT NULL DEFAULT '{}'::varchar[]`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "antenna" ADD CONSTRAINT "FK_ccbf5a8c0be4511133dcc50ddeb" FOREIGN KEY ("userGroupJoiningId") REFERENCES "user_group_joining"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + undefined, + ); + } + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "antenna" DROP CONSTRAINT "FK_ccbf5a8c0be4511133dcc50ddeb"`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "antenna" DROP COLUMN "users"`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "antenna" ADD "users" character varying array NOT NULL DEFAULT '{}'`, + undefined, + ); + await queryRunner.query( + `CREATE TYPE "antenna_src_enum_old" AS ENUM('home', 'all', 'users', 'list')`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "antenna" ALTER COLUMN "src" TYPE "antenna_src_enum_old" USING "src"::"text"::"antenna_src_enum_old"`, + undefined, + ); + await queryRunner.query(`DROP TYPE "antenna_src_enum"`, undefined); + await queryRunner.query( + `ALTER TYPE "antenna_src_enum_old" RENAME TO "antenna_src_enum"`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "antenna" DROP COLUMN "userGroupJoiningId"`, + undefined, + ); + } } diff --git a/packages/backend/migration/1581708415836-drive-user-folder-id-index.js b/packages/backend/migration/1581708415836-drive-user-folder-id-index.js index 43c2ce6cee..41902dc53f 100644 --- a/packages/backend/migration/1581708415836-drive-user-folder-id-index.js +++ b/packages/backend/migration/1581708415836-drive-user-folder-id-index.js @@ -1,13 +1,17 @@ - - export class driveUserFolderIdIndex1581708415836 { - constructor() { - this.name = 'driveUserFolderIdIndex1581708415836'; - } - async up(queryRunner) { - await queryRunner.query(`CREATE INDEX "IDX_55720b33a61a7c806a8215b825" ON "drive_file" ("userId", "folderId", "id") `, undefined); - } - async down(queryRunner) { - await queryRunner.query(`DROP INDEX "IDX_55720b33a61a7c806a8215b825"`, undefined); - } + constructor() { + this.name = "driveUserFolderIdIndex1581708415836"; + } + async up(queryRunner) { + await queryRunner.query( + `CREATE INDEX "IDX_55720b33a61a7c806a8215b825" ON "drive_file" ("userId", "folderId", "id") `, + undefined, + ); + } + async down(queryRunner) { + await queryRunner.query( + `DROP INDEX "IDX_55720b33a61a7c806a8215b825"`, + undefined, + ); + } } diff --git a/packages/backend/migration/1581979837262-promo.js b/packages/backend/migration/1581979837262-promo.js index 4813a5f480..39e79965b7 100644 --- a/packages/backend/migration/1581979837262-promo.js +++ b/packages/backend/migration/1581979837262-promo.js @@ -1,27 +1,67 @@ - - export class promo1581979837262 { - constructor() { - this.name = 'promo1581979837262'; - } - async up(queryRunner) { - await queryRunner.query(`CREATE TABLE "promo_note" ("noteId" character varying(32) NOT NULL, "expiresAt" TIMESTAMP WITH TIME ZONE NOT NULL, "userId" character varying(32) NOT NULL, CONSTRAINT "REL_e263909ca4fe5d57f8d4230dd5" UNIQUE ("noteId"), CONSTRAINT "PK_e263909ca4fe5d57f8d4230dd5c" PRIMARY KEY ("noteId"))`, undefined); - await queryRunner.query(`CREATE INDEX "IDX_83f0862e9bae44af52ced7099e" ON "promo_note" ("userId") `, undefined); - await queryRunner.query(`CREATE TABLE "promo_read" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "userId" character varying(32) NOT NULL, "noteId" character varying(32) NOT NULL, CONSTRAINT "PK_61917c1541002422b703318b7c9" PRIMARY KEY ("id"))`, undefined); - await queryRunner.query(`CREATE INDEX "IDX_9657d55550c3d37bfafaf7d4b0" ON "promo_read" ("userId") `, undefined); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_2882b8a1a07c7d281a98b6db16" ON "promo_read" ("userId", "noteId") `, undefined); - await queryRunner.query(`ALTER TABLE "promo_note" ADD CONSTRAINT "FK_e263909ca4fe5d57f8d4230dd5c" FOREIGN KEY ("noteId") REFERENCES "note"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, undefined); - await queryRunner.query(`ALTER TABLE "promo_read" ADD CONSTRAINT "FK_9657d55550c3d37bfafaf7d4b05" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, undefined); - await queryRunner.query(`ALTER TABLE "promo_read" ADD CONSTRAINT "FK_a46a1a603ecee695d7db26da5f4" FOREIGN KEY ("noteId") REFERENCES "note"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, undefined); - } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "promo_read" DROP CONSTRAINT "FK_a46a1a603ecee695d7db26da5f4"`, undefined); - await queryRunner.query(`ALTER TABLE "promo_read" DROP CONSTRAINT "FK_9657d55550c3d37bfafaf7d4b05"`, undefined); - await queryRunner.query(`ALTER TABLE "promo_note" DROP CONSTRAINT "FK_e263909ca4fe5d57f8d4230dd5c"`, undefined); - await queryRunner.query(`DROP INDEX "IDX_2882b8a1a07c7d281a98b6db16"`, undefined); - await queryRunner.query(`DROP INDEX "IDX_9657d55550c3d37bfafaf7d4b0"`, undefined); - await queryRunner.query(`DROP TABLE "promo_read"`, undefined); - await queryRunner.query(`DROP INDEX "IDX_83f0862e9bae44af52ced7099e"`, undefined); - await queryRunner.query(`DROP TABLE "promo_note"`, undefined); - } + constructor() { + this.name = "promo1581979837262"; + } + async up(queryRunner) { + await queryRunner.query( + `CREATE TABLE "promo_note" ("noteId" character varying(32) NOT NULL, "expiresAt" TIMESTAMP WITH TIME ZONE NOT NULL, "userId" character varying(32) NOT NULL, CONSTRAINT "REL_e263909ca4fe5d57f8d4230dd5" UNIQUE ("noteId"), CONSTRAINT "PK_e263909ca4fe5d57f8d4230dd5c" PRIMARY KEY ("noteId"))`, + undefined, + ); + await queryRunner.query( + `CREATE INDEX "IDX_83f0862e9bae44af52ced7099e" ON "promo_note" ("userId") `, + undefined, + ); + await queryRunner.query( + `CREATE TABLE "promo_read" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "userId" character varying(32) NOT NULL, "noteId" character varying(32) NOT NULL, CONSTRAINT "PK_61917c1541002422b703318b7c9" PRIMARY KEY ("id"))`, + undefined, + ); + await queryRunner.query( + `CREATE INDEX "IDX_9657d55550c3d37bfafaf7d4b0" ON "promo_read" ("userId") `, + undefined, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_2882b8a1a07c7d281a98b6db16" ON "promo_read" ("userId", "noteId") `, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "promo_note" ADD CONSTRAINT "FK_e263909ca4fe5d57f8d4230dd5c" FOREIGN KEY ("noteId") REFERENCES "note"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "promo_read" ADD CONSTRAINT "FK_9657d55550c3d37bfafaf7d4b05" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "promo_read" ADD CONSTRAINT "FK_a46a1a603ecee695d7db26da5f4" FOREIGN KEY ("noteId") REFERENCES "note"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + undefined, + ); + } + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "promo_read" DROP CONSTRAINT "FK_a46a1a603ecee695d7db26da5f4"`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "promo_read" DROP CONSTRAINT "FK_9657d55550c3d37bfafaf7d4b05"`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "promo_note" DROP CONSTRAINT "FK_e263909ca4fe5d57f8d4230dd5c"`, + undefined, + ); + await queryRunner.query( + `DROP INDEX "IDX_2882b8a1a07c7d281a98b6db16"`, + undefined, + ); + await queryRunner.query( + `DROP INDEX "IDX_9657d55550c3d37bfafaf7d4b0"`, + undefined, + ); + await queryRunner.query(`DROP TABLE "promo_read"`, undefined); + await queryRunner.query( + `DROP INDEX "IDX_83f0862e9bae44af52ced7099e"`, + undefined, + ); + await queryRunner.query(`DROP TABLE "promo_note"`, undefined); + } } diff --git a/packages/backend/migration/1582019042083-featured-injecttion.js b/packages/backend/migration/1582019042083-featured-injecttion.js index 7f8790b01b..61b7d0c272 100644 --- a/packages/backend/migration/1582019042083-featured-injecttion.js +++ b/packages/backend/migration/1582019042083-featured-injecttion.js @@ -1,13 +1,17 @@ - - export class featuredInjecttion1582019042083 { - constructor() { - this.name = 'featuredInjecttion1582019042083'; - } - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "user_profile" ADD "injectFeaturedNote" boolean NOT NULL DEFAULT true`, undefined); - } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "injectFeaturedNote"`, undefined); - } + constructor() { + this.name = "featuredInjecttion1582019042083"; + } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "user_profile" ADD "injectFeaturedNote" boolean NOT NULL DEFAULT true`, + undefined, + ); + } + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "user_profile" DROP COLUMN "injectFeaturedNote"`, + undefined, + ); + } } diff --git a/packages/backend/migration/1582210532752-antenna-exclude.js b/packages/backend/migration/1582210532752-antenna-exclude.js index ff8d7b80d8..5a394270ef 100644 --- a/packages/backend/migration/1582210532752-antenna-exclude.js +++ b/packages/backend/migration/1582210532752-antenna-exclude.js @@ -1,13 +1,17 @@ - - export class antennaExclude1582210532752 { - constructor() { - this.name = 'antennaExclude1582210532752'; - } - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "antenna" ADD "excludeKeywords" jsonb NOT NULL DEFAULT '[]'`, undefined); - } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "antenna" DROP COLUMN "excludeKeywords"`, undefined); - } + constructor() { + this.name = "antennaExclude1582210532752"; + } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "antenna" ADD "excludeKeywords" jsonb NOT NULL DEFAULT '[]'`, + undefined, + ); + } + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "antenna" DROP COLUMN "excludeKeywords"`, + undefined, + ); + } } diff --git a/packages/backend/migration/1582875306439-note-reaction-length.js b/packages/backend/migration/1582875306439-note-reaction-length.js index e99501f012..2c9b726732 100644 --- a/packages/backend/migration/1582875306439-note-reaction-length.js +++ b/packages/backend/migration/1582875306439-note-reaction-length.js @@ -1,13 +1,17 @@ - - export class noteReactionLength1582875306439 { - constructor() { - this.name = 'noteReactionLength1582875306439'; - } - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "note_reaction" ALTER COLUMN "reaction" TYPE character varying(130)`, undefined); - } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "note_reaction" ALTER COLUMN "reaction" TYPE character varying(128)`, undefined); - } + constructor() { + this.name = "noteReactionLength1582875306439"; + } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "note_reaction" ALTER COLUMN "reaction" TYPE character varying(130)`, + undefined, + ); + } + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "note_reaction" ALTER COLUMN "reaction" TYPE character varying(128)`, + undefined, + ); + } } diff --git a/packages/backend/migration/1585361548360-miauth.js b/packages/backend/migration/1585361548360-miauth.js index e59aa3b6ef..2e052cb89a 100644 --- a/packages/backend/migration/1585361548360-miauth.js +++ b/packages/backend/migration/1585361548360-miauth.js @@ -1,35 +1,105 @@ - - export class miauth1585361548360 { - constructor() { - this.name = 'miauth1585361548360'; - } - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "access_token" ADD "lastUsedAt" TIMESTAMP WITH TIME ZONE DEFAULT null`, undefined); - await queryRunner.query(`ALTER TABLE "access_token" ADD "session" character varying(128) DEFAULT null`, undefined); - await queryRunner.query(`ALTER TABLE "access_token" ADD "name" character varying(128) DEFAULT null`, undefined); - await queryRunner.query(`ALTER TABLE "access_token" ADD "description" character varying(512) DEFAULT null`, undefined); - await queryRunner.query(`ALTER TABLE "access_token" ADD "iconUrl" character varying(512) DEFAULT null`, undefined); - await queryRunner.query(`ALTER TABLE "access_token" ADD "permission" character varying(64) array NOT NULL DEFAULT '{}'::varchar[]`, undefined); - await queryRunner.query(`ALTER TABLE "access_token" ADD "fetched" boolean NOT NULL DEFAULT false`, undefined); - await queryRunner.query(`ALTER TABLE "access_token" DROP CONSTRAINT "FK_a3ff16c90cc87a82a0b5959e560"`, undefined); - await queryRunner.query(`ALTER TABLE "access_token" ALTER COLUMN "appId" DROP NOT NULL`, undefined); - await queryRunner.query(`ALTER TABLE "access_token" ALTER COLUMN "appId" SET DEFAULT null`, undefined); - await queryRunner.query(`CREATE INDEX "IDX_bf3a053c07d9fb5d87317c56ee" ON "access_token" ("session") `, undefined); - await queryRunner.query(`ALTER TABLE "access_token" ADD CONSTRAINT "FK_a3ff16c90cc87a82a0b5959e560" FOREIGN KEY ("appId") REFERENCES "app"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, undefined); - } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "access_token" DROP CONSTRAINT "FK_a3ff16c90cc87a82a0b5959e560"`, undefined); - await queryRunner.query(`DROP INDEX "IDX_bf3a053c07d9fb5d87317c56ee"`, undefined); - await queryRunner.query(`ALTER TABLE "access_token" ALTER COLUMN "appId" DROP DEFAULT`, undefined); - await queryRunner.query(`ALTER TABLE "access_token" ALTER COLUMN "appId" SET NOT NULL`, undefined); - await queryRunner.query(`ALTER TABLE "access_token" ADD CONSTRAINT "FK_a3ff16c90cc87a82a0b5959e560" FOREIGN KEY ("appId") REFERENCES "app"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, undefined); - await queryRunner.query(`ALTER TABLE "access_token" DROP COLUMN "fetched"`, undefined); - await queryRunner.query(`ALTER TABLE "access_token" DROP COLUMN "permission"`, undefined); - await queryRunner.query(`ALTER TABLE "access_token" DROP COLUMN "iconUrl"`, undefined); - await queryRunner.query(`ALTER TABLE "access_token" DROP COLUMN "description"`, undefined); - await queryRunner.query(`ALTER TABLE "access_token" DROP COLUMN "name"`, undefined); - await queryRunner.query(`ALTER TABLE "access_token" DROP COLUMN "session"`, undefined); - await queryRunner.query(`ALTER TABLE "access_token" DROP COLUMN "lastUsedAt"`, undefined); - } + constructor() { + this.name = "miauth1585361548360"; + } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "access_token" ADD "lastUsedAt" TIMESTAMP WITH TIME ZONE DEFAULT null`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "access_token" ADD "session" character varying(128) DEFAULT null`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "access_token" ADD "name" character varying(128) DEFAULT null`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "access_token" ADD "description" character varying(512) DEFAULT null`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "access_token" ADD "iconUrl" character varying(512) DEFAULT null`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "access_token" ADD "permission" character varying(64) array NOT NULL DEFAULT '{}'::varchar[]`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "access_token" ADD "fetched" boolean NOT NULL DEFAULT false`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "access_token" DROP CONSTRAINT "FK_a3ff16c90cc87a82a0b5959e560"`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "access_token" ALTER COLUMN "appId" DROP NOT NULL`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "access_token" ALTER COLUMN "appId" SET DEFAULT null`, + undefined, + ); + await queryRunner.query( + `CREATE INDEX "IDX_bf3a053c07d9fb5d87317c56ee" ON "access_token" ("session") `, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "access_token" ADD CONSTRAINT "FK_a3ff16c90cc87a82a0b5959e560" FOREIGN KEY ("appId") REFERENCES "app"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + undefined, + ); + } + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "access_token" DROP CONSTRAINT "FK_a3ff16c90cc87a82a0b5959e560"`, + undefined, + ); + await queryRunner.query( + `DROP INDEX "IDX_bf3a053c07d9fb5d87317c56ee"`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "access_token" ALTER COLUMN "appId" DROP DEFAULT`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "access_token" ALTER COLUMN "appId" SET NOT NULL`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "access_token" ADD CONSTRAINT "FK_a3ff16c90cc87a82a0b5959e560" FOREIGN KEY ("appId") REFERENCES "app"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "access_token" DROP COLUMN "fetched"`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "access_token" DROP COLUMN "permission"`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "access_token" DROP COLUMN "iconUrl"`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "access_token" DROP COLUMN "description"`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "access_token" DROP COLUMN "name"`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "access_token" DROP COLUMN "session"`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "access_token" DROP COLUMN "lastUsedAt"`, + undefined, + ); + } } diff --git a/packages/backend/migration/1585385921215-custom-notification.js b/packages/backend/migration/1585385921215-custom-notification.js index c3ddb2be17..23142cd3a8 100644 --- a/packages/backend/migration/1585385921215-custom-notification.js +++ b/packages/backend/migration/1585385921215-custom-notification.js @@ -1,47 +1,150 @@ - - export class customNotification1585385921215 { - constructor() { - this.name = 'customNotification1585385921215'; - } - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "notification" ADD "customBody" character varying(2048)`, undefined); - await queryRunner.query(`ALTER TABLE "notification" ADD "customHeader" character varying(256)`, undefined); - await queryRunner.query(`ALTER TABLE "notification" ADD "customIcon" character varying(1024)`, undefined); - await queryRunner.query(`ALTER TABLE "notification" ADD "appAccessTokenId" character varying(32)`, undefined); - await queryRunner.query(`ALTER TABLE "notification" DROP CONSTRAINT "FK_3b4e96eec8d36a8bbb9d02aa710"`, undefined); - await queryRunner.query(`ALTER TABLE "notification" ALTER COLUMN "notifierId" DROP NOT NULL`, undefined); - await queryRunner.query(`COMMENT ON COLUMN "notification"."notifierId" IS 'The ID of sender user of the Notification.'`, undefined); - await queryRunner.query(`ALTER TYPE "public"."notification_type_enum" RENAME TO "notification_type_enum_old"`, undefined); - await queryRunner.query(`CREATE TYPE "notification_type_enum" AS ENUM('follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'receiveFollowRequest', 'followRequestAccepted', 'groupInvited', 'app')`, undefined); - await queryRunner.query(`ALTER TABLE "notification" ALTER COLUMN "type" TYPE "notification_type_enum" USING "type"::"text"::"notification_type_enum"`, undefined); - await queryRunner.query(`DROP TYPE "notification_type_enum_old"`, undefined); - await queryRunner.query(`COMMENT ON COLUMN "notification"."type" IS 'The type of the Notification.'`, undefined); - await queryRunner.query(`CREATE INDEX "IDX_3b4e96eec8d36a8bbb9d02aa71" ON "notification" ("notifierId") `, undefined); - await queryRunner.query(`CREATE INDEX "IDX_33f33cc8ef29d805a97ff4628b" ON "notification" ("type") `, undefined); - await queryRunner.query(`CREATE INDEX "IDX_080ab397c379af09b9d2169e5b" ON "notification" ("isRead") `, undefined); - await queryRunner.query(`CREATE INDEX "IDX_e22bf6bda77b6adc1fd9e75c8c" ON "notification" ("appAccessTokenId") `, undefined); - await queryRunner.query(`ALTER TABLE "notification" ADD CONSTRAINT "FK_3b4e96eec8d36a8bbb9d02aa710" FOREIGN KEY ("notifierId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, undefined); - await queryRunner.query(`ALTER TABLE "notification" ADD CONSTRAINT "FK_e22bf6bda77b6adc1fd9e75c8c9" FOREIGN KEY ("appAccessTokenId") REFERENCES "access_token"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, undefined); - } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "notification" DROP CONSTRAINT "FK_e22bf6bda77b6adc1fd9e75c8c9"`, undefined); - await queryRunner.query(`ALTER TABLE "notification" DROP CONSTRAINT "FK_3b4e96eec8d36a8bbb9d02aa710"`, undefined); - await queryRunner.query(`DROP INDEX "IDX_e22bf6bda77b6adc1fd9e75c8c"`, undefined); - await queryRunner.query(`DROP INDEX "IDX_080ab397c379af09b9d2169e5b"`, undefined); - await queryRunner.query(`DROP INDEX "IDX_33f33cc8ef29d805a97ff4628b"`, undefined); - await queryRunner.query(`DROP INDEX "IDX_3b4e96eec8d36a8bbb9d02aa71"`, undefined); - await queryRunner.query(`COMMENT ON COLUMN "notification"."type" IS ''`, undefined); - await queryRunner.query(`CREATE TYPE "notification_type_enum_old" AS ENUM('follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'receiveFollowRequest', 'followRequestAccepted', 'groupInvited')`, undefined); - await queryRunner.query(`ALTER TABLE "notification" ALTER COLUMN "type" TYPE "notification_type_enum_old" USING "type"::"text"::"notification_type_enum_old"`, undefined); - await queryRunner.query(`DROP TYPE "notification_type_enum"`, undefined); - await queryRunner.query(`ALTER TYPE "notification_type_enum_old" RENAME TO "notification_type_enum"`, undefined); - await queryRunner.query(`COMMENT ON COLUMN "notification"."notifierId" IS ''`, undefined); - await queryRunner.query(`ALTER TABLE "notification" ALTER COLUMN "notifierId" SET NOT NULL`, undefined); - await queryRunner.query(`ALTER TABLE "notification" ADD CONSTRAINT "FK_3b4e96eec8d36a8bbb9d02aa710" FOREIGN KEY ("notifierId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, undefined); - await queryRunner.query(`ALTER TABLE "notification" DROP COLUMN "appAccessTokenId"`, undefined); - await queryRunner.query(`ALTER TABLE "notification" DROP COLUMN "customIcon"`, undefined); - await queryRunner.query(`ALTER TABLE "notification" DROP COLUMN "customHeader"`, undefined); - await queryRunner.query(`ALTER TABLE "notification" DROP COLUMN "customBody"`, undefined); - } + constructor() { + this.name = "customNotification1585385921215"; + } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "notification" ADD "customBody" character varying(2048)`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "notification" ADD "customHeader" character varying(256)`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "notification" ADD "customIcon" character varying(1024)`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "notification" ADD "appAccessTokenId" character varying(32)`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "notification" DROP CONSTRAINT "FK_3b4e96eec8d36a8bbb9d02aa710"`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "notification" ALTER COLUMN "notifierId" DROP NOT NULL`, + undefined, + ); + await queryRunner.query( + `COMMENT ON COLUMN "notification"."notifierId" IS 'The ID of sender user of the Notification.'`, + undefined, + ); + await queryRunner.query( + `ALTER TYPE "public"."notification_type_enum" RENAME TO "notification_type_enum_old"`, + undefined, + ); + await queryRunner.query( + `CREATE TYPE "notification_type_enum" AS ENUM('follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'receiveFollowRequest', 'followRequestAccepted', 'groupInvited', 'app')`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "notification" ALTER COLUMN "type" TYPE "notification_type_enum" USING "type"::"text"::"notification_type_enum"`, + undefined, + ); + await queryRunner.query( + `DROP TYPE "notification_type_enum_old"`, + undefined, + ); + await queryRunner.query( + `COMMENT ON COLUMN "notification"."type" IS 'The type of the Notification.'`, + undefined, + ); + await queryRunner.query( + `CREATE INDEX "IDX_3b4e96eec8d36a8bbb9d02aa71" ON "notification" ("notifierId") `, + undefined, + ); + await queryRunner.query( + `CREATE INDEX "IDX_33f33cc8ef29d805a97ff4628b" ON "notification" ("type") `, + undefined, + ); + await queryRunner.query( + `CREATE INDEX "IDX_080ab397c379af09b9d2169e5b" ON "notification" ("isRead") `, + undefined, + ); + await queryRunner.query( + `CREATE INDEX "IDX_e22bf6bda77b6adc1fd9e75c8c" ON "notification" ("appAccessTokenId") `, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "notification" ADD CONSTRAINT "FK_3b4e96eec8d36a8bbb9d02aa710" FOREIGN KEY ("notifierId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "notification" ADD CONSTRAINT "FK_e22bf6bda77b6adc1fd9e75c8c9" FOREIGN KEY ("appAccessTokenId") REFERENCES "access_token"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + undefined, + ); + } + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "notification" DROP CONSTRAINT "FK_e22bf6bda77b6adc1fd9e75c8c9"`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "notification" DROP CONSTRAINT "FK_3b4e96eec8d36a8bbb9d02aa710"`, + undefined, + ); + await queryRunner.query( + `DROP INDEX "IDX_e22bf6bda77b6adc1fd9e75c8c"`, + undefined, + ); + await queryRunner.query( + `DROP INDEX "IDX_080ab397c379af09b9d2169e5b"`, + undefined, + ); + await queryRunner.query( + `DROP INDEX "IDX_33f33cc8ef29d805a97ff4628b"`, + undefined, + ); + await queryRunner.query( + `DROP INDEX "IDX_3b4e96eec8d36a8bbb9d02aa71"`, + undefined, + ); + await queryRunner.query( + `COMMENT ON COLUMN "notification"."type" IS ''`, + undefined, + ); + await queryRunner.query( + `CREATE TYPE "notification_type_enum_old" AS ENUM('follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'receiveFollowRequest', 'followRequestAccepted', 'groupInvited')`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "notification" ALTER COLUMN "type" TYPE "notification_type_enum_old" USING "type"::"text"::"notification_type_enum_old"`, + undefined, + ); + await queryRunner.query(`DROP TYPE "notification_type_enum"`, undefined); + await queryRunner.query( + `ALTER TYPE "notification_type_enum_old" RENAME TO "notification_type_enum"`, + undefined, + ); + await queryRunner.query( + `COMMENT ON COLUMN "notification"."notifierId" IS ''`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "notification" ALTER COLUMN "notifierId" SET NOT NULL`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "notification" ADD CONSTRAINT "FK_3b4e96eec8d36a8bbb9d02aa710" FOREIGN KEY ("notifierId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "notification" DROP COLUMN "appAccessTokenId"`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "notification" DROP COLUMN "customIcon"`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "notification" DROP COLUMN "customHeader"`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "notification" DROP COLUMN "customBody"`, + undefined, + ); + } } diff --git a/packages/backend/migration/1585772678853-ap-url.js b/packages/backend/migration/1585772678853-ap-url.js index 5fb809ff53..18cc4588e6 100644 --- a/packages/backend/migration/1585772678853-ap-url.js +++ b/packages/backend/migration/1585772678853-ap-url.js @@ -1,13 +1,14 @@ - - export class apUrl1585772678853 { - constructor() { - this.name = 'apUrl1585772678853'; - } - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "note" ADD "url" character varying(512)`, undefined); - } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "note" DROP COLUMN "url"`, undefined); - } + constructor() { + this.name = "apUrl1585772678853"; + } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "note" ADD "url" character varying(512)`, + undefined, + ); + } + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "note" DROP COLUMN "url"`, undefined); + } } diff --git a/packages/backend/migration/1586624197029-AddObjectStorageUseProxy.js b/packages/backend/migration/1586624197029-AddObjectStorageUseProxy.js index e13bb217e3..89694f84b2 100644 --- a/packages/backend/migration/1586624197029-AddObjectStorageUseProxy.js +++ b/packages/backend/migration/1586624197029-AddObjectStorageUseProxy.js @@ -1,13 +1,17 @@ - - export class AddObjectStorageUseProxy1586624197029 { - constructor() { - this.name = 'AddObjectStorageUseProxy1586624197029'; - } - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "meta" ADD "objectStorageUseProxy" boolean NOT NULL DEFAULT true`, undefined); - } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "objectStorageUseProxy"`, undefined); - } + constructor() { + this.name = "AddObjectStorageUseProxy1586624197029"; + } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "meta" ADD "objectStorageUseProxy" boolean NOT NULL DEFAULT true`, + undefined, + ); + } + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "meta" DROP COLUMN "objectStorageUseProxy"`, + undefined, + ); + } } diff --git a/packages/backend/migration/1586641139527-remote-reaction.js b/packages/backend/migration/1586641139527-remote-reaction.js index 5b23103a17..d7390544c4 100644 --- a/packages/backend/migration/1586641139527-remote-reaction.js +++ b/packages/backend/migration/1586641139527-remote-reaction.js @@ -1,13 +1,17 @@ - - export class remoteReaction1586641139527 { - constructor() { - this.name = 'remoteReaction1586641139527'; - } - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "note_reaction" ALTER COLUMN "reaction" TYPE character varying(260)`, undefined); - } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "note_reaction" ALTER COLUMN "reaction" TYPE character varying(130)`, undefined); - } + constructor() { + this.name = "remoteReaction1586641139527"; + } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "note_reaction" ALTER COLUMN "reaction" TYPE character varying(260)`, + undefined, + ); + } + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "note_reaction" ALTER COLUMN "reaction" TYPE character varying(130)`, + undefined, + ); + } } diff --git a/packages/backend/migration/1586708940386-pageAiScript.js b/packages/backend/migration/1586708940386-pageAiScript.js index eed616c111..25eb710988 100644 --- a/packages/backend/migration/1586708940386-pageAiScript.js +++ b/packages/backend/migration/1586708940386-pageAiScript.js @@ -1,13 +1,17 @@ - - export class pageAiScript1586708940386 { - constructor() { - this.name = 'pageAiScript1586708940386'; - } - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "page" ADD "script" character varying(16384) NOT NULL DEFAULT ''`, undefined); - } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "page" DROP COLUMN "script"`, undefined); - } + constructor() { + this.name = "pageAiScript1586708940386"; + } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "page" ADD "script" character varying(16384) NOT NULL DEFAULT ''`, + undefined, + ); + } + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "page" DROP COLUMN "script"`, + undefined, + ); + } } diff --git a/packages/backend/migration/1588044505511-hCaptcha.js b/packages/backend/migration/1588044505511-hCaptcha.js index a33dbd7133..29ba87370f 100644 --- a/packages/backend/migration/1588044505511-hCaptcha.js +++ b/packages/backend/migration/1588044505511-hCaptcha.js @@ -1,17 +1,33 @@ - - export class hCaptcha1588044505511 { - constructor() { - this.name = 'hCaptcha1588044505511'; - } - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "meta" ADD "enableHcaptcha" boolean NOT NULL DEFAULT false`, undefined); - await queryRunner.query(`ALTER TABLE "meta" ADD "hcaptchaSiteKey" character varying(64)`, undefined); - await queryRunner.query(`ALTER TABLE "meta" ADD "hcaptchaSecretKey" character varying(64)`, undefined); - } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "hcaptchaSecretKey"`, undefined); - await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "hcaptchaSiteKey"`, undefined); - await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "enableHcaptcha"`, undefined); - } + constructor() { + this.name = "hCaptcha1588044505511"; + } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "meta" ADD "enableHcaptcha" boolean NOT NULL DEFAULT false`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "meta" ADD "hcaptchaSiteKey" character varying(64)`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "meta" ADD "hcaptchaSecretKey" character varying(64)`, + undefined, + ); + } + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "meta" DROP COLUMN "hcaptchaSecretKey"`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "meta" DROP COLUMN "hcaptchaSiteKey"`, + undefined, + ); + await queryRunner.query( + `ALTER TABLE "meta" DROP COLUMN "enableHcaptcha"`, + undefined, + ); + } } diff --git a/packages/backend/migration/1589023282116-pubRelay.js b/packages/backend/migration/1589023282116-pubRelay.js index 48a1028d39..a1a2299192 100644 --- a/packages/backend/migration/1589023282116-pubRelay.js +++ b/packages/backend/migration/1589023282116-pubRelay.js @@ -1,17 +1,27 @@ - - export class pubRelay1589023282116 { - constructor() { - this.name = 'pubRelay1589023282116'; - } - async up(queryRunner) { - await queryRunner.query(`CREATE TYPE "relay_status_enum" AS ENUM('requesting', 'accepted', 'rejected')`, undefined); - await queryRunner.query(`CREATE TABLE "relay" ("id" character varying(32) NOT NULL, "inbox" character varying(512) NOT NULL, "status" "relay_status_enum" NOT NULL, CONSTRAINT "PK_78ebc9cfddf4292633b7ba57aee" PRIMARY KEY ("id"))`, undefined); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_0d9a1738f2cf7f3b1c3334dfab" ON "relay" ("inbox") `, undefined); - } - async down(queryRunner) { - await queryRunner.query(`DROP INDEX "IDX_0d9a1738f2cf7f3b1c3334dfab"`, undefined); - await queryRunner.query(`DROP TABLE "relay"`, undefined); - await queryRunner.query(`DROP TYPE "relay_status_enum"`, undefined); - } + constructor() { + this.name = "pubRelay1589023282116"; + } + async up(queryRunner) { + await queryRunner.query( + `CREATE TYPE "relay_status_enum" AS ENUM('requesting', 'accepted', 'rejected')`, + undefined, + ); + await queryRunner.query( + `CREATE TABLE "relay" ("id" character varying(32) NOT NULL, "inbox" character varying(512) NOT NULL, "status" "relay_status_enum" NOT NULL, CONSTRAINT "PK_78ebc9cfddf4292633b7ba57aee" PRIMARY KEY ("id"))`, + undefined, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_0d9a1738f2cf7f3b1c3334dfab" ON "relay" ("inbox") `, + undefined, + ); + } + async down(queryRunner) { + await queryRunner.query( + `DROP INDEX "IDX_0d9a1738f2cf7f3b1c3334dfab"`, + undefined, + ); + await queryRunner.query(`DROP TABLE "relay"`, undefined); + await queryRunner.query(`DROP TYPE "relay_status_enum"`, undefined); + } } diff --git a/packages/backend/migration/1595075960584-blurhash.js b/packages/backend/migration/1595075960584-blurhash.js index f24d3722cf..50660cd055 100644 --- a/packages/backend/migration/1595075960584-blurhash.js +++ b/packages/backend/migration/1595075960584-blurhash.js @@ -1,13 +1,13 @@ - - export class blurhash1595075960584 { - constructor() { - this.name = 'blurhash1595075960584'; - } - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "drive_file" ADD "blurhash" character varying(128)`); - } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "drive_file" DROP COLUMN "blurhash"`); - } + constructor() { + this.name = "blurhash1595075960584"; + } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "drive_file" ADD "blurhash" character varying(128)`, + ); + } + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "drive_file" DROP COLUMN "blurhash"`); + } } diff --git a/packages/backend/migration/1595077605646-blurhash-for-avatar-banner.js b/packages/backend/migration/1595077605646-blurhash-for-avatar-banner.js index f18f6f972a..612ab648cb 100644 --- a/packages/backend/migration/1595077605646-blurhash-for-avatar-banner.js +++ b/packages/backend/migration/1595077605646-blurhash-for-avatar-banner.js @@ -1,19 +1,25 @@ - - export class blurhashForAvatarBanner1595077605646 { - constructor() { - this.name = 'blurhashForAvatarBanner1595077605646'; - } - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "avatarColor"`); - await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "bannerColor"`); - await queryRunner.query(`ALTER TABLE "user" ADD "avatarBlurhash" character varying(128)`); - await queryRunner.query(`ALTER TABLE "user" ADD "bannerBlurhash" character varying(128)`); - } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "bannerBlurhash"`); - await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "avatarBlurhash"`); - await queryRunner.query(`ALTER TABLE "user" ADD "bannerColor" character varying(32)`); - await queryRunner.query(`ALTER TABLE "user" ADD "avatarColor" character varying(32)`); - } + constructor() { + this.name = "blurhashForAvatarBanner1595077605646"; + } + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "avatarColor"`); + await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "bannerColor"`); + await queryRunner.query( + `ALTER TABLE "user" ADD "avatarBlurhash" character varying(128)`, + ); + await queryRunner.query( + `ALTER TABLE "user" ADD "bannerBlurhash" character varying(128)`, + ); + } + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "bannerBlurhash"`); + await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "avatarBlurhash"`); + await queryRunner.query( + `ALTER TABLE "user" ADD "bannerColor" character varying(32)`, + ); + await queryRunner.query( + `ALTER TABLE "user" ADD "avatarColor" character varying(32)`, + ); + } } diff --git a/packages/backend/migration/1595676934834-instance-icon-url.js b/packages/backend/migration/1595676934834-instance-icon-url.js index df9d8199bd..c228b14b6c 100644 --- a/packages/backend/migration/1595676934834-instance-icon-url.js +++ b/packages/backend/migration/1595676934834-instance-icon-url.js @@ -1,13 +1,13 @@ - - export class instanceIconUrl1595676934834 { - constructor() { - this.name = 'instanceIconUrl1595676934834'; - } - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "instance" ADD "iconUrl" character varying(256) DEFAULT null`); - } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "instance" DROP COLUMN "iconUrl"`); - } + constructor() { + this.name = "instanceIconUrl1595676934834"; + } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "instance" ADD "iconUrl" character varying(256) DEFAULT null`, + ); + } + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "instance" DROP COLUMN "iconUrl"`); + } } diff --git a/packages/backend/migration/1595771249699-word-mute.js b/packages/backend/migration/1595771249699-word-mute.js index e8e4ac838b..0a8a63294d 100644 --- a/packages/backend/migration/1595771249699-word-mute.js +++ b/packages/backend/migration/1595771249699-word-mute.js @@ -1,29 +1,53 @@ - - export class wordMute1595771249699 { - constructor() { - this.name = 'wordMute1595771249699'; - } - async up(queryRunner) { - await queryRunner.query(`CREATE TABLE "muted_note" ("id" character varying(32) NOT NULL, "noteId" character varying(32) NOT NULL, "userId" character varying(32) NOT NULL, CONSTRAINT "PK_897e2eff1c0b9b64e55ca1418a4" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE INDEX "IDX_70ab9786313d78e4201d81cdb8" ON "muted_note" ("noteId") `); - await queryRunner.query(`CREATE INDEX "IDX_d8e07aa18c2d64e86201601aec" ON "muted_note" ("userId") `); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_a8c6bfd637d3f1d67a27c48e27" ON "muted_note" ("noteId", "userId") `); - await queryRunner.query(`ALTER TABLE "user_profile" ADD "enableWordMute" boolean NOT NULL DEFAULT false`); - await queryRunner.query(`ALTER TABLE "user_profile" ADD "mutedWords" jsonb NOT NULL DEFAULT '[]'`); - await queryRunner.query(`CREATE INDEX "IDX_3befe6f999c86aff06eb0257b4" ON "user_profile" ("enableWordMute") `); - await queryRunner.query(`ALTER TABLE "muted_note" ADD CONSTRAINT "FK_70ab9786313d78e4201d81cdb89" FOREIGN KEY ("noteId") REFERENCES "note"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "muted_note" ADD CONSTRAINT "FK_d8e07aa18c2d64e86201601aec1" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "muted_note" DROP CONSTRAINT "FK_d8e07aa18c2d64e86201601aec1"`); - await queryRunner.query(`ALTER TABLE "muted_note" DROP CONSTRAINT "FK_70ab9786313d78e4201d81cdb89"`); - await queryRunner.query(`DROP INDEX "IDX_3befe6f999c86aff06eb0257b4"`); - await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "mutedWords"`); - await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "enableWordMute"`); - await queryRunner.query(`DROP INDEX "IDX_a8c6bfd637d3f1d67a27c48e27"`); - await queryRunner.query(`DROP INDEX "IDX_d8e07aa18c2d64e86201601aec"`); - await queryRunner.query(`DROP INDEX "IDX_70ab9786313d78e4201d81cdb8"`); - await queryRunner.query(`DROP TABLE "muted_note"`); - } + constructor() { + this.name = "wordMute1595771249699"; + } + async up(queryRunner) { + await queryRunner.query( + `CREATE TABLE "muted_note" ("id" character varying(32) NOT NULL, "noteId" character varying(32) NOT NULL, "userId" character varying(32) NOT NULL, CONSTRAINT "PK_897e2eff1c0b9b64e55ca1418a4" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE INDEX "IDX_70ab9786313d78e4201d81cdb8" ON "muted_note" ("noteId") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_d8e07aa18c2d64e86201601aec" ON "muted_note" ("userId") `, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_a8c6bfd637d3f1d67a27c48e27" ON "muted_note" ("noteId", "userId") `, + ); + await queryRunner.query( + `ALTER TABLE "user_profile" ADD "enableWordMute" boolean NOT NULL DEFAULT false`, + ); + await queryRunner.query( + `ALTER TABLE "user_profile" ADD "mutedWords" jsonb NOT NULL DEFAULT '[]'`, + ); + await queryRunner.query( + `CREATE INDEX "IDX_3befe6f999c86aff06eb0257b4" ON "user_profile" ("enableWordMute") `, + ); + await queryRunner.query( + `ALTER TABLE "muted_note" ADD CONSTRAINT "FK_70ab9786313d78e4201d81cdb89" FOREIGN KEY ("noteId") REFERENCES "note"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "muted_note" ADD CONSTRAINT "FK_d8e07aa18c2d64e86201601aec1" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + } + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "muted_note" DROP CONSTRAINT "FK_d8e07aa18c2d64e86201601aec1"`, + ); + await queryRunner.query( + `ALTER TABLE "muted_note" DROP CONSTRAINT "FK_70ab9786313d78e4201d81cdb89"`, + ); + await queryRunner.query(`DROP INDEX "IDX_3befe6f999c86aff06eb0257b4"`); + await queryRunner.query( + `ALTER TABLE "user_profile" DROP COLUMN "mutedWords"`, + ); + await queryRunner.query( + `ALTER TABLE "user_profile" DROP COLUMN "enableWordMute"`, + ); + await queryRunner.query(`DROP INDEX "IDX_a8c6bfd637d3f1d67a27c48e27"`); + await queryRunner.query(`DROP INDEX "IDX_d8e07aa18c2d64e86201601aec"`); + await queryRunner.query(`DROP INDEX "IDX_70ab9786313d78e4201d81cdb8"`); + await queryRunner.query(`DROP TABLE "muted_note"`); + } } diff --git a/packages/backend/migration/1595782306083-word-mute2.js b/packages/backend/migration/1595782306083-word-mute2.js index ab1e40a041..61f06b3474 100644 --- a/packages/backend/migration/1595782306083-word-mute2.js +++ b/packages/backend/migration/1595782306083-word-mute2.js @@ -1,17 +1,21 @@ - - export class wordMute21595782306083 { - constructor() { - this.name = 'wordMute21595782306083'; - } - async up(queryRunner) { - await queryRunner.query(`CREATE TYPE "muted_note_reason_enum" AS ENUM('word', 'manual', 'spam', 'other')`); - await queryRunner.query(`ALTER TABLE "muted_note" ADD "reason" "muted_note_reason_enum" NOT NULL`); - await queryRunner.query(`CREATE INDEX "IDX_636e977ff90b23676fb5624b25" ON "muted_note" ("reason") `); - } - async down(queryRunner) { - await queryRunner.query(`DROP INDEX "IDX_636e977ff90b23676fb5624b25"`); - await queryRunner.query(`ALTER TABLE "muted_note" DROP COLUMN "reason"`); - await queryRunner.query(`DROP TYPE "muted_note_reason_enum"`); - } + constructor() { + this.name = "wordMute21595782306083"; + } + async up(queryRunner) { + await queryRunner.query( + `CREATE TYPE "muted_note_reason_enum" AS ENUM('word', 'manual', 'spam', 'other')`, + ); + await queryRunner.query( + `ALTER TABLE "muted_note" ADD "reason" "muted_note_reason_enum" NOT NULL`, + ); + await queryRunner.query( + `CREATE INDEX "IDX_636e977ff90b23676fb5624b25" ON "muted_note" ("reason") `, + ); + } + async down(queryRunner) { + await queryRunner.query(`DROP INDEX "IDX_636e977ff90b23676fb5624b25"`); + await queryRunner.query(`ALTER TABLE "muted_note" DROP COLUMN "reason"`); + await queryRunner.query(`DROP TYPE "muted_note_reason_enum"`); + } } diff --git a/packages/backend/migration/1596548170836-channel.js b/packages/backend/migration/1596548170836-channel.js index 242db7d45a..2aaaf95503 100644 --- a/packages/backend/migration/1596548170836-channel.js +++ b/packages/backend/migration/1596548170836-channel.js @@ -1,57 +1,115 @@ - - export class channel1596548170836 { - constructor() { - this.name = 'channel1596548170836'; - } - async up(queryRunner) { - await queryRunner.query(`CREATE TABLE "channel" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "lastNotedAt" TIMESTAMP WITH TIME ZONE, "userId" character varying(32) NOT NULL, "name" character varying(128) NOT NULL, "description" character varying(2048), "bannerId" character varying(32), "notesCount" integer NOT NULL DEFAULT 0, "usersCount" integer NOT NULL DEFAULT 0, CONSTRAINT "PK_590f33ee6ee7d76437acf362e39" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE INDEX "IDX_71cb7b435b7c0d4843317e7e16" ON "channel" ("createdAt") `); - await queryRunner.query(`CREATE INDEX "IDX_29ef80c6f13bcea998447fce43" ON "channel" ("lastNotedAt") `); - await queryRunner.query(`CREATE INDEX "IDX_823bae55bd81b3be6e05cff438" ON "channel" ("userId") `); - await queryRunner.query(`CREATE INDEX "IDX_0f58c11241e649d2a638a8de94" ON "channel" ("notesCount") `); - await queryRunner.query(`CREATE INDEX "IDX_094b86cd36bb805d1aa1e8cc9a" ON "channel" ("usersCount") `); - await queryRunner.query(`CREATE TABLE "channel_following" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "followeeId" character varying(32) NOT NULL, "followerId" character varying(32) NOT NULL, CONSTRAINT "PK_8b104be7f7415113f2a02cd5bdd" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE INDEX "IDX_11e71f2511589dcc8a4d3214f9" ON "channel_following" ("createdAt") `); - await queryRunner.query(`CREATE INDEX "IDX_0e43068c3f92cab197c3d3cd86" ON "channel_following" ("followeeId") `); - await queryRunner.query(`CREATE INDEX "IDX_6d8084ec9496e7334a4602707e" ON "channel_following" ("followerId") `); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_2e230dd45a10e671d781d99f3e" ON "channel_following" ("followerId", "followeeId") `); - await queryRunner.query(`CREATE TABLE "channel_note_pining" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "channelId" character varying(32) NOT NULL, "noteId" character varying(32) NOT NULL, CONSTRAINT "PK_44f7474496bcf2e4b741681146d" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE INDEX "IDX_8125f950afd3093acb10d2db8a" ON "channel_note_pining" ("channelId") `); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_f36fed37d6d4cdcc68c803cd9c" ON "channel_note_pining" ("channelId", "noteId") `); - await queryRunner.query(`ALTER TABLE "note" ADD "channelId" character varying(32) DEFAULT null`); - await queryRunner.query(`CREATE INDEX "IDX_f22169eb10657bded6d875ac8f" ON "note" ("channelId") `); - await queryRunner.query(`ALTER TABLE "channel" ADD CONSTRAINT "FK_823bae55bd81b3be6e05cff4383" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE SET NULL ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "channel" ADD CONSTRAINT "FK_999da2bcc7efadbfe0e92d3bc19" FOREIGN KEY ("bannerId") REFERENCES "drive_file"("id") ON DELETE SET NULL ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "note" ADD CONSTRAINT "FK_f22169eb10657bded6d875ac8f9" FOREIGN KEY ("channelId") REFERENCES "channel"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "channel_following" ADD CONSTRAINT "FK_0e43068c3f92cab197c3d3cd86e" FOREIGN KEY ("followeeId") REFERENCES "channel"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "channel_following" ADD CONSTRAINT "FK_6d8084ec9496e7334a4602707e1" FOREIGN KEY ("followerId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "channel_note_pining" ADD CONSTRAINT "FK_8125f950afd3093acb10d2db8a8" FOREIGN KEY ("channelId") REFERENCES "channel"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "channel_note_pining" ADD CONSTRAINT "FK_10b19ef67d297ea9de325cd4502" FOREIGN KEY ("noteId") REFERENCES "note"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "channel_note_pining" DROP CONSTRAINT "FK_10b19ef67d297ea9de325cd4502"`); - await queryRunner.query(`ALTER TABLE "channel_note_pining" DROP CONSTRAINT "FK_8125f950afd3093acb10d2db8a8"`); - await queryRunner.query(`ALTER TABLE "channel_following" DROP CONSTRAINT "FK_6d8084ec9496e7334a4602707e1"`); - await queryRunner.query(`ALTER TABLE "channel_following" DROP CONSTRAINT "FK_0e43068c3f92cab197c3d3cd86e"`); - await queryRunner.query(`ALTER TABLE "note" DROP CONSTRAINT "FK_f22169eb10657bded6d875ac8f9"`); - await queryRunner.query(`ALTER TABLE "channel" DROP CONSTRAINT "FK_999da2bcc7efadbfe0e92d3bc19"`); - await queryRunner.query(`ALTER TABLE "channel" DROP CONSTRAINT "FK_823bae55bd81b3be6e05cff4383"`); - await queryRunner.query(`DROP INDEX "IDX_f22169eb10657bded6d875ac8f"`); - await queryRunner.query(`ALTER TABLE "note" DROP COLUMN "channelId"`); - await queryRunner.query(`DROP INDEX "IDX_f36fed37d6d4cdcc68c803cd9c"`); - await queryRunner.query(`DROP INDEX "IDX_8125f950afd3093acb10d2db8a"`); - await queryRunner.query(`DROP TABLE "channel_note_pining"`); - await queryRunner.query(`DROP INDEX "IDX_2e230dd45a10e671d781d99f3e"`); - await queryRunner.query(`DROP INDEX "IDX_6d8084ec9496e7334a4602707e"`); - await queryRunner.query(`DROP INDEX "IDX_0e43068c3f92cab197c3d3cd86"`); - await queryRunner.query(`DROP INDEX "IDX_11e71f2511589dcc8a4d3214f9"`); - await queryRunner.query(`DROP TABLE "channel_following"`); - await queryRunner.query(`DROP INDEX "IDX_094b86cd36bb805d1aa1e8cc9a"`); - await queryRunner.query(`DROP INDEX "IDX_0f58c11241e649d2a638a8de94"`); - await queryRunner.query(`DROP INDEX "IDX_823bae55bd81b3be6e05cff438"`); - await queryRunner.query(`DROP INDEX "IDX_29ef80c6f13bcea998447fce43"`); - await queryRunner.query(`DROP INDEX "IDX_71cb7b435b7c0d4843317e7e16"`); - await queryRunner.query(`DROP TABLE "channel"`); - } + constructor() { + this.name = "channel1596548170836"; + } + async up(queryRunner) { + await queryRunner.query( + `CREATE TABLE "channel" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "lastNotedAt" TIMESTAMP WITH TIME ZONE, "userId" character varying(32) NOT NULL, "name" character varying(128) NOT NULL, "description" character varying(2048), "bannerId" character varying(32), "notesCount" integer NOT NULL DEFAULT 0, "usersCount" integer NOT NULL DEFAULT 0, CONSTRAINT "PK_590f33ee6ee7d76437acf362e39" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE INDEX "IDX_71cb7b435b7c0d4843317e7e16" ON "channel" ("createdAt") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_29ef80c6f13bcea998447fce43" ON "channel" ("lastNotedAt") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_823bae55bd81b3be6e05cff438" ON "channel" ("userId") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_0f58c11241e649d2a638a8de94" ON "channel" ("notesCount") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_094b86cd36bb805d1aa1e8cc9a" ON "channel" ("usersCount") `, + ); + await queryRunner.query( + `CREATE TABLE "channel_following" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "followeeId" character varying(32) NOT NULL, "followerId" character varying(32) NOT NULL, CONSTRAINT "PK_8b104be7f7415113f2a02cd5bdd" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE INDEX "IDX_11e71f2511589dcc8a4d3214f9" ON "channel_following" ("createdAt") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_0e43068c3f92cab197c3d3cd86" ON "channel_following" ("followeeId") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_6d8084ec9496e7334a4602707e" ON "channel_following" ("followerId") `, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_2e230dd45a10e671d781d99f3e" ON "channel_following" ("followerId", "followeeId") `, + ); + await queryRunner.query( + `CREATE TABLE "channel_note_pining" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "channelId" character varying(32) NOT NULL, "noteId" character varying(32) NOT NULL, CONSTRAINT "PK_44f7474496bcf2e4b741681146d" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE INDEX "IDX_8125f950afd3093acb10d2db8a" ON "channel_note_pining" ("channelId") `, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_f36fed37d6d4cdcc68c803cd9c" ON "channel_note_pining" ("channelId", "noteId") `, + ); + await queryRunner.query( + `ALTER TABLE "note" ADD "channelId" character varying(32) DEFAULT null`, + ); + await queryRunner.query( + `CREATE INDEX "IDX_f22169eb10657bded6d875ac8f" ON "note" ("channelId") `, + ); + await queryRunner.query( + `ALTER TABLE "channel" ADD CONSTRAINT "FK_823bae55bd81b3be6e05cff4383" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE SET NULL ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "channel" ADD CONSTRAINT "FK_999da2bcc7efadbfe0e92d3bc19" FOREIGN KEY ("bannerId") REFERENCES "drive_file"("id") ON DELETE SET NULL ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "note" ADD CONSTRAINT "FK_f22169eb10657bded6d875ac8f9" FOREIGN KEY ("channelId") REFERENCES "channel"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "channel_following" ADD CONSTRAINT "FK_0e43068c3f92cab197c3d3cd86e" FOREIGN KEY ("followeeId") REFERENCES "channel"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "channel_following" ADD CONSTRAINT "FK_6d8084ec9496e7334a4602707e1" FOREIGN KEY ("followerId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "channel_note_pining" ADD CONSTRAINT "FK_8125f950afd3093acb10d2db8a8" FOREIGN KEY ("channelId") REFERENCES "channel"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "channel_note_pining" ADD CONSTRAINT "FK_10b19ef67d297ea9de325cd4502" FOREIGN KEY ("noteId") REFERENCES "note"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + } + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "channel_note_pining" DROP CONSTRAINT "FK_10b19ef67d297ea9de325cd4502"`, + ); + await queryRunner.query( + `ALTER TABLE "channel_note_pining" DROP CONSTRAINT "FK_8125f950afd3093acb10d2db8a8"`, + ); + await queryRunner.query( + `ALTER TABLE "channel_following" DROP CONSTRAINT "FK_6d8084ec9496e7334a4602707e1"`, + ); + await queryRunner.query( + `ALTER TABLE "channel_following" DROP CONSTRAINT "FK_0e43068c3f92cab197c3d3cd86e"`, + ); + await queryRunner.query( + `ALTER TABLE "note" DROP CONSTRAINT "FK_f22169eb10657bded6d875ac8f9"`, + ); + await queryRunner.query( + `ALTER TABLE "channel" DROP CONSTRAINT "FK_999da2bcc7efadbfe0e92d3bc19"`, + ); + await queryRunner.query( + `ALTER TABLE "channel" DROP CONSTRAINT "FK_823bae55bd81b3be6e05cff4383"`, + ); + await queryRunner.query(`DROP INDEX "IDX_f22169eb10657bded6d875ac8f"`); + await queryRunner.query(`ALTER TABLE "note" DROP COLUMN "channelId"`); + await queryRunner.query(`DROP INDEX "IDX_f36fed37d6d4cdcc68c803cd9c"`); + await queryRunner.query(`DROP INDEX "IDX_8125f950afd3093acb10d2db8a"`); + await queryRunner.query(`DROP TABLE "channel_note_pining"`); + await queryRunner.query(`DROP INDEX "IDX_2e230dd45a10e671d781d99f3e"`); + await queryRunner.query(`DROP INDEX "IDX_6d8084ec9496e7334a4602707e"`); + await queryRunner.query(`DROP INDEX "IDX_0e43068c3f92cab197c3d3cd86"`); + await queryRunner.query(`DROP INDEX "IDX_11e71f2511589dcc8a4d3214f9"`); + await queryRunner.query(`DROP TABLE "channel_following"`); + await queryRunner.query(`DROP INDEX "IDX_094b86cd36bb805d1aa1e8cc9a"`); + await queryRunner.query(`DROP INDEX "IDX_0f58c11241e649d2a638a8de94"`); + await queryRunner.query(`DROP INDEX "IDX_823bae55bd81b3be6e05cff438"`); + await queryRunner.query(`DROP INDEX "IDX_29ef80c6f13bcea998447fce43"`); + await queryRunner.query(`DROP INDEX "IDX_71cb7b435b7c0d4843317e7e16"`); + await queryRunner.query(`DROP TABLE "channel"`); + } } diff --git a/packages/backend/migration/1596786425167-channel2.js b/packages/backend/migration/1596786425167-channel2.js index 4b17048fef..ae871f5ff6 100644 --- a/packages/backend/migration/1596786425167-channel2.js +++ b/packages/backend/migration/1596786425167-channel2.js @@ -1,13 +1,15 @@ - - export class channel21596786425167 { - constructor() { - this.name = 'channel21596786425167'; - } - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "channel_following" ADD "readCursor" TIMESTAMP WITH TIME ZONE NOT NULL`); - } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "channel_following" DROP COLUMN "readCursor"`); - } + constructor() { + this.name = "channel21596786425167"; + } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "channel_following" ADD "readCursor" TIMESTAMP WITH TIME ZONE NOT NULL`, + ); + } + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "channel_following" DROP COLUMN "readCursor"`, + ); + } } diff --git a/packages/backend/migration/1597230137744-objectStorageSetPublicRead.js b/packages/backend/migration/1597230137744-objectStorageSetPublicRead.js index 07283e31df..81540d5e8c 100644 --- a/packages/backend/migration/1597230137744-objectStorageSetPublicRead.js +++ b/packages/backend/migration/1597230137744-objectStorageSetPublicRead.js @@ -1,13 +1,15 @@ - - export class objectStorageSetPublicRead1597230137744 { - constructor() { - this.name = 'objectStorageSetPublicRead1597230137744'; - } - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "meta" ADD "objectStorageSetPublicRead" boolean NOT NULL DEFAULT false`); - } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "objectStorageSetPublicRead"`); - } + constructor() { + this.name = "objectStorageSetPublicRead1597230137744"; + } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "meta" ADD "objectStorageSetPublicRead" boolean NOT NULL DEFAULT false`, + ); + } + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "meta" DROP COLUMN "objectStorageSetPublicRead"`, + ); + } } diff --git a/packages/backend/migration/1597236229720-IncludingNotificationTypes.js b/packages/backend/migration/1597236229720-IncludingNotificationTypes.js index f498fa7d9a..ce02148d28 100644 --- a/packages/backend/migration/1597236229720-IncludingNotificationTypes.js +++ b/packages/backend/migration/1597236229720-IncludingNotificationTypes.js @@ -1,15 +1,21 @@ - - export class IncludingNotificationTypes1597236229720 { - constructor() { - this.name = 'IncludingNotificationTypes1597236229720'; - } - async up(queryRunner) { - await queryRunner.query(`CREATE TYPE "user_profile_includingnotificationtypes_enum" AS ENUM('follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'receiveFollowRequest', 'followRequestAccepted', 'groupInvited', 'app')`); - await queryRunner.query(`ALTER TABLE "user_profile" ADD "includingNotificationTypes" "user_profile_includingnotificationtypes_enum" array`); - } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "includingNotificationTypes"`); - await queryRunner.query(`DROP TYPE "user_profile_includingnotificationtypes_enum"`); - } + constructor() { + this.name = "IncludingNotificationTypes1597236229720"; + } + async up(queryRunner) { + await queryRunner.query( + `CREATE TYPE "user_profile_includingnotificationtypes_enum" AS ENUM('follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'receiveFollowRequest', 'followRequestAccepted', 'groupInvited', 'app')`, + ); + await queryRunner.query( + `ALTER TABLE "user_profile" ADD "includingNotificationTypes" "user_profile_includingnotificationtypes_enum" array`, + ); + } + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "user_profile" DROP COLUMN "includingNotificationTypes"`, + ); + await queryRunner.query( + `DROP TYPE "user_profile_includingnotificationtypes_enum"`, + ); + } } diff --git a/packages/backend/migration/1597385880794-add-sensitive-index.js b/packages/backend/migration/1597385880794-add-sensitive-index.js index 8c5c040ba0..d7c818bbfe 100644 --- a/packages/backend/migration/1597385880794-add-sensitive-index.js +++ b/packages/backend/migration/1597385880794-add-sensitive-index.js @@ -1,13 +1,13 @@ - - export class addSensitiveIndex1597385880794 { - constructor() { - this.name = 'addSensitiveIndex1597385880794'; - } - async up(queryRunner) { - await queryRunner.query(`CREATE INDEX "IDX_a7eba67f8b3fa27271e85d2e26" ON "drive_file" ("isSensitive") `); - } - async down(queryRunner) { - await queryRunner.query(`DROP INDEX "IDX_a7eba67f8b3fa27271e85d2e26"`); - } + constructor() { + this.name = "addSensitiveIndex1597385880794"; + } + async up(queryRunner) { + await queryRunner.query( + `CREATE INDEX "IDX_a7eba67f8b3fa27271e85d2e26" ON "drive_file" ("isSensitive") `, + ); + } + async down(queryRunner) { + await queryRunner.query(`DROP INDEX "IDX_a7eba67f8b3fa27271e85d2e26"`); + } } diff --git a/packages/backend/migration/1597459042300-channel-unread.js b/packages/backend/migration/1597459042300-channel-unread.js index 3157ab7793..4975a84f92 100644 --- a/packages/backend/migration/1597459042300-channel-unread.js +++ b/packages/backend/migration/1597459042300-channel-unread.js @@ -1,26 +1,44 @@ - - export class channelUnread1597459042300 { - constructor() { - this.name = 'channelUnread1597459042300'; - } - async up(queryRunner) { - await queryRunner.query(`TRUNCATE TABLE "note_unread"`, undefined); - await queryRunner.query(`ALTER TABLE "channel_following" DROP COLUMN "readCursor"`); - await queryRunner.query(`ALTER TABLE "note_unread" ADD "isMentioned" boolean NOT NULL`); - await queryRunner.query(`ALTER TABLE "note_unread" ADD "noteChannelId" character varying(32)`); - await queryRunner.query(`CREATE INDEX "IDX_25b1dd384bec391b07b74b861c" ON "note_unread" ("isMentioned") `); - await queryRunner.query(`CREATE INDEX "IDX_89a29c9237b8c3b6b3cbb4cb30" ON "note_unread" ("isSpecified") `); - await queryRunner.query(`CREATE INDEX "IDX_29e8c1d579af54d4232939f994" ON "note_unread" ("noteUserId") `); - await queryRunner.query(`CREATE INDEX "IDX_6a57f051d82c6d4036c141e107" ON "note_unread" ("noteChannelId") `); - } - async down(queryRunner) { - await queryRunner.query(`DROP INDEX "IDX_6a57f051d82c6d4036c141e107"`); - await queryRunner.query(`DROP INDEX "IDX_29e8c1d579af54d4232939f994"`); - await queryRunner.query(`DROP INDEX "IDX_89a29c9237b8c3b6b3cbb4cb30"`); - await queryRunner.query(`DROP INDEX "IDX_25b1dd384bec391b07b74b861c"`); - await queryRunner.query(`ALTER TABLE "note_unread" DROP COLUMN "noteChannelId"`); - await queryRunner.query(`ALTER TABLE "note_unread" DROP COLUMN "isMentioned"`); - await queryRunner.query(`ALTER TABLE "channel_following" ADD "readCursor" TIMESTAMP WITH TIME ZONE NOT NULL`); - } + constructor() { + this.name = "channelUnread1597459042300"; + } + async up(queryRunner) { + await queryRunner.query(`TRUNCATE TABLE "note_unread"`, undefined); + await queryRunner.query( + `ALTER TABLE "channel_following" DROP COLUMN "readCursor"`, + ); + await queryRunner.query( + `ALTER TABLE "note_unread" ADD "isMentioned" boolean NOT NULL`, + ); + await queryRunner.query( + `ALTER TABLE "note_unread" ADD "noteChannelId" character varying(32)`, + ); + await queryRunner.query( + `CREATE INDEX "IDX_25b1dd384bec391b07b74b861c" ON "note_unread" ("isMentioned") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_89a29c9237b8c3b6b3cbb4cb30" ON "note_unread" ("isSpecified") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_29e8c1d579af54d4232939f994" ON "note_unread" ("noteUserId") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_6a57f051d82c6d4036c141e107" ON "note_unread" ("noteChannelId") `, + ); + } + async down(queryRunner) { + await queryRunner.query(`DROP INDEX "IDX_6a57f051d82c6d4036c141e107"`); + await queryRunner.query(`DROP INDEX "IDX_29e8c1d579af54d4232939f994"`); + await queryRunner.query(`DROP INDEX "IDX_89a29c9237b8c3b6b3cbb4cb30"`); + await queryRunner.query(`DROP INDEX "IDX_25b1dd384bec391b07b74b861c"`); + await queryRunner.query( + `ALTER TABLE "note_unread" DROP COLUMN "noteChannelId"`, + ); + await queryRunner.query( + `ALTER TABLE "note_unread" DROP COLUMN "isMentioned"`, + ); + await queryRunner.query( + `ALTER TABLE "channel_following" ADD "readCursor" TIMESTAMP WITH TIME ZONE NOT NULL`, + ); + } } diff --git a/packages/backend/migration/1597893996136-ChannelNoteIdDescIndex.js b/packages/backend/migration/1597893996136-ChannelNoteIdDescIndex.js index 2bd8aee358..5a31d51e8b 100644 --- a/packages/backend/migration/1597893996136-ChannelNoteIdDescIndex.js +++ b/packages/backend/migration/1597893996136-ChannelNoteIdDescIndex.js @@ -1,15 +1,17 @@ - - export class ChannelNoteIdDescIndex1597893996136 { - constructor() { - this.name = 'ChannelNoteIdDescIndex1597893996136'; - } - async up(queryRunner) { - await queryRunner.query(`DROP INDEX "IDX_f22169eb10657bded6d875ac8f"`); - await queryRunner.query(`CREATE INDEX "IDX_note_on_channelId_and_id_desc" ON "note" ("channelId", "id" desc)`); - } - async down(queryRunner) { - await queryRunner.query(`DROP INDEX "IDX_note_on_channelId_and_id_desc"`); - await queryRunner.query(`CREATE INDEX "IDX_f22169eb10657bded6d875ac8f" ON "note" ("channelId") `); - } + constructor() { + this.name = "ChannelNoteIdDescIndex1597893996136"; + } + async up(queryRunner) { + await queryRunner.query(`DROP INDEX "IDX_f22169eb10657bded6d875ac8f"`); + await queryRunner.query( + `CREATE INDEX "IDX_note_on_channelId_and_id_desc" ON "note" ("channelId", "id" desc)`, + ); + } + async down(queryRunner) { + await queryRunner.query(`DROP INDEX "IDX_note_on_channelId_and_id_desc"`); + await queryRunner.query( + `CREATE INDEX "IDX_f22169eb10657bded6d875ac8f" ON "note" ("channelId") `, + ); + } } diff --git a/packages/backend/migration/1600353287890-mutingNotificationTypes.js b/packages/backend/migration/1600353287890-mutingNotificationTypes.js index ed3eb7d146..2a317b3f9f 100644 --- a/packages/backend/migration/1600353287890-mutingNotificationTypes.js +++ b/packages/backend/migration/1600353287890-mutingNotificationTypes.js @@ -1,19 +1,33 @@ - - export class mutingNotificationTypes1600353287890 { - constructor() { - this.name = 'mutingNotificationTypes1600353287890'; - } - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "includingNotificationTypes"`); - await queryRunner.query(`DROP TYPE "public"."user_profile_includingnotificationtypes_enum"`); - await queryRunner.query(`CREATE TYPE "user_profile_mutingnotificationtypes_enum" AS ENUM('follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'receiveFollowRequest', 'followRequestAccepted', 'groupInvited', 'app')`); - await queryRunner.query(`ALTER TABLE "user_profile" ADD "mutingNotificationTypes" "user_profile_mutingnotificationtypes_enum" array NOT NULL DEFAULT '{}'`); - } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "mutingNotificationTypes"`); - await queryRunner.query(`DROP TYPE "user_profile_mutingnotificationtypes_enum"`); - await queryRunner.query(`CREATE TYPE "public"."user_profile_includingnotificationtypes_enum" AS ENUM('follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'receiveFollowRequest', 'followRequestAccepted', 'groupInvited', 'app')`); - await queryRunner.query(`ALTER TABLE "user_profile" ADD "includingNotificationTypes" "user_profile_includingnotificationtypes_enum" array`); - } + constructor() { + this.name = "mutingNotificationTypes1600353287890"; + } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "user_profile" DROP COLUMN "includingNotificationTypes"`, + ); + await queryRunner.query( + `DROP TYPE "public"."user_profile_includingnotificationtypes_enum"`, + ); + await queryRunner.query( + `CREATE TYPE "user_profile_mutingnotificationtypes_enum" AS ENUM('follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'receiveFollowRequest', 'followRequestAccepted', 'groupInvited', 'app')`, + ); + await queryRunner.query( + `ALTER TABLE "user_profile" ADD "mutingNotificationTypes" "user_profile_mutingnotificationtypes_enum" array NOT NULL DEFAULT '{}'`, + ); + } + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "user_profile" DROP COLUMN "mutingNotificationTypes"`, + ); + await queryRunner.query( + `DROP TYPE "user_profile_mutingnotificationtypes_enum"`, + ); + await queryRunner.query( + `CREATE TYPE "public"."user_profile_includingnotificationtypes_enum" AS ENUM('follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'receiveFollowRequest', 'followRequestAccepted', 'groupInvited', 'app')`, + ); + await queryRunner.query( + `ALTER TABLE "user_profile" ADD "includingNotificationTypes" "user_profile_includingnotificationtypes_enum" array`, + ); + } } diff --git a/packages/backend/migration/1603094348345-refine-abuse-user-report.js b/packages/backend/migration/1603094348345-refine-abuse-user-report.js index 4918032a2b..1aaf2685e5 100644 --- a/packages/backend/migration/1603094348345-refine-abuse-user-report.js +++ b/packages/backend/migration/1603094348345-refine-abuse-user-report.js @@ -1,31 +1,63 @@ - - export class refineAbuseUserReport1603094348345 { - constructor() { - this.name = 'refineAbuseUserReport1603094348345'; - } - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "abuse_user_report" DROP CONSTRAINT "FK_d049123c413e68ca52abe734203"`); - await queryRunner.query(`DROP INDEX "IDX_d049123c413e68ca52abe73420"`); - await queryRunner.query(`DROP INDEX "IDX_5cd442c3b2e74fdd99dae20243"`); - await queryRunner.query(`ALTER TABLE "abuse_user_report" RENAME COLUMN "userId" TO "targetUserId"`); - await queryRunner.query(`ALTER TABLE "abuse_user_report" ADD "assigneeId" character varying(32)`); - await queryRunner.query(`ALTER TABLE "abuse_user_report" ADD "resolved" boolean NOT NULL DEFAULT false`); - await queryRunner.query(`ALTER TABLE "abuse_user_report" DROP COLUMN "comment"`); - await queryRunner.query(`ALTER TABLE "abuse_user_report" ADD "comment" character varying(2048) NOT NULL DEFAULT '{}'::varchar[]`); - await queryRunner.query(`CREATE INDEX "IDX_2b15aaf4a0dc5be3499af7ab6a" ON "abuse_user_report" ("resolved") `); - await queryRunner.query(`ALTER TABLE "abuse_user_report" ADD CONSTRAINT "FK_08b883dd5fdd6f9c4c1572b36de" FOREIGN KEY ("assigneeId") REFERENCES "user"("id") ON DELETE SET NULL ON UPDATE NO ACTION`); - } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "abuse_user_report" DROP CONSTRAINT "FK_08b883dd5fdd6f9c4c1572b36de"`); - await queryRunner.query(`DROP INDEX "IDX_2b15aaf4a0dc5be3499af7ab6a"`); - await queryRunner.query(`ALTER TABLE "abuse_user_report" DROP COLUMN "comment"`); - await queryRunner.query(`ALTER TABLE "abuse_user_report" ADD "comment" character varying(512) NOT NULL DEFAULT '{}'::varchar[]`); - await queryRunner.query(`ALTER TABLE "abuse_user_report" DROP COLUMN "resolved"`); - await queryRunner.query(`ALTER TABLE "abuse_user_report" DROP COLUMN "assigneeId"`); - await queryRunner.query(`ALTER TABLE "abuse_user_report" RENAME COLUMN "targetUserId" TO "userId"`); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_5cd442c3b2e74fdd99dae20243" ON "abuse_user_report" ("userId", "reporterId") `); - await queryRunner.query(`CREATE INDEX "IDX_d049123c413e68ca52abe73420" ON "abuse_user_report" ("userId") `); - await queryRunner.query(`ALTER TABLE "abuse_user_report" ADD CONSTRAINT "FK_d049123c413e68ca52abe734203" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - } + constructor() { + this.name = "refineAbuseUserReport1603094348345"; + } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "abuse_user_report" DROP CONSTRAINT "FK_d049123c413e68ca52abe734203"`, + ); + await queryRunner.query(`DROP INDEX "IDX_d049123c413e68ca52abe73420"`); + await queryRunner.query(`DROP INDEX "IDX_5cd442c3b2e74fdd99dae20243"`); + await queryRunner.query( + `ALTER TABLE "abuse_user_report" RENAME COLUMN "userId" TO "targetUserId"`, + ); + await queryRunner.query( + `ALTER TABLE "abuse_user_report" ADD "assigneeId" character varying(32)`, + ); + await queryRunner.query( + `ALTER TABLE "abuse_user_report" ADD "resolved" boolean NOT NULL DEFAULT false`, + ); + await queryRunner.query( + `ALTER TABLE "abuse_user_report" DROP COLUMN "comment"`, + ); + await queryRunner.query( + `ALTER TABLE "abuse_user_report" ADD "comment" character varying(2048) NOT NULL DEFAULT '{}'::varchar[]`, + ); + await queryRunner.query( + `CREATE INDEX "IDX_2b15aaf4a0dc5be3499af7ab6a" ON "abuse_user_report" ("resolved") `, + ); + await queryRunner.query( + `ALTER TABLE "abuse_user_report" ADD CONSTRAINT "FK_08b883dd5fdd6f9c4c1572b36de" FOREIGN KEY ("assigneeId") REFERENCES "user"("id") ON DELETE SET NULL ON UPDATE NO ACTION`, + ); + } + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "abuse_user_report" DROP CONSTRAINT "FK_08b883dd5fdd6f9c4c1572b36de"`, + ); + await queryRunner.query(`DROP INDEX "IDX_2b15aaf4a0dc5be3499af7ab6a"`); + await queryRunner.query( + `ALTER TABLE "abuse_user_report" DROP COLUMN "comment"`, + ); + await queryRunner.query( + `ALTER TABLE "abuse_user_report" ADD "comment" character varying(512) NOT NULL DEFAULT '{}'::varchar[]`, + ); + await queryRunner.query( + `ALTER TABLE "abuse_user_report" DROP COLUMN "resolved"`, + ); + await queryRunner.query( + `ALTER TABLE "abuse_user_report" DROP COLUMN "assigneeId"`, + ); + await queryRunner.query( + `ALTER TABLE "abuse_user_report" RENAME COLUMN "targetUserId" TO "userId"`, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_5cd442c3b2e74fdd99dae20243" ON "abuse_user_report" ("userId", "reporterId") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_d049123c413e68ca52abe73420" ON "abuse_user_report" ("userId") `, + ); + await queryRunner.query( + `ALTER TABLE "abuse_user_report" ADD CONSTRAINT "FK_d049123c413e68ca52abe734203" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + } } diff --git a/packages/backend/migration/1603095701770-refine-abuse-user-report2.js b/packages/backend/migration/1603095701770-refine-abuse-user-report2.js index 64e92672f2..301a8e5382 100644 --- a/packages/backend/migration/1603095701770-refine-abuse-user-report2.js +++ b/packages/backend/migration/1603095701770-refine-abuse-user-report2.js @@ -1,19 +1,29 @@ - - export class refineAbuseUserReport21603095701770 { - constructor() { - this.name = 'refineAbuseUserReport21603095701770'; - } - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "abuse_user_report" ADD "targetUserHost" character varying(128)`); - await queryRunner.query(`ALTER TABLE "abuse_user_report" ADD "reporterHost" character varying(128)`); - await queryRunner.query(`CREATE INDEX "IDX_4ebbf7f93cdc10e8d1ef2fc6cd" ON "abuse_user_report" ("targetUserHost") `); - await queryRunner.query(`CREATE INDEX "IDX_f8d8b93740ad12c4ce8213a199" ON "abuse_user_report" ("reporterHost") `); - } - async down(queryRunner) { - await queryRunner.query(`DROP INDEX "IDX_f8d8b93740ad12c4ce8213a199"`); - await queryRunner.query(`DROP INDEX "IDX_4ebbf7f93cdc10e8d1ef2fc6cd"`); - await queryRunner.query(`ALTER TABLE "abuse_user_report" DROP COLUMN "reporterHost"`); - await queryRunner.query(`ALTER TABLE "abuse_user_report" DROP COLUMN "targetUserHost"`); - } + constructor() { + this.name = "refineAbuseUserReport21603095701770"; + } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "abuse_user_report" ADD "targetUserHost" character varying(128)`, + ); + await queryRunner.query( + `ALTER TABLE "abuse_user_report" ADD "reporterHost" character varying(128)`, + ); + await queryRunner.query( + `CREATE INDEX "IDX_4ebbf7f93cdc10e8d1ef2fc6cd" ON "abuse_user_report" ("targetUserHost") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_f8d8b93740ad12c4ce8213a199" ON "abuse_user_report" ("reporterHost") `, + ); + } + async down(queryRunner) { + await queryRunner.query(`DROP INDEX "IDX_f8d8b93740ad12c4ce8213a199"`); + await queryRunner.query(`DROP INDEX "IDX_4ebbf7f93cdc10e8d1ef2fc6cd"`); + await queryRunner.query( + `ALTER TABLE "abuse_user_report" DROP COLUMN "reporterHost"`, + ); + await queryRunner.query( + `ALTER TABLE "abuse_user_report" DROP COLUMN "targetUserHost"`, + ); + } } diff --git a/packages/backend/migration/1603776877564-instance-theme-color.js b/packages/backend/migration/1603776877564-instance-theme-color.js index 92440d3f64..9bb3d65c0e 100644 --- a/packages/backend/migration/1603776877564-instance-theme-color.js +++ b/packages/backend/migration/1603776877564-instance-theme-color.js @@ -1,13 +1,13 @@ - - export class instanceThemeColor1603776877564 { - constructor() { - this.name = 'instanceThemeColor1603776877564'; - } - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "instance" ADD "themeColor" character varying(64) DEFAULT null`); - } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "instance" DROP COLUMN "themeColor"`); - } + constructor() { + this.name = "instanceThemeColor1603776877564"; + } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "instance" ADD "themeColor" character varying(64) DEFAULT null`, + ); + } + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "instance" DROP COLUMN "themeColor"`); + } } diff --git a/packages/backend/migration/1603781553011-instance-favicon.js b/packages/backend/migration/1603781553011-instance-favicon.js index f607c49ffb..4b2442692f 100644 --- a/packages/backend/migration/1603781553011-instance-favicon.js +++ b/packages/backend/migration/1603781553011-instance-favicon.js @@ -1,13 +1,13 @@ - - export class instanceFavicon1603781553011 { - constructor() { - this.name = 'instanceFavicon1603781553011'; - } - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "instance" ADD "faviconUrl" character varying(256) DEFAULT null`); - } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "instance" DROP COLUMN "faviconUrl"`); - } + constructor() { + this.name = "instanceFavicon1603781553011"; + } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "instance" ADD "faviconUrl" character varying(256) DEFAULT null`, + ); + } + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "instance" DROP COLUMN "faviconUrl"`); + } } diff --git a/packages/backend/migration/1604821689616-delete-auto-watch.js b/packages/backend/migration/1604821689616-delete-auto-watch.js index 4706e8bae9..6a8333ce16 100644 --- a/packages/backend/migration/1604821689616-delete-auto-watch.js +++ b/packages/backend/migration/1604821689616-delete-auto-watch.js @@ -1,13 +1,15 @@ - - export class deleteAutoWatch1604821689616 { - constructor() { - this.name = 'deleteAutoWatch1604821689616'; - } - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "autoWatch"`); - } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "user_profile" ADD "autoWatch" boolean NOT NULL DEFAULT false`); - } + constructor() { + this.name = "deleteAutoWatch1604821689616"; + } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "user_profile" DROP COLUMN "autoWatch"`, + ); + } + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "user_profile" ADD "autoWatch" boolean NOT NULL DEFAULT false`, + ); + } } diff --git a/packages/backend/migration/1605408848373-clip-description.js b/packages/backend/migration/1605408848373-clip-description.js index edd5505b30..99cda65346 100644 --- a/packages/backend/migration/1605408848373-clip-description.js +++ b/packages/backend/migration/1605408848373-clip-description.js @@ -1,13 +1,13 @@ - - export class clipDescription1605408848373 { - constructor() { - this.name = 'clipDescription1605408848373'; - } - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "clip" ADD "description" character varying(2048) DEFAULT null`); - } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "clip" DROP COLUMN "description"`); - } + constructor() { + this.name = "clipDescription1605408848373"; + } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "clip" ADD "description" character varying(2048) DEFAULT null`, + ); + } + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "clip" DROP COLUMN "description"`); + } } diff --git a/packages/backend/migration/1605408971051-comments.js b/packages/backend/migration/1605408971051-comments.js index 400efd5e70..363fec5cdd 100644 --- a/packages/backend/migration/1605408971051-comments.js +++ b/packages/backend/migration/1605408971051-comments.js @@ -1,433 +1,1067 @@ - - export class comments1605408971051 { - constructor() { - this.name = 'comments1605408971051'; - } - async up(queryRunner) { - await queryRunner.query(`COMMENT ON COLUMN "log"."createdAt" IS 'The created date of the Log.'`); - await queryRunner.query(`COMMENT ON COLUMN "drive_folder"."createdAt" IS 'The created date of the DriveFolder.'`); - await queryRunner.query(`COMMENT ON COLUMN "drive_folder"."name" IS 'The name of the DriveFolder.'`); - await queryRunner.query(`COMMENT ON COLUMN "drive_folder"."userId" IS 'The owner ID.'`); - await queryRunner.query(`COMMENT ON COLUMN "drive_folder"."parentId" IS 'The parent folder ID. If null, it means the DriveFolder is located in root.'`); - await queryRunner.query(`COMMENT ON COLUMN "drive_file"."createdAt" IS 'The created date of the DriveFile.'`); - await queryRunner.query(`COMMENT ON COLUMN "drive_file"."userId" IS 'The owner ID.'`); - await queryRunner.query(`COMMENT ON COLUMN "drive_file"."userHost" IS 'The host of owner. It will be null if the user in local.'`); - await queryRunner.query(`COMMENT ON COLUMN "drive_file"."md5" IS 'The MD5 hash of the DriveFile.'`); - await queryRunner.query(`COMMENT ON COLUMN "drive_file"."name" IS 'The file name of the DriveFile.'`); - await queryRunner.query(`COMMENT ON COLUMN "drive_file"."type" IS 'The content type (MIME) of the DriveFile.'`); - await queryRunner.query(`COMMENT ON COLUMN "drive_file"."size" IS 'The file size (bytes) of the DriveFile.'`); - await queryRunner.query(`COMMENT ON COLUMN "drive_file"."comment" IS 'The comment of the DriveFile.'`); - await queryRunner.query(`COMMENT ON COLUMN "drive_file"."blurhash" IS 'The BlurHash string.'`); - await queryRunner.query(`COMMENT ON COLUMN "drive_file"."properties" IS 'The any properties of the DriveFile. For example, it includes image width/height.'`); - await queryRunner.query(`COMMENT ON COLUMN "drive_file"."url" IS 'The URL of the DriveFile.'`); - await queryRunner.query(`COMMENT ON COLUMN "drive_file"."thumbnailUrl" IS 'The URL of the thumbnail of the DriveFile.'`); - await queryRunner.query(`COMMENT ON COLUMN "drive_file"."webpublicUrl" IS 'The URL of the webpublic of the DriveFile.'`); - await queryRunner.query(`COMMENT ON COLUMN "drive_file"."uri" IS 'The URI of the DriveFile. it will be null when the DriveFile is local.'`); - await queryRunner.query(`COMMENT ON COLUMN "drive_file"."folderId" IS 'The parent folder ID. If null, it means the DriveFile is located in root.'`); - await queryRunner.query(`COMMENT ON COLUMN "drive_file"."isSensitive" IS 'Whether the DriveFile is NSFW.'`); - await queryRunner.query(`COMMENT ON COLUMN "drive_file"."isLink" IS 'Whether the DriveFile is direct link to remote server.'`); - await queryRunner.query(`COMMENT ON COLUMN "user"."createdAt" IS 'The created date of the User.'`); - await queryRunner.query(`COMMENT ON COLUMN "user"."updatedAt" IS 'The updated date of the User.'`); - await queryRunner.query(`COMMENT ON COLUMN "user"."username" IS 'The username of the User.'`); - await queryRunner.query(`COMMENT ON COLUMN "user"."usernameLower" IS 'The username (lowercased) of the User.'`); - await queryRunner.query(`COMMENT ON COLUMN "user"."name" IS 'The name of the User.'`); - await queryRunner.query(`COMMENT ON COLUMN "user"."followersCount" IS 'The count of followers.'`); - await queryRunner.query(`COMMENT ON COLUMN "user"."followingCount" IS 'The count of following.'`); - await queryRunner.query(`COMMENT ON COLUMN "user"."notesCount" IS 'The count of notes.'`); - await queryRunner.query(`COMMENT ON COLUMN "user"."avatarId" IS 'The ID of avatar DriveFile.'`); - await queryRunner.query(`COMMENT ON COLUMN "user"."bannerId" IS 'The ID of banner DriveFile.'`); - await queryRunner.query(`COMMENT ON COLUMN "user"."isSuspended" IS 'Whether the User is suspended.'`); - await queryRunner.query(`COMMENT ON COLUMN "user"."isSilenced" IS 'Whether the User is silenced.'`); - await queryRunner.query(`COMMENT ON COLUMN "user"."isLocked" IS 'Whether the User is locked.'`); - await queryRunner.query(`COMMENT ON COLUMN "user"."isBot" IS 'Whether the User is a bot.'`); - await queryRunner.query(`COMMENT ON COLUMN "user"."isCat" IS 'Whether the User is a cat.'`); - await queryRunner.query(`COMMENT ON COLUMN "user"."isAdmin" IS 'Whether the User is the admin.'`); - await queryRunner.query(`COMMENT ON COLUMN "user"."isModerator" IS 'Whether the User is a moderator.'`); - await queryRunner.query(`COMMENT ON COLUMN "user"."host" IS 'The host of the User. It will be null if the origin of the user is local.'`); - await queryRunner.query(`COMMENT ON COLUMN "user"."inbox" IS 'The inbox URL of the User. It will be null if the origin of the user is local.'`); - await queryRunner.query(`COMMENT ON COLUMN "user"."sharedInbox" IS 'The sharedInbox URL of the User. It will be null if the origin of the user is local.'`); - await queryRunner.query(`COMMENT ON COLUMN "user"."featured" IS 'The featured URL of the User. It will be null if the origin of the user is local.'`); - await queryRunner.query(`COMMENT ON COLUMN "user"."uri" IS 'The URI of the User. It will be null if the origin of the user is local.'`); - await queryRunner.query(`COMMENT ON COLUMN "user"."token" IS 'The native access token of the User. It will be null if the origin of the user is local.'`); - await queryRunner.query(`COMMENT ON COLUMN "app"."createdAt" IS 'The created date of the App.'`); - await queryRunner.query(`COMMENT ON COLUMN "app"."userId" IS 'The owner ID.'`); - await queryRunner.query(`COMMENT ON COLUMN "app"."secret" IS 'The secret key of the App.'`); - await queryRunner.query(`COMMENT ON COLUMN "app"."name" IS 'The name of the App.'`); - await queryRunner.query(`COMMENT ON COLUMN "app"."description" IS 'The description of the App.'`); - await queryRunner.query(`COMMENT ON COLUMN "app"."permission" IS 'The permission of the App.'`); - await queryRunner.query(`COMMENT ON COLUMN "app"."callbackUrl" IS 'The callbackUrl of the App.'`); - await queryRunner.query(`COMMENT ON COLUMN "access_token"."createdAt" IS 'The created date of the AccessToken.'`); - await queryRunner.query(`COMMENT ON COLUMN "access_token"."lastUsedAt" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "access_token"."session" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "access_token"."appId" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "access_token"."name" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "access_token"."description" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "access_token"."iconUrl" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "channel"."createdAt" IS 'The created date of the Channel.'`); - await queryRunner.query(`COMMENT ON COLUMN "channel"."userId" IS 'The owner ID.'`); - await queryRunner.query(`COMMENT ON COLUMN "channel"."name" IS 'The name of the Channel.'`); - await queryRunner.query(`COMMENT ON COLUMN "channel"."description" IS 'The description of the Channel.'`); - await queryRunner.query(`COMMENT ON COLUMN "channel"."bannerId" IS 'The ID of banner Channel.'`); - await queryRunner.query(`COMMENT ON COLUMN "channel"."notesCount" IS 'The count of notes.'`); - await queryRunner.query(`COMMENT ON COLUMN "channel"."usersCount" IS 'The count of users.'`); - await queryRunner.query(`COMMENT ON COLUMN "note"."createdAt" IS 'The created date of the Note.'`); - await queryRunner.query(`COMMENT ON COLUMN "note"."replyId" IS 'The ID of reply target.'`); - await queryRunner.query(`COMMENT ON COLUMN "note"."renoteId" IS 'The ID of renote target.'`); - await queryRunner.query(`COMMENT ON COLUMN "note"."userId" IS 'The ID of author.'`); - await queryRunner.query(`COMMENT ON COLUMN "note"."uri" IS 'The URI of a note. it will be null when the note is local.'`); - await queryRunner.query(`COMMENT ON COLUMN "note"."url" IS 'The human readable url of a note. it will be null when the note is local.'`); - await queryRunner.query(`COMMENT ON COLUMN "note"."channelId" IS 'The ID of source channel.'`); - await queryRunner.query(`COMMENT ON COLUMN "note"."userHost" IS '[Denormalized]'`); - await queryRunner.query(`COMMENT ON COLUMN "note"."replyUserId" IS '[Denormalized]'`); - await queryRunner.query(`COMMENT ON COLUMN "note"."replyUserHost" IS '[Denormalized]'`); - await queryRunner.query(`COMMENT ON COLUMN "note"."renoteUserId" IS '[Denormalized]'`); - await queryRunner.query(`COMMENT ON COLUMN "note"."renoteUserHost" IS '[Denormalized]'`); - await queryRunner.query(`COMMENT ON COLUMN "poll_vote"."createdAt" IS 'The created date of the PollVote.'`); - await queryRunner.query(`COMMENT ON COLUMN "note_reaction"."createdAt" IS 'The created date of the NoteReaction.'`); - await queryRunner.query(`COMMENT ON COLUMN "note_watching"."createdAt" IS 'The created date of the NoteWatching.'`); - await queryRunner.query(`COMMENT ON COLUMN "note_watching"."userId" IS 'The watcher ID.'`); - await queryRunner.query(`COMMENT ON COLUMN "note_watching"."noteId" IS 'The target Note ID.'`); - await queryRunner.query(`COMMENT ON COLUMN "note_watching"."noteUserId" IS '[Denormalized]'`); - await queryRunner.query(`COMMENT ON COLUMN "note_unread"."noteUserId" IS '[Denormalized]'`); - await queryRunner.query(`COMMENT ON COLUMN "note_unread"."noteChannelId" IS '[Denormalized]'`); - await queryRunner.query(`COMMENT ON COLUMN "follow_request"."createdAt" IS 'The created date of the FollowRequest.'`); - await queryRunner.query(`COMMENT ON COLUMN "follow_request"."followeeId" IS 'The followee user ID.'`); - await queryRunner.query(`COMMENT ON COLUMN "follow_request"."followerId" IS 'The follower user ID.'`); - await queryRunner.query(`COMMENT ON COLUMN "follow_request"."requestId" IS 'id of Follow Activity.'`); - await queryRunner.query(`COMMENT ON COLUMN "follow_request"."followerHost" IS '[Denormalized]'`); - await queryRunner.query(`COMMENT ON COLUMN "follow_request"."followerInbox" IS '[Denormalized]'`); - await queryRunner.query(`COMMENT ON COLUMN "follow_request"."followerSharedInbox" IS '[Denormalized]'`); - await queryRunner.query(`COMMENT ON COLUMN "follow_request"."followeeHost" IS '[Denormalized]'`); - await queryRunner.query(`COMMENT ON COLUMN "follow_request"."followeeInbox" IS '[Denormalized]'`); - await queryRunner.query(`COMMENT ON COLUMN "follow_request"."followeeSharedInbox" IS '[Denormalized]'`); - await queryRunner.query(`COMMENT ON COLUMN "user_group"."createdAt" IS 'The created date of the UserGroup.'`); - await queryRunner.query(`COMMENT ON COLUMN "user_group"."userId" IS 'The ID of owner.'`); - await queryRunner.query(`COMMENT ON COLUMN "user_group_invitation"."createdAt" IS 'The created date of the UserGroupInvitation.'`); - await queryRunner.query(`COMMENT ON COLUMN "user_group_invitation"."userId" IS 'The user ID.'`); - await queryRunner.query(`COMMENT ON COLUMN "user_group_invitation"."userGroupId" IS 'The group ID.'`); - await queryRunner.query(`COMMENT ON COLUMN "notification"."createdAt" IS 'The created date of the Notification.'`); - await queryRunner.query(`COMMENT ON COLUMN "notification"."notifieeId" IS 'The ID of recipient user of the Notification.'`); - await queryRunner.query(`COMMENT ON COLUMN "notification"."isRead" IS 'Whether the Notification is read.'`); - await queryRunner.query(`COMMENT ON COLUMN "meta"."localDriveCapacityMb" IS 'Drive capacity of a local user (MB)'`); - await queryRunner.query(`COMMENT ON COLUMN "meta"."remoteDriveCapacityMb" IS 'Drive capacity of a remote user (MB)'`); - await queryRunner.query(`COMMENT ON COLUMN "meta"."maxNoteTextLength" IS 'Max allowed note text length in characters'`); - await queryRunner.query(`COMMENT ON COLUMN "following"."createdAt" IS 'The created date of the Following.'`); - await queryRunner.query(`COMMENT ON COLUMN "following"."followeeId" IS 'The followee user ID.'`); - await queryRunner.query(`COMMENT ON COLUMN "following"."followerId" IS 'The follower user ID.'`); - await queryRunner.query(`COMMENT ON COLUMN "following"."followerHost" IS '[Denormalized]'`); - await queryRunner.query(`COMMENT ON COLUMN "following"."followerInbox" IS '[Denormalized]'`); - await queryRunner.query(`COMMENT ON COLUMN "following"."followerSharedInbox" IS '[Denormalized]'`); - await queryRunner.query(`COMMENT ON COLUMN "following"."followeeHost" IS '[Denormalized]'`); - await queryRunner.query(`COMMENT ON COLUMN "following"."followeeInbox" IS '[Denormalized]'`); - await queryRunner.query(`COMMENT ON COLUMN "following"."followeeSharedInbox" IS '[Denormalized]'`); - await queryRunner.query(`COMMENT ON COLUMN "instance"."caughtAt" IS 'The caught date of the Instance.'`); - await queryRunner.query(`COMMENT ON COLUMN "instance"."host" IS 'The host of the Instance.'`); - await queryRunner.query(`COMMENT ON COLUMN "instance"."usersCount" IS 'The count of the users of the Instance.'`); - await queryRunner.query(`COMMENT ON COLUMN "instance"."notesCount" IS 'The count of the notes of the Instance.'`); - await queryRunner.query(`COMMENT ON COLUMN "instance"."softwareName" IS 'The software of the Instance.'`); - await queryRunner.query(`COMMENT ON COLUMN "instance"."softwareVersion" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "instance"."openRegistrations" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "instance"."name" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "instance"."description" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "instance"."maintainerName" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "instance"."maintainerEmail" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "instance"."iconUrl" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "instance"."faviconUrl" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "instance"."themeColor" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "muting"."createdAt" IS 'The created date of the Muting.'`); - await queryRunner.query(`COMMENT ON COLUMN "muting"."muteeId" IS 'The mutee user ID.'`); - await queryRunner.query(`COMMENT ON COLUMN "muting"."muterId" IS 'The muter user ID.'`); - await queryRunner.query(`COMMENT ON COLUMN "blocking"."createdAt" IS 'The created date of the Blocking.'`); - await queryRunner.query(`COMMENT ON COLUMN "blocking"."blockeeId" IS 'The blockee user ID.'`); - await queryRunner.query(`COMMENT ON COLUMN "blocking"."blockerId" IS 'The blocker user ID.'`); - await queryRunner.query(`COMMENT ON COLUMN "user_list"."createdAt" IS 'The created date of the UserList.'`); - await queryRunner.query(`COMMENT ON COLUMN "user_list"."userId" IS 'The owner ID.'`); - await queryRunner.query(`COMMENT ON COLUMN "user_list"."name" IS 'The name of the UserList.'`); - await queryRunner.query(`COMMENT ON COLUMN "user_list_joining"."createdAt" IS 'The created date of the UserListJoining.'`); - await queryRunner.query(`COMMENT ON COLUMN "user_list_joining"."userId" IS 'The user ID.'`); - await queryRunner.query(`COMMENT ON COLUMN "user_list_joining"."userListId" IS 'The list ID.'`); - await queryRunner.query(`COMMENT ON COLUMN "user_group_joining"."createdAt" IS 'The created date of the UserGroupJoining.'`); - await queryRunner.query(`COMMENT ON COLUMN "user_group_joining"."userId" IS 'The user ID.'`); - await queryRunner.query(`COMMENT ON COLUMN "user_group_joining"."userGroupId" IS 'The group ID.'`); - await queryRunner.query(`COMMENT ON COLUMN "note_favorite"."createdAt" IS 'The created date of the NoteFavorite.'`); - await queryRunner.query(`COMMENT ON COLUMN "abuse_user_report"."createdAt" IS 'The created date of the AbuseUserReport.'`); - await queryRunner.query(`COMMENT ON COLUMN "abuse_user_report"."targetUserHost" IS '[Denormalized]'`); - await queryRunner.query(`COMMENT ON COLUMN "abuse_user_report"."reporterHost" IS '[Denormalized]'`); - await queryRunner.query(`COMMENT ON COLUMN "messaging_message"."createdAt" IS 'The created date of the MessagingMessage.'`); - await queryRunner.query(`COMMENT ON COLUMN "messaging_message"."userId" IS 'The sender user ID.'`); - await queryRunner.query(`COMMENT ON COLUMN "messaging_message"."groupId" IS 'The recipient group ID.'`); - await queryRunner.query(`COMMENT ON COLUMN "signin"."createdAt" IS 'The created date of the Signin.'`); - await queryRunner.query(`COMMENT ON COLUMN "auth_session"."createdAt" IS 'The created date of the AuthSession.'`); - await queryRunner.query(`COMMENT ON COLUMN "reversi_game"."createdAt" IS 'The created date of the ReversiGame.'`); - await queryRunner.query(`COMMENT ON COLUMN "reversi_game"."startedAt" IS 'The started date of the ReversiGame.'`); - await queryRunner.query(`COMMENT ON COLUMN "reversi_game"."form1" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "reversi_game"."form2" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "reversi_matching"."createdAt" IS 'The created date of the ReversiMatching.'`); - await queryRunner.query(`COMMENT ON COLUMN "user_note_pining"."createdAt" IS 'The created date of the UserNotePinings.'`); - await queryRunner.query(`COMMENT ON COLUMN "poll"."noteId" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "poll"."noteVisibility" IS '[Denormalized]'`); - await queryRunner.query(`COMMENT ON COLUMN "poll"."userId" IS '[Denormalized]'`); - await queryRunner.query(`COMMENT ON COLUMN "poll"."userHost" IS '[Denormalized]'`); - await queryRunner.query(`COMMENT ON COLUMN "user_keypair"."userId" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "user_publickey"."userId" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "page"."createdAt" IS 'The created date of the Page.'`); - await queryRunner.query(`COMMENT ON COLUMN "page"."updatedAt" IS 'The updated date of the Page.'`); - await queryRunner.query(`COMMENT ON COLUMN "page"."userId" IS 'The ID of author.'`); - await queryRunner.query(`COMMENT ON COLUMN "user_profile"."userId" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "user_profile"."location" IS 'The location of the User.'`); - await queryRunner.query(`COMMENT ON COLUMN "user_profile"."birthday" IS 'The birthday (YYYY-MM-DD) of the User.'`); - await queryRunner.query(`COMMENT ON COLUMN "user_profile"."description" IS 'The description (bio) of the User.'`); - await queryRunner.query(`COMMENT ON COLUMN "user_profile"."url" IS 'Remote URL of the user.'`); - await queryRunner.query(`COMMENT ON COLUMN "user_profile"."email" IS 'The email address of the User.'`); - await queryRunner.query(`COMMENT ON COLUMN "user_profile"."password" IS 'The password hash of the User. It will be null if the origin of the user is local.'`); - await queryRunner.query(`COMMENT ON COLUMN "user_profile"."clientData" IS 'The client-specific data of the User.'`); - await queryRunner.query(`COMMENT ON COLUMN "user_profile"."room" IS 'The room data of the User.'`); - await queryRunner.query(`COMMENT ON COLUMN "user_profile"."userHost" IS '[Denormalized]'`); - await queryRunner.query(`COMMENT ON COLUMN "user_security_key"."id" IS 'Variable-length id given to navigator.credentials.get()'`); - await queryRunner.query(`COMMENT ON COLUMN "user_security_key"."publicKey" IS 'Variable-length public key used to verify attestations (hex-encoded).'`); - await queryRunner.query(`COMMENT ON COLUMN "user_security_key"."lastUsed" IS 'The date of the last time the UserSecurityKey was successfully validated.'`); - await queryRunner.query(`COMMENT ON COLUMN "user_security_key"."name" IS 'User-defined name for this key'`); - await queryRunner.query(`COMMENT ON COLUMN "attestation_challenge"."challenge" IS 'Hex-encoded sha256 hash of the challenge.'`); - await queryRunner.query(`COMMENT ON COLUMN "attestation_challenge"."createdAt" IS 'The date challenge was created for expiry purposes.'`); - await queryRunner.query(`COMMENT ON COLUMN "attestation_challenge"."registrationChallenge" IS 'Indicates that the challenge is only for registration purposes if true to prevent the challenge for being used as authentication.'`); - await queryRunner.query(`COMMENT ON COLUMN "moderation_log"."createdAt" IS 'The created date of the ModerationLog.'`); - await queryRunner.query(`COMMENT ON COLUMN "announcement"."createdAt" IS 'The created date of the Announcement.'`); - await queryRunner.query(`COMMENT ON COLUMN "announcement"."updatedAt" IS 'The updated date of the Announcement.'`); - await queryRunner.query(`COMMENT ON COLUMN "announcement_read"."createdAt" IS 'The created date of the AnnouncementRead.'`); - await queryRunner.query(`COMMENT ON COLUMN "clip"."createdAt" IS 'The created date of the Clip.'`); - await queryRunner.query(`COMMENT ON COLUMN "clip"."userId" IS 'The owner ID.'`); - await queryRunner.query(`COMMENT ON COLUMN "clip"."name" IS 'The name of the Clip.'`); - await queryRunner.query(`COMMENT ON COLUMN "clip"."description" IS 'The description of the Clip.'`); - await queryRunner.query(`COMMENT ON COLUMN "clip_note"."noteId" IS 'The note ID.'`); - await queryRunner.query(`COMMENT ON COLUMN "clip_note"."clipId" IS 'The clip ID.'`); - await queryRunner.query(`COMMENT ON COLUMN "antenna"."createdAt" IS 'The created date of the Antenna.'`); - await queryRunner.query(`COMMENT ON COLUMN "antenna"."userId" IS 'The owner ID.'`); - await queryRunner.query(`COMMENT ON COLUMN "antenna"."name" IS 'The name of the Antenna.'`); - await queryRunner.query(`COMMENT ON COLUMN "antenna_note"."noteId" IS 'The note ID.'`); - await queryRunner.query(`COMMENT ON COLUMN "antenna_note"."antennaId" IS 'The antenna ID.'`); - await queryRunner.query(`COMMENT ON COLUMN "promo_note"."noteId" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "promo_note"."userId" IS '[Denormalized]'`); - await queryRunner.query(`COMMENT ON COLUMN "promo_read"."createdAt" IS 'The created date of the PromoRead.'`); - await queryRunner.query(`COMMENT ON COLUMN "muted_note"."noteId" IS 'The note ID.'`); - await queryRunner.query(`COMMENT ON COLUMN "muted_note"."userId" IS 'The user ID.'`); - await queryRunner.query(`COMMENT ON COLUMN "muted_note"."reason" IS 'The reason of the MutedNote.'`); - await queryRunner.query(`COMMENT ON COLUMN "channel_following"."createdAt" IS 'The created date of the ChannelFollowing.'`); - await queryRunner.query(`COMMENT ON COLUMN "channel_following"."followeeId" IS 'The followee channel ID.'`); - await queryRunner.query(`COMMENT ON COLUMN "channel_following"."followerId" IS 'The follower user ID.'`); - await queryRunner.query(`COMMENT ON COLUMN "channel_note_pining"."createdAt" IS 'The created date of the ChannelNotePining.'`); - } - async down(queryRunner) { - await queryRunner.query(`COMMENT ON COLUMN "channel_note_pining"."createdAt" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "channel_following"."followerId" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "channel_following"."followeeId" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "channel_following"."createdAt" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "muted_note"."reason" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "muted_note"."userId" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "muted_note"."noteId" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "promo_read"."createdAt" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "promo_note"."userId" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "promo_note"."noteId" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "antenna_note"."antennaId" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "antenna_note"."noteId" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "antenna"."name" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "antenna"."userId" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "antenna"."createdAt" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "clip_note"."clipId" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "clip_note"."noteId" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "clip"."description" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "clip"."name" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "clip"."userId" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "clip"."createdAt" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "announcement_read"."createdAt" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "announcement"."updatedAt" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "announcement"."createdAt" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "moderation_log"."createdAt" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "attestation_challenge"."registrationChallenge" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "attestation_challenge"."createdAt" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "attestation_challenge"."challenge" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "user_security_key"."name" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "user_security_key"."lastUsed" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "user_security_key"."publicKey" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "user_security_key"."id" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "user_profile"."userHost" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "user_profile"."room" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "user_profile"."clientData" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "user_profile"."password" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "user_profile"."email" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "user_profile"."url" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "user_profile"."description" IS 'The description (bio) of the User.'`); - await queryRunner.query(`COMMENT ON COLUMN "user_profile"."birthday" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "user_profile"."location" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "user_profile"."userId" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "page"."userId" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "page"."updatedAt" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "page"."createdAt" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "user_publickey"."userId" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "user_keypair"."userId" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "poll"."userHost" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "poll"."userId" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "poll"."noteVisibility" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "poll"."noteId" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "user_note_pining"."createdAt" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "reversi_matching"."createdAt" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "reversi_game"."form2" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "reversi_game"."form1" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "reversi_game"."startedAt" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "reversi_game"."createdAt" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "auth_session"."createdAt" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "signin"."createdAt" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "messaging_message"."groupId" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "messaging_message"."userId" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "messaging_message"."createdAt" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "abuse_user_report"."reporterHost" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "abuse_user_report"."targetUserHost" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "abuse_user_report"."createdAt" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "note_favorite"."createdAt" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "user_group_joining"."userGroupId" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "user_group_joining"."userId" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "user_group_joining"."createdAt" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "user_list_joining"."userListId" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "user_list_joining"."userId" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "user_list_joining"."createdAt" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "user_list"."name" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "user_list"."userId" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "user_list"."createdAt" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "blocking"."blockerId" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "blocking"."blockeeId" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "blocking"."createdAt" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "muting"."muterId" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "muting"."muteeId" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "muting"."createdAt" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "instance"."themeColor" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "instance"."faviconUrl" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "instance"."iconUrl" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "instance"."maintainerEmail" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "instance"."maintainerName" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "instance"."description" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "instance"."name" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "instance"."openRegistrations" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "instance"."softwareVersion" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "instance"."softwareName" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "instance"."notesCount" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "instance"."usersCount" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "instance"."host" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "instance"."caughtAt" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "following"."followeeSharedInbox" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "following"."followeeInbox" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "following"."followeeHost" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "following"."followerSharedInbox" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "following"."followerInbox" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "following"."followerHost" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "following"."followerId" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "following"."followeeId" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "following"."createdAt" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "meta"."maxNoteTextLength" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "meta"."remoteDriveCapacityMb" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "meta"."localDriveCapacityMb" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "notification"."isRead" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "notification"."notifieeId" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "notification"."createdAt" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "user_group_invitation"."userGroupId" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "user_group_invitation"."userId" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "user_group_invitation"."createdAt" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "user_group"."userId" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "user_group"."createdAt" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "follow_request"."followeeSharedInbox" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "follow_request"."followeeInbox" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "follow_request"."followeeHost" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "follow_request"."followerSharedInbox" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "follow_request"."followerInbox" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "follow_request"."followerHost" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "follow_request"."requestId" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "follow_request"."followerId" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "follow_request"."followeeId" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "follow_request"."createdAt" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "note_unread"."noteChannelId" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "note_unread"."noteUserId" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "note_watching"."noteUserId" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "note_watching"."noteId" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "note_watching"."userId" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "note_watching"."createdAt" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "note_reaction"."createdAt" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "poll_vote"."createdAt" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "note"."renoteUserHost" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "note"."renoteUserId" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "note"."replyUserHost" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "note"."replyUserId" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "note"."userHost" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "note"."channelId" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "note"."url" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "note"."uri" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "note"."userId" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "note"."renoteId" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "note"."replyId" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "note"."createdAt" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "channel"."usersCount" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "channel"."notesCount" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "channel"."bannerId" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "channel"."description" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "channel"."name" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "channel"."userId" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "channel"."createdAt" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "access_token"."iconUrl" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "access_token"."description" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "access_token"."name" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "access_token"."appId" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "access_token"."session" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "access_token"."lastUsedAt" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "access_token"."createdAt" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "app"."callbackUrl" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "app"."permission" IS 'The permission of the App.'`); - await queryRunner.query(`COMMENT ON COLUMN "app"."description" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "app"."name" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "app"."secret" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "app"."userId" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "app"."createdAt" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "user"."token" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "user"."uri" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "user"."featured" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "user"."sharedInbox" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "user"."inbox" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "user"."host" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "user"."isModerator" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "user"."isAdmin" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "user"."isCat" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "user"."isBot" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "user"."isLocked" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "user"."isSilenced" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "user"."isSuspended" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "user"."bannerId" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "user"."avatarId" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "user"."notesCount" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "user"."followingCount" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "user"."followersCount" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "user"."name" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "user"."usernameLower" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "user"."username" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "user"."updatedAt" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "user"."createdAt" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "drive_file"."isLink" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "drive_file"."isSensitive" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "drive_file"."folderId" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "drive_file"."uri" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "drive_file"."webpublicUrl" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "drive_file"."thumbnailUrl" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "drive_file"."url" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "drive_file"."properties" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "drive_file"."blurhash" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "drive_file"."comment" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "drive_file"."size" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "drive_file"."type" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "drive_file"."name" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "drive_file"."md5" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "drive_file"."userHost" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "drive_file"."userId" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "drive_file"."createdAt" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "drive_folder"."parentId" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "drive_folder"."userId" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "drive_folder"."name" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "drive_folder"."createdAt" IS NULL`); - await queryRunner.query(`COMMENT ON COLUMN "log"."createdAt" IS NULL`); - } + constructor() { + this.name = "comments1605408971051"; + } + async up(queryRunner) { + await queryRunner.query( + `COMMENT ON COLUMN "log"."createdAt" IS 'The created date of the Log.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "drive_folder"."createdAt" IS 'The created date of the DriveFolder.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "drive_folder"."name" IS 'The name of the DriveFolder.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "drive_folder"."userId" IS 'The owner ID.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "drive_folder"."parentId" IS 'The parent folder ID. If null, it means the DriveFolder is located in root.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "drive_file"."createdAt" IS 'The created date of the DriveFile.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "drive_file"."userId" IS 'The owner ID.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "drive_file"."userHost" IS 'The host of owner. It will be null if the user in local.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "drive_file"."md5" IS 'The MD5 hash of the DriveFile.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "drive_file"."name" IS 'The file name of the DriveFile.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "drive_file"."type" IS 'The content type (MIME) of the DriveFile.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "drive_file"."size" IS 'The file size (bytes) of the DriveFile.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "drive_file"."comment" IS 'The comment of the DriveFile.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "drive_file"."blurhash" IS 'The BlurHash string.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "drive_file"."properties" IS 'The any properties of the DriveFile. For example, it includes image width/height.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "drive_file"."url" IS 'The URL of the DriveFile.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "drive_file"."thumbnailUrl" IS 'The URL of the thumbnail of the DriveFile.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "drive_file"."webpublicUrl" IS 'The URL of the webpublic of the DriveFile.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "drive_file"."uri" IS 'The URI of the DriveFile. it will be null when the DriveFile is local.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "drive_file"."folderId" IS 'The parent folder ID. If null, it means the DriveFile is located in root.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "drive_file"."isSensitive" IS 'Whether the DriveFile is NSFW.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "drive_file"."isLink" IS 'Whether the DriveFile is direct link to remote server.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "user"."createdAt" IS 'The created date of the User.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "user"."updatedAt" IS 'The updated date of the User.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "user"."username" IS 'The username of the User.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "user"."usernameLower" IS 'The username (lowercased) of the User.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "user"."name" IS 'The name of the User.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "user"."followersCount" IS 'The count of followers.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "user"."followingCount" IS 'The count of following.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "user"."notesCount" IS 'The count of notes.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "user"."avatarId" IS 'The ID of avatar DriveFile.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "user"."bannerId" IS 'The ID of banner DriveFile.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "user"."isSuspended" IS 'Whether the User is suspended.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "user"."isSilenced" IS 'Whether the User is silenced.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "user"."isLocked" IS 'Whether the User is locked.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "user"."isBot" IS 'Whether the User is a bot.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "user"."isCat" IS 'Whether the User is a cat.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "user"."isAdmin" IS 'Whether the User is the admin.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "user"."isModerator" IS 'Whether the User is a moderator.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "user"."host" IS 'The host of the User. It will be null if the origin of the user is local.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "user"."inbox" IS 'The inbox URL of the User. It will be null if the origin of the user is local.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "user"."sharedInbox" IS 'The sharedInbox URL of the User. It will be null if the origin of the user is local.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "user"."featured" IS 'The featured URL of the User. It will be null if the origin of the user is local.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "user"."uri" IS 'The URI of the User. It will be null if the origin of the user is local.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "user"."token" IS 'The native access token of the User. It will be null if the origin of the user is local.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "app"."createdAt" IS 'The created date of the App.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "app"."userId" IS 'The owner ID.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "app"."secret" IS 'The secret key of the App.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "app"."name" IS 'The name of the App.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "app"."description" IS 'The description of the App.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "app"."permission" IS 'The permission of the App.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "app"."callbackUrl" IS 'The callbackUrl of the App.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "access_token"."createdAt" IS 'The created date of the AccessToken.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "access_token"."lastUsedAt" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "access_token"."session" IS NULL`, + ); + await queryRunner.query(`COMMENT ON COLUMN "access_token"."appId" IS NULL`); + await queryRunner.query(`COMMENT ON COLUMN "access_token"."name" IS NULL`); + await queryRunner.query( + `COMMENT ON COLUMN "access_token"."description" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "access_token"."iconUrl" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "channel"."createdAt" IS 'The created date of the Channel.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "channel"."userId" IS 'The owner ID.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "channel"."name" IS 'The name of the Channel.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "channel"."description" IS 'The description of the Channel.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "channel"."bannerId" IS 'The ID of banner Channel.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "channel"."notesCount" IS 'The count of notes.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "channel"."usersCount" IS 'The count of users.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "note"."createdAt" IS 'The created date of the Note.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "note"."replyId" IS 'The ID of reply target.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "note"."renoteId" IS 'The ID of renote target.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "note"."userId" IS 'The ID of author.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "note"."uri" IS 'The URI of a note. it will be null when the note is local.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "note"."url" IS 'The human readable url of a note. it will be null when the note is local.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "note"."channelId" IS 'The ID of source channel.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "note"."userHost" IS '[Denormalized]'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "note"."replyUserId" IS '[Denormalized]'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "note"."replyUserHost" IS '[Denormalized]'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "note"."renoteUserId" IS '[Denormalized]'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "note"."renoteUserHost" IS '[Denormalized]'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "poll_vote"."createdAt" IS 'The created date of the PollVote.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "note_reaction"."createdAt" IS 'The created date of the NoteReaction.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "note_watching"."createdAt" IS 'The created date of the NoteWatching.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "note_watching"."userId" IS 'The watcher ID.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "note_watching"."noteId" IS 'The target Note ID.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "note_watching"."noteUserId" IS '[Denormalized]'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "note_unread"."noteUserId" IS '[Denormalized]'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "note_unread"."noteChannelId" IS '[Denormalized]'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "follow_request"."createdAt" IS 'The created date of the FollowRequest.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "follow_request"."followeeId" IS 'The followee user ID.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "follow_request"."followerId" IS 'The follower user ID.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "follow_request"."requestId" IS 'id of Follow Activity.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "follow_request"."followerHost" IS '[Denormalized]'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "follow_request"."followerInbox" IS '[Denormalized]'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "follow_request"."followerSharedInbox" IS '[Denormalized]'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "follow_request"."followeeHost" IS '[Denormalized]'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "follow_request"."followeeInbox" IS '[Denormalized]'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "follow_request"."followeeSharedInbox" IS '[Denormalized]'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "user_group"."createdAt" IS 'The created date of the UserGroup.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "user_group"."userId" IS 'The ID of owner.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "user_group_invitation"."createdAt" IS 'The created date of the UserGroupInvitation.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "user_group_invitation"."userId" IS 'The user ID.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "user_group_invitation"."userGroupId" IS 'The group ID.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "notification"."createdAt" IS 'The created date of the Notification.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "notification"."notifieeId" IS 'The ID of recipient user of the Notification.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "notification"."isRead" IS 'Whether the Notification is read.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "meta"."localDriveCapacityMb" IS 'Drive capacity of a local user (MB)'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "meta"."remoteDriveCapacityMb" IS 'Drive capacity of a remote user (MB)'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "meta"."maxNoteTextLength" IS 'Max allowed note text length in characters'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "following"."createdAt" IS 'The created date of the Following.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "following"."followeeId" IS 'The followee user ID.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "following"."followerId" IS 'The follower user ID.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "following"."followerHost" IS '[Denormalized]'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "following"."followerInbox" IS '[Denormalized]'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "following"."followerSharedInbox" IS '[Denormalized]'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "following"."followeeHost" IS '[Denormalized]'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "following"."followeeInbox" IS '[Denormalized]'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "following"."followeeSharedInbox" IS '[Denormalized]'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "instance"."caughtAt" IS 'The caught date of the Instance.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "instance"."host" IS 'The host of the Instance.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "instance"."usersCount" IS 'The count of the users of the Instance.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "instance"."notesCount" IS 'The count of the notes of the Instance.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "instance"."softwareName" IS 'The software of the Instance.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "instance"."softwareVersion" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "instance"."openRegistrations" IS NULL`, + ); + await queryRunner.query(`COMMENT ON COLUMN "instance"."name" IS NULL`); + await queryRunner.query( + `COMMENT ON COLUMN "instance"."description" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "instance"."maintainerName" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "instance"."maintainerEmail" IS NULL`, + ); + await queryRunner.query(`COMMENT ON COLUMN "instance"."iconUrl" IS NULL`); + await queryRunner.query( + `COMMENT ON COLUMN "instance"."faviconUrl" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "instance"."themeColor" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "muting"."createdAt" IS 'The created date of the Muting.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "muting"."muteeId" IS 'The mutee user ID.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "muting"."muterId" IS 'The muter user ID.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "blocking"."createdAt" IS 'The created date of the Blocking.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "blocking"."blockeeId" IS 'The blockee user ID.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "blocking"."blockerId" IS 'The blocker user ID.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "user_list"."createdAt" IS 'The created date of the UserList.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "user_list"."userId" IS 'The owner ID.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "user_list"."name" IS 'The name of the UserList.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "user_list_joining"."createdAt" IS 'The created date of the UserListJoining.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "user_list_joining"."userId" IS 'The user ID.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "user_list_joining"."userListId" IS 'The list ID.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "user_group_joining"."createdAt" IS 'The created date of the UserGroupJoining.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "user_group_joining"."userId" IS 'The user ID.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "user_group_joining"."userGroupId" IS 'The group ID.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "note_favorite"."createdAt" IS 'The created date of the NoteFavorite.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "abuse_user_report"."createdAt" IS 'The created date of the AbuseUserReport.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "abuse_user_report"."targetUserHost" IS '[Denormalized]'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "abuse_user_report"."reporterHost" IS '[Denormalized]'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "messaging_message"."createdAt" IS 'The created date of the MessagingMessage.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "messaging_message"."userId" IS 'The sender user ID.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "messaging_message"."groupId" IS 'The recipient group ID.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "signin"."createdAt" IS 'The created date of the Signin.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "auth_session"."createdAt" IS 'The created date of the AuthSession.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "reversi_game"."createdAt" IS 'The created date of the ReversiGame.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "reversi_game"."startedAt" IS 'The started date of the ReversiGame.'`, + ); + await queryRunner.query(`COMMENT ON COLUMN "reversi_game"."form1" IS NULL`); + await queryRunner.query(`COMMENT ON COLUMN "reversi_game"."form2" IS NULL`); + await queryRunner.query( + `COMMENT ON COLUMN "reversi_matching"."createdAt" IS 'The created date of the ReversiMatching.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "user_note_pining"."createdAt" IS 'The created date of the UserNotePinings.'`, + ); + await queryRunner.query(`COMMENT ON COLUMN "poll"."noteId" IS NULL`); + await queryRunner.query( + `COMMENT ON COLUMN "poll"."noteVisibility" IS '[Denormalized]'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "poll"."userId" IS '[Denormalized]'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "poll"."userHost" IS '[Denormalized]'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "user_keypair"."userId" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "user_publickey"."userId" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "page"."createdAt" IS 'The created date of the Page.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "page"."updatedAt" IS 'The updated date of the Page.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "page"."userId" IS 'The ID of author.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "user_profile"."userId" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "user_profile"."location" IS 'The location of the User.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "user_profile"."birthday" IS 'The birthday (YYYY-MM-DD) of the User.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "user_profile"."description" IS 'The description (bio) of the User.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "user_profile"."url" IS 'Remote URL of the user.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "user_profile"."email" IS 'The email address of the User.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "user_profile"."password" IS 'The password hash of the User. It will be null if the origin of the user is local.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "user_profile"."clientData" IS 'The client-specific data of the User.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "user_profile"."room" IS 'The room data of the User.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "user_profile"."userHost" IS '[Denormalized]'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "user_security_key"."id" IS 'Variable-length id given to navigator.credentials.get()'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "user_security_key"."publicKey" IS 'Variable-length public key used to verify attestations (hex-encoded).'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "user_security_key"."lastUsed" IS 'The date of the last time the UserSecurityKey was successfully validated.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "user_security_key"."name" IS 'User-defined name for this key'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "attestation_challenge"."challenge" IS 'Hex-encoded sha256 hash of the challenge.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "attestation_challenge"."createdAt" IS 'The date challenge was created for expiry purposes.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "attestation_challenge"."registrationChallenge" IS 'Indicates that the challenge is only for registration purposes if true to prevent the challenge for being used as authentication.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "moderation_log"."createdAt" IS 'The created date of the ModerationLog.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "announcement"."createdAt" IS 'The created date of the Announcement.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "announcement"."updatedAt" IS 'The updated date of the Announcement.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "announcement_read"."createdAt" IS 'The created date of the AnnouncementRead.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "clip"."createdAt" IS 'The created date of the Clip.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "clip"."userId" IS 'The owner ID.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "clip"."name" IS 'The name of the Clip.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "clip"."description" IS 'The description of the Clip.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "clip_note"."noteId" IS 'The note ID.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "clip_note"."clipId" IS 'The clip ID.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "antenna"."createdAt" IS 'The created date of the Antenna.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "antenna"."userId" IS 'The owner ID.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "antenna"."name" IS 'The name of the Antenna.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "antenna_note"."noteId" IS 'The note ID.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "antenna_note"."antennaId" IS 'The antenna ID.'`, + ); + await queryRunner.query(`COMMENT ON COLUMN "promo_note"."noteId" IS NULL`); + await queryRunner.query( + `COMMENT ON COLUMN "promo_note"."userId" IS '[Denormalized]'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "promo_read"."createdAt" IS 'The created date of the PromoRead.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "muted_note"."noteId" IS 'The note ID.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "muted_note"."userId" IS 'The user ID.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "muted_note"."reason" IS 'The reason of the MutedNote.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "channel_following"."createdAt" IS 'The created date of the ChannelFollowing.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "channel_following"."followeeId" IS 'The followee channel ID.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "channel_following"."followerId" IS 'The follower user ID.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "channel_note_pining"."createdAt" IS 'The created date of the ChannelNotePining.'`, + ); + } + async down(queryRunner) { + await queryRunner.query( + `COMMENT ON COLUMN "channel_note_pining"."createdAt" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "channel_following"."followerId" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "channel_following"."followeeId" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "channel_following"."createdAt" IS NULL`, + ); + await queryRunner.query(`COMMENT ON COLUMN "muted_note"."reason" IS NULL`); + await queryRunner.query(`COMMENT ON COLUMN "muted_note"."userId" IS NULL`); + await queryRunner.query(`COMMENT ON COLUMN "muted_note"."noteId" IS NULL`); + await queryRunner.query( + `COMMENT ON COLUMN "promo_read"."createdAt" IS NULL`, + ); + await queryRunner.query(`COMMENT ON COLUMN "promo_note"."userId" IS NULL`); + await queryRunner.query(`COMMENT ON COLUMN "promo_note"."noteId" IS NULL`); + await queryRunner.query( + `COMMENT ON COLUMN "antenna_note"."antennaId" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "antenna_note"."noteId" IS NULL`, + ); + await queryRunner.query(`COMMENT ON COLUMN "antenna"."name" IS NULL`); + await queryRunner.query(`COMMENT ON COLUMN "antenna"."userId" IS NULL`); + await queryRunner.query(`COMMENT ON COLUMN "antenna"."createdAt" IS NULL`); + await queryRunner.query(`COMMENT ON COLUMN "clip_note"."clipId" IS NULL`); + await queryRunner.query(`COMMENT ON COLUMN "clip_note"."noteId" IS NULL`); + await queryRunner.query(`COMMENT ON COLUMN "clip"."description" IS NULL`); + await queryRunner.query(`COMMENT ON COLUMN "clip"."name" IS NULL`); + await queryRunner.query(`COMMENT ON COLUMN "clip"."userId" IS NULL`); + await queryRunner.query(`COMMENT ON COLUMN "clip"."createdAt" IS NULL`); + await queryRunner.query( + `COMMENT ON COLUMN "announcement_read"."createdAt" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "announcement"."updatedAt" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "announcement"."createdAt" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "moderation_log"."createdAt" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "attestation_challenge"."registrationChallenge" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "attestation_challenge"."createdAt" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "attestation_challenge"."challenge" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "user_security_key"."name" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "user_security_key"."lastUsed" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "user_security_key"."publicKey" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "user_security_key"."id" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "user_profile"."userHost" IS NULL`, + ); + await queryRunner.query(`COMMENT ON COLUMN "user_profile"."room" IS NULL`); + await queryRunner.query( + `COMMENT ON COLUMN "user_profile"."clientData" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "user_profile"."password" IS NULL`, + ); + await queryRunner.query(`COMMENT ON COLUMN "user_profile"."email" IS NULL`); + await queryRunner.query(`COMMENT ON COLUMN "user_profile"."url" IS NULL`); + await queryRunner.query( + `COMMENT ON COLUMN "user_profile"."description" IS 'The description (bio) of the User.'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "user_profile"."birthday" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "user_profile"."location" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "user_profile"."userId" IS NULL`, + ); + await queryRunner.query(`COMMENT ON COLUMN "page"."userId" IS NULL`); + await queryRunner.query(`COMMENT ON COLUMN "page"."updatedAt" IS NULL`); + await queryRunner.query(`COMMENT ON COLUMN "page"."createdAt" IS NULL`); + await queryRunner.query( + `COMMENT ON COLUMN "user_publickey"."userId" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "user_keypair"."userId" IS NULL`, + ); + await queryRunner.query(`COMMENT ON COLUMN "poll"."userHost" IS NULL`); + await queryRunner.query(`COMMENT ON COLUMN "poll"."userId" IS NULL`); + await queryRunner.query( + `COMMENT ON COLUMN "poll"."noteVisibility" IS NULL`, + ); + await queryRunner.query(`COMMENT ON COLUMN "poll"."noteId" IS NULL`); + await queryRunner.query( + `COMMENT ON COLUMN "user_note_pining"."createdAt" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "reversi_matching"."createdAt" IS NULL`, + ); + await queryRunner.query(`COMMENT ON COLUMN "reversi_game"."form2" IS NULL`); + await queryRunner.query(`COMMENT ON COLUMN "reversi_game"."form1" IS NULL`); + await queryRunner.query( + `COMMENT ON COLUMN "reversi_game"."startedAt" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "reversi_game"."createdAt" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "auth_session"."createdAt" IS NULL`, + ); + await queryRunner.query(`COMMENT ON COLUMN "signin"."createdAt" IS NULL`); + await queryRunner.query( + `COMMENT ON COLUMN "messaging_message"."groupId" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "messaging_message"."userId" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "messaging_message"."createdAt" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "abuse_user_report"."reporterHost" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "abuse_user_report"."targetUserHost" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "abuse_user_report"."createdAt" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "note_favorite"."createdAt" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "user_group_joining"."userGroupId" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "user_group_joining"."userId" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "user_group_joining"."createdAt" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "user_list_joining"."userListId" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "user_list_joining"."userId" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "user_list_joining"."createdAt" IS NULL`, + ); + await queryRunner.query(`COMMENT ON COLUMN "user_list"."name" IS NULL`); + await queryRunner.query(`COMMENT ON COLUMN "user_list"."userId" IS NULL`); + await queryRunner.query( + `COMMENT ON COLUMN "user_list"."createdAt" IS NULL`, + ); + await queryRunner.query(`COMMENT ON COLUMN "blocking"."blockerId" IS NULL`); + await queryRunner.query(`COMMENT ON COLUMN "blocking"."blockeeId" IS NULL`); + await queryRunner.query(`COMMENT ON COLUMN "blocking"."createdAt" IS NULL`); + await queryRunner.query(`COMMENT ON COLUMN "muting"."muterId" IS NULL`); + await queryRunner.query(`COMMENT ON COLUMN "muting"."muteeId" IS NULL`); + await queryRunner.query(`COMMENT ON COLUMN "muting"."createdAt" IS NULL`); + await queryRunner.query( + `COMMENT ON COLUMN "instance"."themeColor" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "instance"."faviconUrl" IS NULL`, + ); + await queryRunner.query(`COMMENT ON COLUMN "instance"."iconUrl" IS NULL`); + await queryRunner.query( + `COMMENT ON COLUMN "instance"."maintainerEmail" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "instance"."maintainerName" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "instance"."description" IS NULL`, + ); + await queryRunner.query(`COMMENT ON COLUMN "instance"."name" IS NULL`); + await queryRunner.query( + `COMMENT ON COLUMN "instance"."openRegistrations" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "instance"."softwareVersion" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "instance"."softwareName" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "instance"."notesCount" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "instance"."usersCount" IS NULL`, + ); + await queryRunner.query(`COMMENT ON COLUMN "instance"."host" IS NULL`); + await queryRunner.query(`COMMENT ON COLUMN "instance"."caughtAt" IS NULL`); + await queryRunner.query( + `COMMENT ON COLUMN "following"."followeeSharedInbox" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "following"."followeeInbox" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "following"."followeeHost" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "following"."followerSharedInbox" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "following"."followerInbox" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "following"."followerHost" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "following"."followerId" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "following"."followeeId" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "following"."createdAt" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "meta"."maxNoteTextLength" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "meta"."remoteDriveCapacityMb" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "meta"."localDriveCapacityMb" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "notification"."isRead" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "notification"."notifieeId" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "notification"."createdAt" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "user_group_invitation"."userGroupId" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "user_group_invitation"."userId" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "user_group_invitation"."createdAt" IS NULL`, + ); + await queryRunner.query(`COMMENT ON COLUMN "user_group"."userId" IS NULL`); + await queryRunner.query( + `COMMENT ON COLUMN "user_group"."createdAt" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "follow_request"."followeeSharedInbox" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "follow_request"."followeeInbox" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "follow_request"."followeeHost" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "follow_request"."followerSharedInbox" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "follow_request"."followerInbox" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "follow_request"."followerHost" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "follow_request"."requestId" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "follow_request"."followerId" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "follow_request"."followeeId" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "follow_request"."createdAt" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "note_unread"."noteChannelId" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "note_unread"."noteUserId" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "note_watching"."noteUserId" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "note_watching"."noteId" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "note_watching"."userId" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "note_watching"."createdAt" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "note_reaction"."createdAt" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "poll_vote"."createdAt" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "note"."renoteUserHost" IS NULL`, + ); + await queryRunner.query(`COMMENT ON COLUMN "note"."renoteUserId" IS NULL`); + await queryRunner.query(`COMMENT ON COLUMN "note"."replyUserHost" IS NULL`); + await queryRunner.query(`COMMENT ON COLUMN "note"."replyUserId" IS NULL`); + await queryRunner.query(`COMMENT ON COLUMN "note"."userHost" IS NULL`); + await queryRunner.query(`COMMENT ON COLUMN "note"."channelId" IS NULL`); + await queryRunner.query(`COMMENT ON COLUMN "note"."url" IS NULL`); + await queryRunner.query(`COMMENT ON COLUMN "note"."uri" IS NULL`); + await queryRunner.query(`COMMENT ON COLUMN "note"."userId" IS NULL`); + await queryRunner.query(`COMMENT ON COLUMN "note"."renoteId" IS NULL`); + await queryRunner.query(`COMMENT ON COLUMN "note"."replyId" IS NULL`); + await queryRunner.query(`COMMENT ON COLUMN "note"."createdAt" IS NULL`); + await queryRunner.query(`COMMENT ON COLUMN "channel"."usersCount" IS NULL`); + await queryRunner.query(`COMMENT ON COLUMN "channel"."notesCount" IS NULL`); + await queryRunner.query(`COMMENT ON COLUMN "channel"."bannerId" IS NULL`); + await queryRunner.query( + `COMMENT ON COLUMN "channel"."description" IS NULL`, + ); + await queryRunner.query(`COMMENT ON COLUMN "channel"."name" IS NULL`); + await queryRunner.query(`COMMENT ON COLUMN "channel"."userId" IS NULL`); + await queryRunner.query(`COMMENT ON COLUMN "channel"."createdAt" IS NULL`); + await queryRunner.query( + `COMMENT ON COLUMN "access_token"."iconUrl" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "access_token"."description" IS NULL`, + ); + await queryRunner.query(`COMMENT ON COLUMN "access_token"."name" IS NULL`); + await queryRunner.query(`COMMENT ON COLUMN "access_token"."appId" IS NULL`); + await queryRunner.query( + `COMMENT ON COLUMN "access_token"."session" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "access_token"."lastUsedAt" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "access_token"."createdAt" IS NULL`, + ); + await queryRunner.query(`COMMENT ON COLUMN "app"."callbackUrl" IS NULL`); + await queryRunner.query( + `COMMENT ON COLUMN "app"."permission" IS 'The permission of the App.'`, + ); + await queryRunner.query(`COMMENT ON COLUMN "app"."description" IS NULL`); + await queryRunner.query(`COMMENT ON COLUMN "app"."name" IS NULL`); + await queryRunner.query(`COMMENT ON COLUMN "app"."secret" IS NULL`); + await queryRunner.query(`COMMENT ON COLUMN "app"."userId" IS NULL`); + await queryRunner.query(`COMMENT ON COLUMN "app"."createdAt" IS NULL`); + await queryRunner.query(`COMMENT ON COLUMN "user"."token" IS NULL`); + await queryRunner.query(`COMMENT ON COLUMN "user"."uri" IS NULL`); + await queryRunner.query(`COMMENT ON COLUMN "user"."featured" IS NULL`); + await queryRunner.query(`COMMENT ON COLUMN "user"."sharedInbox" IS NULL`); + await queryRunner.query(`COMMENT ON COLUMN "user"."inbox" IS NULL`); + await queryRunner.query(`COMMENT ON COLUMN "user"."host" IS NULL`); + await queryRunner.query(`COMMENT ON COLUMN "user"."isModerator" IS NULL`); + await queryRunner.query(`COMMENT ON COLUMN "user"."isAdmin" IS NULL`); + await queryRunner.query(`COMMENT ON COLUMN "user"."isCat" IS NULL`); + await queryRunner.query(`COMMENT ON COLUMN "user"."isBot" IS NULL`); + await queryRunner.query(`COMMENT ON COLUMN "user"."isLocked" IS NULL`); + await queryRunner.query(`COMMENT ON COLUMN "user"."isSilenced" IS NULL`); + await queryRunner.query(`COMMENT ON COLUMN "user"."isSuspended" IS NULL`); + await queryRunner.query(`COMMENT ON COLUMN "user"."bannerId" IS NULL`); + await queryRunner.query(`COMMENT ON COLUMN "user"."avatarId" IS NULL`); + await queryRunner.query(`COMMENT ON COLUMN "user"."notesCount" IS NULL`); + await queryRunner.query( + `COMMENT ON COLUMN "user"."followingCount" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "user"."followersCount" IS NULL`, + ); + await queryRunner.query(`COMMENT ON COLUMN "user"."name" IS NULL`); + await queryRunner.query(`COMMENT ON COLUMN "user"."usernameLower" IS NULL`); + await queryRunner.query(`COMMENT ON COLUMN "user"."username" IS NULL`); + await queryRunner.query(`COMMENT ON COLUMN "user"."updatedAt" IS NULL`); + await queryRunner.query(`COMMENT ON COLUMN "user"."createdAt" IS NULL`); + await queryRunner.query(`COMMENT ON COLUMN "drive_file"."isLink" IS NULL`); + await queryRunner.query( + `COMMENT ON COLUMN "drive_file"."isSensitive" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "drive_file"."folderId" IS NULL`, + ); + await queryRunner.query(`COMMENT ON COLUMN "drive_file"."uri" IS NULL`); + await queryRunner.query( + `COMMENT ON COLUMN "drive_file"."webpublicUrl" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "drive_file"."thumbnailUrl" IS NULL`, + ); + await queryRunner.query(`COMMENT ON COLUMN "drive_file"."url" IS NULL`); + await queryRunner.query( + `COMMENT ON COLUMN "drive_file"."properties" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "drive_file"."blurhash" IS NULL`, + ); + await queryRunner.query(`COMMENT ON COLUMN "drive_file"."comment" IS NULL`); + await queryRunner.query(`COMMENT ON COLUMN "drive_file"."size" IS NULL`); + await queryRunner.query(`COMMENT ON COLUMN "drive_file"."type" IS NULL`); + await queryRunner.query(`COMMENT ON COLUMN "drive_file"."name" IS NULL`); + await queryRunner.query(`COMMENT ON COLUMN "drive_file"."md5" IS NULL`); + await queryRunner.query( + `COMMENT ON COLUMN "drive_file"."userHost" IS NULL`, + ); + await queryRunner.query(`COMMENT ON COLUMN "drive_file"."userId" IS NULL`); + await queryRunner.query( + `COMMENT ON COLUMN "drive_file"."createdAt" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "drive_folder"."parentId" IS NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "drive_folder"."userId" IS NULL`, + ); + await queryRunner.query(`COMMENT ON COLUMN "drive_folder"."name" IS NULL`); + await queryRunner.query( + `COMMENT ON COLUMN "drive_folder"."createdAt" IS NULL`, + ); + await queryRunner.query(`COMMENT ON COLUMN "log"."createdAt" IS NULL`); + } } diff --git a/packages/backend/migration/1605585339718-instance-pinned-pages.js b/packages/backend/migration/1605585339718-instance-pinned-pages.js index 56ccd44c8e..09e1617641 100644 --- a/packages/backend/migration/1605585339718-instance-pinned-pages.js +++ b/packages/backend/migration/1605585339718-instance-pinned-pages.js @@ -1,13 +1,13 @@ - - export class instancePinnedPages1605585339718 { - constructor() { - this.name = 'instancePinnedPages1605585339718'; - } - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "meta" ADD "pinnedPages" character varying(512) array NOT NULL DEFAULT '{"/featured", "/channels", "/explore", "/pages", "/about-misskey"}'::varchar[]`); - } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "pinnedPages"`); - } + constructor() { + this.name = "instancePinnedPages1605585339718"; + } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "meta" ADD "pinnedPages" character varying(512) array NOT NULL DEFAULT '{"/featured", "/channels", "/explore", "/pages", "/about-misskey"}'::varchar[]`, + ); + } + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "pinnedPages"`); + } } diff --git a/packages/backend/migration/1605965516823-instance-images.js b/packages/backend/migration/1605965516823-instance-images.js index 710c75981d..ed9a26a655 100644 --- a/packages/backend/migration/1605965516823-instance-images.js +++ b/packages/backend/migration/1605965516823-instance-images.js @@ -1,15 +1,19 @@ - - export class instanceImages1605965516823 { - constructor() { - this.name = 'instanceImages1605965516823'; - } - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "meta" ADD "backgroundImageUrl" character varying(512)`); - await queryRunner.query(`ALTER TABLE "meta" ADD "logoImageUrl" character varying(512)`); - } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "logoImageUrl"`); - await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "backgroundImageUrl"`); - } + constructor() { + this.name = "instanceImages1605965516823"; + } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "meta" ADD "backgroundImageUrl" character varying(512)`, + ); + await queryRunner.query( + `ALTER TABLE "meta" ADD "logoImageUrl" character varying(512)`, + ); + } + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "logoImageUrl"`); + await queryRunner.query( + `ALTER TABLE "meta" DROP COLUMN "backgroundImageUrl"`, + ); + } } diff --git a/packages/backend/migration/1606191203881-no-crawle.js b/packages/backend/migration/1606191203881-no-crawle.js index b9ada4354e..4e7e822771 100644 --- a/packages/backend/migration/1606191203881-no-crawle.js +++ b/packages/backend/migration/1606191203881-no-crawle.js @@ -1,15 +1,21 @@ - - export class noCrawle1606191203881 { - constructor() { - this.name = 'noCrawle1606191203881'; - } - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "user_profile" ADD "noCrawle" boolean NOT NULL DEFAULT false`); - await queryRunner.query(`COMMENT ON COLUMN "user_profile"."noCrawle" IS 'Whether reject index by crawler.'`); - } - async down(queryRunner) { - await queryRunner.query(`COMMENT ON COLUMN "user_profile"."noCrawle" IS 'Whether reject index by crawler.'`); - await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "noCrawle"`); - } + constructor() { + this.name = "noCrawle1606191203881"; + } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "user_profile" ADD "noCrawle" boolean NOT NULL DEFAULT false`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "user_profile"."noCrawle" IS 'Whether reject index by crawler.'`, + ); + } + async down(queryRunner) { + await queryRunner.query( + `COMMENT ON COLUMN "user_profile"."noCrawle" IS 'Whether reject index by crawler.'`, + ); + await queryRunner.query( + `ALTER TABLE "user_profile" DROP COLUMN "noCrawle"`, + ); + } } diff --git a/packages/backend/migration/1607151207216-instance-pinned-clip.js b/packages/backend/migration/1607151207216-instance-pinned-clip.js index 9a4195e74c..655b4c7fbd 100644 --- a/packages/backend/migration/1607151207216-instance-pinned-clip.js +++ b/packages/backend/migration/1607151207216-instance-pinned-clip.js @@ -1,13 +1,13 @@ - - export class instancePinnedClip1607151207216 { - constructor() { - this.name = 'instancePinnedClip1607151207216'; - } - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "meta" ADD "pinnedClipId" character varying(32)`); - } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "pinnedClipId"`); - } + constructor() { + this.name = "instancePinnedClip1607151207216"; + } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "meta" ADD "pinnedClipId" character varying(32)`, + ); + } + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "pinnedClipId"`); + } } diff --git a/packages/backend/migration/1607353487793-isExplorable.js b/packages/backend/migration/1607353487793-isExplorable.js index d9f1ff4c69..d654ee121f 100644 --- a/packages/backend/migration/1607353487793-isExplorable.js +++ b/packages/backend/migration/1607353487793-isExplorable.js @@ -1,17 +1,23 @@ - - export class isExplorable1607353487793 { - constructor() { - this.name = 'isExplorable1607353487793'; - } - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "user" ADD "isExplorable" boolean NOT NULL DEFAULT true`); - await queryRunner.query(`COMMENT ON COLUMN "user"."isExplorable" IS 'Whether the User is explorable.'`); - await queryRunner.query(`CREATE INDEX "IDX_d5a1b83c7cab66f167e6888188" ON "user" ("isExplorable") `); - } - async down(queryRunner) { - await queryRunner.query(`DROP INDEX "IDX_d5a1b83c7cab66f167e6888188"`); - await queryRunner.query(`COMMENT ON COLUMN "user"."isExplorable" IS 'Whether the User is explorable.'`); - await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "isExplorable"`); - } + constructor() { + this.name = "isExplorable1607353487793"; + } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "user" ADD "isExplorable" boolean NOT NULL DEFAULT true`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "user"."isExplorable" IS 'Whether the User is explorable.'`, + ); + await queryRunner.query( + `CREATE INDEX "IDX_d5a1b83c7cab66f167e6888188" ON "user" ("isExplorable") `, + ); + } + async down(queryRunner) { + await queryRunner.query(`DROP INDEX "IDX_d5a1b83c7cab66f167e6888188"`); + await queryRunner.query( + `COMMENT ON COLUMN "user"."isExplorable" IS 'Whether the User is explorable.'`, + ); + await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "isExplorable"`); + } } diff --git a/packages/backend/migration/1610277136869-registry.js b/packages/backend/migration/1610277136869-registry.js index 184c062ddb..023519302e 100644 --- a/packages/backend/migration/1610277136869-registry.js +++ b/packages/backend/migration/1610277136869-registry.js @@ -1,21 +1,31 @@ - - export class registry1610277136869 { - constructor() { - this.name = 'registry1610277136869'; - } - async up(queryRunner) { - await queryRunner.query(`CREATE TABLE "registry_item" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL, "userId" character varying(32) NOT NULL, "key" character varying(1024) NOT NULL, "scope" character varying(1024) array NOT NULL DEFAULT '{}'::varchar[], "domain" character varying(512), CONSTRAINT "PK_64b3f7e6008b4d89b826cd3af95" PRIMARY KEY ("id")); COMMENT ON COLUMN "registry_item"."createdAt" IS 'The created date of the RegistryItem.'; COMMENT ON COLUMN "registry_item"."updatedAt" IS 'The updated date of the RegistryItem.'; COMMENT ON COLUMN "registry_item"."userId" IS 'The owner ID.'; COMMENT ON COLUMN "registry_item"."key" IS 'The key of the RegistryItem.'`); - await queryRunner.query(`CREATE INDEX "IDX_fb9d21ba0abb83223263df6bcb" ON "registry_item" ("userId") `); - await queryRunner.query(`CREATE INDEX "IDX_22baca135bb8a3ea1a83d13df3" ON "registry_item" ("scope") `); - await queryRunner.query(`CREATE INDEX "IDX_0a72bdfcdb97c0eca11fe7ecad" ON "registry_item" ("domain") `); - await queryRunner.query(`ALTER TABLE "registry_item" ADD CONSTRAINT "FK_fb9d21ba0abb83223263df6bcb3" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "registry_item" DROP CONSTRAINT "FK_fb9d21ba0abb83223263df6bcb3"`); - await queryRunner.query(`DROP INDEX "IDX_0a72bdfcdb97c0eca11fe7ecad"`); - await queryRunner.query(`DROP INDEX "IDX_22baca135bb8a3ea1a83d13df3"`); - await queryRunner.query(`DROP INDEX "IDX_fb9d21ba0abb83223263df6bcb"`); - await queryRunner.query(`DROP TABLE "registry_item"`); - } + constructor() { + this.name = "registry1610277136869"; + } + async up(queryRunner) { + await queryRunner.query( + `CREATE TABLE "registry_item" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL, "userId" character varying(32) NOT NULL, "key" character varying(1024) NOT NULL, "scope" character varying(1024) array NOT NULL DEFAULT '{}'::varchar[], "domain" character varying(512), CONSTRAINT "PK_64b3f7e6008b4d89b826cd3af95" PRIMARY KEY ("id")); COMMENT ON COLUMN "registry_item"."createdAt" IS 'The created date of the RegistryItem.'; COMMENT ON COLUMN "registry_item"."updatedAt" IS 'The updated date of the RegistryItem.'; COMMENT ON COLUMN "registry_item"."userId" IS 'The owner ID.'; COMMENT ON COLUMN "registry_item"."key" IS 'The key of the RegistryItem.'`, + ); + await queryRunner.query( + `CREATE INDEX "IDX_fb9d21ba0abb83223263df6bcb" ON "registry_item" ("userId") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_22baca135bb8a3ea1a83d13df3" ON "registry_item" ("scope") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_0a72bdfcdb97c0eca11fe7ecad" ON "registry_item" ("domain") `, + ); + await queryRunner.query( + `ALTER TABLE "registry_item" ADD CONSTRAINT "FK_fb9d21ba0abb83223263df6bcb3" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + } + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "registry_item" DROP CONSTRAINT "FK_fb9d21ba0abb83223263df6bcb3"`, + ); + await queryRunner.query(`DROP INDEX "IDX_0a72bdfcdb97c0eca11fe7ecad"`); + await queryRunner.query(`DROP INDEX "IDX_22baca135bb8a3ea1a83d13df3"`); + await queryRunner.query(`DROP INDEX "IDX_fb9d21ba0abb83223263df6bcb"`); + await queryRunner.query(`DROP TABLE "registry_item"`); + } } diff --git a/packages/backend/migration/1610277585759-registry2.js b/packages/backend/migration/1610277585759-registry2.js index 591bafae31..a2c1fb01bf 100644 --- a/packages/backend/migration/1610277585759-registry2.js +++ b/packages/backend/migration/1610277585759-registry2.js @@ -1,15 +1,19 @@ - - export class registry21610277585759 { - constructor() { - this.name = 'registry21610277585759'; - } - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "registry_item" ADD "value" jsonb NOT NULL DEFAULT '{}'`); - await queryRunner.query(`COMMENT ON COLUMN "registry_item"."value" IS 'The value of the RegistryItem.'`); - } - async down(queryRunner) { - await queryRunner.query(`COMMENT ON COLUMN "registry_item"."value" IS 'The value of the RegistryItem.'`); - await queryRunner.query(`ALTER TABLE "registry_item" DROP COLUMN "value"`); - } + constructor() { + this.name = "registry21610277585759"; + } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "registry_item" ADD "value" jsonb NOT NULL DEFAULT '{}'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "registry_item"."value" IS 'The value of the RegistryItem.'`, + ); + } + async down(queryRunner) { + await queryRunner.query( + `COMMENT ON COLUMN "registry_item"."value" IS 'The value of the RegistryItem.'`, + ); + await queryRunner.query(`ALTER TABLE "registry_item" DROP COLUMN "value"`); + } } diff --git a/packages/backend/migration/1610283021566-registry3.js b/packages/backend/migration/1610283021566-registry3.js index e0289f17ee..a1d55b2dc1 100644 --- a/packages/backend/migration/1610283021566-registry3.js +++ b/packages/backend/migration/1610283021566-registry3.js @@ -1,13 +1,15 @@ - - export class registry31610283021566 { - constructor() { - this.name = 'registry31610283021566'; - } - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "registry_item" ALTER COLUMN "value" DROP NOT NULL`); - } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "registry_item" ALTER COLUMN "value" SET NOT NULL`); - } + constructor() { + this.name = "registry31610283021566"; + } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "registry_item" ALTER COLUMN "value" DROP NOT NULL`, + ); + } + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "registry_item" ALTER COLUMN "value" SET NOT NULL`, + ); + } } diff --git a/packages/backend/migration/1611354329133-followersUri.js b/packages/backend/migration/1611354329133-followersUri.js index 669ddb480e..c7ca5c895b 100644 --- a/packages/backend/migration/1611354329133-followersUri.js +++ b/packages/backend/migration/1611354329133-followersUri.js @@ -1,15 +1,19 @@ - - export class followersUri1611354329133 { - constructor() { - this.name = 'followersUri1611354329133'; - } - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "user" ADD "followersUri" varchar(512) DEFAULT NULL`); - await queryRunner.query(`COMMENT ON COLUMN "user"."followersUri" IS 'The URI of the user Follower Collection. It will be null if the origin of the user is local.'`); - } - async down(queryRunner) { - await queryRunner.query(`COMMENT ON COLUMN "user"."followersUri" IS 'The URI of the user Follower Collection. It will be null if the origin of the user is local.'`); - await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "followersUri"`); - } + constructor() { + this.name = "followersUri1611354329133"; + } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "user" ADD "followersUri" varchar(512) DEFAULT NULL`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "user"."followersUri" IS 'The URI of the user Follower Collection. It will be null if the origin of the user is local.'`, + ); + } + async down(queryRunner) { + await queryRunner.query( + `COMMENT ON COLUMN "user"."followersUri" IS 'The URI of the user Follower Collection. It will be null if the origin of the user is local.'`, + ); + await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "followersUri"`); + } } diff --git a/packages/backend/migration/1611397665007-gallery.js b/packages/backend/migration/1611397665007-gallery.js index f49b2df468..1f36acbc17 100644 --- a/packages/backend/migration/1611397665007-gallery.js +++ b/packages/backend/migration/1611397665007-gallery.js @@ -1,39 +1,71 @@ - - export class gallery1611397665007 { - constructor() { - this.name = 'gallery1611397665007'; - } - async up(queryRunner) { - await queryRunner.query(`CREATE TABLE "gallery_post" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL, "title" character varying(256) NOT NULL, "description" character varying(2048), "userId" character varying(32) NOT NULL, "fileIds" character varying(32) array NOT NULL DEFAULT '{}'::varchar[], "isSensitive" boolean NOT NULL DEFAULT false, "likedCount" integer NOT NULL DEFAULT '0', "tags" character varying(128) array NOT NULL DEFAULT '{}'::varchar[], CONSTRAINT "PK_8e90d7b6015f2c4518881b14753" PRIMARY KEY ("id")); COMMENT ON COLUMN "gallery_post"."createdAt" IS 'The created date of the GalleryPost.'; COMMENT ON COLUMN "gallery_post"."updatedAt" IS 'The updated date of the GalleryPost.'; COMMENT ON COLUMN "gallery_post"."userId" IS 'The ID of author.'; COMMENT ON COLUMN "gallery_post"."isSensitive" IS 'Whether the post is sensitive.'`); - await queryRunner.query(`CREATE INDEX "IDX_8f1a239bd077c8864a20c62c2c" ON "gallery_post" ("createdAt") `); - await queryRunner.query(`CREATE INDEX "IDX_f631d37835adb04792e361807c" ON "gallery_post" ("updatedAt") `); - await queryRunner.query(`CREATE INDEX "IDX_985b836dddd8615e432d7043dd" ON "gallery_post" ("userId") `); - await queryRunner.query(`CREATE INDEX "IDX_3ca50563facd913c425e7a89ee" ON "gallery_post" ("fileIds") `); - await queryRunner.query(`CREATE INDEX "IDX_f2d744d9a14d0dfb8b96cb7fc5" ON "gallery_post" ("isSensitive") `); - await queryRunner.query(`CREATE INDEX "IDX_1a165c68a49d08f11caffbd206" ON "gallery_post" ("likedCount") `); - await queryRunner.query(`CREATE INDEX "IDX_05cca34b985d1b8edc1d1e28df" ON "gallery_post" ("tags") `); - await queryRunner.query(`CREATE TABLE "gallery_like" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "userId" character varying(32) NOT NULL, "postId" character varying(32) NOT NULL, CONSTRAINT "PK_853ab02be39b8de45cd720cc15f" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE INDEX "IDX_8fd5215095473061855ceb948c" ON "gallery_like" ("userId") `); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_df1b5f4099e99fb0bc5eae53b6" ON "gallery_like" ("userId", "postId") `); - await queryRunner.query(`ALTER TABLE "gallery_post" ADD CONSTRAINT "FK_985b836dddd8615e432d7043ddb" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "gallery_like" ADD CONSTRAINT "FK_8fd5215095473061855ceb948cf" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - await queryRunner.query(`ALTER TABLE "gallery_like" ADD CONSTRAINT "FK_b1cb568bfe569e47b7051699fc8" FOREIGN KEY ("postId") REFERENCES "gallery_post"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "gallery_like" DROP CONSTRAINT "FK_b1cb568bfe569e47b7051699fc8"`); - await queryRunner.query(`ALTER TABLE "gallery_like" DROP CONSTRAINT "FK_8fd5215095473061855ceb948cf"`); - await queryRunner.query(`ALTER TABLE "gallery_post" DROP CONSTRAINT "FK_985b836dddd8615e432d7043ddb"`); - await queryRunner.query(`DROP INDEX "IDX_df1b5f4099e99fb0bc5eae53b6"`); - await queryRunner.query(`DROP INDEX "IDX_8fd5215095473061855ceb948c"`); - await queryRunner.query(`DROP TABLE "gallery_like"`); - await queryRunner.query(`DROP INDEX "IDX_05cca34b985d1b8edc1d1e28df"`); - await queryRunner.query(`DROP INDEX "IDX_1a165c68a49d08f11caffbd206"`); - await queryRunner.query(`DROP INDEX "IDX_f2d744d9a14d0dfb8b96cb7fc5"`); - await queryRunner.query(`DROP INDEX "IDX_3ca50563facd913c425e7a89ee"`); - await queryRunner.query(`DROP INDEX "IDX_985b836dddd8615e432d7043dd"`); - await queryRunner.query(`DROP INDEX "IDX_f631d37835adb04792e361807c"`); - await queryRunner.query(`DROP INDEX "IDX_8f1a239bd077c8864a20c62c2c"`); - await queryRunner.query(`DROP TABLE "gallery_post"`); - } + constructor() { + this.name = "gallery1611397665007"; + } + async up(queryRunner) { + await queryRunner.query( + `CREATE TABLE "gallery_post" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL, "title" character varying(256) NOT NULL, "description" character varying(2048), "userId" character varying(32) NOT NULL, "fileIds" character varying(32) array NOT NULL DEFAULT '{}'::varchar[], "isSensitive" boolean NOT NULL DEFAULT false, "likedCount" integer NOT NULL DEFAULT '0', "tags" character varying(128) array NOT NULL DEFAULT '{}'::varchar[], CONSTRAINT "PK_8e90d7b6015f2c4518881b14753" PRIMARY KEY ("id")); COMMENT ON COLUMN "gallery_post"."createdAt" IS 'The created date of the GalleryPost.'; COMMENT ON COLUMN "gallery_post"."updatedAt" IS 'The updated date of the GalleryPost.'; COMMENT ON COLUMN "gallery_post"."userId" IS 'The ID of author.'; COMMENT ON COLUMN "gallery_post"."isSensitive" IS 'Whether the post is sensitive.'`, + ); + await queryRunner.query( + `CREATE INDEX "IDX_8f1a239bd077c8864a20c62c2c" ON "gallery_post" ("createdAt") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_f631d37835adb04792e361807c" ON "gallery_post" ("updatedAt") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_985b836dddd8615e432d7043dd" ON "gallery_post" ("userId") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_3ca50563facd913c425e7a89ee" ON "gallery_post" ("fileIds") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_f2d744d9a14d0dfb8b96cb7fc5" ON "gallery_post" ("isSensitive") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_1a165c68a49d08f11caffbd206" ON "gallery_post" ("likedCount") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_05cca34b985d1b8edc1d1e28df" ON "gallery_post" ("tags") `, + ); + await queryRunner.query( + `CREATE TABLE "gallery_like" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "userId" character varying(32) NOT NULL, "postId" character varying(32) NOT NULL, CONSTRAINT "PK_853ab02be39b8de45cd720cc15f" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE INDEX "IDX_8fd5215095473061855ceb948c" ON "gallery_like" ("userId") `, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_df1b5f4099e99fb0bc5eae53b6" ON "gallery_like" ("userId", "postId") `, + ); + await queryRunner.query( + `ALTER TABLE "gallery_post" ADD CONSTRAINT "FK_985b836dddd8615e432d7043ddb" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "gallery_like" ADD CONSTRAINT "FK_8fd5215095473061855ceb948cf" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + await queryRunner.query( + `ALTER TABLE "gallery_like" ADD CONSTRAINT "FK_b1cb568bfe569e47b7051699fc8" FOREIGN KEY ("postId") REFERENCES "gallery_post"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + } + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "gallery_like" DROP CONSTRAINT "FK_b1cb568bfe569e47b7051699fc8"`, + ); + await queryRunner.query( + `ALTER TABLE "gallery_like" DROP CONSTRAINT "FK_8fd5215095473061855ceb948cf"`, + ); + await queryRunner.query( + `ALTER TABLE "gallery_post" DROP CONSTRAINT "FK_985b836dddd8615e432d7043ddb"`, + ); + await queryRunner.query(`DROP INDEX "IDX_df1b5f4099e99fb0bc5eae53b6"`); + await queryRunner.query(`DROP INDEX "IDX_8fd5215095473061855ceb948c"`); + await queryRunner.query(`DROP TABLE "gallery_like"`); + await queryRunner.query(`DROP INDEX "IDX_05cca34b985d1b8edc1d1e28df"`); + await queryRunner.query(`DROP INDEX "IDX_1a165c68a49d08f11caffbd206"`); + await queryRunner.query(`DROP INDEX "IDX_f2d744d9a14d0dfb8b96cb7fc5"`); + await queryRunner.query(`DROP INDEX "IDX_3ca50563facd913c425e7a89ee"`); + await queryRunner.query(`DROP INDEX "IDX_985b836dddd8615e432d7043dd"`); + await queryRunner.query(`DROP INDEX "IDX_f631d37835adb04792e361807c"`); + await queryRunner.query(`DROP INDEX "IDX_8f1a239bd077c8864a20c62c2c"`); + await queryRunner.query(`DROP TABLE "gallery_post"`); + } } diff --git a/packages/backend/migration/1611547387175-objectStorageS3ForcePathStyle.js b/packages/backend/migration/1611547387175-objectStorageS3ForcePathStyle.js index e4d3c0e8ec..b65ca2a577 100644 --- a/packages/backend/migration/1611547387175-objectStorageS3ForcePathStyle.js +++ b/packages/backend/migration/1611547387175-objectStorageS3ForcePathStyle.js @@ -1,13 +1,15 @@ - - export class objectStorageS3ForcePathStyle1611547387175 { - constructor() { - this.name = 'objectStorageS3ForcePathStyle1611547387175'; - } - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "meta" ADD "objectStorageS3ForcePathStyle" boolean NOT NULL DEFAULT true`); - } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "objectStorageS3ForcePathStyle"`); - } + constructor() { + this.name = "objectStorageS3ForcePathStyle1611547387175"; + } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "meta" ADD "objectStorageS3ForcePathStyle" boolean NOT NULL DEFAULT true`, + ); + } + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "meta" DROP COLUMN "objectStorageS3ForcePathStyle"`, + ); + } } diff --git a/packages/backend/migration/1612619156584-announcement-email.js b/packages/backend/migration/1612619156584-announcement-email.js index bcc718d1c2..a3b7292f8a 100644 --- a/packages/backend/migration/1612619156584-announcement-email.js +++ b/packages/backend/migration/1612619156584-announcement-email.js @@ -1,13 +1,15 @@ - - export class announcementEmail1612619156584 { - constructor() { - this.name = 'announcementEmail1612619156584'; - } - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "user_profile" ADD "receiveAnnouncementEmail" boolean NOT NULL DEFAULT true`); - } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "receiveAnnouncementEmail"`); - } + constructor() { + this.name = "announcementEmail1612619156584"; + } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "user_profile" ADD "receiveAnnouncementEmail" boolean NOT NULL DEFAULT true`, + ); + } + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "user_profile" DROP COLUMN "receiveAnnouncementEmail"`, + ); + } } diff --git a/packages/backend/migration/1613155914446-emailNotificationTypes.js b/packages/backend/migration/1613155914446-emailNotificationTypes.js index cd49924d2d..e64aada7b3 100644 --- a/packages/backend/migration/1613155914446-emailNotificationTypes.js +++ b/packages/backend/migration/1613155914446-emailNotificationTypes.js @@ -1,13 +1,15 @@ - - export class emailNotificationTypes1613155914446 { - constructor() { - this.name = 'emailNotificationTypes1613155914446'; - } - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "user_profile" ADD "emailNotificationTypes" jsonb NOT NULL DEFAULT '["follow","receiveFollowRequest","groupInvited"]'`); - } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "emailNotificationTypes"`); - } + constructor() { + this.name = "emailNotificationTypes1613155914446"; + } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "user_profile" ADD "emailNotificationTypes" jsonb NOT NULL DEFAULT '["follow","receiveFollowRequest","groupInvited"]'`, + ); + } + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "user_profile" DROP COLUMN "emailNotificationTypes"`, + ); + } } diff --git a/packages/backend/migration/1613181457597-user-lang.js b/packages/backend/migration/1613181457597-user-lang.js index d2cd06848e..aa5bf7de8d 100644 --- a/packages/backend/migration/1613181457597-user-lang.js +++ b/packages/backend/migration/1613181457597-user-lang.js @@ -1,13 +1,13 @@ - - export class userLang1613181457597 { - constructor() { - this.name = 'userLang1613181457597'; - } - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "user_profile" ADD "lang" character varying(32)`); - } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "lang"`); - } + constructor() { + this.name = "userLang1613181457597"; + } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "user_profile" ADD "lang" character varying(32)`, + ); + } + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "lang"`); + } } diff --git a/packages/backend/migration/1613503367223-use-bigint-for-driveUsage.js b/packages/backend/migration/1613503367223-use-bigint-for-driveUsage.js index f2e2c5d357..a8f1e33bfc 100644 --- a/packages/backend/migration/1613503367223-use-bigint-for-driveUsage.js +++ b/packages/backend/migration/1613503367223-use-bigint-for-driveUsage.js @@ -1,14 +1,16 @@ - - export class useBigintForDriveUsage1613503367223 { - constructor() { - this.name = 'useBigintForDriveUsage1613503367223'; - } - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "instance" ALTER COLUMN "driveUsage" TYPE bigint`); - } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "instance" DROP COLUMN "driveUsage"`); - await queryRunner.query(`ALTER TABLE "instance" ADD "driveUsage" integer NOT NULL DEFAULT 0`); - } + constructor() { + this.name = "useBigintForDriveUsage1613503367223"; + } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "instance" ALTER COLUMN "driveUsage" TYPE bigint`, + ); + } + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "instance" DROP COLUMN "driveUsage"`); + await queryRunner.query( + `ALTER TABLE "instance" ADD "driveUsage" integer NOT NULL DEFAULT 0`, + ); + } } diff --git a/packages/backend/migration/1615965918224-chart-v2.js b/packages/backend/migration/1615965918224-chart-v2.js index 86fa5b0c00..0460a57f55 100644 --- a/packages/backend/migration/1615965918224-chart-v2.js +++ b/packages/backend/migration/1615965918224-chart-v2.js @@ -1,216 +1,508 @@ - - export class chartV21615965918224 { - constructor() { - this.name = 'chartV21615965918224'; - } - async up(queryRunner) { - await queryRunner.query(`DELETE FROM "__chart__active_users" WHERE "span" = 'day'`); - await queryRunner.query(`DELETE FROM "__chart__drive" WHERE "span" = 'day'`); - await queryRunner.query(`DELETE FROM "__chart__federation" WHERE "span" = 'day'`); - await queryRunner.query(`DELETE FROM "__chart__hashtag" WHERE "span" = 'day'`); - await queryRunner.query(`DELETE FROM "__chart__instance" WHERE "span" = 'day'`); - await queryRunner.query(`DELETE FROM "__chart__network" WHERE "span" = 'day'`); - await queryRunner.query(`DELETE FROM "__chart__notes" WHERE "span" = 'day'`); - await queryRunner.query(`DELETE FROM "__chart__per_user_drive" WHERE "span" = 'day'`); - await queryRunner.query(`DELETE FROM "__chart__per_user_following" WHERE "span" = 'day'`); - await queryRunner.query(`DELETE FROM "__chart__per_user_notes" WHERE "span" = 'day'`); - await queryRunner.query(`DELETE FROM "__chart__per_user_reaction" WHERE "span" = 'day'`); - await queryRunner.query(`DELETE FROM "__chart__test" WHERE "span" = 'day'`); - await queryRunner.query(`DELETE FROM "__chart__test_grouped" WHERE "span" = 'day'`); - await queryRunner.query(`DELETE FROM "__chart__test_unique" WHERE "span" = 'day'`); - await queryRunner.query(`DELETE FROM "__chart__users" WHERE "span" = 'day'`); - await queryRunner.query(`DROP INDEX "IDX_15e91a03aeeac9dbccdf43fc06"`); - await queryRunner.query(`DROP INDEX "IDX_20f57cc8f142c131340ee16742"`); - await queryRunner.query(`DROP INDEX "IDX_c26e2c1cbb6e911e0554b27416"`); - await queryRunner.query(`DROP INDEX "IDX_3fa0d0f17ca72e3dc80999a032"`); - await queryRunner.query(`DROP INDEX "IDX_6e1df243476e20cbf86572ecc0"`); - await queryRunner.query(`DROP INDEX "IDX_06690fc959f1c9fdaf21928222"`); - await queryRunner.query(`DROP INDEX "IDX_e447064455928cf627590ef527"`); - await queryRunner.query(`DROP INDEX "IDX_2d416e6af791a82e338c79d480"`); - await queryRunner.query(`DROP INDEX "IDX_e9cd07672b37d8966cf3709283"`); - await queryRunner.query(`DROP INDEX "IDX_fcc181fb8283009c61cc4083ef"`); - await queryRunner.query(`DROP INDEX "IDX_49975586f50ed7b800fdd88fbd"`); - await queryRunner.query(`DROP INDEX "IDX_6d6f156ceefc6bc5f273a0e370"`); - await queryRunner.query(`DROP INDEX "IDX_c12f0af4a66cdd30c2287ce8aa"`); - await queryRunner.query(`DROP INDEX "IDX_d0a4f79af5a97b08f37b547197"`); - await queryRunner.query(`DROP INDEX "IDX_f5448d9633cff74208d850aabe"`); - await queryRunner.query(`DROP INDEX "IDX_f8dd01baeded2ffa833e0a610a"`); - await queryRunner.query(`DROP INDEX "IDX_08fac0eb3b11f04c200c0b40dd"`); - await queryRunner.query(`DROP INDEX "IDX_9ff6944f01acb756fdc92d7563"`); - await queryRunner.query(`DROP INDEX "IDX_e69096589f11e3baa98ddd64d0"`); - await queryRunner.query(`DROP INDEX "IDX_0c9a159c5082cbeef3ca6706b5"`); - await queryRunner.query(`DROP INDEX "IDX_924fc196c80ca24bae01dd37e4"`); - await queryRunner.query(`DROP INDEX "IDX_328f259961e60c4fa0bfcf55ca"`); - await queryRunner.query(`DROP INDEX "IDX_42ea9381f0fda8dfe0fa1c8b53"`); - await queryRunner.query(`DROP INDEX "IDX_f2aeafde2ae6fbad38e857631b"`); - await queryRunner.query(`DROP INDEX "IDX_f92dd6d03f8d994f29987f6214"`); - await queryRunner.query(`DROP INDEX "IDX_57b5458d0d3d6d1e7f13d4e57f"`); - await queryRunner.query(`DROP INDEX "IDX_4db3b84c7be0d3464714f3e0b1"`); - await queryRunner.query(`DROP INDEX "IDX_8d2cbbc8114d90d19b44d626b6"`); - await queryRunner.query(`DROP INDEX "IDX_046feeb12e9ef5f783f409866a"`); - await queryRunner.query(`DROP INDEX "IDX_f68a5ab958f9f5fa17a32ac23b"`); - await queryRunner.query(`DROP INDEX "IDX_65633a106bce43fc7c5c30a5c7"`); - await queryRunner.query(`DROP INDEX "IDX_edeb73c09c3143a81bcb34d569"`); - await queryRunner.query(`DROP INDEX "IDX_e316f01a6d24eb31db27f88262"`); - await queryRunner.query(`DROP INDEX "IDX_2be7ec6cebddc14dc11e206686"`); - await queryRunner.query(`DROP INDEX "IDX_a5133470f4825902e170328ca5"`); - await queryRunner.query(`DROP INDEX "IDX_84e661abb7bd1e51b690d4b017"`); - await queryRunner.query(`DROP INDEX "IDX_5c73bf61da4f6e6f15bae88ed1"`); - await queryRunner.query(`DROP INDEX "IDX_d70c86baedc68326be11f9c0ce"`); - await queryRunner.query(`DROP INDEX "IDX_66e1e1ecd2f29e57778af35b59"`); - await queryRunner.query(`DROP INDEX "IDX_92255988735563f0fe4aba1f05"`); - await queryRunner.query(`DROP INDEX "IDX_c5870993e25c3d5771f91f5003"`); - await queryRunner.query(`DROP INDEX "IDX_f170de677ea75ad4533de2723e"`); - await queryRunner.query(`DROP INDEX "IDX_7c184198ecf66a8d3ecb253ab3"`); - await queryRunner.query(`DROP INDEX "IDX_f091abb24193d50c653c6b77fc"`); - await queryRunner.query(`DROP INDEX "IDX_a770a57c70e668cc61590c9161"`); - await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "span"`); - await queryRunner.query(`DROP TYPE "public"."__chart__active_users_span_enum"`); - await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "unique"`); - await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "___local_count"`); - await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "___remote_count"`); - await queryRunner.query(`ALTER TABLE "__chart__drive" DROP COLUMN "span"`); - await queryRunner.query(`DROP TYPE "public"."__chart__drive_span_enum"`); - await queryRunner.query(`ALTER TABLE "__chart__drive" DROP COLUMN "unique"`); - await queryRunner.query(`ALTER TABLE "__chart__federation" DROP COLUMN "span"`); - await queryRunner.query(`DROP TYPE "public"."__chart__federation_span_enum"`); - await queryRunner.query(`ALTER TABLE "__chart__federation" DROP COLUMN "unique"`); - await queryRunner.query(`ALTER TABLE "__chart__hashtag" DROP COLUMN "span"`); - await queryRunner.query(`DROP TYPE "public"."__chart__hashtag_span_enum"`); - await queryRunner.query(`ALTER TABLE "__chart__hashtag" DROP COLUMN "unique"`); - await queryRunner.query(`ALTER TABLE "__chart__hashtag" DROP COLUMN "___local_count"`); - await queryRunner.query(`ALTER TABLE "__chart__hashtag" DROP COLUMN "___remote_count"`); - await queryRunner.query(`ALTER TABLE "__chart__instance" DROP COLUMN "span"`); - await queryRunner.query(`DROP TYPE "public"."__chart__instance_span_enum"`); - await queryRunner.query(`ALTER TABLE "__chart__instance" DROP COLUMN "unique"`); - await queryRunner.query(`ALTER TABLE "__chart__network" DROP COLUMN "span"`); - await queryRunner.query(`DROP TYPE "public"."__chart__network_span_enum"`); - await queryRunner.query(`ALTER TABLE "__chart__network" DROP COLUMN "unique"`); - await queryRunner.query(`ALTER TABLE "__chart__notes" DROP COLUMN "span"`); - await queryRunner.query(`DROP TYPE "public"."__chart__notes_span_enum"`); - await queryRunner.query(`ALTER TABLE "__chart__notes" DROP COLUMN "unique"`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_drive" DROP COLUMN "span"`); - await queryRunner.query(`DROP TYPE "public"."__chart__per_user_drive_span_enum"`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_drive" DROP COLUMN "unique"`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_following" DROP COLUMN "span"`); - await queryRunner.query(`DROP TYPE "public"."__chart__per_user_following_span_enum"`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_following" DROP COLUMN "unique"`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_notes" DROP COLUMN "span"`); - await queryRunner.query(`DROP TYPE "public"."__chart__per_user_notes_span_enum"`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_notes" DROP COLUMN "unique"`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_reaction" DROP COLUMN "span"`); - await queryRunner.query(`DROP TYPE "public"."__chart__per_user_reaction_span_enum"`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_reaction" DROP COLUMN "unique"`); - await queryRunner.query(`ALTER TABLE "__chart__test_grouped" DROP COLUMN "span"`); - await queryRunner.query(`DROP TYPE "public"."__chart__test_grouped_span_enum"`); - await queryRunner.query(`ALTER TABLE "__chart__test_grouped" DROP COLUMN "unique"`); - await queryRunner.query(`ALTER TABLE "__chart__test_unique" DROP COLUMN "span"`); - await queryRunner.query(`DROP TYPE "public"."__chart__test_unique_span_enum"`); - await queryRunner.query(`ALTER TABLE "__chart__test_unique" DROP COLUMN "unique"`); - await queryRunner.query(`ALTER TABLE "__chart__test_unique" DROP COLUMN "___foo"`); - await queryRunner.query(`ALTER TABLE "__chart__test" DROP COLUMN "span"`); - await queryRunner.query(`DROP TYPE "public"."__chart__test_span_enum"`); - await queryRunner.query(`ALTER TABLE "__chart__test" DROP COLUMN "unique"`); - await queryRunner.query(`ALTER TABLE "__chart__users" DROP COLUMN "span"`); - await queryRunner.query(`DROP TYPE "public"."__chart__users_span_enum"`); - await queryRunner.query(`ALTER TABLE "__chart__users" DROP COLUMN "unique"`); - } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "__chart__users" ADD "unique" jsonb NOT NULL DEFAULT '{}'`); - await queryRunner.query(`CREATE TYPE "public"."__chart__users_span_enum" AS ENUM('hour', 'day')`); - await queryRunner.query(`ALTER TABLE "__chart__users" ADD "span" "__chart__users_span_enum" NOT NULL`); - await queryRunner.query(`ALTER TABLE "__chart__test" ADD "unique" jsonb NOT NULL DEFAULT '{}'`); - await queryRunner.query(`CREATE TYPE "public"."__chart__test_span_enum" AS ENUM('hour', 'day')`); - await queryRunner.query(`ALTER TABLE "__chart__test" ADD "span" "__chart__test_span_enum" NOT NULL`); - await queryRunner.query(`ALTER TABLE "__chart__test_unique" ADD "___foo" bigint NOT NULL`); - await queryRunner.query(`ALTER TABLE "__chart__test_unique" ADD "unique" jsonb NOT NULL DEFAULT '{}'`); - await queryRunner.query(`CREATE TYPE "public"."__chart__test_unique_span_enum" AS ENUM('hour', 'day')`); - await queryRunner.query(`ALTER TABLE "__chart__test_unique" ADD "span" "__chart__test_unique_span_enum" NOT NULL`); - await queryRunner.query(`ALTER TABLE "__chart__test_grouped" ADD "unique" jsonb NOT NULL DEFAULT '{}'`); - await queryRunner.query(`CREATE TYPE "public"."__chart__test_grouped_span_enum" AS ENUM('hour', 'day')`); - await queryRunner.query(`ALTER TABLE "__chart__test_grouped" ADD "span" "__chart__test_grouped_span_enum" NOT NULL`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_reaction" ADD "unique" jsonb NOT NULL DEFAULT '{}'`); - await queryRunner.query(`CREATE TYPE "public"."__chart__per_user_reaction_span_enum" AS ENUM('hour', 'day')`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_reaction" ADD "span" "__chart__per_user_reaction_span_enum" NOT NULL`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_notes" ADD "unique" jsonb NOT NULL DEFAULT '{}'`); - await queryRunner.query(`CREATE TYPE "public"."__chart__per_user_notes_span_enum" AS ENUM('hour', 'day')`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_notes" ADD "span" "__chart__per_user_notes_span_enum" NOT NULL`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_following" ADD "unique" jsonb NOT NULL DEFAULT '{}'`); - await queryRunner.query(`CREATE TYPE "public"."__chart__per_user_following_span_enum" AS ENUM('hour', 'day')`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_following" ADD "span" "__chart__per_user_following_span_enum" NOT NULL`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_drive" ADD "unique" jsonb NOT NULL DEFAULT '{}'`); - await queryRunner.query(`CREATE TYPE "public"."__chart__per_user_drive_span_enum" AS ENUM('hour', 'day')`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_drive" ADD "span" "__chart__per_user_drive_span_enum" NOT NULL`); - await queryRunner.query(`ALTER TABLE "__chart__notes" ADD "unique" jsonb NOT NULL DEFAULT '{}'`); - await queryRunner.query(`CREATE TYPE "public"."__chart__notes_span_enum" AS ENUM('hour', 'day')`); - await queryRunner.query(`ALTER TABLE "__chart__notes" ADD "span" "__chart__notes_span_enum" NOT NULL`); - await queryRunner.query(`ALTER TABLE "__chart__network" ADD "unique" jsonb NOT NULL DEFAULT '{}'`); - await queryRunner.query(`CREATE TYPE "public"."__chart__network_span_enum" AS ENUM('hour', 'day')`); - await queryRunner.query(`ALTER TABLE "__chart__network" ADD "span" "__chart__network_span_enum" NOT NULL`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ADD "unique" jsonb NOT NULL DEFAULT '{}'`); - await queryRunner.query(`CREATE TYPE "public"."__chart__instance_span_enum" AS ENUM('hour', 'day')`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ADD "span" "__chart__instance_span_enum" NOT NULL`); - await queryRunner.query(`ALTER TABLE "__chart__hashtag" ADD "___remote_count" bigint NOT NULL`); - await queryRunner.query(`ALTER TABLE "__chart__hashtag" ADD "___local_count" bigint NOT NULL`); - await queryRunner.query(`ALTER TABLE "__chart__hashtag" ADD "unique" jsonb NOT NULL DEFAULT '{}'`); - await queryRunner.query(`CREATE TYPE "public"."__chart__hashtag_span_enum" AS ENUM('hour', 'day')`); - await queryRunner.query(`ALTER TABLE "__chart__hashtag" ADD "span" "__chart__hashtag_span_enum" NOT NULL`); - await queryRunner.query(`ALTER TABLE "__chart__federation" ADD "unique" jsonb NOT NULL DEFAULT '{}'`); - await queryRunner.query(`CREATE TYPE "public"."__chart__federation_span_enum" AS ENUM('hour', 'day')`); - await queryRunner.query(`ALTER TABLE "__chart__federation" ADD "span" "__chart__federation_span_enum" NOT NULL`); - await queryRunner.query(`ALTER TABLE "__chart__drive" ADD "unique" jsonb NOT NULL DEFAULT '{}'`); - await queryRunner.query(`CREATE TYPE "public"."__chart__drive_span_enum" AS ENUM('hour', 'day')`); - await queryRunner.query(`ALTER TABLE "__chart__drive" ADD "span" "__chart__drive_span_enum" NOT NULL`); - await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "___remote_count" bigint NOT NULL`); - await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "___local_count" bigint NOT NULL`); - await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "unique" jsonb NOT NULL DEFAULT '{}'`); - await queryRunner.query(`CREATE TYPE "public"."__chart__active_users_span_enum" AS ENUM('hour', 'day')`); - await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "span" "__chart__active_users_span_enum" NOT NULL`); - await queryRunner.query(`CREATE INDEX "IDX_a770a57c70e668cc61590c9161" ON "__chart__users" ("date", "group", "span") `); - await queryRunner.query(`CREATE INDEX "IDX_f091abb24193d50c653c6b77fc" ON "__chart__users" ("date", "span") `); - await queryRunner.query(`CREATE INDEX "IDX_7c184198ecf66a8d3ecb253ab3" ON "__chart__users" ("span") `); - await queryRunner.query(`CREATE INDEX "IDX_f170de677ea75ad4533de2723e" ON "__chart__test" ("date", "group", "span") `); - await queryRunner.query(`CREATE INDEX "IDX_c5870993e25c3d5771f91f5003" ON "__chart__test" ("date", "span") `); - await queryRunner.query(`CREATE INDEX "IDX_92255988735563f0fe4aba1f05" ON "__chart__test" ("span") `); - await queryRunner.query(`CREATE INDEX "IDX_66e1e1ecd2f29e57778af35b59" ON "__chart__test_unique" ("date", "group", "span") `); - await queryRunner.query(`CREATE INDEX "IDX_d70c86baedc68326be11f9c0ce" ON "__chart__test_unique" ("date", "span") `); - await queryRunner.query(`CREATE INDEX "IDX_5c73bf61da4f6e6f15bae88ed1" ON "__chart__test_unique" ("span") `); - await queryRunner.query(`CREATE INDEX "IDX_84e661abb7bd1e51b690d4b017" ON "__chart__test_grouped" ("date", "group", "span") `); - await queryRunner.query(`CREATE INDEX "IDX_a5133470f4825902e170328ca5" ON "__chart__test_grouped" ("date", "span") `); - await queryRunner.query(`CREATE INDEX "IDX_2be7ec6cebddc14dc11e206686" ON "__chart__test_grouped" ("span") `); - await queryRunner.query(`CREATE INDEX "IDX_e316f01a6d24eb31db27f88262" ON "__chart__per_user_reaction" ("date", "group", "span") `); - await queryRunner.query(`CREATE INDEX "IDX_edeb73c09c3143a81bcb34d569" ON "__chart__per_user_reaction" ("date", "span") `); - await queryRunner.query(`CREATE INDEX "IDX_65633a106bce43fc7c5c30a5c7" ON "__chart__per_user_reaction" ("span") `); - await queryRunner.query(`CREATE INDEX "IDX_f68a5ab958f9f5fa17a32ac23b" ON "__chart__per_user_notes" ("date", "group", "span") `); - await queryRunner.query(`CREATE INDEX "IDX_046feeb12e9ef5f783f409866a" ON "__chart__per_user_notes" ("date", "span") `); - await queryRunner.query(`CREATE INDEX "IDX_8d2cbbc8114d90d19b44d626b6" ON "__chart__per_user_notes" ("span") `); - await queryRunner.query(`CREATE INDEX "IDX_4db3b84c7be0d3464714f3e0b1" ON "__chart__per_user_following" ("date", "group", "span") `); - await queryRunner.query(`CREATE INDEX "IDX_57b5458d0d3d6d1e7f13d4e57f" ON "__chart__per_user_following" ("date", "span") `); - await queryRunner.query(`CREATE INDEX "IDX_f92dd6d03f8d994f29987f6214" ON "__chart__per_user_following" ("span") `); - await queryRunner.query(`CREATE INDEX "IDX_f2aeafde2ae6fbad38e857631b" ON "__chart__per_user_drive" ("date", "group", "span") `); - await queryRunner.query(`CREATE INDEX "IDX_42ea9381f0fda8dfe0fa1c8b53" ON "__chart__per_user_drive" ("date", "span") `); - await queryRunner.query(`CREATE INDEX "IDX_328f259961e60c4fa0bfcf55ca" ON "__chart__per_user_drive" ("span") `); - await queryRunner.query(`CREATE INDEX "IDX_924fc196c80ca24bae01dd37e4" ON "__chart__notes" ("date", "group", "span") `); - await queryRunner.query(`CREATE INDEX "IDX_0c9a159c5082cbeef3ca6706b5" ON "__chart__notes" ("date", "span") `); - await queryRunner.query(`CREATE INDEX "IDX_e69096589f11e3baa98ddd64d0" ON "__chart__notes" ("span") `); - await queryRunner.query(`CREATE INDEX "IDX_9ff6944f01acb756fdc92d7563" ON "__chart__network" ("date", "group", "span") `); - await queryRunner.query(`CREATE INDEX "IDX_08fac0eb3b11f04c200c0b40dd" ON "__chart__network" ("date", "span") `); - await queryRunner.query(`CREATE INDEX "IDX_f8dd01baeded2ffa833e0a610a" ON "__chart__network" ("span") `); - await queryRunner.query(`CREATE INDEX "IDX_f5448d9633cff74208d850aabe" ON "__chart__instance" ("date", "group", "span") `); - await queryRunner.query(`CREATE INDEX "IDX_d0a4f79af5a97b08f37b547197" ON "__chart__instance" ("date", "span") `); - await queryRunner.query(`CREATE INDEX "IDX_c12f0af4a66cdd30c2287ce8aa" ON "__chart__instance" ("span") `); - await queryRunner.query(`CREATE INDEX "IDX_6d6f156ceefc6bc5f273a0e370" ON "__chart__hashtag" ("date", "group", "span") `); - await queryRunner.query(`CREATE INDEX "IDX_49975586f50ed7b800fdd88fbd" ON "__chart__hashtag" ("date", "span") `); - await queryRunner.query(`CREATE INDEX "IDX_fcc181fb8283009c61cc4083ef" ON "__chart__hashtag" ("span") `); - await queryRunner.query(`CREATE INDEX "IDX_e9cd07672b37d8966cf3709283" ON "__chart__federation" ("date", "group", "span") `); - await queryRunner.query(`CREATE INDEX "IDX_2d416e6af791a82e338c79d480" ON "__chart__federation" ("date", "span") `); - await queryRunner.query(`CREATE INDEX "IDX_e447064455928cf627590ef527" ON "__chart__federation" ("span") `); - await queryRunner.query(`CREATE INDEX "IDX_06690fc959f1c9fdaf21928222" ON "__chart__drive" ("date", "group", "span") `); - await queryRunner.query(`CREATE INDEX "IDX_6e1df243476e20cbf86572ecc0" ON "__chart__drive" ("date", "span") `); - await queryRunner.query(`CREATE INDEX "IDX_3fa0d0f17ca72e3dc80999a032" ON "__chart__drive" ("span") `); - await queryRunner.query(`CREATE INDEX "IDX_c26e2c1cbb6e911e0554b27416" ON "__chart__active_users" ("date", "group", "span") `); - await queryRunner.query(`CREATE INDEX "IDX_20f57cc8f142c131340ee16742" ON "__chart__active_users" ("date", "span") `); - await queryRunner.query(`CREATE INDEX "IDX_15e91a03aeeac9dbccdf43fc06" ON "__chart__active_users" ("span") `); - } + constructor() { + this.name = "chartV21615965918224"; + } + async up(queryRunner) { + await queryRunner.query( + `DELETE FROM "__chart__active_users" WHERE "span" = 'day'`, + ); + await queryRunner.query( + `DELETE FROM "__chart__drive" WHERE "span" = 'day'`, + ); + await queryRunner.query( + `DELETE FROM "__chart__federation" WHERE "span" = 'day'`, + ); + await queryRunner.query( + `DELETE FROM "__chart__hashtag" WHERE "span" = 'day'`, + ); + await queryRunner.query( + `DELETE FROM "__chart__instance" WHERE "span" = 'day'`, + ); + await queryRunner.query( + `DELETE FROM "__chart__network" WHERE "span" = 'day'`, + ); + await queryRunner.query( + `DELETE FROM "__chart__notes" WHERE "span" = 'day'`, + ); + await queryRunner.query( + `DELETE FROM "__chart__per_user_drive" WHERE "span" = 'day'`, + ); + await queryRunner.query( + `DELETE FROM "__chart__per_user_following" WHERE "span" = 'day'`, + ); + await queryRunner.query( + `DELETE FROM "__chart__per_user_notes" WHERE "span" = 'day'`, + ); + await queryRunner.query( + `DELETE FROM "__chart__per_user_reaction" WHERE "span" = 'day'`, + ); + await queryRunner.query(`DELETE FROM "__chart__test" WHERE "span" = 'day'`); + await queryRunner.query( + `DELETE FROM "__chart__test_grouped" WHERE "span" = 'day'`, + ); + await queryRunner.query( + `DELETE FROM "__chart__test_unique" WHERE "span" = 'day'`, + ); + await queryRunner.query( + `DELETE FROM "__chart__users" WHERE "span" = 'day'`, + ); + await queryRunner.query(`DROP INDEX "IDX_15e91a03aeeac9dbccdf43fc06"`); + await queryRunner.query(`DROP INDEX "IDX_20f57cc8f142c131340ee16742"`); + await queryRunner.query(`DROP INDEX "IDX_c26e2c1cbb6e911e0554b27416"`); + await queryRunner.query(`DROP INDEX "IDX_3fa0d0f17ca72e3dc80999a032"`); + await queryRunner.query(`DROP INDEX "IDX_6e1df243476e20cbf86572ecc0"`); + await queryRunner.query(`DROP INDEX "IDX_06690fc959f1c9fdaf21928222"`); + await queryRunner.query(`DROP INDEX "IDX_e447064455928cf627590ef527"`); + await queryRunner.query(`DROP INDEX "IDX_2d416e6af791a82e338c79d480"`); + await queryRunner.query(`DROP INDEX "IDX_e9cd07672b37d8966cf3709283"`); + await queryRunner.query(`DROP INDEX "IDX_fcc181fb8283009c61cc4083ef"`); + await queryRunner.query(`DROP INDEX "IDX_49975586f50ed7b800fdd88fbd"`); + await queryRunner.query(`DROP INDEX "IDX_6d6f156ceefc6bc5f273a0e370"`); + await queryRunner.query(`DROP INDEX "IDX_c12f0af4a66cdd30c2287ce8aa"`); + await queryRunner.query(`DROP INDEX "IDX_d0a4f79af5a97b08f37b547197"`); + await queryRunner.query(`DROP INDEX "IDX_f5448d9633cff74208d850aabe"`); + await queryRunner.query(`DROP INDEX "IDX_f8dd01baeded2ffa833e0a610a"`); + await queryRunner.query(`DROP INDEX "IDX_08fac0eb3b11f04c200c0b40dd"`); + await queryRunner.query(`DROP INDEX "IDX_9ff6944f01acb756fdc92d7563"`); + await queryRunner.query(`DROP INDEX "IDX_e69096589f11e3baa98ddd64d0"`); + await queryRunner.query(`DROP INDEX "IDX_0c9a159c5082cbeef3ca6706b5"`); + await queryRunner.query(`DROP INDEX "IDX_924fc196c80ca24bae01dd37e4"`); + await queryRunner.query(`DROP INDEX "IDX_328f259961e60c4fa0bfcf55ca"`); + await queryRunner.query(`DROP INDEX "IDX_42ea9381f0fda8dfe0fa1c8b53"`); + await queryRunner.query(`DROP INDEX "IDX_f2aeafde2ae6fbad38e857631b"`); + await queryRunner.query(`DROP INDEX "IDX_f92dd6d03f8d994f29987f6214"`); + await queryRunner.query(`DROP INDEX "IDX_57b5458d0d3d6d1e7f13d4e57f"`); + await queryRunner.query(`DROP INDEX "IDX_4db3b84c7be0d3464714f3e0b1"`); + await queryRunner.query(`DROP INDEX "IDX_8d2cbbc8114d90d19b44d626b6"`); + await queryRunner.query(`DROP INDEX "IDX_046feeb12e9ef5f783f409866a"`); + await queryRunner.query(`DROP INDEX "IDX_f68a5ab958f9f5fa17a32ac23b"`); + await queryRunner.query(`DROP INDEX "IDX_65633a106bce43fc7c5c30a5c7"`); + await queryRunner.query(`DROP INDEX "IDX_edeb73c09c3143a81bcb34d569"`); + await queryRunner.query(`DROP INDEX "IDX_e316f01a6d24eb31db27f88262"`); + await queryRunner.query(`DROP INDEX "IDX_2be7ec6cebddc14dc11e206686"`); + await queryRunner.query(`DROP INDEX "IDX_a5133470f4825902e170328ca5"`); + await queryRunner.query(`DROP INDEX "IDX_84e661abb7bd1e51b690d4b017"`); + await queryRunner.query(`DROP INDEX "IDX_5c73bf61da4f6e6f15bae88ed1"`); + await queryRunner.query(`DROP INDEX "IDX_d70c86baedc68326be11f9c0ce"`); + await queryRunner.query(`DROP INDEX "IDX_66e1e1ecd2f29e57778af35b59"`); + await queryRunner.query(`DROP INDEX "IDX_92255988735563f0fe4aba1f05"`); + await queryRunner.query(`DROP INDEX "IDX_c5870993e25c3d5771f91f5003"`); + await queryRunner.query(`DROP INDEX "IDX_f170de677ea75ad4533de2723e"`); + await queryRunner.query(`DROP INDEX "IDX_7c184198ecf66a8d3ecb253ab3"`); + await queryRunner.query(`DROP INDEX "IDX_f091abb24193d50c653c6b77fc"`); + await queryRunner.query(`DROP INDEX "IDX_a770a57c70e668cc61590c9161"`); + await queryRunner.query( + `ALTER TABLE "__chart__active_users" DROP COLUMN "span"`, + ); + await queryRunner.query( + `DROP TYPE "public"."__chart__active_users_span_enum"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__active_users" DROP COLUMN "unique"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__active_users" DROP COLUMN "___local_count"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__active_users" DROP COLUMN "___remote_count"`, + ); + await queryRunner.query(`ALTER TABLE "__chart__drive" DROP COLUMN "span"`); + await queryRunner.query(`DROP TYPE "public"."__chart__drive_span_enum"`); + await queryRunner.query( + `ALTER TABLE "__chart__drive" DROP COLUMN "unique"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__federation" DROP COLUMN "span"`, + ); + await queryRunner.query( + `DROP TYPE "public"."__chart__federation_span_enum"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__federation" DROP COLUMN "unique"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__hashtag" DROP COLUMN "span"`, + ); + await queryRunner.query(`DROP TYPE "public"."__chart__hashtag_span_enum"`); + await queryRunner.query( + `ALTER TABLE "__chart__hashtag" DROP COLUMN "unique"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__hashtag" DROP COLUMN "___local_count"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__hashtag" DROP COLUMN "___remote_count"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" DROP COLUMN "span"`, + ); + await queryRunner.query(`DROP TYPE "public"."__chart__instance_span_enum"`); + await queryRunner.query( + `ALTER TABLE "__chart__instance" DROP COLUMN "unique"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__network" DROP COLUMN "span"`, + ); + await queryRunner.query(`DROP TYPE "public"."__chart__network_span_enum"`); + await queryRunner.query( + `ALTER TABLE "__chart__network" DROP COLUMN "unique"`, + ); + await queryRunner.query(`ALTER TABLE "__chart__notes" DROP COLUMN "span"`); + await queryRunner.query(`DROP TYPE "public"."__chart__notes_span_enum"`); + await queryRunner.query( + `ALTER TABLE "__chart__notes" DROP COLUMN "unique"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_drive" DROP COLUMN "span"`, + ); + await queryRunner.query( + `DROP TYPE "public"."__chart__per_user_drive_span_enum"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_drive" DROP COLUMN "unique"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_following" DROP COLUMN "span"`, + ); + await queryRunner.query( + `DROP TYPE "public"."__chart__per_user_following_span_enum"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_following" DROP COLUMN "unique"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_notes" DROP COLUMN "span"`, + ); + await queryRunner.query( + `DROP TYPE "public"."__chart__per_user_notes_span_enum"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_notes" DROP COLUMN "unique"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_reaction" DROP COLUMN "span"`, + ); + await queryRunner.query( + `DROP TYPE "public"."__chart__per_user_reaction_span_enum"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_reaction" DROP COLUMN "unique"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__test_grouped" DROP COLUMN "span"`, + ); + await queryRunner.query( + `DROP TYPE "public"."__chart__test_grouped_span_enum"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__test_grouped" DROP COLUMN "unique"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__test_unique" DROP COLUMN "span"`, + ); + await queryRunner.query( + `DROP TYPE "public"."__chart__test_unique_span_enum"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__test_unique" DROP COLUMN "unique"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__test_unique" DROP COLUMN "___foo"`, + ); + await queryRunner.query(`ALTER TABLE "__chart__test" DROP COLUMN "span"`); + await queryRunner.query(`DROP TYPE "public"."__chart__test_span_enum"`); + await queryRunner.query(`ALTER TABLE "__chart__test" DROP COLUMN "unique"`); + await queryRunner.query(`ALTER TABLE "__chart__users" DROP COLUMN "span"`); + await queryRunner.query(`DROP TYPE "public"."__chart__users_span_enum"`); + await queryRunner.query( + `ALTER TABLE "__chart__users" DROP COLUMN "unique"`, + ); + } + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "__chart__users" ADD "unique" jsonb NOT NULL DEFAULT '{}'`, + ); + await queryRunner.query( + `CREATE TYPE "public"."__chart__users_span_enum" AS ENUM('hour', 'day')`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__users" ADD "span" "__chart__users_span_enum" NOT NULL`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__test" ADD "unique" jsonb NOT NULL DEFAULT '{}'`, + ); + await queryRunner.query( + `CREATE TYPE "public"."__chart__test_span_enum" AS ENUM('hour', 'day')`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__test" ADD "span" "__chart__test_span_enum" NOT NULL`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__test_unique" ADD "___foo" bigint NOT NULL`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__test_unique" ADD "unique" jsonb NOT NULL DEFAULT '{}'`, + ); + await queryRunner.query( + `CREATE TYPE "public"."__chart__test_unique_span_enum" AS ENUM('hour', 'day')`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__test_unique" ADD "span" "__chart__test_unique_span_enum" NOT NULL`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__test_grouped" ADD "unique" jsonb NOT NULL DEFAULT '{}'`, + ); + await queryRunner.query( + `CREATE TYPE "public"."__chart__test_grouped_span_enum" AS ENUM('hour', 'day')`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__test_grouped" ADD "span" "__chart__test_grouped_span_enum" NOT NULL`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_reaction" ADD "unique" jsonb NOT NULL DEFAULT '{}'`, + ); + await queryRunner.query( + `CREATE TYPE "public"."__chart__per_user_reaction_span_enum" AS ENUM('hour', 'day')`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_reaction" ADD "span" "__chart__per_user_reaction_span_enum" NOT NULL`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_notes" ADD "unique" jsonb NOT NULL DEFAULT '{}'`, + ); + await queryRunner.query( + `CREATE TYPE "public"."__chart__per_user_notes_span_enum" AS ENUM('hour', 'day')`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_notes" ADD "span" "__chart__per_user_notes_span_enum" NOT NULL`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_following" ADD "unique" jsonb NOT NULL DEFAULT '{}'`, + ); + await queryRunner.query( + `CREATE TYPE "public"."__chart__per_user_following_span_enum" AS ENUM('hour', 'day')`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_following" ADD "span" "__chart__per_user_following_span_enum" NOT NULL`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_drive" ADD "unique" jsonb NOT NULL DEFAULT '{}'`, + ); + await queryRunner.query( + `CREATE TYPE "public"."__chart__per_user_drive_span_enum" AS ENUM('hour', 'day')`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_drive" ADD "span" "__chart__per_user_drive_span_enum" NOT NULL`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__notes" ADD "unique" jsonb NOT NULL DEFAULT '{}'`, + ); + await queryRunner.query( + `CREATE TYPE "public"."__chart__notes_span_enum" AS ENUM('hour', 'day')`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__notes" ADD "span" "__chart__notes_span_enum" NOT NULL`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__network" ADD "unique" jsonb NOT NULL DEFAULT '{}'`, + ); + await queryRunner.query( + `CREATE TYPE "public"."__chart__network_span_enum" AS ENUM('hour', 'day')`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__network" ADD "span" "__chart__network_span_enum" NOT NULL`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ADD "unique" jsonb NOT NULL DEFAULT '{}'`, + ); + await queryRunner.query( + `CREATE TYPE "public"."__chart__instance_span_enum" AS ENUM('hour', 'day')`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ADD "span" "__chart__instance_span_enum" NOT NULL`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__hashtag" ADD "___remote_count" bigint NOT NULL`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__hashtag" ADD "___local_count" bigint NOT NULL`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__hashtag" ADD "unique" jsonb NOT NULL DEFAULT '{}'`, + ); + await queryRunner.query( + `CREATE TYPE "public"."__chart__hashtag_span_enum" AS ENUM('hour', 'day')`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__hashtag" ADD "span" "__chart__hashtag_span_enum" NOT NULL`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__federation" ADD "unique" jsonb NOT NULL DEFAULT '{}'`, + ); + await queryRunner.query( + `CREATE TYPE "public"."__chart__federation_span_enum" AS ENUM('hour', 'day')`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__federation" ADD "span" "__chart__federation_span_enum" NOT NULL`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__drive" ADD "unique" jsonb NOT NULL DEFAULT '{}'`, + ); + await queryRunner.query( + `CREATE TYPE "public"."__chart__drive_span_enum" AS ENUM('hour', 'day')`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__drive" ADD "span" "__chart__drive_span_enum" NOT NULL`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__active_users" ADD "___remote_count" bigint NOT NULL`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__active_users" ADD "___local_count" bigint NOT NULL`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__active_users" ADD "unique" jsonb NOT NULL DEFAULT '{}'`, + ); + await queryRunner.query( + `CREATE TYPE "public"."__chart__active_users_span_enum" AS ENUM('hour', 'day')`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__active_users" ADD "span" "__chart__active_users_span_enum" NOT NULL`, + ); + await queryRunner.query( + `CREATE INDEX "IDX_a770a57c70e668cc61590c9161" ON "__chart__users" ("date", "group", "span") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_f091abb24193d50c653c6b77fc" ON "__chart__users" ("date", "span") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_7c184198ecf66a8d3ecb253ab3" ON "__chart__users" ("span") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_f170de677ea75ad4533de2723e" ON "__chart__test" ("date", "group", "span") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_c5870993e25c3d5771f91f5003" ON "__chart__test" ("date", "span") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_92255988735563f0fe4aba1f05" ON "__chart__test" ("span") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_66e1e1ecd2f29e57778af35b59" ON "__chart__test_unique" ("date", "group", "span") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_d70c86baedc68326be11f9c0ce" ON "__chart__test_unique" ("date", "span") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_5c73bf61da4f6e6f15bae88ed1" ON "__chart__test_unique" ("span") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_84e661abb7bd1e51b690d4b017" ON "__chart__test_grouped" ("date", "group", "span") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_a5133470f4825902e170328ca5" ON "__chart__test_grouped" ("date", "span") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_2be7ec6cebddc14dc11e206686" ON "__chart__test_grouped" ("span") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_e316f01a6d24eb31db27f88262" ON "__chart__per_user_reaction" ("date", "group", "span") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_edeb73c09c3143a81bcb34d569" ON "__chart__per_user_reaction" ("date", "span") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_65633a106bce43fc7c5c30a5c7" ON "__chart__per_user_reaction" ("span") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_f68a5ab958f9f5fa17a32ac23b" ON "__chart__per_user_notes" ("date", "group", "span") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_046feeb12e9ef5f783f409866a" ON "__chart__per_user_notes" ("date", "span") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_8d2cbbc8114d90d19b44d626b6" ON "__chart__per_user_notes" ("span") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_4db3b84c7be0d3464714f3e0b1" ON "__chart__per_user_following" ("date", "group", "span") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_57b5458d0d3d6d1e7f13d4e57f" ON "__chart__per_user_following" ("date", "span") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_f92dd6d03f8d994f29987f6214" ON "__chart__per_user_following" ("span") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_f2aeafde2ae6fbad38e857631b" ON "__chart__per_user_drive" ("date", "group", "span") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_42ea9381f0fda8dfe0fa1c8b53" ON "__chart__per_user_drive" ("date", "span") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_328f259961e60c4fa0bfcf55ca" ON "__chart__per_user_drive" ("span") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_924fc196c80ca24bae01dd37e4" ON "__chart__notes" ("date", "group", "span") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_0c9a159c5082cbeef3ca6706b5" ON "__chart__notes" ("date", "span") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_e69096589f11e3baa98ddd64d0" ON "__chart__notes" ("span") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_9ff6944f01acb756fdc92d7563" ON "__chart__network" ("date", "group", "span") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_08fac0eb3b11f04c200c0b40dd" ON "__chart__network" ("date", "span") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_f8dd01baeded2ffa833e0a610a" ON "__chart__network" ("span") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_f5448d9633cff74208d850aabe" ON "__chart__instance" ("date", "group", "span") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_d0a4f79af5a97b08f37b547197" ON "__chart__instance" ("date", "span") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_c12f0af4a66cdd30c2287ce8aa" ON "__chart__instance" ("span") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_6d6f156ceefc6bc5f273a0e370" ON "__chart__hashtag" ("date", "group", "span") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_49975586f50ed7b800fdd88fbd" ON "__chart__hashtag" ("date", "span") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_fcc181fb8283009c61cc4083ef" ON "__chart__hashtag" ("span") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_e9cd07672b37d8966cf3709283" ON "__chart__federation" ("date", "group", "span") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_2d416e6af791a82e338c79d480" ON "__chart__federation" ("date", "span") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_e447064455928cf627590ef527" ON "__chart__federation" ("span") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_06690fc959f1c9fdaf21928222" ON "__chart__drive" ("date", "group", "span") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_6e1df243476e20cbf86572ecc0" ON "__chart__drive" ("date", "span") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_3fa0d0f17ca72e3dc80999a032" ON "__chart__drive" ("span") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_c26e2c1cbb6e911e0554b27416" ON "__chart__active_users" ("date", "group", "span") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_20f57cc8f142c131340ee16742" ON "__chart__active_users" ("date", "span") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_15e91a03aeeac9dbccdf43fc06" ON "__chart__active_users" ("span") `, + ); + } } diff --git a/packages/backend/migration/1615966519402-chart-v2-2.js b/packages/backend/migration/1615966519402-chart-v2-2.js index c62f1b875c..ce75961035 100644 --- a/packages/backend/migration/1615966519402-chart-v2-2.js +++ b/packages/backend/migration/1615966519402-chart-v2-2.js @@ -1,21 +1,39 @@ - - export class chartV221615966519402 { - constructor() { - this.name = 'chartV221615966519402'; - } - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "___local_users" character varying array NOT NULL DEFAULT '{}'::varchar[]`); - await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "___remote_users" character varying array NOT NULL DEFAULT '{}'::varchar[]`); - await queryRunner.query(`ALTER TABLE "__chart__hashtag" ADD "___local_users" character varying array NOT NULL DEFAULT '{}'::varchar[]`); - await queryRunner.query(`ALTER TABLE "__chart__hashtag" ADD "___remote_users" character varying array NOT NULL DEFAULT '{}'::varchar[]`); - await queryRunner.query(`ALTER TABLE "__chart__test_unique" ADD "___foo" character varying array NOT NULL DEFAULT '{}'::varchar[]`); - } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "__chart__test_unique" DROP COLUMN "___foo"`); - await queryRunner.query(`ALTER TABLE "__chart__hashtag" DROP COLUMN "___remote_users"`); - await queryRunner.query(`ALTER TABLE "__chart__hashtag" DROP COLUMN "___local_users"`); - await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "___remote_users"`); - await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "___local_users"`); - } + constructor() { + this.name = "chartV221615966519402"; + } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "__chart__active_users" ADD "___local_users" character varying array NOT NULL DEFAULT '{}'::varchar[]`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__active_users" ADD "___remote_users" character varying array NOT NULL DEFAULT '{}'::varchar[]`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__hashtag" ADD "___local_users" character varying array NOT NULL DEFAULT '{}'::varchar[]`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__hashtag" ADD "___remote_users" character varying array NOT NULL DEFAULT '{}'::varchar[]`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__test_unique" ADD "___foo" character varying array NOT NULL DEFAULT '{}'::varchar[]`, + ); + } + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "__chart__test_unique" DROP COLUMN "___foo"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__hashtag" DROP COLUMN "___remote_users"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__hashtag" DROP COLUMN "___local_users"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__active_users" DROP COLUMN "___remote_users"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__active_users" DROP COLUMN "___local_users"`, + ); + } } diff --git a/packages/backend/migration/1618637372000-user-last-active-date.js b/packages/backend/migration/1618637372000-user-last-active-date.js index 6c77ace467..0211c256bc 100644 --- a/packages/backend/migration/1618637372000-user-last-active-date.js +++ b/packages/backend/migration/1618637372000-user-last-active-date.js @@ -1,15 +1,17 @@ - - export class userLastActiveDate1618637372000 { - constructor() { - this.name = 'userLastActiveDate1618637372000'; - } - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "user" ADD "lastActiveDate" TIMESTAMP WITH TIME ZONE DEFAULT NULL`); - await queryRunner.query(`CREATE INDEX "IDX_seoignmeoprigmkpodgrjmkpormg" ON "user" ("lastActiveDate") `); - } - async down(queryRunner) { - await queryRunner.query(`DROP INDEX "IDX_seoignmeoprigmkpodgrjmkpormg"`); - await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "lastActiveDate"`); - } + constructor() { + this.name = "userLastActiveDate1618637372000"; + } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "user" ADD "lastActiveDate" TIMESTAMP WITH TIME ZONE DEFAULT NULL`, + ); + await queryRunner.query( + `CREATE INDEX "IDX_seoignmeoprigmkpodgrjmkpormg" ON "user" ("lastActiveDate") `, + ); + } + async down(queryRunner) { + await queryRunner.query(`DROP INDEX "IDX_seoignmeoprigmkpodgrjmkpormg"`); + await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "lastActiveDate"`); + } } diff --git a/packages/backend/migration/1618639857000-user-hide-online-status.js b/packages/backend/migration/1618639857000-user-hide-online-status.js index e63c8ae11f..8d76326084 100644 --- a/packages/backend/migration/1618639857000-user-hide-online-status.js +++ b/packages/backend/migration/1618639857000-user-hide-online-status.js @@ -1,13 +1,15 @@ - - export class userHideOnlineStatus1618639857000 { - constructor() { - this.name = 'userHideOnlineStatus1618639857000'; - } - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "user" ADD "hideOnlineStatus" boolean NOT NULL DEFAULT false`); - } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "hideOnlineStatus"`); - } + constructor() { + this.name = "userHideOnlineStatus1618639857000"; + } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "user" ADD "hideOnlineStatus" boolean NOT NULL DEFAULT false`, + ); + } + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "user" DROP COLUMN "hideOnlineStatus"`, + ); + } } diff --git a/packages/backend/migration/1619942102890-password-reset.js b/packages/backend/migration/1619942102890-password-reset.js index 922d225dc9..e3adeb95e7 100644 --- a/packages/backend/migration/1619942102890-password-reset.js +++ b/packages/backend/migration/1619942102890-password-reset.js @@ -1,19 +1,27 @@ - - export class passwordReset1619942102890 { - constructor() { - this.name = 'passwordReset1619942102890'; - } - async up(queryRunner) { - await queryRunner.query(`CREATE TABLE "password_reset_request" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "token" character varying(256) NOT NULL, "userId" character varying(32) NOT NULL, CONSTRAINT "PK_fcf4b02eae1403a2edaf87fd074" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_0b575fa9a4cfe638a925949285" ON "password_reset_request" ("token") `); - await queryRunner.query(`CREATE INDEX "IDX_4bb7fd4a34492ae0e6cc8d30ac" ON "password_reset_request" ("userId") `); - await queryRunner.query(`ALTER TABLE "password_reset_request" ADD CONSTRAINT "FK_4bb7fd4a34492ae0e6cc8d30ac8" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "password_reset_request" DROP CONSTRAINT "FK_4bb7fd4a34492ae0e6cc8d30ac8"`); - await queryRunner.query(`DROP INDEX "IDX_4bb7fd4a34492ae0e6cc8d30ac"`); - await queryRunner.query(`DROP INDEX "IDX_0b575fa9a4cfe638a925949285"`); - await queryRunner.query(`DROP TABLE "password_reset_request"`); - } + constructor() { + this.name = "passwordReset1619942102890"; + } + async up(queryRunner) { + await queryRunner.query( + `CREATE TABLE "password_reset_request" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "token" character varying(256) NOT NULL, "userId" character varying(32) NOT NULL, CONSTRAINT "PK_fcf4b02eae1403a2edaf87fd074" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_0b575fa9a4cfe638a925949285" ON "password_reset_request" ("token") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_4bb7fd4a34492ae0e6cc8d30ac" ON "password_reset_request" ("userId") `, + ); + await queryRunner.query( + `ALTER TABLE "password_reset_request" ADD CONSTRAINT "FK_4bb7fd4a34492ae0e6cc8d30ac8" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + } + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "password_reset_request" DROP CONSTRAINT "FK_4bb7fd4a34492ae0e6cc8d30ac8"`, + ); + await queryRunner.query(`DROP INDEX "IDX_4bb7fd4a34492ae0e6cc8d30ac"`); + await queryRunner.query(`DROP INDEX "IDX_0b575fa9a4cfe638a925949285"`); + await queryRunner.query(`DROP TABLE "password_reset_request"`); + } } diff --git a/packages/backend/migration/1620019354680-ad.js b/packages/backend/migration/1620019354680-ad.js index c96d2bfb33..e39b381013 100644 --- a/packages/backend/migration/1620019354680-ad.js +++ b/packages/backend/migration/1620019354680-ad.js @@ -1,17 +1,21 @@ - - export class ad1620019354680 { - constructor() { - this.name = 'ad1620019354680'; - } - async up(queryRunner) { - await queryRunner.query(`CREATE TABLE "ad" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "expiresAt" TIMESTAMP WITH TIME ZONE NOT NULL, "place" character varying(32) NOT NULL, "priority" character varying(32) NOT NULL, "url" character varying(1024) NOT NULL, "imageUrl" character varying(1024) NOT NULL, "memo" character varying(8192) NOT NULL, CONSTRAINT "PK_0193d5ef09746e88e9ea92c634d" PRIMARY KEY ("id")); COMMENT ON COLUMN "ad"."createdAt" IS 'The created date of the Ad.'; COMMENT ON COLUMN "ad"."expiresAt" IS 'The expired date of the Ad.'`); - await queryRunner.query(`CREATE INDEX "IDX_1129c2ef687fc272df040bafaa" ON "ad" ("createdAt") `); - await queryRunner.query(`CREATE INDEX "IDX_2da24ce20ad209f1d9dc032457" ON "ad" ("expiresAt") `); - } - async down(queryRunner) { - await queryRunner.query(`DROP INDEX "IDX_2da24ce20ad209f1d9dc032457"`); - await queryRunner.query(`DROP INDEX "IDX_1129c2ef687fc272df040bafaa"`); - await queryRunner.query(`DROP TABLE "ad"`); - } + constructor() { + this.name = "ad1620019354680"; + } + async up(queryRunner) { + await queryRunner.query( + `CREATE TABLE "ad" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "expiresAt" TIMESTAMP WITH TIME ZONE NOT NULL, "place" character varying(32) NOT NULL, "priority" character varying(32) NOT NULL, "url" character varying(1024) NOT NULL, "imageUrl" character varying(1024) NOT NULL, "memo" character varying(8192) NOT NULL, CONSTRAINT "PK_0193d5ef09746e88e9ea92c634d" PRIMARY KEY ("id")); COMMENT ON COLUMN "ad"."createdAt" IS 'The created date of the Ad.'; COMMENT ON COLUMN "ad"."expiresAt" IS 'The expired date of the Ad.'`, + ); + await queryRunner.query( + `CREATE INDEX "IDX_1129c2ef687fc272df040bafaa" ON "ad" ("createdAt") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_2da24ce20ad209f1d9dc032457" ON "ad" ("expiresAt") `, + ); + } + async down(queryRunner) { + await queryRunner.query(`DROP INDEX "IDX_2da24ce20ad209f1d9dc032457"`); + await queryRunner.query(`DROP INDEX "IDX_1129c2ef687fc272df040bafaa"`); + await queryRunner.query(`DROP TABLE "ad"`); + } } diff --git a/packages/backend/migration/1620364649428-ad2.js b/packages/backend/migration/1620364649428-ad2.js index db1c3e1de1..a61c3ef088 100644 --- a/packages/backend/migration/1620364649428-ad2.js +++ b/packages/backend/migration/1620364649428-ad2.js @@ -1,13 +1,13 @@ - - export class ad21620364649428 { - constructor() { - this.name = 'ad21620364649428'; - } - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "ad" ADD "ratio" integer NOT NULL DEFAULT '1'`); - } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "ad" DROP COLUMN "ratio"`); - } + constructor() { + this.name = "ad21620364649428"; + } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "ad" ADD "ratio" integer NOT NULL DEFAULT '1'`, + ); + } + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "ad" DROP COLUMN "ratio"`); + } } diff --git a/packages/backend/migration/1621479946000-add-note-indexes.js b/packages/backend/migration/1621479946000-add-note-indexes.js index dcf97fa4dc..f9ffbbff17 100644 --- a/packages/backend/migration/1621479946000-add-note-indexes.js +++ b/packages/backend/migration/1621479946000-add-note-indexes.js @@ -1,15 +1,22 @@ - - export class addNoteIndexes1621479946000 { - constructor() { - this.name = 'addNoteIndexes1621479946000'; - } - async up(queryRunner) { - await queryRunner.query(`CREATE INDEX "IDX_NOTE_MENTIONS" ON "note" USING gin ("mentions")`, undefined); - await queryRunner.query(`CREATE INDEX "IDX_NOTE_VISIBLE_USER_IDS" ON "note" USING gin ("visibleUserIds")`, undefined); - } - async down(queryRunner) { - await queryRunner.query(`DROP INDEX "IDX_NOTE_MENTIONS"`, undefined); - await queryRunner.query(`DROP INDEX "IDX_NOTE_VISIBLE_USER_IDS"`, undefined); - } + constructor() { + this.name = "addNoteIndexes1621479946000"; + } + async up(queryRunner) { + await queryRunner.query( + `CREATE INDEX "IDX_NOTE_MENTIONS" ON "note" USING gin ("mentions")`, + undefined, + ); + await queryRunner.query( + `CREATE INDEX "IDX_NOTE_VISIBLE_USER_IDS" ON "note" USING gin ("visibleUserIds")`, + undefined, + ); + } + async down(queryRunner) { + await queryRunner.query(`DROP INDEX "IDX_NOTE_MENTIONS"`, undefined); + await queryRunner.query( + `DROP INDEX "IDX_NOTE_VISIBLE_USER_IDS"`, + undefined, + ); + } } diff --git a/packages/backend/migration/1622679304522-user-profile-description-length.js b/packages/backend/migration/1622679304522-user-profile-description-length.js index 22f6c1c5d9..7c761c92a1 100644 --- a/packages/backend/migration/1622679304522-user-profile-description-length.js +++ b/packages/backend/migration/1622679304522-user-profile-description-length.js @@ -1,13 +1,17 @@ - - export class userProfileDescriptionLength1622679304522 { - constructor() { - this.name = 'userProfileDescriptionLength1622679304522'; - } - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "user_profile" ALTER COLUMN "description" TYPE character varying(2048)`, undefined); - } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "user_profile" ALTER COLUMN "description" TYPE character varying(1024)`, undefined); - } + constructor() { + this.name = "userProfileDescriptionLength1622679304522"; + } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "user_profile" ALTER COLUMN "description" TYPE character varying(2048)`, + undefined, + ); + } + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "user_profile" ALTER COLUMN "description" TYPE character varying(1024)`, + undefined, + ); + } } diff --git a/packages/backend/migration/1622681548499-log-message-length.js b/packages/backend/migration/1622681548499-log-message-length.js index ac16c0e1ba..9dfb7ed3d2 100644 --- a/packages/backend/migration/1622681548499-log-message-length.js +++ b/packages/backend/migration/1622681548499-log-message-length.js @@ -1,13 +1,17 @@ - - export class logMessageLength1622681548499 { - constructor() { - this.name = 'logMessageLength1622681548499'; - } - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "log" ALTER COLUMN "message" TYPE character varying(2048)`, undefined); - } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "log" ALTER COLUMN "message" TYPE character varying(1024)`, undefined); - } + constructor() { + this.name = "logMessageLength1622681548499"; + } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "log" ALTER COLUMN "message" TYPE character varying(2048)`, + undefined, + ); + } + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "log" ALTER COLUMN "message" TYPE character varying(1024)`, + undefined, + ); + } } diff --git a/packages/backend/migration/1626509500668-fix-remote-file-proxy.js b/packages/backend/migration/1626509500668-fix-remote-file-proxy.js index 30c562007b..f79cfb0066 100644 --- a/packages/backend/migration/1626509500668-fix-remote-file-proxy.js +++ b/packages/backend/migration/1626509500668-fix-remote-file-proxy.js @@ -1,22 +1,31 @@ - - export class fixRemoteFileProxy1626509500668 { - constructor() { - this.name = 'fixRemoteFileProxy1626509500668'; - } - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "avatarUrl"`); - await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "bannerUrl"`); - await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "avatarBlurhash"`); - await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "bannerBlurhash"`); - await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "proxyRemoteFiles"`); - } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "meta" ADD "proxyRemoteFiles" boolean NOT NULL DEFAULT false`); - await queryRunner.query(`ALTER TABLE "user" ADD "bannerBlurhash" character varying(128)`); - await queryRunner.query(`ALTER TABLE "user" ADD "avatarBlurhash" character varying(128)`); - await queryRunner.query(`ALTER TABLE "user" ADD "bannerUrl" character varying(512)`); - await queryRunner.query(`ALTER TABLE "user" ADD "avatarUrl" character varying(512)`); - } + constructor() { + this.name = "fixRemoteFileProxy1626509500668"; + } + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "avatarUrl"`); + await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "bannerUrl"`); + await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "avatarBlurhash"`); + await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "bannerBlurhash"`); + await queryRunner.query( + `ALTER TABLE "meta" DROP COLUMN "proxyRemoteFiles"`, + ); + } + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "meta" ADD "proxyRemoteFiles" boolean NOT NULL DEFAULT false`, + ); + await queryRunner.query( + `ALTER TABLE "user" ADD "bannerBlurhash" character varying(128)`, + ); + await queryRunner.query( + `ALTER TABLE "user" ADD "avatarBlurhash" character varying(128)`, + ); + await queryRunner.query( + `ALTER TABLE "user" ADD "bannerUrl" character varying(512)`, + ); + await queryRunner.query( + `ALTER TABLE "user" ADD "avatarUrl" character varying(512)`, + ); + } } - diff --git a/packages/backend/migration/1626733991004-allowlist-secure-mode.js b/packages/backend/migration/1626733991004-allowlist-secure-mode.js index aa3fcf8752..6d2dd6842f 100644 --- a/packages/backend/migration/1626733991004-allowlist-secure-mode.js +++ b/packages/backend/migration/1626733991004-allowlist-secure-mode.js @@ -1,11 +1,15 @@ - - -export class allowlistSecureMode1626733991004 { - name = 'allowlistSecureMode1626733991004'; +export class allowlistSecureMode1626733991004 { + name = "allowlistSecureMode1626733991004"; async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "meta" ADD "allowedHosts" character varying(256) [] default '{}'`); - await queryRunner.query(`ALTER TABLE "meta" ADD "secureMode" bool default false`); - await queryRunner.query(`ALTER TABLE "meta" ADD "privateMode" bool default false`); + await queryRunner.query( + `ALTER TABLE "meta" ADD "allowedHosts" character varying(256) [] default '{}'`, + ); + await queryRunner.query( + `ALTER TABLE "meta" ADD "secureMode" bool default false`, + ); + await queryRunner.query( + `ALTER TABLE "meta" ADD "privateMode" bool default false`, + ); } async down(queryRunner) { @@ -14,4 +18,3 @@ export class allowlistSecureMode1626733991004 { await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "privateMode"`); } } - diff --git a/packages/backend/migration/1629004542760-chart-reindex.js b/packages/backend/migration/1629004542760-chart-reindex.js index a7d459276d..8cc564b32e 100644 --- a/packages/backend/migration/1629004542760-chart-reindex.js +++ b/packages/backend/migration/1629004542760-chart-reindex.js @@ -1,181 +1,357 @@ - - export class chartReindex1629004542760 { - constructor() { - this.name = 'chartReindex1629004542760'; - } - async up(queryRunner) { - await queryRunner.query(`DELETE FROM "__chart__active_users" a USING "__chart__active_users" b WHERE a.id < b.id AND ((a.group IS NULL AND b.group IS NULL) OR a.group = b.group) AND a.date = b.date;`); - await queryRunner.query(`DELETE FROM "__chart__drive" a USING "__chart__drive" b WHERE a.id < b.id AND ((a.group IS NULL AND b.group IS NULL) OR a.group = b.group) AND a.date = b.date;`); - await queryRunner.query(`DELETE FROM "__chart__federation" a USING "__chart__federation" b WHERE a.id < b.id AND ((a.group IS NULL AND b.group IS NULL) OR a.group = b.group) AND a.date = b.date;`); - await queryRunner.query(`DELETE FROM "__chart__hashtag" a USING "__chart__hashtag" b WHERE a.id < b.id AND ((a.group IS NULL AND b.group IS NULL) OR a.group = b.group) AND a.date = b.date;`); - await queryRunner.query(`DELETE FROM "__chart__instance" a USING "__chart__instance" b WHERE a.id < b.id AND ((a.group IS NULL AND b.group IS NULL) OR a.group = b.group) AND a.date = b.date;`); - await queryRunner.query(`DELETE FROM "__chart__network" a USING "__chart__network" b WHERE a.id < b.id AND ((a.group IS NULL AND b.group IS NULL) OR a.group = b.group) AND a.date = b.date;`); - await queryRunner.query(`DELETE FROM "__chart__notes" a USING "__chart__notes" b WHERE a.id < b.id AND ((a.group IS NULL AND b.group IS NULL) OR a.group = b.group) AND a.date = b.date;`); - await queryRunner.query(`DELETE FROM "__chart__per_user_drive" a USING "__chart__per_user_drive" b WHERE a.id < b.id AND ((a.group IS NULL AND b.group IS NULL) OR a.group = b.group) AND a.date = b.date;`); - await queryRunner.query(`DELETE FROM "__chart__per_user_following" a USING "__chart__per_user_following" b WHERE a.id < b.id AND ((a.group IS NULL AND b.group IS NULL) OR a.group = b.group) AND a.date = b.date;`); - await queryRunner.query(`DELETE FROM "__chart__per_user_notes" a USING "__chart__per_user_notes" b WHERE a.id < b.id AND ((a.group IS NULL AND b.group IS NULL) OR a.group = b.group) AND a.date = b.date;`); - await queryRunner.query(`DELETE FROM "__chart__per_user_reaction" a USING "__chart__per_user_reaction" b WHERE a.id < b.id AND ((a.group IS NULL AND b.group IS NULL) OR a.group = b.group) AND a.date = b.date;`); - await queryRunner.query(`DELETE FROM "__chart__test_grouped" a USING "__chart__test_grouped" b WHERE a.id < b.id AND ((a.group IS NULL AND b.group IS NULL) OR a.group = b.group) AND a.date = b.date;`); - await queryRunner.query(`DELETE FROM "__chart__test_unique" a USING "__chart__test_unique" b WHERE a.id < b.id AND ((a.group IS NULL AND b.group IS NULL) OR a.group = b.group) AND a.date = b.date;`); - await queryRunner.query(`DELETE FROM "__chart__users" a USING "__chart__users" b WHERE a.id < b.id AND ((a.group IS NULL AND b.group IS NULL) OR a.group = b.group) AND a.date = b.date;`); - await queryRunner.query(`DROP INDEX "IDX_0ad37b7ef50f4ddc84363d7ccc"`); - await queryRunner.query(`DROP INDEX "IDX_00ed5f86db1f7efafb1978bf21"`); - await queryRunner.query(`DROP INDEX "IDX_9a3ed15a30ab7e3a37702e6e08"`); - await queryRunner.query(`DROP INDEX "IDX_13565815f618a1ff53886c5b28"`); - await queryRunner.query(`DROP INDEX "IDX_7a170f67425e62a8fabb76c872"`); - await queryRunner.query(`DROP INDEX "IDX_3313d7288855ec105b5bbf6c21"`); - await queryRunner.query(`DROP INDEX "IDX_36cb699c49580d4e6c2e6159f9"`); - await queryRunner.query(`DROP INDEX "IDX_76e87c7bfc5d925fcbba405d84"`); - await queryRunner.query(`DROP INDEX "IDX_dd907becf76104e4b656659e6b"`); - await queryRunner.query(`DROP INDEX "IDX_07747a1038c05f532a718fe1de"`); - await queryRunner.query(`DROP INDEX "IDX_99a7d2faaef84a6f728d714ad6"`); - await queryRunner.query(`DROP INDEX "IDX_25a97c02003338124b2b75fdbc"`); - await queryRunner.query(`DROP INDEX "IDX_6b8f34a1a64b06014b6fb66824"`); - await queryRunner.query(`DROP INDEX "IDX_da8a46ba84ca1d8bb5a29bfb63"`); - await queryRunner.query(`DROP INDEX "IDX_39ee857ab2f23493037c6b6631"`); - await queryRunner.query(`DROP INDEX "IDX_a1efd3e0048a5f2793a47360dc"`); - await queryRunner.query(`DROP INDEX "IDX_7b5da130992ec9df96712d4290"`); - await queryRunner.query(`DROP INDEX "IDX_0a905b992fecd2b5c3fb98759e"`); - await queryRunner.query(`DROP INDEX "IDX_42eb716a37d381cdf566192b2b"`); - await queryRunner.query(`DROP INDEX "IDX_7036f2957151588b813185c794"`); - await queryRunner.query(`DROP INDEX "IDX_f09d543e3acb16c5976bdb31fa"`); - await queryRunner.query(`DROP INDEX "IDX_5f86db6492274e07c1a3cdf286"`); - await queryRunner.query(`DROP INDEX "IDX_e496ca8096d28f6b9b509264dc"`); - await queryRunner.query(`DROP INDEX "IDX_30bf67687f483ace115c5ca642"`); - await queryRunner.query(`DROP INDEX "IDX_7af07790712aa3438ff6773f3b"`); - await queryRunner.query(`DROP INDEX "IDX_4b3593098b6edc9c5afe36b18b"`); - await queryRunner.query(`DROP INDEX "IDX_b77d4dd9562c3a899d9a286fcd"`); - await queryRunner.query(`DROP INDEX "IDX_84234bd1abb873f07329681c83"`); - await queryRunner.query(`DROP INDEX "IDX_55bf20f366979f2436de99206b"`); - await queryRunner.query(`DROP INDEX "IDX_5048e9daccbbbc6d567bb142d3"`); - await queryRunner.query(`DROP INDEX "IDX_f7bf4c62059764c2c2bb40fdab"`); - await queryRunner.query(`DROP INDEX "IDX_8cf3156fd7a6b15c43459c6e3b"`); - await queryRunner.query(`DROP INDEX "IDX_229a41ad465f9205f1f5703291"`); - await queryRunner.query(`DROP INDEX "IDX_0c641990ecf47d2545df4edb75"`); - await queryRunner.query(`DROP INDEX "IDX_234dff3c0b56a6150b95431ab9"`); - await queryRunner.query(`DROP INDEX "IDX_b14489029e4b3aaf4bba5fb524"`); - await queryRunner.query(`DROP INDEX "IDX_437bab3c6061d90f6bb65fd2cc"`); - await queryRunner.query(`DROP INDEX "IDX_bbfa573a8181018851ed0b6357"`); - await queryRunner.query(`DROP INDEX "IDX_a0cd75442dd10d0643a17c4a49"`); - await queryRunner.query(`DROP INDEX "IDX_b070a906db04b44c67c6c2144d"`); - await queryRunner.query(`DROP INDEX "IDX_d41cce6aee1a50bfc062038f9b"`); - await queryRunner.query(`DROP INDEX "IDX_a319e5dbf47e8a17497623beae"`); - await queryRunner.query(`DROP INDEX "IDX_845254b3eaf708ae8a6cac3026"`); - await queryRunner.query(`DROP INDEX "IDX_ed9b95919c672a13008e9487ee"`); - await queryRunner.query(`DROP INDEX "IDX_337e9599f278bd7537fe30876f"`); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_9a3ed15a30ab7e3a37702e6e08" ON "__chart__active_users" ("date", "group") `); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_60c5c6e7e538c09aa274ecd1cf" ON "__chart__active_users" ("date") WHERE "group" IS NULL`); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_3313d7288855ec105b5bbf6c21" ON "__chart__drive" ("date", "group") `); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_ceab80a6729f8e2e6f5b8a1a3d" ON "__chart__drive" ("date") WHERE "group" IS NULL`); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_dd907becf76104e4b656659e6b" ON "__chart__federation" ("date", "group") `); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_eddfed8fb40305a04c6f941050" ON "__chart__federation" ("date") WHERE "group" IS NULL`); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_25a97c02003338124b2b75fdbc" ON "__chart__hashtag" ("date", "group") `); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_53a3604b939e2b479eb2cfaac8" ON "__chart__hashtag" ("date") WHERE "group" IS NULL`); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_39ee857ab2f23493037c6b6631" ON "__chart__instance" ("date", "group") `); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_8111b817b9818c04d7eb8475b1" ON "__chart__instance" ("date") WHERE "group" IS NULL`); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_0a905b992fecd2b5c3fb98759e" ON "__chart__network" ("date", "group") `); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_2082327b2699ce924fa654afc5" ON "__chart__network" ("date") WHERE "group" IS NULL`); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_f09d543e3acb16c5976bdb31fa" ON "__chart__notes" ("date", "group") `); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_e60c358aaced5aab8900a4af31" ON "__chart__notes" ("date") WHERE "group" IS NULL`); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_30bf67687f483ace115c5ca642" ON "__chart__per_user_drive" ("date", "group") `); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_a9a806d466b314f253a1a611c4" ON "__chart__per_user_drive" ("date") WHERE "group" IS NULL`); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_b77d4dd9562c3a899d9a286fcd" ON "__chart__per_user_following" ("date", "group") `); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_dabbb38a51ab86ee3cab291326" ON "__chart__per_user_following" ("date") WHERE "group" IS NULL`); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_5048e9daccbbbc6d567bb142d3" ON "__chart__per_user_notes" ("date", "group") `); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_583a157ed0cf0ed1b5ec2a833f" ON "__chart__per_user_notes" ("date") WHERE "group" IS NULL`); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_229a41ad465f9205f1f5703291" ON "__chart__per_user_reaction" ("date", "group") `); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_3b7697a96f522d0478972e6d6f" ON "__chart__per_user_reaction" ("date") WHERE "group" IS NULL`); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_b14489029e4b3aaf4bba5fb524" ON "__chart__test_grouped" ("date", "group") `); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_da522b4008a9f5d7743b87ad55" ON "__chart__test_grouped" ("date") WHERE "group" IS NULL`); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_a0cd75442dd10d0643a17c4a49" ON "__chart__test_unique" ("date", "group") `); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_16effb2e888f6763673b579f80" ON "__chart__test_unique" ("date") WHERE "group" IS NULL`); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_a319e5dbf47e8a17497623beae" ON "__chart__test" ("date", "group") `); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_dab383a36f3c9db4a0c9b02cf3" ON "__chart__test" ("date") WHERE "group" IS NULL`); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_337e9599f278bd7537fe30876f" ON "__chart__users" ("date", "group") `); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_66feba81e1795d176d06c0b1e6" ON "__chart__users" ("date") WHERE "group" IS NULL`); - } - async down(queryRunner) { - await queryRunner.query(`DROP INDEX "IDX_66feba81e1795d176d06c0b1e6"`); - await queryRunner.query(`DROP INDEX "IDX_337e9599f278bd7537fe30876f"`); - await queryRunner.query(`DROP INDEX "IDX_dab383a36f3c9db4a0c9b02cf3"`); - await queryRunner.query(`DROP INDEX "IDX_a319e5dbf47e8a17497623beae"`); - await queryRunner.query(`DROP INDEX "IDX_16effb2e888f6763673b579f80"`); - await queryRunner.query(`DROP INDEX "IDX_a0cd75442dd10d0643a17c4a49"`); - await queryRunner.query(`DROP INDEX "IDX_da522b4008a9f5d7743b87ad55"`); - await queryRunner.query(`DROP INDEX "IDX_b14489029e4b3aaf4bba5fb524"`); - await queryRunner.query(`DROP INDEX "IDX_3b7697a96f522d0478972e6d6f"`); - await queryRunner.query(`DROP INDEX "IDX_229a41ad465f9205f1f5703291"`); - await queryRunner.query(`DROP INDEX "IDX_583a157ed0cf0ed1b5ec2a833f"`); - await queryRunner.query(`DROP INDEX "IDX_5048e9daccbbbc6d567bb142d3"`); - await queryRunner.query(`DROP INDEX "IDX_dabbb38a51ab86ee3cab291326"`); - await queryRunner.query(`DROP INDEX "IDX_b77d4dd9562c3a899d9a286fcd"`); - await queryRunner.query(`DROP INDEX "IDX_a9a806d466b314f253a1a611c4"`); - await queryRunner.query(`DROP INDEX "IDX_30bf67687f483ace115c5ca642"`); - await queryRunner.query(`DROP INDEX "IDX_e60c358aaced5aab8900a4af31"`); - await queryRunner.query(`DROP INDEX "IDX_f09d543e3acb16c5976bdb31fa"`); - await queryRunner.query(`DROP INDEX "IDX_2082327b2699ce924fa654afc5"`); - await queryRunner.query(`DROP INDEX "IDX_0a905b992fecd2b5c3fb98759e"`); - await queryRunner.query(`DROP INDEX "IDX_8111b817b9818c04d7eb8475b1"`); - await queryRunner.query(`DROP INDEX "IDX_39ee857ab2f23493037c6b6631"`); - await queryRunner.query(`DROP INDEX "IDX_53a3604b939e2b479eb2cfaac8"`); - await queryRunner.query(`DROP INDEX "IDX_25a97c02003338124b2b75fdbc"`); - await queryRunner.query(`DROP INDEX "IDX_eddfed8fb40305a04c6f941050"`); - await queryRunner.query(`DROP INDEX "IDX_dd907becf76104e4b656659e6b"`); - await queryRunner.query(`DROP INDEX "IDX_ceab80a6729f8e2e6f5b8a1a3d"`); - await queryRunner.query(`DROP INDEX "IDX_3313d7288855ec105b5bbf6c21"`); - await queryRunner.query(`DROP INDEX "IDX_60c5c6e7e538c09aa274ecd1cf"`); - await queryRunner.query(`DROP INDEX "IDX_9a3ed15a30ab7e3a37702e6e08"`); - await queryRunner.query(`DROP INDEX "IDX_a9021cc2e1feb5f72d3db6e9f5"`); - await queryRunner.query(`DROP INDEX "IDX_f22169eb10657bded6d875ac8f"`); - await queryRunner.query(`DROP INDEX "IDX_c8cc87bd0f2f4487d17c651fbf"`); - await queryRunner.query(`DROP INDEX "IDX_754499f9b2642336433769518d"`); - await queryRunner.query(`DROP INDEX "IDX_315c779174fe8247ab324f036e"`); - await queryRunner.query(`DROP INDEX "IDX_c5d46cbfda48b1c33ed852e21b"`); - await queryRunner.query(`CREATE INDEX "IDX_337e9599f278bd7537fe30876f" ON "__chart__users" ("date", "group") `); - await queryRunner.query(`CREATE INDEX "IDX_ed9b95919c672a13008e9487ee" ON "__chart__users" ("group") `); - await queryRunner.query(`CREATE INDEX "IDX_845254b3eaf708ae8a6cac3026" ON "__chart__users" ("date") `); - await queryRunner.query(`CREATE INDEX "IDX_a319e5dbf47e8a17497623beae" ON "__chart__test" ("date", "group") `); - await queryRunner.query(`CREATE INDEX "IDX_d41cce6aee1a50bfc062038f9b" ON "__chart__test" ("group") `); - await queryRunner.query(`CREATE INDEX "IDX_b070a906db04b44c67c6c2144d" ON "__chart__test" ("date") `); - await queryRunner.query(`CREATE INDEX "IDX_a0cd75442dd10d0643a17c4a49" ON "__chart__test_unique" ("date", "group") `); - await queryRunner.query(`CREATE INDEX "IDX_bbfa573a8181018851ed0b6357" ON "__chart__test_unique" ("group") `); - await queryRunner.query(`CREATE INDEX "IDX_437bab3c6061d90f6bb65fd2cc" ON "__chart__test_unique" ("date") `); - await queryRunner.query(`CREATE INDEX "IDX_b14489029e4b3aaf4bba5fb524" ON "__chart__test_grouped" ("date", "group") `); - await queryRunner.query(`CREATE INDEX "IDX_234dff3c0b56a6150b95431ab9" ON "__chart__test_grouped" ("group") `); - await queryRunner.query(`CREATE INDEX "IDX_0c641990ecf47d2545df4edb75" ON "__chart__test_grouped" ("date") `); - await queryRunner.query(`CREATE INDEX "IDX_229a41ad465f9205f1f5703291" ON "__chart__per_user_reaction" ("date", "group") `); - await queryRunner.query(`CREATE INDEX "IDX_8cf3156fd7a6b15c43459c6e3b" ON "__chart__per_user_reaction" ("group") `); - await queryRunner.query(`CREATE INDEX "IDX_f7bf4c62059764c2c2bb40fdab" ON "__chart__per_user_reaction" ("date") `); - await queryRunner.query(`CREATE INDEX "IDX_5048e9daccbbbc6d567bb142d3" ON "__chart__per_user_notes" ("date", "group") `); - await queryRunner.query(`CREATE INDEX "IDX_55bf20f366979f2436de99206b" ON "__chart__per_user_notes" ("group") `); - await queryRunner.query(`CREATE INDEX "IDX_84234bd1abb873f07329681c83" ON "__chart__per_user_notes" ("date") `); - await queryRunner.query(`CREATE INDEX "IDX_b77d4dd9562c3a899d9a286fcd" ON "__chart__per_user_following" ("date", "group") `); - await queryRunner.query(`CREATE INDEX "IDX_4b3593098b6edc9c5afe36b18b" ON "__chart__per_user_following" ("group") `); - await queryRunner.query(`CREATE INDEX "IDX_7af07790712aa3438ff6773f3b" ON "__chart__per_user_following" ("date") `); - await queryRunner.query(`CREATE INDEX "IDX_30bf67687f483ace115c5ca642" ON "__chart__per_user_drive" ("date", "group") `); - await queryRunner.query(`CREATE INDEX "IDX_e496ca8096d28f6b9b509264dc" ON "__chart__per_user_drive" ("group") `); - await queryRunner.query(`CREATE INDEX "IDX_5f86db6492274e07c1a3cdf286" ON "__chart__per_user_drive" ("date") `); - await queryRunner.query(`CREATE INDEX "IDX_f09d543e3acb16c5976bdb31fa" ON "__chart__notes" ("date", "group") `); - await queryRunner.query(`CREATE INDEX "IDX_7036f2957151588b813185c794" ON "__chart__notes" ("group") `); - await queryRunner.query(`CREATE INDEX "IDX_42eb716a37d381cdf566192b2b" ON "__chart__notes" ("date") `); - await queryRunner.query(`CREATE INDEX "IDX_0a905b992fecd2b5c3fb98759e" ON "__chart__network" ("date", "group") `); - await queryRunner.query(`CREATE INDEX "IDX_7b5da130992ec9df96712d4290" ON "__chart__network" ("group") `); - await queryRunner.query(`CREATE INDEX "IDX_a1efd3e0048a5f2793a47360dc" ON "__chart__network" ("date") `); - await queryRunner.query(`CREATE INDEX "IDX_39ee857ab2f23493037c6b6631" ON "__chart__instance" ("date", "group") `); - await queryRunner.query(`CREATE INDEX "IDX_da8a46ba84ca1d8bb5a29bfb63" ON "__chart__instance" ("group") `); - await queryRunner.query(`CREATE INDEX "IDX_6b8f34a1a64b06014b6fb66824" ON "__chart__instance" ("date") `); - await queryRunner.query(`CREATE INDEX "IDX_25a97c02003338124b2b75fdbc" ON "__chart__hashtag" ("date", "group") `); - await queryRunner.query(`CREATE INDEX "IDX_99a7d2faaef84a6f728d714ad6" ON "__chart__hashtag" ("group") `); - await queryRunner.query(`CREATE INDEX "IDX_07747a1038c05f532a718fe1de" ON "__chart__hashtag" ("date") `); - await queryRunner.query(`CREATE INDEX "IDX_dd907becf76104e4b656659e6b" ON "__chart__federation" ("date", "group") `); - await queryRunner.query(`CREATE INDEX "IDX_76e87c7bfc5d925fcbba405d84" ON "__chart__federation" ("group") `); - await queryRunner.query(`CREATE INDEX "IDX_36cb699c49580d4e6c2e6159f9" ON "__chart__federation" ("date") `); - await queryRunner.query(`CREATE INDEX "IDX_3313d7288855ec105b5bbf6c21" ON "__chart__drive" ("date", "group") `); - await queryRunner.query(`CREATE INDEX "IDX_7a170f67425e62a8fabb76c872" ON "__chart__drive" ("group") `); - await queryRunner.query(`CREATE INDEX "IDX_13565815f618a1ff53886c5b28" ON "__chart__drive" ("date") `); - await queryRunner.query(`CREATE INDEX "IDX_9a3ed15a30ab7e3a37702e6e08" ON "__chart__active_users" ("date", "group") `); - await queryRunner.query(`CREATE INDEX "IDX_00ed5f86db1f7efafb1978bf21" ON "__chart__active_users" ("group") `); - await queryRunner.query(`CREATE INDEX "IDX_0ad37b7ef50f4ddc84363d7ccc" ON "__chart__active_users" ("date") `); - } + constructor() { + this.name = "chartReindex1629004542760"; + } + async up(queryRunner) { + await queryRunner.query( + `DELETE FROM "__chart__active_users" a USING "__chart__active_users" b WHERE a.id < b.id AND ((a.group IS NULL AND b.group IS NULL) OR a.group = b.group) AND a.date = b.date;`, + ); + await queryRunner.query( + `DELETE FROM "__chart__drive" a USING "__chart__drive" b WHERE a.id < b.id AND ((a.group IS NULL AND b.group IS NULL) OR a.group = b.group) AND a.date = b.date;`, + ); + await queryRunner.query( + `DELETE FROM "__chart__federation" a USING "__chart__federation" b WHERE a.id < b.id AND ((a.group IS NULL AND b.group IS NULL) OR a.group = b.group) AND a.date = b.date;`, + ); + await queryRunner.query( + `DELETE FROM "__chart__hashtag" a USING "__chart__hashtag" b WHERE a.id < b.id AND ((a.group IS NULL AND b.group IS NULL) OR a.group = b.group) AND a.date = b.date;`, + ); + await queryRunner.query( + `DELETE FROM "__chart__instance" a USING "__chart__instance" b WHERE a.id < b.id AND ((a.group IS NULL AND b.group IS NULL) OR a.group = b.group) AND a.date = b.date;`, + ); + await queryRunner.query( + `DELETE FROM "__chart__network" a USING "__chart__network" b WHERE a.id < b.id AND ((a.group IS NULL AND b.group IS NULL) OR a.group = b.group) AND a.date = b.date;`, + ); + await queryRunner.query( + `DELETE FROM "__chart__notes" a USING "__chart__notes" b WHERE a.id < b.id AND ((a.group IS NULL AND b.group IS NULL) OR a.group = b.group) AND a.date = b.date;`, + ); + await queryRunner.query( + `DELETE FROM "__chart__per_user_drive" a USING "__chart__per_user_drive" b WHERE a.id < b.id AND ((a.group IS NULL AND b.group IS NULL) OR a.group = b.group) AND a.date = b.date;`, + ); + await queryRunner.query( + `DELETE FROM "__chart__per_user_following" a USING "__chart__per_user_following" b WHERE a.id < b.id AND ((a.group IS NULL AND b.group IS NULL) OR a.group = b.group) AND a.date = b.date;`, + ); + await queryRunner.query( + `DELETE FROM "__chart__per_user_notes" a USING "__chart__per_user_notes" b WHERE a.id < b.id AND ((a.group IS NULL AND b.group IS NULL) OR a.group = b.group) AND a.date = b.date;`, + ); + await queryRunner.query( + `DELETE FROM "__chart__per_user_reaction" a USING "__chart__per_user_reaction" b WHERE a.id < b.id AND ((a.group IS NULL AND b.group IS NULL) OR a.group = b.group) AND a.date = b.date;`, + ); + await queryRunner.query( + `DELETE FROM "__chart__test_grouped" a USING "__chart__test_grouped" b WHERE a.id < b.id AND ((a.group IS NULL AND b.group IS NULL) OR a.group = b.group) AND a.date = b.date;`, + ); + await queryRunner.query( + `DELETE FROM "__chart__test_unique" a USING "__chart__test_unique" b WHERE a.id < b.id AND ((a.group IS NULL AND b.group IS NULL) OR a.group = b.group) AND a.date = b.date;`, + ); + await queryRunner.query( + `DELETE FROM "__chart__users" a USING "__chart__users" b WHERE a.id < b.id AND ((a.group IS NULL AND b.group IS NULL) OR a.group = b.group) AND a.date = b.date;`, + ); + await queryRunner.query(`DROP INDEX "IDX_0ad37b7ef50f4ddc84363d7ccc"`); + await queryRunner.query(`DROP INDEX "IDX_00ed5f86db1f7efafb1978bf21"`); + await queryRunner.query(`DROP INDEX "IDX_9a3ed15a30ab7e3a37702e6e08"`); + await queryRunner.query(`DROP INDEX "IDX_13565815f618a1ff53886c5b28"`); + await queryRunner.query(`DROP INDEX "IDX_7a170f67425e62a8fabb76c872"`); + await queryRunner.query(`DROP INDEX "IDX_3313d7288855ec105b5bbf6c21"`); + await queryRunner.query(`DROP INDEX "IDX_36cb699c49580d4e6c2e6159f9"`); + await queryRunner.query(`DROP INDEX "IDX_76e87c7bfc5d925fcbba405d84"`); + await queryRunner.query(`DROP INDEX "IDX_dd907becf76104e4b656659e6b"`); + await queryRunner.query(`DROP INDEX "IDX_07747a1038c05f532a718fe1de"`); + await queryRunner.query(`DROP INDEX "IDX_99a7d2faaef84a6f728d714ad6"`); + await queryRunner.query(`DROP INDEX "IDX_25a97c02003338124b2b75fdbc"`); + await queryRunner.query(`DROP INDEX "IDX_6b8f34a1a64b06014b6fb66824"`); + await queryRunner.query(`DROP INDEX "IDX_da8a46ba84ca1d8bb5a29bfb63"`); + await queryRunner.query(`DROP INDEX "IDX_39ee857ab2f23493037c6b6631"`); + await queryRunner.query(`DROP INDEX "IDX_a1efd3e0048a5f2793a47360dc"`); + await queryRunner.query(`DROP INDEX "IDX_7b5da130992ec9df96712d4290"`); + await queryRunner.query(`DROP INDEX "IDX_0a905b992fecd2b5c3fb98759e"`); + await queryRunner.query(`DROP INDEX "IDX_42eb716a37d381cdf566192b2b"`); + await queryRunner.query(`DROP INDEX "IDX_7036f2957151588b813185c794"`); + await queryRunner.query(`DROP INDEX "IDX_f09d543e3acb16c5976bdb31fa"`); + await queryRunner.query(`DROP INDEX "IDX_5f86db6492274e07c1a3cdf286"`); + await queryRunner.query(`DROP INDEX "IDX_e496ca8096d28f6b9b509264dc"`); + await queryRunner.query(`DROP INDEX "IDX_30bf67687f483ace115c5ca642"`); + await queryRunner.query(`DROP INDEX "IDX_7af07790712aa3438ff6773f3b"`); + await queryRunner.query(`DROP INDEX "IDX_4b3593098b6edc9c5afe36b18b"`); + await queryRunner.query(`DROP INDEX "IDX_b77d4dd9562c3a899d9a286fcd"`); + await queryRunner.query(`DROP INDEX "IDX_84234bd1abb873f07329681c83"`); + await queryRunner.query(`DROP INDEX "IDX_55bf20f366979f2436de99206b"`); + await queryRunner.query(`DROP INDEX "IDX_5048e9daccbbbc6d567bb142d3"`); + await queryRunner.query(`DROP INDEX "IDX_f7bf4c62059764c2c2bb40fdab"`); + await queryRunner.query(`DROP INDEX "IDX_8cf3156fd7a6b15c43459c6e3b"`); + await queryRunner.query(`DROP INDEX "IDX_229a41ad465f9205f1f5703291"`); + await queryRunner.query(`DROP INDEX "IDX_0c641990ecf47d2545df4edb75"`); + await queryRunner.query(`DROP INDEX "IDX_234dff3c0b56a6150b95431ab9"`); + await queryRunner.query(`DROP INDEX "IDX_b14489029e4b3aaf4bba5fb524"`); + await queryRunner.query(`DROP INDEX "IDX_437bab3c6061d90f6bb65fd2cc"`); + await queryRunner.query(`DROP INDEX "IDX_bbfa573a8181018851ed0b6357"`); + await queryRunner.query(`DROP INDEX "IDX_a0cd75442dd10d0643a17c4a49"`); + await queryRunner.query(`DROP INDEX "IDX_b070a906db04b44c67c6c2144d"`); + await queryRunner.query(`DROP INDEX "IDX_d41cce6aee1a50bfc062038f9b"`); + await queryRunner.query(`DROP INDEX "IDX_a319e5dbf47e8a17497623beae"`); + await queryRunner.query(`DROP INDEX "IDX_845254b3eaf708ae8a6cac3026"`); + await queryRunner.query(`DROP INDEX "IDX_ed9b95919c672a13008e9487ee"`); + await queryRunner.query(`DROP INDEX "IDX_337e9599f278bd7537fe30876f"`); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_9a3ed15a30ab7e3a37702e6e08" ON "__chart__active_users" ("date", "group") `, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_60c5c6e7e538c09aa274ecd1cf" ON "__chart__active_users" ("date") WHERE "group" IS NULL`, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_3313d7288855ec105b5bbf6c21" ON "__chart__drive" ("date", "group") `, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_ceab80a6729f8e2e6f5b8a1a3d" ON "__chart__drive" ("date") WHERE "group" IS NULL`, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_dd907becf76104e4b656659e6b" ON "__chart__federation" ("date", "group") `, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_eddfed8fb40305a04c6f941050" ON "__chart__federation" ("date") WHERE "group" IS NULL`, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_25a97c02003338124b2b75fdbc" ON "__chart__hashtag" ("date", "group") `, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_53a3604b939e2b479eb2cfaac8" ON "__chart__hashtag" ("date") WHERE "group" IS NULL`, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_39ee857ab2f23493037c6b6631" ON "__chart__instance" ("date", "group") `, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_8111b817b9818c04d7eb8475b1" ON "__chart__instance" ("date") WHERE "group" IS NULL`, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_0a905b992fecd2b5c3fb98759e" ON "__chart__network" ("date", "group") `, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_2082327b2699ce924fa654afc5" ON "__chart__network" ("date") WHERE "group" IS NULL`, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_f09d543e3acb16c5976bdb31fa" ON "__chart__notes" ("date", "group") `, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_e60c358aaced5aab8900a4af31" ON "__chart__notes" ("date") WHERE "group" IS NULL`, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_30bf67687f483ace115c5ca642" ON "__chart__per_user_drive" ("date", "group") `, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_a9a806d466b314f253a1a611c4" ON "__chart__per_user_drive" ("date") WHERE "group" IS NULL`, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_b77d4dd9562c3a899d9a286fcd" ON "__chart__per_user_following" ("date", "group") `, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_dabbb38a51ab86ee3cab291326" ON "__chart__per_user_following" ("date") WHERE "group" IS NULL`, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_5048e9daccbbbc6d567bb142d3" ON "__chart__per_user_notes" ("date", "group") `, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_583a157ed0cf0ed1b5ec2a833f" ON "__chart__per_user_notes" ("date") WHERE "group" IS NULL`, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_229a41ad465f9205f1f5703291" ON "__chart__per_user_reaction" ("date", "group") `, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_3b7697a96f522d0478972e6d6f" ON "__chart__per_user_reaction" ("date") WHERE "group" IS NULL`, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_b14489029e4b3aaf4bba5fb524" ON "__chart__test_grouped" ("date", "group") `, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_da522b4008a9f5d7743b87ad55" ON "__chart__test_grouped" ("date") WHERE "group" IS NULL`, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_a0cd75442dd10d0643a17c4a49" ON "__chart__test_unique" ("date", "group") `, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_16effb2e888f6763673b579f80" ON "__chart__test_unique" ("date") WHERE "group" IS NULL`, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_a319e5dbf47e8a17497623beae" ON "__chart__test" ("date", "group") `, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_dab383a36f3c9db4a0c9b02cf3" ON "__chart__test" ("date") WHERE "group" IS NULL`, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_337e9599f278bd7537fe30876f" ON "__chart__users" ("date", "group") `, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_66feba81e1795d176d06c0b1e6" ON "__chart__users" ("date") WHERE "group" IS NULL`, + ); + } + async down(queryRunner) { + await queryRunner.query(`DROP INDEX "IDX_66feba81e1795d176d06c0b1e6"`); + await queryRunner.query(`DROP INDEX "IDX_337e9599f278bd7537fe30876f"`); + await queryRunner.query(`DROP INDEX "IDX_dab383a36f3c9db4a0c9b02cf3"`); + await queryRunner.query(`DROP INDEX "IDX_a319e5dbf47e8a17497623beae"`); + await queryRunner.query(`DROP INDEX "IDX_16effb2e888f6763673b579f80"`); + await queryRunner.query(`DROP INDEX "IDX_a0cd75442dd10d0643a17c4a49"`); + await queryRunner.query(`DROP INDEX "IDX_da522b4008a9f5d7743b87ad55"`); + await queryRunner.query(`DROP INDEX "IDX_b14489029e4b3aaf4bba5fb524"`); + await queryRunner.query(`DROP INDEX "IDX_3b7697a96f522d0478972e6d6f"`); + await queryRunner.query(`DROP INDEX "IDX_229a41ad465f9205f1f5703291"`); + await queryRunner.query(`DROP INDEX "IDX_583a157ed0cf0ed1b5ec2a833f"`); + await queryRunner.query(`DROP INDEX "IDX_5048e9daccbbbc6d567bb142d3"`); + await queryRunner.query(`DROP INDEX "IDX_dabbb38a51ab86ee3cab291326"`); + await queryRunner.query(`DROP INDEX "IDX_b77d4dd9562c3a899d9a286fcd"`); + await queryRunner.query(`DROP INDEX "IDX_a9a806d466b314f253a1a611c4"`); + await queryRunner.query(`DROP INDEX "IDX_30bf67687f483ace115c5ca642"`); + await queryRunner.query(`DROP INDEX "IDX_e60c358aaced5aab8900a4af31"`); + await queryRunner.query(`DROP INDEX "IDX_f09d543e3acb16c5976bdb31fa"`); + await queryRunner.query(`DROP INDEX "IDX_2082327b2699ce924fa654afc5"`); + await queryRunner.query(`DROP INDEX "IDX_0a905b992fecd2b5c3fb98759e"`); + await queryRunner.query(`DROP INDEX "IDX_8111b817b9818c04d7eb8475b1"`); + await queryRunner.query(`DROP INDEX "IDX_39ee857ab2f23493037c6b6631"`); + await queryRunner.query(`DROP INDEX "IDX_53a3604b939e2b479eb2cfaac8"`); + await queryRunner.query(`DROP INDEX "IDX_25a97c02003338124b2b75fdbc"`); + await queryRunner.query(`DROP INDEX "IDX_eddfed8fb40305a04c6f941050"`); + await queryRunner.query(`DROP INDEX "IDX_dd907becf76104e4b656659e6b"`); + await queryRunner.query(`DROP INDEX "IDX_ceab80a6729f8e2e6f5b8a1a3d"`); + await queryRunner.query(`DROP INDEX "IDX_3313d7288855ec105b5bbf6c21"`); + await queryRunner.query(`DROP INDEX "IDX_60c5c6e7e538c09aa274ecd1cf"`); + await queryRunner.query(`DROP INDEX "IDX_9a3ed15a30ab7e3a37702e6e08"`); + await queryRunner.query(`DROP INDEX "IDX_a9021cc2e1feb5f72d3db6e9f5"`); + await queryRunner.query(`DROP INDEX "IDX_f22169eb10657bded6d875ac8f"`); + await queryRunner.query(`DROP INDEX "IDX_c8cc87bd0f2f4487d17c651fbf"`); + await queryRunner.query(`DROP INDEX "IDX_754499f9b2642336433769518d"`); + await queryRunner.query(`DROP INDEX "IDX_315c779174fe8247ab324f036e"`); + await queryRunner.query(`DROP INDEX "IDX_c5d46cbfda48b1c33ed852e21b"`); + await queryRunner.query( + `CREATE INDEX "IDX_337e9599f278bd7537fe30876f" ON "__chart__users" ("date", "group") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_ed9b95919c672a13008e9487ee" ON "__chart__users" ("group") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_845254b3eaf708ae8a6cac3026" ON "__chart__users" ("date") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_a319e5dbf47e8a17497623beae" ON "__chart__test" ("date", "group") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_d41cce6aee1a50bfc062038f9b" ON "__chart__test" ("group") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_b070a906db04b44c67c6c2144d" ON "__chart__test" ("date") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_a0cd75442dd10d0643a17c4a49" ON "__chart__test_unique" ("date", "group") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_bbfa573a8181018851ed0b6357" ON "__chart__test_unique" ("group") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_437bab3c6061d90f6bb65fd2cc" ON "__chart__test_unique" ("date") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_b14489029e4b3aaf4bba5fb524" ON "__chart__test_grouped" ("date", "group") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_234dff3c0b56a6150b95431ab9" ON "__chart__test_grouped" ("group") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_0c641990ecf47d2545df4edb75" ON "__chart__test_grouped" ("date") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_229a41ad465f9205f1f5703291" ON "__chart__per_user_reaction" ("date", "group") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_8cf3156fd7a6b15c43459c6e3b" ON "__chart__per_user_reaction" ("group") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_f7bf4c62059764c2c2bb40fdab" ON "__chart__per_user_reaction" ("date") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_5048e9daccbbbc6d567bb142d3" ON "__chart__per_user_notes" ("date", "group") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_55bf20f366979f2436de99206b" ON "__chart__per_user_notes" ("group") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_84234bd1abb873f07329681c83" ON "__chart__per_user_notes" ("date") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_b77d4dd9562c3a899d9a286fcd" ON "__chart__per_user_following" ("date", "group") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_4b3593098b6edc9c5afe36b18b" ON "__chart__per_user_following" ("group") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_7af07790712aa3438ff6773f3b" ON "__chart__per_user_following" ("date") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_30bf67687f483ace115c5ca642" ON "__chart__per_user_drive" ("date", "group") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_e496ca8096d28f6b9b509264dc" ON "__chart__per_user_drive" ("group") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_5f86db6492274e07c1a3cdf286" ON "__chart__per_user_drive" ("date") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_f09d543e3acb16c5976bdb31fa" ON "__chart__notes" ("date", "group") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_7036f2957151588b813185c794" ON "__chart__notes" ("group") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_42eb716a37d381cdf566192b2b" ON "__chart__notes" ("date") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_0a905b992fecd2b5c3fb98759e" ON "__chart__network" ("date", "group") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_7b5da130992ec9df96712d4290" ON "__chart__network" ("group") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_a1efd3e0048a5f2793a47360dc" ON "__chart__network" ("date") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_39ee857ab2f23493037c6b6631" ON "__chart__instance" ("date", "group") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_da8a46ba84ca1d8bb5a29bfb63" ON "__chart__instance" ("group") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_6b8f34a1a64b06014b6fb66824" ON "__chart__instance" ("date") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_25a97c02003338124b2b75fdbc" ON "__chart__hashtag" ("date", "group") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_99a7d2faaef84a6f728d714ad6" ON "__chart__hashtag" ("group") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_07747a1038c05f532a718fe1de" ON "__chart__hashtag" ("date") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_dd907becf76104e4b656659e6b" ON "__chart__federation" ("date", "group") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_76e87c7bfc5d925fcbba405d84" ON "__chart__federation" ("group") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_36cb699c49580d4e6c2e6159f9" ON "__chart__federation" ("date") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_3313d7288855ec105b5bbf6c21" ON "__chart__drive" ("date", "group") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_7a170f67425e62a8fabb76c872" ON "__chart__drive" ("group") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_13565815f618a1ff53886c5b28" ON "__chart__drive" ("date") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_9a3ed15a30ab7e3a37702e6e08" ON "__chart__active_users" ("date", "group") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_00ed5f86db1f7efafb1978bf21" ON "__chart__active_users" ("group") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_0ad37b7ef50f4ddc84363d7ccc" ON "__chart__active_users" ("date") `, + ); + } } diff --git a/packages/backend/migration/1629024377804-deepl-integration.js b/packages/backend/migration/1629024377804-deepl-integration.js index 19c49ffcde..1dd5465fba 100644 --- a/packages/backend/migration/1629024377804-deepl-integration.js +++ b/packages/backend/migration/1629024377804-deepl-integration.js @@ -1,13 +1,13 @@ - - export class deeplIntegration1629024377804 { - constructor() { - this.name = 'deeplIntegration1629024377804'; - } - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "meta" ADD "deeplAuthKey" character varying(128)`); - } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "deeplAuthKey"`); - } + constructor() { + this.name = "deeplIntegration1629024377804"; + } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "meta" ADD "deeplAuthKey" character varying(128)`, + ); + } + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "deeplAuthKey"`); + } } diff --git a/packages/backend/migration/1629288472000-fix-channel-userId.js b/packages/backend/migration/1629288472000-fix-channel-userId.js index 02a1199b09..e02549b11a 100644 --- a/packages/backend/migration/1629288472000-fix-channel-userId.js +++ b/packages/backend/migration/1629288472000-fix-channel-userId.js @@ -1,13 +1,15 @@ - - export class fixChannelUserId1629288472000 { - constructor() { - this.name = 'fixChannelUserId1629288472000'; - } - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "channel" ALTER COLUMN "userId" DROP NOT NULL;`); - } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "channel" ALTER COLUMN "userId" SET NOT NULL;`); - } + constructor() { + this.name = "fixChannelUserId1629288472000"; + } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "channel" ALTER COLUMN "userId" DROP NOT NULL;`, + ); + } + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "channel" ALTER COLUMN "userId" SET NOT NULL;`, + ); + } } diff --git a/packages/backend/migration/1629512953000-user-is-deleted.js b/packages/backend/migration/1629512953000-user-is-deleted.js index a7848d5690..448535276f 100644 --- a/packages/backend/migration/1629512953000-user-is-deleted.js +++ b/packages/backend/migration/1629512953000-user-is-deleted.js @@ -1,14 +1,16 @@ - - export class isUserDeleted1629512953000 { - constructor() { - this.name = 'isUserDeleted1629512953000'; - } - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "user" ADD "isDeleted" boolean NOT NULL DEFAULT false`); - await queryRunner.query(`COMMENT ON COLUMN "user"."isDeleted" IS 'Whether the User is deleted.'`); - } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "isDeleted"`); - } + constructor() { + this.name = "isUserDeleted1629512953000"; + } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "user" ADD "isDeleted" boolean NOT NULL DEFAULT false`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "user"."isDeleted" IS 'Whether the User is deleted.'`, + ); + } + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "isDeleted"`); + } } diff --git a/packages/backend/migration/1629778475000-deepl-integration2.js b/packages/backend/migration/1629778475000-deepl-integration2.js index 699f06c768..138e00d247 100644 --- a/packages/backend/migration/1629778475000-deepl-integration2.js +++ b/packages/backend/migration/1629778475000-deepl-integration2.js @@ -1,13 +1,13 @@ - - export class deeplIntegration21629778475000 { - constructor() { - this.name = 'deeplIntegration21629778475000'; - } - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "meta" ADD "deeplIsPro" boolean NOT NULL DEFAULT false`); - } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "deeplIsPro"`); - } + constructor() { + this.name = "deeplIntegration21629778475000"; + } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "meta" ADD "deeplIsPro" boolean NOT NULL DEFAULT false`, + ); + } + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "deeplIsPro"`); + } } diff --git a/packages/backend/migration/1629833361000-AddShowTLReplies.js b/packages/backend/migration/1629833361000-AddShowTLReplies.js index 5d4c938a7b..78e9f417b7 100644 --- a/packages/backend/migration/1629833361000-AddShowTLReplies.js +++ b/packages/backend/migration/1629833361000-AddShowTLReplies.js @@ -1,14 +1,18 @@ - - export class addShowTLReplies1629833361000 { constructor() { - this.name = 'addShowTLReplies1629833361000'; + this.name = "addShowTLReplies1629833361000"; } async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "user" ADD "showTimelineReplies" boolean NOT NULL DEFAULT false`); - await queryRunner.query(`COMMENT ON COLUMN "user"."showTimelineReplies" IS 'Whether to show users replying to other users in the timeline.'`); + await queryRunner.query( + `ALTER TABLE "user" ADD "showTimelineReplies" boolean NOT NULL DEFAULT false`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "user"."showTimelineReplies" IS 'Whether to show users replying to other users in the timeline.'`, + ); } async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "showTimelineReplies"`); + await queryRunner.query( + `ALTER TABLE "user" DROP COLUMN "showTimelineReplies"`, + ); } } diff --git a/packages/backend/migration/1629968054000_userInstanceBlocks.js b/packages/backend/migration/1629968054000_userInstanceBlocks.js index 1f202d9f66..666aaba284 100644 --- a/packages/backend/migration/1629968054000_userInstanceBlocks.js +++ b/packages/backend/migration/1629968054000_userInstanceBlocks.js @@ -1,14 +1,18 @@ - - export class userInstanceBlocks1629968054000 { constructor() { - this.name = 'userInstanceBlocks1629968054000'; + this.name = "userInstanceBlocks1629968054000"; } async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "user_profile" ADD "mutedInstances" jsonb NOT NULL DEFAULT '[]'`); - await queryRunner.query(`COMMENT ON COLUMN "user_profile"."mutedInstances" IS 'List of instances muted by the user.'`); + await queryRunner.query( + `ALTER TABLE "user_profile" ADD "mutedInstances" jsonb NOT NULL DEFAULT '[]'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "user_profile"."mutedInstances" IS 'List of instances muted by the user.'`, + ); } async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "mutedInstances"`); + await queryRunner.query( + `ALTER TABLE "user_profile" DROP COLUMN "mutedInstances"`, + ); } } diff --git a/packages/backend/migration/1633068642000-email-required-for-signup.js b/packages/backend/migration/1633068642000-email-required-for-signup.js index d592f3ca21..45c0894a75 100644 --- a/packages/backend/migration/1633068642000-email-required-for-signup.js +++ b/packages/backend/migration/1633068642000-email-required-for-signup.js @@ -1,13 +1,15 @@ - - export class emailRequiredForSignup1633068642000 { - constructor() { - this.name = 'emailRequiredForSignup1633068642000'; - } - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "meta" ADD "emailRequiredForSignup" boolean NOT NULL DEFAULT false`); - } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "emailRequiredForSignup"`); - } + constructor() { + this.name = "emailRequiredForSignup1633068642000"; + } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "meta" ADD "emailRequiredForSignup" boolean NOT NULL DEFAULT false`, + ); + } + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "meta" DROP COLUMN "emailRequiredForSignup"`, + ); + } } diff --git a/packages/backend/migration/1633071909016-user-pending.js b/packages/backend/migration/1633071909016-user-pending.js index 17cf5c11be..b37e51629d 100644 --- a/packages/backend/migration/1633071909016-user-pending.js +++ b/packages/backend/migration/1633071909016-user-pending.js @@ -1,15 +1,17 @@ - - export class userPending1633071909016 { - constructor() { - this.name = 'userPending1633071909016'; - } - async up(queryRunner) { - await queryRunner.query(`CREATE TABLE "user_pending" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "code" character varying(128) NOT NULL, "username" character varying(128) NOT NULL, "email" character varying(128) NOT NULL, "password" character varying(128) NOT NULL, CONSTRAINT "PK_d4c84e013c98ec02d19b8fbbafa" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_4e5c4c99175638ec0761714ab0" ON "user_pending" ("code") `); - } - async down(queryRunner) { - await queryRunner.query(`DROP INDEX "IDX_4e5c4c99175638ec0761714ab0"`); - await queryRunner.query(`DROP TABLE "user_pending"`); - } + constructor() { + this.name = "userPending1633071909016"; + } + async up(queryRunner) { + await queryRunner.query( + `CREATE TABLE "user_pending" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "code" character varying(128) NOT NULL, "username" character varying(128) NOT NULL, "email" character varying(128) NOT NULL, "password" character varying(128) NOT NULL, CONSTRAINT "PK_d4c84e013c98ec02d19b8fbbafa" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_4e5c4c99175638ec0761714ab0" ON "user_pending" ("code") `, + ); + } + async down(queryRunner) { + await queryRunner.query(`DROP INDEX "IDX_4e5c4c99175638ec0761714ab0"`); + await queryRunner.query(`DROP TABLE "user_pending"`); + } } diff --git a/packages/backend/migration/1634486652000-user-public-reactions.js b/packages/backend/migration/1634486652000-user-public-reactions.js index e741122491..95b957f11c 100644 --- a/packages/backend/migration/1634486652000-user-public-reactions.js +++ b/packages/backend/migration/1634486652000-user-public-reactions.js @@ -1,13 +1,15 @@ - - export class userPublicReactions1634486652000 { - constructor() { - this.name = 'userPublicReactions1634486652000'; - } - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "user_profile" ADD "publicReactions" boolean NOT NULL DEFAULT false`); - } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "publicReactions"`); - } + constructor() { + this.name = "userPublicReactions1634486652000"; + } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "user_profile" ADD "publicReactions" boolean NOT NULL DEFAULT false`, + ); + } + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "user_profile" DROP COLUMN "publicReactions"`, + ); + } } diff --git a/packages/backend/migration/1634902659689-delete-log.js b/packages/backend/migration/1634902659689-delete-log.js index 555a0020c3..fc148f0988 100644 --- a/packages/backend/migration/1634902659689-delete-log.js +++ b/packages/backend/migration/1634902659689-delete-log.js @@ -1,12 +1,9 @@ - - export class deleteLog1634902659689 { - constructor() { - this.name = 'deleteLog1634902659689'; - } - async up(queryRunner) { - await queryRunner.query(`DROP TABLE "log"`); - } - async down(queryRunner) { - } + constructor() { + this.name = "deleteLog1634902659689"; + } + async up(queryRunner) { + await queryRunner.query(`DROP TABLE "log"`); + } + async down(queryRunner) {} } diff --git a/packages/backend/migration/1635500777168-note-thread-mute.js b/packages/backend/migration/1635500777168-note-thread-mute.js index a790cace33..5fcfa40c07 100644 --- a/packages/backend/migration/1635500777168-note-thread-mute.js +++ b/packages/backend/migration/1635500777168-note-thread-mute.js @@ -1,25 +1,47 @@ - - export class noteThreadMute1635500777168 { - constructor() { - this.name = 'noteThreadMute1635500777168'; - } - async up(queryRunner) { - await queryRunner.query(`CREATE TABLE "note_thread_muting" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "userId" character varying(32) NOT NULL, "threadId" character varying(256) NOT NULL, CONSTRAINT "PK_ec5936d94d1a0369646d12a3a47" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE INDEX "IDX_29c11c7deb06615076f8c95b80" ON "note_thread_muting" ("userId") `); - await queryRunner.query(`CREATE INDEX "IDX_c426394644267453e76f036926" ON "note_thread_muting" ("threadId") `); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_ae7aab18a2641d3e5f25e0c4ea" ON "note_thread_muting" ("userId", "threadId") `); - await queryRunner.query(`ALTER TABLE "note" ADD "threadId" character varying(256)`); - await queryRunner.query(`CREATE INDEX "IDX_d4ebdef929896d6dc4a3c5bb48" ON "note" ("threadId") `); - await queryRunner.query(`ALTER TABLE "note_thread_muting" ADD CONSTRAINT "FK_29c11c7deb06615076f8c95b80a" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "note_thread_muting" DROP CONSTRAINT "FK_29c11c7deb06615076f8c95b80a"`); - await queryRunner.query(`DROP INDEX "public"."IDX_d4ebdef929896d6dc4a3c5bb48"`); - await queryRunner.query(`ALTER TABLE "note" DROP COLUMN "threadId"`); - await queryRunner.query(`DROP INDEX "public"."IDX_ae7aab18a2641d3e5f25e0c4ea"`); - await queryRunner.query(`DROP INDEX "public"."IDX_c426394644267453e76f036926"`); - await queryRunner.query(`DROP INDEX "public"."IDX_29c11c7deb06615076f8c95b80"`); - await queryRunner.query(`DROP TABLE "note_thread_muting"`); - } + constructor() { + this.name = "noteThreadMute1635500777168"; + } + async up(queryRunner) { + await queryRunner.query( + `CREATE TABLE "note_thread_muting" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "userId" character varying(32) NOT NULL, "threadId" character varying(256) NOT NULL, CONSTRAINT "PK_ec5936d94d1a0369646d12a3a47" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE INDEX "IDX_29c11c7deb06615076f8c95b80" ON "note_thread_muting" ("userId") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_c426394644267453e76f036926" ON "note_thread_muting" ("threadId") `, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_ae7aab18a2641d3e5f25e0c4ea" ON "note_thread_muting" ("userId", "threadId") `, + ); + await queryRunner.query( + `ALTER TABLE "note" ADD "threadId" character varying(256)`, + ); + await queryRunner.query( + `CREATE INDEX "IDX_d4ebdef929896d6dc4a3c5bb48" ON "note" ("threadId") `, + ); + await queryRunner.query( + `ALTER TABLE "note_thread_muting" ADD CONSTRAINT "FK_29c11c7deb06615076f8c95b80a" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + } + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "note_thread_muting" DROP CONSTRAINT "FK_29c11c7deb06615076f8c95b80a"`, + ); + await queryRunner.query( + `DROP INDEX "public"."IDX_d4ebdef929896d6dc4a3c5bb48"`, + ); + await queryRunner.query(`ALTER TABLE "note" DROP COLUMN "threadId"`); + await queryRunner.query( + `DROP INDEX "public"."IDX_ae7aab18a2641d3e5f25e0c4ea"`, + ); + await queryRunner.query( + `DROP INDEX "public"."IDX_c426394644267453e76f036926"`, + ); + await queryRunner.query( + `DROP INDEX "public"."IDX_29c11c7deb06615076f8c95b80"`, + ); + await queryRunner.query(`DROP TABLE "note_thread_muting"`); + } } diff --git a/packages/backend/migration/1636197624383-ff-visibility.js b/packages/backend/migration/1636197624383-ff-visibility.js index 89028f3c22..a3af8f7186 100644 --- a/packages/backend/migration/1636197624383-ff-visibility.js +++ b/packages/backend/migration/1636197624383-ff-visibility.js @@ -1,15 +1,21 @@ - - export class ffVisibility1636197624383 { - constructor() { - this.name = 'ffVisibility1636197624383'; - } - async up(queryRunner) { - await queryRunner.query(`CREATE TYPE "public"."user_profile_ffvisibility_enum" AS ENUM('public', 'followers', 'private')`); - await queryRunner.query(`ALTER TABLE "user_profile" ADD "ffVisibility" "public"."user_profile_ffvisibility_enum" NOT NULL DEFAULT 'public'`); - } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "ffVisibility"`); - await queryRunner.query(`DROP TYPE "public"."user_profile_ffvisibility_enum"`); - } + constructor() { + this.name = "ffVisibility1636197624383"; + } + async up(queryRunner) { + await queryRunner.query( + `CREATE TYPE "public"."user_profile_ffvisibility_enum" AS ENUM('public', 'followers', 'private')`, + ); + await queryRunner.query( + `ALTER TABLE "user_profile" ADD "ffVisibility" "public"."user_profile_ffvisibility_enum" NOT NULL DEFAULT 'public'`, + ); + } + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "user_profile" DROP COLUMN "ffVisibility"`, + ); + await queryRunner.query( + `DROP TYPE "public"."user_profile_ffvisibility_enum"`, + ); + } } diff --git a/packages/backend/migration/1636697408073-remove-via-mobile.js b/packages/backend/migration/1636697408073-remove-via-mobile.js index 36e96fd21e..f862760249 100644 --- a/packages/backend/migration/1636697408073-remove-via-mobile.js +++ b/packages/backend/migration/1636697408073-remove-via-mobile.js @@ -1,13 +1,13 @@ - - export class removeViaMobile1636697408073 { - name = 'removeViaMobile1636697408073' + name = "removeViaMobile1636697408073"; - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "note" DROP COLUMN "viaMobile"`); - } + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "note" DROP COLUMN "viaMobile"`); + } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "note" ADD "viaMobile" boolean NOT NULL DEFAULT false`); - } + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "note" ADD "viaMobile" boolean NOT NULL DEFAULT false`, + ); + } } diff --git a/packages/backend/migration/1637320813000-forwarded-report.js b/packages/backend/migration/1637320813000-forwarded-report.js index 1e39bd5c3f..18ec172063 100644 --- a/packages/backend/migration/1637320813000-forwarded-report.js +++ b/packages/backend/migration/1637320813000-forwarded-report.js @@ -1,13 +1,15 @@ - - export class forwardedReport1637320813000 { - name = 'forwardedReport1637320813000'; + name = "forwardedReport1637320813000"; async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "abuse_user_report" ADD "forwarded" boolean NOT NULL DEFAULT false`); + await queryRunner.query( + `ALTER TABLE "abuse_user_report" ADD "forwarded" boolean NOT NULL DEFAULT false`, + ); } async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "abuse_user_report" DROP COLUMN "forwarded"`); + await queryRunner.query( + `ALTER TABLE "abuse_user_report" DROP COLUMN "forwarded"`, + ); } -}; +} diff --git a/packages/backend/migration/1639325650583-chart-v3.js b/packages/backend/migration/1639325650583-chart-v3.js index e2a4e920c9..011708c0fe 100644 --- a/packages/backend/migration/1639325650583-chart-v3.js +++ b/packages/backend/migration/1639325650583-chart-v3.js @@ -1,189 +1,511 @@ - - export class chartV31639325650583 { - name = 'chartV31639325650583' + name = "chartV31639325650583"; - async up(queryRunner) { - await queryRunner.query(`DELETE FROM "__chart__per_user_drive" WHERE "group" IS NULL`); + async up(queryRunner) { + await queryRunner.query( + `DELETE FROM "__chart__per_user_drive" WHERE "group" IS NULL`, + ); - await queryRunner.query(`DROP INDEX "public"."IDX_dd907becf76104e4b656659e6b"`); - await queryRunner.query(`DROP INDEX "public"."IDX_eddfed8fb40305a04c6f941050"`); - await queryRunner.query(`DROP INDEX "public"."IDX_f09d543e3acb16c5976bdb31fa"`); - await queryRunner.query(`DROP INDEX "public"."IDX_e60c358aaced5aab8900a4af31"`); - await queryRunner.query(`DROP INDEX "public"."IDX_337e9599f278bd7537fe30876f"`); - await queryRunner.query(`DROP INDEX "public"."IDX_66feba81e1795d176d06c0b1e6"`); - await queryRunner.query(`DROP INDEX "public"."IDX_0a905b992fecd2b5c3fb98759e"`); - await queryRunner.query(`DROP INDEX "public"."IDX_2082327b2699ce924fa654afc5"`); - await queryRunner.query(`DROP INDEX "public"."IDX_9a3ed15a30ab7e3a37702e6e08"`); - await queryRunner.query(`DROP INDEX "public"."IDX_60c5c6e7e538c09aa274ecd1cf"`); - await queryRunner.query(`DROP INDEX "public"."IDX_8111b817b9818c04d7eb8475b1"`); - await queryRunner.query(`DROP INDEX "public"."IDX_583a157ed0cf0ed1b5ec2a833f"`); - await queryRunner.query(`DROP INDEX "public"."IDX_3313d7288855ec105b5bbf6c21"`); - await queryRunner.query(`DROP INDEX "public"."IDX_ceab80a6729f8e2e6f5b8a1a3d"`); - await queryRunner.query(`DROP INDEX "public"."IDX_3b7697a96f522d0478972e6d6f"`); - await queryRunner.query(`DROP INDEX "public"."IDX_53a3604b939e2b479eb2cfaac8"`); - await queryRunner.query(`DROP INDEX "public"."IDX_dabbb38a51ab86ee3cab291326"`); - await queryRunner.query(`DROP INDEX "public"."IDX_a9a806d466b314f253a1a611c4"`); - await queryRunner.query(`CREATE TABLE "__chart_day__federation" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "___instance_total" bigint NOT NULL, "___instance_inc" bigint NOT NULL, "___instance_dec" bigint NOT NULL, CONSTRAINT "UQ_617a8fe225a6e701d89e02d2c74" UNIQUE ("date"), CONSTRAINT "PK_7ca721c769f31698e0e1331e8e6" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_617a8fe225a6e701d89e02d2c7" ON "__chart_day__federation" ("date") `); - await queryRunner.query(`CREATE TABLE "__chart_day__notes" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "___local_total" bigint NOT NULL, "___local_inc" bigint NOT NULL, "___local_dec" bigint NOT NULL, "___local_diffs_normal" bigint NOT NULL, "___local_diffs_reply" bigint NOT NULL, "___local_diffs_renote" bigint NOT NULL, "___remote_total" bigint NOT NULL, "___remote_inc" bigint NOT NULL, "___remote_dec" bigint NOT NULL, "___remote_diffs_normal" bigint NOT NULL, "___remote_diffs_reply" bigint NOT NULL, "___remote_diffs_renote" bigint NOT NULL, CONSTRAINT "UQ_1a527b423ad0858a1af5a056d43" UNIQUE ("date"), CONSTRAINT "PK_1fa4139e1f338272b758d05e090" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_1a527b423ad0858a1af5a056d4" ON "__chart_day__notes" ("date") `); - await queryRunner.query(`CREATE TABLE "__chart_day__users" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "___local_total" bigint NOT NULL, "___local_inc" bigint NOT NULL, "___local_dec" bigint NOT NULL, "___remote_total" bigint NOT NULL, "___remote_inc" bigint NOT NULL, "___remote_dec" bigint NOT NULL, CONSTRAINT "UQ_cad6e07c20037f31cdba8a350c3" UNIQUE ("date"), CONSTRAINT "PK_d7f7185abb9851f70c4726c54bd" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_cad6e07c20037f31cdba8a350c" ON "__chart_day__users" ("date") `); - await queryRunner.query(`CREATE TABLE "__chart_day__network" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "___incomingRequests" bigint NOT NULL, "___outgoingRequests" bigint NOT NULL, "___totalTime" bigint NOT NULL, "___incomingBytes" bigint NOT NULL, "___outgoingBytes" bigint NOT NULL, CONSTRAINT "UQ_8bfa548c2b31f9e07db113773ee" UNIQUE ("date"), CONSTRAINT "PK_cac499d6f471042dfed1e7e0132" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_8bfa548c2b31f9e07db113773e" ON "__chart_day__network" ("date") `); - await queryRunner.query(`CREATE TABLE "__chart_day__active_users" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "___local_users" character varying array NOT NULL, "___remote_users" character varying array NOT NULL, CONSTRAINT "UQ_d5954f3df5e5e3bdfc3c03f3906" UNIQUE ("date"), CONSTRAINT "PK_b1790489b14f005ae8f404f5795" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_d5954f3df5e5e3bdfc3c03f390" ON "__chart_day__active_users" ("date") `); - await queryRunner.query(`CREATE TABLE "__chart_day__instance" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "group" character varying(128) NOT NULL, "___requests_failed" bigint NOT NULL, "___requests_succeeded" bigint NOT NULL, "___requests_received" bigint NOT NULL, "___notes_total" bigint NOT NULL, "___notes_inc" bigint NOT NULL, "___notes_dec" bigint NOT NULL, "___notes_diffs_normal" bigint NOT NULL, "___notes_diffs_reply" bigint NOT NULL, "___notes_diffs_renote" bigint NOT NULL, "___users_total" bigint NOT NULL, "___users_inc" bigint NOT NULL, "___users_dec" bigint NOT NULL, "___following_total" bigint NOT NULL, "___following_inc" bigint NOT NULL, "___following_dec" bigint NOT NULL, "___followers_total" bigint NOT NULL, "___followers_inc" bigint NOT NULL, "___followers_dec" bigint NOT NULL, "___drive_totalFiles" bigint NOT NULL, "___drive_totalUsage" bigint NOT NULL, "___drive_incFiles" bigint NOT NULL, "___drive_incUsage" bigint NOT NULL, "___drive_decFiles" bigint NOT NULL, "___drive_decUsage" bigint NOT NULL, CONSTRAINT "UQ_fea7c0278325a1a2492f2d6acbf" UNIQUE ("date", "group"), CONSTRAINT "PK_479a8ff9d959274981087043023" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_fea7c0278325a1a2492f2d6acb" ON "__chart_day__instance" ("date", "group") `); - await queryRunner.query(`CREATE TABLE "__chart_day__per_user_notes" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "group" character varying(128) NOT NULL, "___total" bigint NOT NULL, "___inc" bigint NOT NULL, "___dec" bigint NOT NULL, "___diffs_normal" bigint NOT NULL, "___diffs_reply" bigint NOT NULL, "___diffs_renote" bigint NOT NULL, CONSTRAINT "UQ_c5545d4b31cdc684034e33b81c3" UNIQUE ("date", "group"), CONSTRAINT "PK_58bab6b6d3ad9310cbc7460fd28" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_c5545d4b31cdc684034e33b81c" ON "__chart_day__per_user_notes" ("date", "group") `); - await queryRunner.query(`CREATE TABLE "__chart_day__drive" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "___local_totalCount" bigint NOT NULL, "___local_totalSize" bigint NOT NULL, "___local_incCount" bigint NOT NULL, "___local_incSize" bigint NOT NULL, "___local_decCount" bigint NOT NULL, "___local_decSize" bigint NOT NULL, "___remote_totalCount" bigint NOT NULL, "___remote_totalSize" bigint NOT NULL, "___remote_incCount" bigint NOT NULL, "___remote_incSize" bigint NOT NULL, "___remote_decCount" bigint NOT NULL, "___remote_decSize" bigint NOT NULL, CONSTRAINT "UQ_0b60ebb3aa0065f10b0616c1171" UNIQUE ("date"), CONSTRAINT "PK_e7ec0de057c77c40fc8d8b62151" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_0b60ebb3aa0065f10b0616c117" ON "__chart_day__drive" ("date") `); - await queryRunner.query(`CREATE TABLE "__chart_day__per_user_reaction" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "group" character varying(128) NOT NULL, "___local_count" bigint NOT NULL, "___remote_count" bigint NOT NULL, CONSTRAINT "UQ_d54b653660d808b118e36c184c0" UNIQUE ("date", "group"), CONSTRAINT "PK_8af24e2d51ff781a354fe595eda" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_d54b653660d808b118e36c184c" ON "__chart_day__per_user_reaction" ("date", "group") `); - await queryRunner.query(`CREATE TABLE "__chart_day__hashtag" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "group" character varying(128) NOT NULL, "___local_users" character varying array NOT NULL, "___remote_users" character varying array NOT NULL, CONSTRAINT "UQ_8f589cf056ff51f09d6096f6450" UNIQUE ("date", "group"), CONSTRAINT "PK_13d5a3b089344e5557f8e0980b4" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_8f589cf056ff51f09d6096f645" ON "__chart_day__hashtag" ("date", "group") `); - await queryRunner.query(`CREATE TABLE "__chart_day__per_user_following" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "group" character varying(128) NOT NULL, "___local_followings_total" bigint NOT NULL, "___local_followings_inc" bigint NOT NULL, "___local_followings_dec" bigint NOT NULL, "___local_followers_total" bigint NOT NULL, "___local_followers_inc" bigint NOT NULL, "___local_followers_dec" bigint NOT NULL, "___remote_followings_total" bigint NOT NULL, "___remote_followings_inc" bigint NOT NULL, "___remote_followings_dec" bigint NOT NULL, "___remote_followers_total" bigint NOT NULL, "___remote_followers_inc" bigint NOT NULL, "___remote_followers_dec" bigint NOT NULL, CONSTRAINT "UQ_e4849a3231f38281280ea4c0eee" UNIQUE ("date", "group"), CONSTRAINT "PK_68ce6b67da57166da66fc8fb27e" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_e4849a3231f38281280ea4c0ee" ON "__chart_day__per_user_following" ("date", "group") `); - await queryRunner.query(`CREATE TABLE "__chart_day__per_user_drive" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "group" character varying(128) NOT NULL, "___totalCount" bigint NOT NULL, "___totalSize" bigint NOT NULL, "___incCount" bigint NOT NULL, "___incSize" bigint NOT NULL, "___decCount" bigint NOT NULL, "___decSize" bigint NOT NULL, CONSTRAINT "UQ_62aa5047b5aec92524f24c701d7" UNIQUE ("date", "group"), CONSTRAINT "PK_1ae135254c137011645da7f4045" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_62aa5047b5aec92524f24c701d" ON "__chart_day__per_user_drive" ("date", "group") `); - await queryRunner.query(`ALTER TABLE "__chart__federation" DROP COLUMN "group"`); - await queryRunner.query(`ALTER TABLE "__chart__notes" DROP COLUMN "group"`); - await queryRunner.query(`ALTER TABLE "__chart__users" DROP COLUMN "group"`); - await queryRunner.query(`ALTER TABLE "__chart__network" DROP COLUMN "group"`); - await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "group"`); - await queryRunner.query(`ALTER TABLE "__chart__drive" DROP COLUMN "group"`); - await queryRunner.query(`ALTER TABLE "__chart__federation" ADD CONSTRAINT "UQ_36cb699c49580d4e6c2e6159f97" UNIQUE ("date")`); - await queryRunner.query(`ALTER TABLE "__chart__notes" ADD CONSTRAINT "UQ_42eb716a37d381cdf566192b2be" UNIQUE ("date")`); - await queryRunner.query(`ALTER TABLE "__chart__users" ADD CONSTRAINT "UQ_845254b3eaf708ae8a6cac30265" UNIQUE ("date")`); - await queryRunner.query(`ALTER TABLE "__chart__network" ADD CONSTRAINT "UQ_a1efd3e0048a5f2793a47360dc6" UNIQUE ("date")`); - await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD CONSTRAINT "UQ_0ad37b7ef50f4ddc84363d7ccca" UNIQUE ("date")`); - await queryRunner.query(`ALTER TABLE "__chart__active_users" ALTER COLUMN "___local_users" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart__active_users" ALTER COLUMN "___remote_users" DROP DEFAULT`); - await queryRunner.query(`DROP INDEX "public"."IDX_39ee857ab2f23493037c6b6631"`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "group" SET NOT NULL`); - await queryRunner.query(`DROP INDEX "public"."IDX_5048e9daccbbbc6d567bb142d3"`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_notes" ALTER COLUMN "group" SET NOT NULL`); - await queryRunner.query(`ALTER TABLE "__chart__drive" ADD CONSTRAINT "UQ_13565815f618a1ff53886c5b28a" UNIQUE ("date")`); - await queryRunner.query(`DROP INDEX "public"."IDX_229a41ad465f9205f1f5703291"`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_reaction" ALTER COLUMN "group" SET NOT NULL`); - await queryRunner.query(`DROP INDEX "public"."IDX_25a97c02003338124b2b75fdbc"`); - await queryRunner.query(`ALTER TABLE "__chart__hashtag" ALTER COLUMN "group" SET NOT NULL`); - await queryRunner.query(`ALTER TABLE "__chart__hashtag" ALTER COLUMN "___local_users" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart__hashtag" ALTER COLUMN "___remote_users" DROP DEFAULT`); - await queryRunner.query(`DROP INDEX "public"."IDX_b77d4dd9562c3a899d9a286fcd"`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_following" ALTER COLUMN "group" SET NOT NULL`); - await queryRunner.query(`DROP INDEX "public"."IDX_30bf67687f483ace115c5ca642"`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_drive" ALTER COLUMN "group" SET NOT NULL`); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_36cb699c49580d4e6c2e6159f9" ON "__chart__federation" ("date") `); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_42eb716a37d381cdf566192b2b" ON "__chart__notes" ("date") `); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_845254b3eaf708ae8a6cac3026" ON "__chart__users" ("date") `); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_a1efd3e0048a5f2793a47360dc" ON "__chart__network" ("date") `); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_0ad37b7ef50f4ddc84363d7ccc" ON "__chart__active_users" ("date") `); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_39ee857ab2f23493037c6b6631" ON "__chart__instance" ("date", "group") `); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_5048e9daccbbbc6d567bb142d3" ON "__chart__per_user_notes" ("date", "group") `); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_13565815f618a1ff53886c5b28" ON "__chart__drive" ("date") `); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_229a41ad465f9205f1f5703291" ON "__chart__per_user_reaction" ("date", "group") `); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_25a97c02003338124b2b75fdbc" ON "__chart__hashtag" ("date", "group") `); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_b77d4dd9562c3a899d9a286fcd" ON "__chart__per_user_following" ("date", "group") `); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_30bf67687f483ace115c5ca642" ON "__chart__per_user_drive" ("date", "group") `); - await queryRunner.query(`ALTER TABLE "__chart__instance" ADD CONSTRAINT "UQ_39ee857ab2f23493037c6b66311" UNIQUE ("date", "group")`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_notes" ADD CONSTRAINT "UQ_5048e9daccbbbc6d567bb142d34" UNIQUE ("date", "group")`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_reaction" ADD CONSTRAINT "UQ_229a41ad465f9205f1f57032910" UNIQUE ("date", "group")`); - await queryRunner.query(`ALTER TABLE "__chart__hashtag" ADD CONSTRAINT "UQ_25a97c02003338124b2b75fdbc8" UNIQUE ("date", "group")`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_following" ADD CONSTRAINT "UQ_b77d4dd9562c3a899d9a286fcd7" UNIQUE ("date", "group")`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_drive" ADD CONSTRAINT "UQ_30bf67687f483ace115c5ca6429" UNIQUE ("date", "group")`); - } + await queryRunner.query( + `DROP INDEX "public"."IDX_dd907becf76104e4b656659e6b"`, + ); + await queryRunner.query( + `DROP INDEX "public"."IDX_eddfed8fb40305a04c6f941050"`, + ); + await queryRunner.query( + `DROP INDEX "public"."IDX_f09d543e3acb16c5976bdb31fa"`, + ); + await queryRunner.query( + `DROP INDEX "public"."IDX_e60c358aaced5aab8900a4af31"`, + ); + await queryRunner.query( + `DROP INDEX "public"."IDX_337e9599f278bd7537fe30876f"`, + ); + await queryRunner.query( + `DROP INDEX "public"."IDX_66feba81e1795d176d06c0b1e6"`, + ); + await queryRunner.query( + `DROP INDEX "public"."IDX_0a905b992fecd2b5c3fb98759e"`, + ); + await queryRunner.query( + `DROP INDEX "public"."IDX_2082327b2699ce924fa654afc5"`, + ); + await queryRunner.query( + `DROP INDEX "public"."IDX_9a3ed15a30ab7e3a37702e6e08"`, + ); + await queryRunner.query( + `DROP INDEX "public"."IDX_60c5c6e7e538c09aa274ecd1cf"`, + ); + await queryRunner.query( + `DROP INDEX "public"."IDX_8111b817b9818c04d7eb8475b1"`, + ); + await queryRunner.query( + `DROP INDEX "public"."IDX_583a157ed0cf0ed1b5ec2a833f"`, + ); + await queryRunner.query( + `DROP INDEX "public"."IDX_3313d7288855ec105b5bbf6c21"`, + ); + await queryRunner.query( + `DROP INDEX "public"."IDX_ceab80a6729f8e2e6f5b8a1a3d"`, + ); + await queryRunner.query( + `DROP INDEX "public"."IDX_3b7697a96f522d0478972e6d6f"`, + ); + await queryRunner.query( + `DROP INDEX "public"."IDX_53a3604b939e2b479eb2cfaac8"`, + ); + await queryRunner.query( + `DROP INDEX "public"."IDX_dabbb38a51ab86ee3cab291326"`, + ); + await queryRunner.query( + `DROP INDEX "public"."IDX_a9a806d466b314f253a1a611c4"`, + ); + await queryRunner.query( + `CREATE TABLE "__chart_day__federation" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "___instance_total" bigint NOT NULL, "___instance_inc" bigint NOT NULL, "___instance_dec" bigint NOT NULL, CONSTRAINT "UQ_617a8fe225a6e701d89e02d2c74" UNIQUE ("date"), CONSTRAINT "PK_7ca721c769f31698e0e1331e8e6" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_617a8fe225a6e701d89e02d2c7" ON "__chart_day__federation" ("date") `, + ); + await queryRunner.query( + `CREATE TABLE "__chart_day__notes" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "___local_total" bigint NOT NULL, "___local_inc" bigint NOT NULL, "___local_dec" bigint NOT NULL, "___local_diffs_normal" bigint NOT NULL, "___local_diffs_reply" bigint NOT NULL, "___local_diffs_renote" bigint NOT NULL, "___remote_total" bigint NOT NULL, "___remote_inc" bigint NOT NULL, "___remote_dec" bigint NOT NULL, "___remote_diffs_normal" bigint NOT NULL, "___remote_diffs_reply" bigint NOT NULL, "___remote_diffs_renote" bigint NOT NULL, CONSTRAINT "UQ_1a527b423ad0858a1af5a056d43" UNIQUE ("date"), CONSTRAINT "PK_1fa4139e1f338272b758d05e090" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_1a527b423ad0858a1af5a056d4" ON "__chart_day__notes" ("date") `, + ); + await queryRunner.query( + `CREATE TABLE "__chart_day__users" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "___local_total" bigint NOT NULL, "___local_inc" bigint NOT NULL, "___local_dec" bigint NOT NULL, "___remote_total" bigint NOT NULL, "___remote_inc" bigint NOT NULL, "___remote_dec" bigint NOT NULL, CONSTRAINT "UQ_cad6e07c20037f31cdba8a350c3" UNIQUE ("date"), CONSTRAINT "PK_d7f7185abb9851f70c4726c54bd" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_cad6e07c20037f31cdba8a350c" ON "__chart_day__users" ("date") `, + ); + await queryRunner.query( + `CREATE TABLE "__chart_day__network" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "___incomingRequests" bigint NOT NULL, "___outgoingRequests" bigint NOT NULL, "___totalTime" bigint NOT NULL, "___incomingBytes" bigint NOT NULL, "___outgoingBytes" bigint NOT NULL, CONSTRAINT "UQ_8bfa548c2b31f9e07db113773ee" UNIQUE ("date"), CONSTRAINT "PK_cac499d6f471042dfed1e7e0132" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_8bfa548c2b31f9e07db113773e" ON "__chart_day__network" ("date") `, + ); + await queryRunner.query( + `CREATE TABLE "__chart_day__active_users" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "___local_users" character varying array NOT NULL, "___remote_users" character varying array NOT NULL, CONSTRAINT "UQ_d5954f3df5e5e3bdfc3c03f3906" UNIQUE ("date"), CONSTRAINT "PK_b1790489b14f005ae8f404f5795" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_d5954f3df5e5e3bdfc3c03f390" ON "__chart_day__active_users" ("date") `, + ); + await queryRunner.query( + `CREATE TABLE "__chart_day__instance" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "group" character varying(128) NOT NULL, "___requests_failed" bigint NOT NULL, "___requests_succeeded" bigint NOT NULL, "___requests_received" bigint NOT NULL, "___notes_total" bigint NOT NULL, "___notes_inc" bigint NOT NULL, "___notes_dec" bigint NOT NULL, "___notes_diffs_normal" bigint NOT NULL, "___notes_diffs_reply" bigint NOT NULL, "___notes_diffs_renote" bigint NOT NULL, "___users_total" bigint NOT NULL, "___users_inc" bigint NOT NULL, "___users_dec" bigint NOT NULL, "___following_total" bigint NOT NULL, "___following_inc" bigint NOT NULL, "___following_dec" bigint NOT NULL, "___followers_total" bigint NOT NULL, "___followers_inc" bigint NOT NULL, "___followers_dec" bigint NOT NULL, "___drive_totalFiles" bigint NOT NULL, "___drive_totalUsage" bigint NOT NULL, "___drive_incFiles" bigint NOT NULL, "___drive_incUsage" bigint NOT NULL, "___drive_decFiles" bigint NOT NULL, "___drive_decUsage" bigint NOT NULL, CONSTRAINT "UQ_fea7c0278325a1a2492f2d6acbf" UNIQUE ("date", "group"), CONSTRAINT "PK_479a8ff9d959274981087043023" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_fea7c0278325a1a2492f2d6acb" ON "__chart_day__instance" ("date", "group") `, + ); + await queryRunner.query( + `CREATE TABLE "__chart_day__per_user_notes" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "group" character varying(128) NOT NULL, "___total" bigint NOT NULL, "___inc" bigint NOT NULL, "___dec" bigint NOT NULL, "___diffs_normal" bigint NOT NULL, "___diffs_reply" bigint NOT NULL, "___diffs_renote" bigint NOT NULL, CONSTRAINT "UQ_c5545d4b31cdc684034e33b81c3" UNIQUE ("date", "group"), CONSTRAINT "PK_58bab6b6d3ad9310cbc7460fd28" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_c5545d4b31cdc684034e33b81c" ON "__chart_day__per_user_notes" ("date", "group") `, + ); + await queryRunner.query( + `CREATE TABLE "__chart_day__drive" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "___local_totalCount" bigint NOT NULL, "___local_totalSize" bigint NOT NULL, "___local_incCount" bigint NOT NULL, "___local_incSize" bigint NOT NULL, "___local_decCount" bigint NOT NULL, "___local_decSize" bigint NOT NULL, "___remote_totalCount" bigint NOT NULL, "___remote_totalSize" bigint NOT NULL, "___remote_incCount" bigint NOT NULL, "___remote_incSize" bigint NOT NULL, "___remote_decCount" bigint NOT NULL, "___remote_decSize" bigint NOT NULL, CONSTRAINT "UQ_0b60ebb3aa0065f10b0616c1171" UNIQUE ("date"), CONSTRAINT "PK_e7ec0de057c77c40fc8d8b62151" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_0b60ebb3aa0065f10b0616c117" ON "__chart_day__drive" ("date") `, + ); + await queryRunner.query( + `CREATE TABLE "__chart_day__per_user_reaction" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "group" character varying(128) NOT NULL, "___local_count" bigint NOT NULL, "___remote_count" bigint NOT NULL, CONSTRAINT "UQ_d54b653660d808b118e36c184c0" UNIQUE ("date", "group"), CONSTRAINT "PK_8af24e2d51ff781a354fe595eda" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_d54b653660d808b118e36c184c" ON "__chart_day__per_user_reaction" ("date", "group") `, + ); + await queryRunner.query( + `CREATE TABLE "__chart_day__hashtag" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "group" character varying(128) NOT NULL, "___local_users" character varying array NOT NULL, "___remote_users" character varying array NOT NULL, CONSTRAINT "UQ_8f589cf056ff51f09d6096f6450" UNIQUE ("date", "group"), CONSTRAINT "PK_13d5a3b089344e5557f8e0980b4" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_8f589cf056ff51f09d6096f645" ON "__chart_day__hashtag" ("date", "group") `, + ); + await queryRunner.query( + `CREATE TABLE "__chart_day__per_user_following" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "group" character varying(128) NOT NULL, "___local_followings_total" bigint NOT NULL, "___local_followings_inc" bigint NOT NULL, "___local_followings_dec" bigint NOT NULL, "___local_followers_total" bigint NOT NULL, "___local_followers_inc" bigint NOT NULL, "___local_followers_dec" bigint NOT NULL, "___remote_followings_total" bigint NOT NULL, "___remote_followings_inc" bigint NOT NULL, "___remote_followings_dec" bigint NOT NULL, "___remote_followers_total" bigint NOT NULL, "___remote_followers_inc" bigint NOT NULL, "___remote_followers_dec" bigint NOT NULL, CONSTRAINT "UQ_e4849a3231f38281280ea4c0eee" UNIQUE ("date", "group"), CONSTRAINT "PK_68ce6b67da57166da66fc8fb27e" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_e4849a3231f38281280ea4c0ee" ON "__chart_day__per_user_following" ("date", "group") `, + ); + await queryRunner.query( + `CREATE TABLE "__chart_day__per_user_drive" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "group" character varying(128) NOT NULL, "___totalCount" bigint NOT NULL, "___totalSize" bigint NOT NULL, "___incCount" bigint NOT NULL, "___incSize" bigint NOT NULL, "___decCount" bigint NOT NULL, "___decSize" bigint NOT NULL, CONSTRAINT "UQ_62aa5047b5aec92524f24c701d7" UNIQUE ("date", "group"), CONSTRAINT "PK_1ae135254c137011645da7f4045" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_62aa5047b5aec92524f24c701d" ON "__chart_day__per_user_drive" ("date", "group") `, + ); + await queryRunner.query( + `ALTER TABLE "__chart__federation" DROP COLUMN "group"`, + ); + await queryRunner.query(`ALTER TABLE "__chart__notes" DROP COLUMN "group"`); + await queryRunner.query(`ALTER TABLE "__chart__users" DROP COLUMN "group"`); + await queryRunner.query( + `ALTER TABLE "__chart__network" DROP COLUMN "group"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__active_users" DROP COLUMN "group"`, + ); + await queryRunner.query(`ALTER TABLE "__chart__drive" DROP COLUMN "group"`); + await queryRunner.query( + `ALTER TABLE "__chart__federation" ADD CONSTRAINT "UQ_36cb699c49580d4e6c2e6159f97" UNIQUE ("date")`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__notes" ADD CONSTRAINT "UQ_42eb716a37d381cdf566192b2be" UNIQUE ("date")`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__users" ADD CONSTRAINT "UQ_845254b3eaf708ae8a6cac30265" UNIQUE ("date")`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__network" ADD CONSTRAINT "UQ_a1efd3e0048a5f2793a47360dc6" UNIQUE ("date")`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__active_users" ADD CONSTRAINT "UQ_0ad37b7ef50f4ddc84363d7ccca" UNIQUE ("date")`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__active_users" ALTER COLUMN "___local_users" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__active_users" ALTER COLUMN "___remote_users" DROP DEFAULT`, + ); + await queryRunner.query( + `DROP INDEX "public"."IDX_39ee857ab2f23493037c6b6631"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "group" SET NOT NULL`, + ); + await queryRunner.query( + `DROP INDEX "public"."IDX_5048e9daccbbbc6d567bb142d3"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_notes" ALTER COLUMN "group" SET NOT NULL`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__drive" ADD CONSTRAINT "UQ_13565815f618a1ff53886c5b28a" UNIQUE ("date")`, + ); + await queryRunner.query( + `DROP INDEX "public"."IDX_229a41ad465f9205f1f5703291"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_reaction" ALTER COLUMN "group" SET NOT NULL`, + ); + await queryRunner.query( + `DROP INDEX "public"."IDX_25a97c02003338124b2b75fdbc"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__hashtag" ALTER COLUMN "group" SET NOT NULL`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__hashtag" ALTER COLUMN "___local_users" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__hashtag" ALTER COLUMN "___remote_users" DROP DEFAULT`, + ); + await queryRunner.query( + `DROP INDEX "public"."IDX_b77d4dd9562c3a899d9a286fcd"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_following" ALTER COLUMN "group" SET NOT NULL`, + ); + await queryRunner.query( + `DROP INDEX "public"."IDX_30bf67687f483ace115c5ca642"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_drive" ALTER COLUMN "group" SET NOT NULL`, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_36cb699c49580d4e6c2e6159f9" ON "__chart__federation" ("date") `, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_42eb716a37d381cdf566192b2b" ON "__chart__notes" ("date") `, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_845254b3eaf708ae8a6cac3026" ON "__chart__users" ("date") `, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_a1efd3e0048a5f2793a47360dc" ON "__chart__network" ("date") `, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_0ad37b7ef50f4ddc84363d7ccc" ON "__chart__active_users" ("date") `, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_39ee857ab2f23493037c6b6631" ON "__chart__instance" ("date", "group") `, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_5048e9daccbbbc6d567bb142d3" ON "__chart__per_user_notes" ("date", "group") `, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_13565815f618a1ff53886c5b28" ON "__chart__drive" ("date") `, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_229a41ad465f9205f1f5703291" ON "__chart__per_user_reaction" ("date", "group") `, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_25a97c02003338124b2b75fdbc" ON "__chart__hashtag" ("date", "group") `, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_b77d4dd9562c3a899d9a286fcd" ON "__chart__per_user_following" ("date", "group") `, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_30bf67687f483ace115c5ca642" ON "__chart__per_user_drive" ("date", "group") `, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ADD CONSTRAINT "UQ_39ee857ab2f23493037c6b66311" UNIQUE ("date", "group")`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_notes" ADD CONSTRAINT "UQ_5048e9daccbbbc6d567bb142d34" UNIQUE ("date", "group")`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_reaction" ADD CONSTRAINT "UQ_229a41ad465f9205f1f57032910" UNIQUE ("date", "group")`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__hashtag" ADD CONSTRAINT "UQ_25a97c02003338124b2b75fdbc8" UNIQUE ("date", "group")`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_following" ADD CONSTRAINT "UQ_b77d4dd9562c3a899d9a286fcd7" UNIQUE ("date", "group")`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_drive" ADD CONSTRAINT "UQ_30bf67687f483ace115c5ca6429" UNIQUE ("date", "group")`, + ); + } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "__chart__per_user_drive" DROP CONSTRAINT "UQ_30bf67687f483ace115c5ca6429"`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_following" DROP CONSTRAINT "UQ_b77d4dd9562c3a899d9a286fcd7"`); - await queryRunner.query(`ALTER TABLE "__chart__hashtag" DROP CONSTRAINT "UQ_25a97c02003338124b2b75fdbc8"`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_reaction" DROP CONSTRAINT "UQ_229a41ad465f9205f1f57032910"`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_notes" DROP CONSTRAINT "UQ_5048e9daccbbbc6d567bb142d34"`); - await queryRunner.query(`ALTER TABLE "__chart__instance" DROP CONSTRAINT "UQ_39ee857ab2f23493037c6b66311"`); - await queryRunner.query(`DROP INDEX "public"."IDX_30bf67687f483ace115c5ca642"`); - await queryRunner.query(`DROP INDEX "public"."IDX_b77d4dd9562c3a899d9a286fcd"`); - await queryRunner.query(`DROP INDEX "public"."IDX_25a97c02003338124b2b75fdbc"`); - await queryRunner.query(`DROP INDEX "public"."IDX_229a41ad465f9205f1f5703291"`); - await queryRunner.query(`DROP INDEX "public"."IDX_13565815f618a1ff53886c5b28"`); - await queryRunner.query(`DROP INDEX "public"."IDX_5048e9daccbbbc6d567bb142d3"`); - await queryRunner.query(`DROP INDEX "public"."IDX_39ee857ab2f23493037c6b6631"`); - await queryRunner.query(`DROP INDEX "public"."IDX_0ad37b7ef50f4ddc84363d7ccc"`); - await queryRunner.query(`DROP INDEX "public"."IDX_a1efd3e0048a5f2793a47360dc"`); - await queryRunner.query(`DROP INDEX "public"."IDX_845254b3eaf708ae8a6cac3026"`); - await queryRunner.query(`DROP INDEX "public"."IDX_42eb716a37d381cdf566192b2b"`); - await queryRunner.query(`DROP INDEX "public"."IDX_36cb699c49580d4e6c2e6159f9"`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_drive" ALTER COLUMN "group" DROP NOT NULL`); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_30bf67687f483ace115c5ca642" ON "__chart__per_user_drive" ("date", "group") `); - await queryRunner.query(`ALTER TABLE "__chart__per_user_following" ALTER COLUMN "group" DROP NOT NULL`); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_b77d4dd9562c3a899d9a286fcd" ON "__chart__per_user_following" ("date", "group") `); - await queryRunner.query(`ALTER TABLE "__chart__hashtag" ALTER COLUMN "___remote_users" SET DEFAULT '{}'`); - await queryRunner.query(`ALTER TABLE "__chart__hashtag" ALTER COLUMN "___local_users" SET DEFAULT '{}'`); - await queryRunner.query(`ALTER TABLE "__chart__hashtag" ALTER COLUMN "group" DROP NOT NULL`); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_25a97c02003338124b2b75fdbc" ON "__chart__hashtag" ("date", "group") `); - await queryRunner.query(`ALTER TABLE "__chart__per_user_reaction" ALTER COLUMN "group" DROP NOT NULL`); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_229a41ad465f9205f1f5703291" ON "__chart__per_user_reaction" ("date", "group") `); - await queryRunner.query(`ALTER TABLE "__chart__drive" DROP CONSTRAINT "UQ_13565815f618a1ff53886c5b28a"`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_notes" ALTER COLUMN "group" DROP NOT NULL`); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_5048e9daccbbbc6d567bb142d3" ON "__chart__per_user_notes" ("date", "group") `); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "group" DROP NOT NULL`); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_39ee857ab2f23493037c6b6631" ON "__chart__instance" ("date", "group") `); - await queryRunner.query(`ALTER TABLE "__chart__active_users" ALTER COLUMN "___remote_users" SET DEFAULT '{}'`); - await queryRunner.query(`ALTER TABLE "__chart__active_users" ALTER COLUMN "___local_users" SET DEFAULT '{}'`); - await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP CONSTRAINT "UQ_0ad37b7ef50f4ddc84363d7ccca"`); - await queryRunner.query(`ALTER TABLE "__chart__network" DROP CONSTRAINT "UQ_a1efd3e0048a5f2793a47360dc6"`); - await queryRunner.query(`ALTER TABLE "__chart__users" DROP CONSTRAINT "UQ_845254b3eaf708ae8a6cac30265"`); - await queryRunner.query(`ALTER TABLE "__chart__notes" DROP CONSTRAINT "UQ_42eb716a37d381cdf566192b2be"`); - await queryRunner.query(`ALTER TABLE "__chart__federation" DROP CONSTRAINT "UQ_36cb699c49580d4e6c2e6159f97"`); - await queryRunner.query(`ALTER TABLE "__chart__drive" ADD "group" character varying(128)`); - await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "group" character varying(128)`); - await queryRunner.query(`ALTER TABLE "__chart__network" ADD "group" character varying(128)`); - await queryRunner.query(`ALTER TABLE "__chart__users" ADD "group" character varying(128)`); - await queryRunner.query(`ALTER TABLE "__chart__notes" ADD "group" character varying(128)`); - await queryRunner.query(`ALTER TABLE "__chart__federation" ADD "group" character varying(128)`); - await queryRunner.query(`DROP INDEX "public"."IDX_62aa5047b5aec92524f24c701d"`); - await queryRunner.query(`DROP TABLE "__chart_day__per_user_drive"`); - await queryRunner.query(`DROP INDEX "public"."IDX_e4849a3231f38281280ea4c0ee"`); - await queryRunner.query(`DROP TABLE "__chart_day__per_user_following"`); - await queryRunner.query(`DROP INDEX "public"."IDX_8f589cf056ff51f09d6096f645"`); - await queryRunner.query(`DROP TABLE "__chart_day__hashtag"`); - await queryRunner.query(`DROP INDEX "public"."IDX_d54b653660d808b118e36c184c"`); - await queryRunner.query(`DROP TABLE "__chart_day__per_user_reaction"`); - await queryRunner.query(`DROP INDEX "public"."IDX_0b60ebb3aa0065f10b0616c117"`); - await queryRunner.query(`DROP TABLE "__chart_day__drive"`); - await queryRunner.query(`DROP INDEX "public"."IDX_c5545d4b31cdc684034e33b81c"`); - await queryRunner.query(`DROP TABLE "__chart_day__per_user_notes"`); - await queryRunner.query(`DROP INDEX "public"."IDX_fea7c0278325a1a2492f2d6acb"`); - await queryRunner.query(`DROP TABLE "__chart_day__instance"`); - await queryRunner.query(`DROP INDEX "public"."IDX_d5954f3df5e5e3bdfc3c03f390"`); - await queryRunner.query(`DROP TABLE "__chart_day__active_users"`); - await queryRunner.query(`DROP INDEX "public"."IDX_8bfa548c2b31f9e07db113773e"`); - await queryRunner.query(`DROP TABLE "__chart_day__network"`); - await queryRunner.query(`DROP INDEX "public"."IDX_cad6e07c20037f31cdba8a350c"`); - await queryRunner.query(`DROP TABLE "__chart_day__users"`); - await queryRunner.query(`DROP INDEX "public"."IDX_1a527b423ad0858a1af5a056d4"`); - await queryRunner.query(`DROP TABLE "__chart_day__notes"`); - await queryRunner.query(`DROP INDEX "public"."IDX_617a8fe225a6e701d89e02d2c7"`); - await queryRunner.query(`DROP TABLE "__chart_day__federation"`); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_a9a806d466b314f253a1a611c4" ON "__chart__per_user_drive" ("date") WHERE ("group" IS NULL)`); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_dabbb38a51ab86ee3cab291326" ON "__chart__per_user_following" ("date") WHERE ("group" IS NULL)`); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_53a3604b939e2b479eb2cfaac8" ON "__chart__hashtag" ("date") WHERE ("group" IS NULL)`); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_3b7697a96f522d0478972e6d6f" ON "__chart__per_user_reaction" ("date") WHERE ("group" IS NULL)`); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_ceab80a6729f8e2e6f5b8a1a3d" ON "__chart__drive" ("date") WHERE ("group" IS NULL)`); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_3313d7288855ec105b5bbf6c21" ON "__chart__drive" ("date", "group") `); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_583a157ed0cf0ed1b5ec2a833f" ON "__chart__per_user_notes" ("date") WHERE ("group" IS NULL)`); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_8111b817b9818c04d7eb8475b1" ON "__chart__instance" ("date") WHERE ("group" IS NULL)`); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_60c5c6e7e538c09aa274ecd1cf" ON "__chart__active_users" ("date") WHERE ("group" IS NULL)`); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_9a3ed15a30ab7e3a37702e6e08" ON "__chart__active_users" ("date", "group") `); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_2082327b2699ce924fa654afc5" ON "__chart__network" ("date") WHERE ("group" IS NULL)`); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_0a905b992fecd2b5c3fb98759e" ON "__chart__network" ("date", "group") `); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_66feba81e1795d176d06c0b1e6" ON "__chart__users" ("date") WHERE ("group" IS NULL)`); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_337e9599f278bd7537fe30876f" ON "__chart__users" ("date", "group") `); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_e60c358aaced5aab8900a4af31" ON "__chart__notes" ("date") WHERE ("group" IS NULL)`); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_f09d543e3acb16c5976bdb31fa" ON "__chart__notes" ("date", "group") `); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_eddfed8fb40305a04c6f941050" ON "__chart__federation" ("date") WHERE ("group" IS NULL)`); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_dd907becf76104e4b656659e6b" ON "__chart__federation" ("date", "group") `); - } + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "__chart__per_user_drive" DROP CONSTRAINT "UQ_30bf67687f483ace115c5ca6429"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_following" DROP CONSTRAINT "UQ_b77d4dd9562c3a899d9a286fcd7"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__hashtag" DROP CONSTRAINT "UQ_25a97c02003338124b2b75fdbc8"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_reaction" DROP CONSTRAINT "UQ_229a41ad465f9205f1f57032910"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_notes" DROP CONSTRAINT "UQ_5048e9daccbbbc6d567bb142d34"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" DROP CONSTRAINT "UQ_39ee857ab2f23493037c6b66311"`, + ); + await queryRunner.query( + `DROP INDEX "public"."IDX_30bf67687f483ace115c5ca642"`, + ); + await queryRunner.query( + `DROP INDEX "public"."IDX_b77d4dd9562c3a899d9a286fcd"`, + ); + await queryRunner.query( + `DROP INDEX "public"."IDX_25a97c02003338124b2b75fdbc"`, + ); + await queryRunner.query( + `DROP INDEX "public"."IDX_229a41ad465f9205f1f5703291"`, + ); + await queryRunner.query( + `DROP INDEX "public"."IDX_13565815f618a1ff53886c5b28"`, + ); + await queryRunner.query( + `DROP INDEX "public"."IDX_5048e9daccbbbc6d567bb142d3"`, + ); + await queryRunner.query( + `DROP INDEX "public"."IDX_39ee857ab2f23493037c6b6631"`, + ); + await queryRunner.query( + `DROP INDEX "public"."IDX_0ad37b7ef50f4ddc84363d7ccc"`, + ); + await queryRunner.query( + `DROP INDEX "public"."IDX_a1efd3e0048a5f2793a47360dc"`, + ); + await queryRunner.query( + `DROP INDEX "public"."IDX_845254b3eaf708ae8a6cac3026"`, + ); + await queryRunner.query( + `DROP INDEX "public"."IDX_42eb716a37d381cdf566192b2b"`, + ); + await queryRunner.query( + `DROP INDEX "public"."IDX_36cb699c49580d4e6c2e6159f9"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_drive" ALTER COLUMN "group" DROP NOT NULL`, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_30bf67687f483ace115c5ca642" ON "__chart__per_user_drive" ("date", "group") `, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_following" ALTER COLUMN "group" DROP NOT NULL`, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_b77d4dd9562c3a899d9a286fcd" ON "__chart__per_user_following" ("date", "group") `, + ); + await queryRunner.query( + `ALTER TABLE "__chart__hashtag" ALTER COLUMN "___remote_users" SET DEFAULT '{}'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__hashtag" ALTER COLUMN "___local_users" SET DEFAULT '{}'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__hashtag" ALTER COLUMN "group" DROP NOT NULL`, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_25a97c02003338124b2b75fdbc" ON "__chart__hashtag" ("date", "group") `, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_reaction" ALTER COLUMN "group" DROP NOT NULL`, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_229a41ad465f9205f1f5703291" ON "__chart__per_user_reaction" ("date", "group") `, + ); + await queryRunner.query( + `ALTER TABLE "__chart__drive" DROP CONSTRAINT "UQ_13565815f618a1ff53886c5b28a"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_notes" ALTER COLUMN "group" DROP NOT NULL`, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_5048e9daccbbbc6d567bb142d3" ON "__chart__per_user_notes" ("date", "group") `, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "group" DROP NOT NULL`, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_39ee857ab2f23493037c6b6631" ON "__chart__instance" ("date", "group") `, + ); + await queryRunner.query( + `ALTER TABLE "__chart__active_users" ALTER COLUMN "___remote_users" SET DEFAULT '{}'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__active_users" ALTER COLUMN "___local_users" SET DEFAULT '{}'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__active_users" DROP CONSTRAINT "UQ_0ad37b7ef50f4ddc84363d7ccca"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__network" DROP CONSTRAINT "UQ_a1efd3e0048a5f2793a47360dc6"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__users" DROP CONSTRAINT "UQ_845254b3eaf708ae8a6cac30265"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__notes" DROP CONSTRAINT "UQ_42eb716a37d381cdf566192b2be"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__federation" DROP CONSTRAINT "UQ_36cb699c49580d4e6c2e6159f97"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__drive" ADD "group" character varying(128)`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__active_users" ADD "group" character varying(128)`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__network" ADD "group" character varying(128)`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__users" ADD "group" character varying(128)`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__notes" ADD "group" character varying(128)`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__federation" ADD "group" character varying(128)`, + ); + await queryRunner.query( + `DROP INDEX "public"."IDX_62aa5047b5aec92524f24c701d"`, + ); + await queryRunner.query(`DROP TABLE "__chart_day__per_user_drive"`); + await queryRunner.query( + `DROP INDEX "public"."IDX_e4849a3231f38281280ea4c0ee"`, + ); + await queryRunner.query(`DROP TABLE "__chart_day__per_user_following"`); + await queryRunner.query( + `DROP INDEX "public"."IDX_8f589cf056ff51f09d6096f645"`, + ); + await queryRunner.query(`DROP TABLE "__chart_day__hashtag"`); + await queryRunner.query( + `DROP INDEX "public"."IDX_d54b653660d808b118e36c184c"`, + ); + await queryRunner.query(`DROP TABLE "__chart_day__per_user_reaction"`); + await queryRunner.query( + `DROP INDEX "public"."IDX_0b60ebb3aa0065f10b0616c117"`, + ); + await queryRunner.query(`DROP TABLE "__chart_day__drive"`); + await queryRunner.query( + `DROP INDEX "public"."IDX_c5545d4b31cdc684034e33b81c"`, + ); + await queryRunner.query(`DROP TABLE "__chart_day__per_user_notes"`); + await queryRunner.query( + `DROP INDEX "public"."IDX_fea7c0278325a1a2492f2d6acb"`, + ); + await queryRunner.query(`DROP TABLE "__chart_day__instance"`); + await queryRunner.query( + `DROP INDEX "public"."IDX_d5954f3df5e5e3bdfc3c03f390"`, + ); + await queryRunner.query(`DROP TABLE "__chart_day__active_users"`); + await queryRunner.query( + `DROP INDEX "public"."IDX_8bfa548c2b31f9e07db113773e"`, + ); + await queryRunner.query(`DROP TABLE "__chart_day__network"`); + await queryRunner.query( + `DROP INDEX "public"."IDX_cad6e07c20037f31cdba8a350c"`, + ); + await queryRunner.query(`DROP TABLE "__chart_day__users"`); + await queryRunner.query( + `DROP INDEX "public"."IDX_1a527b423ad0858a1af5a056d4"`, + ); + await queryRunner.query(`DROP TABLE "__chart_day__notes"`); + await queryRunner.query( + `DROP INDEX "public"."IDX_617a8fe225a6e701d89e02d2c7"`, + ); + await queryRunner.query(`DROP TABLE "__chart_day__federation"`); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_a9a806d466b314f253a1a611c4" ON "__chart__per_user_drive" ("date") WHERE ("group" IS NULL)`, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_dabbb38a51ab86ee3cab291326" ON "__chart__per_user_following" ("date") WHERE ("group" IS NULL)`, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_53a3604b939e2b479eb2cfaac8" ON "__chart__hashtag" ("date") WHERE ("group" IS NULL)`, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_3b7697a96f522d0478972e6d6f" ON "__chart__per_user_reaction" ("date") WHERE ("group" IS NULL)`, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_ceab80a6729f8e2e6f5b8a1a3d" ON "__chart__drive" ("date") WHERE ("group" IS NULL)`, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_3313d7288855ec105b5bbf6c21" ON "__chart__drive" ("date", "group") `, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_583a157ed0cf0ed1b5ec2a833f" ON "__chart__per_user_notes" ("date") WHERE ("group" IS NULL)`, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_8111b817b9818c04d7eb8475b1" ON "__chart__instance" ("date") WHERE ("group" IS NULL)`, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_60c5c6e7e538c09aa274ecd1cf" ON "__chart__active_users" ("date") WHERE ("group" IS NULL)`, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_9a3ed15a30ab7e3a37702e6e08" ON "__chart__active_users" ("date", "group") `, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_2082327b2699ce924fa654afc5" ON "__chart__network" ("date") WHERE ("group" IS NULL)`, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_0a905b992fecd2b5c3fb98759e" ON "__chart__network" ("date", "group") `, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_66feba81e1795d176d06c0b1e6" ON "__chart__users" ("date") WHERE ("group" IS NULL)`, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_337e9599f278bd7537fe30876f" ON "__chart__users" ("date", "group") `, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_e60c358aaced5aab8900a4af31" ON "__chart__notes" ("date") WHERE ("group" IS NULL)`, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_f09d543e3acb16c5976bdb31fa" ON "__chart__notes" ("date", "group") `, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_eddfed8fb40305a04c6f941050" ON "__chart__federation" ("date") WHERE ("group" IS NULL)`, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_dd907becf76104e4b656659e6b" ON "__chart__federation" ("date", "group") `, + ); + } } diff --git a/packages/backend/migration/1642611822809-emoji-url.js b/packages/backend/migration/1642611822809-emoji-url.js index d38f8cc08c..e1852c0025 100644 --- a/packages/backend/migration/1642611822809-emoji-url.js +++ b/packages/backend/migration/1642611822809-emoji-url.js @@ -1,15 +1,19 @@ - - export class emojiUrl1642611822809 { - name = 'emojiUrl1642611822809' + name = "emojiUrl1642611822809"; - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "emoji" RENAME COLUMN "url" TO "originalUrl"`); - await queryRunner.query(`ALTER TABLE "emoji" ADD "publicUrl" character varying(512) NOT NULL DEFAULT ''`); - } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "emoji" RENAME COLUMN "url" TO "originalUrl"`, + ); + await queryRunner.query( + `ALTER TABLE "emoji" ADD "publicUrl" character varying(512) NOT NULL DEFAULT ''`, + ); + } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "emoji" DROP COLUMN "publicUrl"`); - await queryRunner.query(`ALTER TABLE "emoji" RENAME COLUMN "originalUrl" TO "url"`); - } + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "emoji" DROP COLUMN "publicUrl"`); + await queryRunner.query( + `ALTER TABLE "emoji" RENAME COLUMN "originalUrl" TO "url"`, + ); + } } diff --git a/packages/backend/migration/1642613870898-drive-file-webpublic-type.js b/packages/backend/migration/1642613870898-drive-file-webpublic-type.js index 15434f7d0c..df5ab7899b 100644 --- a/packages/backend/migration/1642613870898-drive-file-webpublic-type.js +++ b/packages/backend/migration/1642613870898-drive-file-webpublic-type.js @@ -1,13 +1,15 @@ - - export class driveFileWebpublicType1642613870898 { - name = 'driveFileWebpublicType1642613870898' + name = "driveFileWebpublicType1642613870898"; - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "drive_file" ADD "webpublicType" character varying(128)`); - } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "drive_file" ADD "webpublicType" character varying(128)`, + ); + } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "drive_file" DROP COLUMN "webpublicType"`); - } + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "drive_file" DROP COLUMN "webpublicType"`, + ); + } } diff --git a/packages/backend/migration/1643963705770-chart-v4.js b/packages/backend/migration/1643963705770-chart-v4.js index 8b320c2b41..1e7233cc72 100644 --- a/packages/backend/migration/1643963705770-chart-v4.js +++ b/packages/backend/migration/1643963705770-chart-v4.js @@ -1,63 +1,165 @@ - - export class chartV41643963705770 { - name = 'chartV41643963705770' + name = "chartV41643963705770"; - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "__chart__instance" DROP COLUMN "___drive_totalUsage"`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" DROP COLUMN "___drive_totalUsage"`); - await queryRunner.query(`ALTER TABLE "__chart__drive" DROP COLUMN "___local_totalCount"`); - await queryRunner.query(`ALTER TABLE "__chart__drive" DROP COLUMN "___local_totalSize"`); - await queryRunner.query(`ALTER TABLE "__chart__drive" DROP COLUMN "___remote_totalCount"`); - await queryRunner.query(`ALTER TABLE "__chart__drive" DROP COLUMN "___remote_totalSize"`); - await queryRunner.query(`ALTER TABLE "__chart_day__drive" DROP COLUMN "___local_totalCount"`); - await queryRunner.query(`ALTER TABLE "__chart_day__drive" DROP COLUMN "___local_totalSize"`); - await queryRunner.query(`ALTER TABLE "__chart_day__drive" DROP COLUMN "___remote_totalCount"`); - await queryRunner.query(`ALTER TABLE "__chart_day__drive" DROP COLUMN "___remote_totalSize"`); - await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "___local_users"`); - await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "___local_users" bigint NOT NULL DEFAULT 0`); - await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "___remote_users"`); - await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "___remote_users" bigint NOT NULL DEFAULT 0`); - await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "___local_users"`); - await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "___local_users" bigint NOT NULL DEFAULT 0`); - await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "___remote_users"`); - await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "___remote_users" bigint NOT NULL DEFAULT 0`); - await queryRunner.query(`ALTER TABLE "__chart__hashtag" DROP COLUMN "___local_users"`); - await queryRunner.query(`ALTER TABLE "__chart__hashtag" ADD "___local_users" bigint NOT NULL DEFAULT 0`); - await queryRunner.query(`ALTER TABLE "__chart__hashtag" DROP COLUMN "___remote_users"`); - await queryRunner.query(`ALTER TABLE "__chart__hashtag" ADD "___remote_users" bigint NOT NULL DEFAULT 0`); - await queryRunner.query(`ALTER TABLE "__chart_day__hashtag" DROP COLUMN "___local_users"`); - await queryRunner.query(`ALTER TABLE "__chart_day__hashtag" ADD "___local_users" bigint NOT NULL DEFAULT 0`); - await queryRunner.query(`ALTER TABLE "__chart_day__hashtag" DROP COLUMN "___remote_users"`); - await queryRunner.query(`ALTER TABLE "__chart_day__hashtag" ADD "___remote_users" bigint NOT NULL DEFAULT 0`); - } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "__chart__instance" DROP COLUMN "___drive_totalUsage"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" DROP COLUMN "___drive_totalUsage"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__drive" DROP COLUMN "___local_totalCount"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__drive" DROP COLUMN "___local_totalSize"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__drive" DROP COLUMN "___remote_totalCount"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__drive" DROP COLUMN "___remote_totalSize"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__drive" DROP COLUMN "___local_totalCount"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__drive" DROP COLUMN "___local_totalSize"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__drive" DROP COLUMN "___remote_totalCount"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__drive" DROP COLUMN "___remote_totalSize"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__active_users" DROP COLUMN "___local_users"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__active_users" ADD "___local_users" bigint NOT NULL DEFAULT 0`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__active_users" DROP COLUMN "___remote_users"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__active_users" ADD "___remote_users" bigint NOT NULL DEFAULT 0`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__active_users" DROP COLUMN "___local_users"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__active_users" ADD "___local_users" bigint NOT NULL DEFAULT 0`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__active_users" DROP COLUMN "___remote_users"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__active_users" ADD "___remote_users" bigint NOT NULL DEFAULT 0`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__hashtag" DROP COLUMN "___local_users"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__hashtag" ADD "___local_users" bigint NOT NULL DEFAULT 0`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__hashtag" DROP COLUMN "___remote_users"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__hashtag" ADD "___remote_users" bigint NOT NULL DEFAULT 0`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__hashtag" DROP COLUMN "___local_users"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__hashtag" ADD "___local_users" bigint NOT NULL DEFAULT 0`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__hashtag" DROP COLUMN "___remote_users"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__hashtag" ADD "___remote_users" bigint NOT NULL DEFAULT 0`, + ); + } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "__chart_day__hashtag" DROP COLUMN "___remote_users"`); - await queryRunner.query(`ALTER TABLE "__chart_day__hashtag" ADD "___remote_users" character varying array NOT NULL`); - await queryRunner.query(`ALTER TABLE "__chart_day__hashtag" DROP COLUMN "___local_users"`); - await queryRunner.query(`ALTER TABLE "__chart_day__hashtag" ADD "___local_users" character varying array NOT NULL`); - await queryRunner.query(`ALTER TABLE "__chart__hashtag" DROP COLUMN "___remote_users"`); - await queryRunner.query(`ALTER TABLE "__chart__hashtag" ADD "___remote_users" character varying array NOT NULL`); - await queryRunner.query(`ALTER TABLE "__chart__hashtag" DROP COLUMN "___local_users"`); - await queryRunner.query(`ALTER TABLE "__chart__hashtag" ADD "___local_users" character varying array NOT NULL`); - await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "___remote_users"`); - await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "___remote_users" character varying array NOT NULL`); - await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "___local_users"`); - await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "___local_users" character varying array NOT NULL`); - await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "___remote_users"`); - await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "___remote_users" character varying array NOT NULL`); - await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "___local_users"`); - await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "___local_users" character varying array NOT NULL`); - await queryRunner.query(`ALTER TABLE "__chart_day__drive" ADD "___remote_totalSize" bigint NOT NULL`); - await queryRunner.query(`ALTER TABLE "__chart_day__drive" ADD "___remote_totalCount" bigint NOT NULL`); - await queryRunner.query(`ALTER TABLE "__chart_day__drive" ADD "___local_totalSize" bigint NOT NULL`); - await queryRunner.query(`ALTER TABLE "__chart_day__drive" ADD "___local_totalCount" bigint NOT NULL`); - await queryRunner.query(`ALTER TABLE "__chart__drive" ADD "___remote_totalSize" bigint NOT NULL`); - await queryRunner.query(`ALTER TABLE "__chart__drive" ADD "___remote_totalCount" bigint NOT NULL`); - await queryRunner.query(`ALTER TABLE "__chart__drive" ADD "___local_totalSize" bigint NOT NULL`); - await queryRunner.query(`ALTER TABLE "__chart__drive" ADD "___local_totalCount" bigint NOT NULL`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ADD "___drive_totalUsage" bigint NOT NULL`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ADD "___drive_totalUsage" bigint NOT NULL`); - } + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "__chart_day__hashtag" DROP COLUMN "___remote_users"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__hashtag" ADD "___remote_users" character varying array NOT NULL`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__hashtag" DROP COLUMN "___local_users"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__hashtag" ADD "___local_users" character varying array NOT NULL`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__hashtag" DROP COLUMN "___remote_users"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__hashtag" ADD "___remote_users" character varying array NOT NULL`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__hashtag" DROP COLUMN "___local_users"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__hashtag" ADD "___local_users" character varying array NOT NULL`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__active_users" DROP COLUMN "___remote_users"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__active_users" ADD "___remote_users" character varying array NOT NULL`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__active_users" DROP COLUMN "___local_users"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__active_users" ADD "___local_users" character varying array NOT NULL`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__active_users" DROP COLUMN "___remote_users"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__active_users" ADD "___remote_users" character varying array NOT NULL`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__active_users" DROP COLUMN "___local_users"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__active_users" ADD "___local_users" character varying array NOT NULL`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__drive" ADD "___remote_totalSize" bigint NOT NULL`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__drive" ADD "___remote_totalCount" bigint NOT NULL`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__drive" ADD "___local_totalSize" bigint NOT NULL`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__drive" ADD "___local_totalCount" bigint NOT NULL`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__drive" ADD "___remote_totalSize" bigint NOT NULL`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__drive" ADD "___remote_totalCount" bigint NOT NULL`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__drive" ADD "___local_totalSize" bigint NOT NULL`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__drive" ADD "___local_totalCount" bigint NOT NULL`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ADD "___drive_totalUsage" bigint NOT NULL`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ADD "___drive_totalUsage" bigint NOT NULL`, + ); + } } diff --git a/packages/backend/migration/1643966656277-chart-v5.js b/packages/backend/migration/1643966656277-chart-v5.js index df84002f78..826f28ee34 100644 --- a/packages/backend/migration/1643966656277-chart-v5.js +++ b/packages/backend/migration/1643966656277-chart-v5.js @@ -1,27 +1,57 @@ - - export class chartV51643966656277 { - name = 'chartV51643966656277' + name = "chartV51643966656277"; - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "unique_temp___local_users" character varying array NOT NULL DEFAULT '{}'`); - await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "unique_temp___remote_users" character varying array NOT NULL DEFAULT '{}'`); - await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "unique_temp___local_users" character varying array NOT NULL DEFAULT '{}'`); - await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "unique_temp___remote_users" character varying array NOT NULL DEFAULT '{}'`); - await queryRunner.query(`ALTER TABLE "__chart__hashtag" ADD "unique_temp___local_users" character varying array NOT NULL DEFAULT '{}'`); - await queryRunner.query(`ALTER TABLE "__chart__hashtag" ADD "unique_temp___remote_users" character varying array NOT NULL DEFAULT '{}'`); - await queryRunner.query(`ALTER TABLE "__chart_day__hashtag" ADD "unique_temp___local_users" character varying array NOT NULL DEFAULT '{}'`); - await queryRunner.query(`ALTER TABLE "__chart_day__hashtag" ADD "unique_temp___remote_users" character varying array NOT NULL DEFAULT '{}'`); - } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "__chart__active_users" ADD "unique_temp___local_users" character varying array NOT NULL DEFAULT '{}'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__active_users" ADD "unique_temp___remote_users" character varying array NOT NULL DEFAULT '{}'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__active_users" ADD "unique_temp___local_users" character varying array NOT NULL DEFAULT '{}'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__active_users" ADD "unique_temp___remote_users" character varying array NOT NULL DEFAULT '{}'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__hashtag" ADD "unique_temp___local_users" character varying array NOT NULL DEFAULT '{}'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__hashtag" ADD "unique_temp___remote_users" character varying array NOT NULL DEFAULT '{}'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__hashtag" ADD "unique_temp___local_users" character varying array NOT NULL DEFAULT '{}'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__hashtag" ADD "unique_temp___remote_users" character varying array NOT NULL DEFAULT '{}'`, + ); + } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "__chart_day__hashtag" DROP COLUMN "unique_temp___remote_users"`); - await queryRunner.query(`ALTER TABLE "__chart_day__hashtag" DROP COLUMN "unique_temp___local_users"`); - await queryRunner.query(`ALTER TABLE "__chart__hashtag" DROP COLUMN "unique_temp___remote_users"`); - await queryRunner.query(`ALTER TABLE "__chart__hashtag" DROP COLUMN "unique_temp___local_users"`); - await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "unique_temp___remote_users"`); - await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "unique_temp___local_users"`); - await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "unique_temp___remote_users"`); - await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "unique_temp___local_users"`); - } + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "__chart_day__hashtag" DROP COLUMN "unique_temp___remote_users"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__hashtag" DROP COLUMN "unique_temp___local_users"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__hashtag" DROP COLUMN "unique_temp___remote_users"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__hashtag" DROP COLUMN "unique_temp___local_users"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__active_users" DROP COLUMN "unique_temp___remote_users"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__active_users" DROP COLUMN "unique_temp___local_users"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__active_users" DROP COLUMN "unique_temp___remote_users"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__active_users" DROP COLUMN "unique_temp___local_users"`, + ); + } } diff --git a/packages/backend/migration/1643967331284-chart-v6.js b/packages/backend/migration/1643967331284-chart-v6.js index 119198f4a5..9dfbf24bec 100644 --- a/packages/backend/migration/1643967331284-chart-v6.js +++ b/packages/backend/migration/1643967331284-chart-v6.js @@ -1,343 +1,1005 @@ - - export class chartV61643967331284 { - name = 'chartV61643967331284' + name = "chartV61643967331284"; - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "__chart__federation" ALTER COLUMN "___instance_total" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__federation" ALTER COLUMN "___instance_inc" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__federation" ALTER COLUMN "___instance_dec" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__federation" ALTER COLUMN "___instance_total" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__federation" ALTER COLUMN "___instance_inc" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__federation" ALTER COLUMN "___instance_dec" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__notes" ALTER COLUMN "___local_total" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__notes" ALTER COLUMN "___local_inc" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__notes" ALTER COLUMN "___local_dec" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__notes" ALTER COLUMN "___local_diffs_normal" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__notes" ALTER COLUMN "___local_diffs_reply" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__notes" ALTER COLUMN "___local_diffs_renote" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__notes" ALTER COLUMN "___remote_total" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__notes" ALTER COLUMN "___remote_inc" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__notes" ALTER COLUMN "___remote_dec" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__notes" ALTER COLUMN "___remote_diffs_normal" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__notes" ALTER COLUMN "___remote_diffs_reply" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__notes" ALTER COLUMN "___remote_diffs_renote" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__notes" ALTER COLUMN "___local_total" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__notes" ALTER COLUMN "___local_inc" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__notes" ALTER COLUMN "___local_dec" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__notes" ALTER COLUMN "___local_diffs_normal" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__notes" ALTER COLUMN "___local_diffs_reply" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__notes" ALTER COLUMN "___local_diffs_renote" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__notes" ALTER COLUMN "___remote_total" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__notes" ALTER COLUMN "___remote_inc" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__notes" ALTER COLUMN "___remote_dec" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__notes" ALTER COLUMN "___remote_diffs_normal" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__notes" ALTER COLUMN "___remote_diffs_reply" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__notes" ALTER COLUMN "___remote_diffs_renote" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__users" ALTER COLUMN "___local_total" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__users" ALTER COLUMN "___local_inc" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__users" ALTER COLUMN "___local_dec" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__users" ALTER COLUMN "___remote_total" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__users" ALTER COLUMN "___remote_inc" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__users" ALTER COLUMN "___remote_dec" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__users" ALTER COLUMN "___local_total" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__users" ALTER COLUMN "___local_inc" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__users" ALTER COLUMN "___local_dec" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__users" ALTER COLUMN "___remote_total" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__users" ALTER COLUMN "___remote_inc" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__users" ALTER COLUMN "___remote_dec" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__network" ALTER COLUMN "___incomingRequests" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__network" ALTER COLUMN "___outgoingRequests" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__network" ALTER COLUMN "___totalTime" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__network" ALTER COLUMN "___incomingBytes" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__network" ALTER COLUMN "___outgoingBytes" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__network" ALTER COLUMN "___incomingRequests" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__network" ALTER COLUMN "___outgoingRequests" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__network" ALTER COLUMN "___totalTime" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__network" ALTER COLUMN "___incomingBytes" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__network" ALTER COLUMN "___outgoingBytes" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "___requests_failed" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "___requests_succeeded" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "___requests_received" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "___notes_total" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "___notes_inc" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "___notes_dec" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "___notes_diffs_normal" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "___notes_diffs_reply" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "___notes_diffs_renote" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "___users_total" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "___users_inc" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "___users_dec" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "___following_total" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "___following_inc" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "___following_dec" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "___followers_total" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "___followers_inc" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "___followers_dec" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "___drive_totalFiles" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "___drive_incFiles" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "___drive_decFiles" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "___drive_incUsage" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "___drive_decUsage" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ALTER COLUMN "___requests_failed" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ALTER COLUMN "___requests_succeeded" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ALTER COLUMN "___requests_received" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ALTER COLUMN "___notes_total" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ALTER COLUMN "___notes_inc" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ALTER COLUMN "___notes_dec" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ALTER COLUMN "___notes_diffs_normal" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ALTER COLUMN "___notes_diffs_reply" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ALTER COLUMN "___notes_diffs_renote" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ALTER COLUMN "___users_total" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ALTER COLUMN "___users_inc" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ALTER COLUMN "___users_dec" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ALTER COLUMN "___following_total" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ALTER COLUMN "___following_inc" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ALTER COLUMN "___following_dec" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ALTER COLUMN "___followers_total" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ALTER COLUMN "___followers_inc" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ALTER COLUMN "___followers_dec" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ALTER COLUMN "___drive_totalFiles" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ALTER COLUMN "___drive_incFiles" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ALTER COLUMN "___drive_decFiles" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ALTER COLUMN "___drive_incUsage" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ALTER COLUMN "___drive_decUsage" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_notes" ALTER COLUMN "___total" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_notes" ALTER COLUMN "___inc" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_notes" ALTER COLUMN "___dec" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_notes" ALTER COLUMN "___diffs_normal" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_notes" ALTER COLUMN "___diffs_reply" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_notes" ALTER COLUMN "___diffs_renote" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_notes" ALTER COLUMN "___total" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_notes" ALTER COLUMN "___inc" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_notes" ALTER COLUMN "___dec" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_notes" ALTER COLUMN "___diffs_normal" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_notes" ALTER COLUMN "___diffs_reply" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_notes" ALTER COLUMN "___diffs_renote" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__drive" ALTER COLUMN "___local_incCount" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__drive" ALTER COLUMN "___local_incSize" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__drive" ALTER COLUMN "___local_decCount" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__drive" ALTER COLUMN "___local_decSize" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__drive" ALTER COLUMN "___remote_incCount" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__drive" ALTER COLUMN "___remote_incSize" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__drive" ALTER COLUMN "___remote_decCount" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__drive" ALTER COLUMN "___remote_decSize" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__drive" ALTER COLUMN "___local_incCount" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__drive" ALTER COLUMN "___local_incSize" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__drive" ALTER COLUMN "___local_decCount" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__drive" ALTER COLUMN "___local_decSize" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__drive" ALTER COLUMN "___remote_incCount" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__drive" ALTER COLUMN "___remote_incSize" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__drive" ALTER COLUMN "___remote_decCount" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__drive" ALTER COLUMN "___remote_decSize" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_reaction" ALTER COLUMN "___local_count" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_reaction" ALTER COLUMN "___remote_count" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_reaction" ALTER COLUMN "___local_count" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_reaction" ALTER COLUMN "___remote_count" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___local_followings_total" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___local_followings_inc" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___local_followings_dec" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___local_followers_total" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___local_followers_inc" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___local_followers_dec" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___remote_followings_total" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___remote_followings_inc" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___remote_followings_dec" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___remote_followers_total" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___remote_followers_inc" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___remote_followers_dec" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___local_followings_total" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___local_followings_inc" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___local_followings_dec" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___local_followers_total" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___local_followers_inc" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___local_followers_dec" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___remote_followings_total" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___remote_followings_inc" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___remote_followings_dec" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___remote_followers_total" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___remote_followers_inc" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___remote_followers_dec" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_drive" ALTER COLUMN "___totalCount" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_drive" ALTER COLUMN "___totalSize" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_drive" ALTER COLUMN "___incCount" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_drive" ALTER COLUMN "___incSize" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_drive" ALTER COLUMN "___decCount" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_drive" ALTER COLUMN "___decSize" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_drive" ALTER COLUMN "___totalCount" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_drive" ALTER COLUMN "___totalSize" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_drive" ALTER COLUMN "___incCount" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_drive" ALTER COLUMN "___incSize" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_drive" ALTER COLUMN "___decCount" SET DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_drive" ALTER COLUMN "___decSize" SET DEFAULT '0'`); - } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "__chart__federation" ALTER COLUMN "___instance_total" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__federation" ALTER COLUMN "___instance_inc" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__federation" ALTER COLUMN "___instance_dec" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__federation" ALTER COLUMN "___instance_total" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__federation" ALTER COLUMN "___instance_inc" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__federation" ALTER COLUMN "___instance_dec" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__notes" ALTER COLUMN "___local_total" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__notes" ALTER COLUMN "___local_inc" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__notes" ALTER COLUMN "___local_dec" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__notes" ALTER COLUMN "___local_diffs_normal" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__notes" ALTER COLUMN "___local_diffs_reply" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__notes" ALTER COLUMN "___local_diffs_renote" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__notes" ALTER COLUMN "___remote_total" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__notes" ALTER COLUMN "___remote_inc" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__notes" ALTER COLUMN "___remote_dec" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__notes" ALTER COLUMN "___remote_diffs_normal" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__notes" ALTER COLUMN "___remote_diffs_reply" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__notes" ALTER COLUMN "___remote_diffs_renote" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__notes" ALTER COLUMN "___local_total" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__notes" ALTER COLUMN "___local_inc" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__notes" ALTER COLUMN "___local_dec" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__notes" ALTER COLUMN "___local_diffs_normal" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__notes" ALTER COLUMN "___local_diffs_reply" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__notes" ALTER COLUMN "___local_diffs_renote" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__notes" ALTER COLUMN "___remote_total" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__notes" ALTER COLUMN "___remote_inc" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__notes" ALTER COLUMN "___remote_dec" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__notes" ALTER COLUMN "___remote_diffs_normal" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__notes" ALTER COLUMN "___remote_diffs_reply" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__notes" ALTER COLUMN "___remote_diffs_renote" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__users" ALTER COLUMN "___local_total" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__users" ALTER COLUMN "___local_inc" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__users" ALTER COLUMN "___local_dec" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__users" ALTER COLUMN "___remote_total" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__users" ALTER COLUMN "___remote_inc" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__users" ALTER COLUMN "___remote_dec" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__users" ALTER COLUMN "___local_total" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__users" ALTER COLUMN "___local_inc" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__users" ALTER COLUMN "___local_dec" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__users" ALTER COLUMN "___remote_total" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__users" ALTER COLUMN "___remote_inc" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__users" ALTER COLUMN "___remote_dec" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__network" ALTER COLUMN "___incomingRequests" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__network" ALTER COLUMN "___outgoingRequests" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__network" ALTER COLUMN "___totalTime" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__network" ALTER COLUMN "___incomingBytes" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__network" ALTER COLUMN "___outgoingBytes" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__network" ALTER COLUMN "___incomingRequests" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__network" ALTER COLUMN "___outgoingRequests" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__network" ALTER COLUMN "___totalTime" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__network" ALTER COLUMN "___incomingBytes" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__network" ALTER COLUMN "___outgoingBytes" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "___requests_failed" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "___requests_succeeded" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "___requests_received" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "___notes_total" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "___notes_inc" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "___notes_dec" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "___notes_diffs_normal" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "___notes_diffs_reply" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "___notes_diffs_renote" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "___users_total" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "___users_inc" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "___users_dec" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "___following_total" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "___following_inc" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "___following_dec" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "___followers_total" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "___followers_inc" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "___followers_dec" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "___drive_totalFiles" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "___drive_incFiles" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "___drive_decFiles" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "___drive_incUsage" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "___drive_decUsage" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ALTER COLUMN "___requests_failed" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ALTER COLUMN "___requests_succeeded" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ALTER COLUMN "___requests_received" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ALTER COLUMN "___notes_total" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ALTER COLUMN "___notes_inc" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ALTER COLUMN "___notes_dec" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ALTER COLUMN "___notes_diffs_normal" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ALTER COLUMN "___notes_diffs_reply" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ALTER COLUMN "___notes_diffs_renote" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ALTER COLUMN "___users_total" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ALTER COLUMN "___users_inc" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ALTER COLUMN "___users_dec" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ALTER COLUMN "___following_total" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ALTER COLUMN "___following_inc" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ALTER COLUMN "___following_dec" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ALTER COLUMN "___followers_total" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ALTER COLUMN "___followers_inc" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ALTER COLUMN "___followers_dec" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ALTER COLUMN "___drive_totalFiles" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ALTER COLUMN "___drive_incFiles" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ALTER COLUMN "___drive_decFiles" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ALTER COLUMN "___drive_incUsage" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ALTER COLUMN "___drive_decUsage" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_notes" ALTER COLUMN "___total" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_notes" ALTER COLUMN "___inc" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_notes" ALTER COLUMN "___dec" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_notes" ALTER COLUMN "___diffs_normal" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_notes" ALTER COLUMN "___diffs_reply" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_notes" ALTER COLUMN "___diffs_renote" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_notes" ALTER COLUMN "___total" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_notes" ALTER COLUMN "___inc" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_notes" ALTER COLUMN "___dec" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_notes" ALTER COLUMN "___diffs_normal" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_notes" ALTER COLUMN "___diffs_reply" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_notes" ALTER COLUMN "___diffs_renote" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__drive" ALTER COLUMN "___local_incCount" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__drive" ALTER COLUMN "___local_incSize" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__drive" ALTER COLUMN "___local_decCount" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__drive" ALTER COLUMN "___local_decSize" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__drive" ALTER COLUMN "___remote_incCount" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__drive" ALTER COLUMN "___remote_incSize" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__drive" ALTER COLUMN "___remote_decCount" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__drive" ALTER COLUMN "___remote_decSize" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__drive" ALTER COLUMN "___local_incCount" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__drive" ALTER COLUMN "___local_incSize" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__drive" ALTER COLUMN "___local_decCount" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__drive" ALTER COLUMN "___local_decSize" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__drive" ALTER COLUMN "___remote_incCount" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__drive" ALTER COLUMN "___remote_incSize" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__drive" ALTER COLUMN "___remote_decCount" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__drive" ALTER COLUMN "___remote_decSize" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_reaction" ALTER COLUMN "___local_count" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_reaction" ALTER COLUMN "___remote_count" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_reaction" ALTER COLUMN "___local_count" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_reaction" ALTER COLUMN "___remote_count" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___local_followings_total" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___local_followings_inc" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___local_followings_dec" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___local_followers_total" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___local_followers_inc" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___local_followers_dec" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___remote_followings_total" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___remote_followings_inc" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___remote_followings_dec" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___remote_followers_total" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___remote_followers_inc" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___remote_followers_dec" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___local_followings_total" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___local_followings_inc" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___local_followings_dec" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___local_followers_total" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___local_followers_inc" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___local_followers_dec" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___remote_followings_total" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___remote_followings_inc" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___remote_followings_dec" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___remote_followers_total" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___remote_followers_inc" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___remote_followers_dec" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_drive" ALTER COLUMN "___totalCount" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_drive" ALTER COLUMN "___totalSize" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_drive" ALTER COLUMN "___incCount" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_drive" ALTER COLUMN "___incSize" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_drive" ALTER COLUMN "___decCount" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_drive" ALTER COLUMN "___decSize" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_drive" ALTER COLUMN "___totalCount" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_drive" ALTER COLUMN "___totalSize" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_drive" ALTER COLUMN "___incCount" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_drive" ALTER COLUMN "___incSize" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_drive" ALTER COLUMN "___decCount" SET DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_drive" ALTER COLUMN "___decSize" SET DEFAULT '0'`, + ); + } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_drive" ALTER COLUMN "___decSize" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_drive" ALTER COLUMN "___decCount" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_drive" ALTER COLUMN "___incSize" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_drive" ALTER COLUMN "___incCount" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_drive" ALTER COLUMN "___totalSize" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_drive" ALTER COLUMN "___totalCount" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_drive" ALTER COLUMN "___decSize" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_drive" ALTER COLUMN "___decCount" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_drive" ALTER COLUMN "___incSize" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_drive" ALTER COLUMN "___incCount" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_drive" ALTER COLUMN "___totalSize" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_drive" ALTER COLUMN "___totalCount" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___remote_followers_dec" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___remote_followers_inc" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___remote_followers_total" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___remote_followings_dec" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___remote_followings_inc" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___remote_followings_total" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___local_followers_dec" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___local_followers_inc" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___local_followers_total" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___local_followings_dec" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___local_followings_inc" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___local_followings_total" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___remote_followers_dec" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___remote_followers_inc" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___remote_followers_total" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___remote_followings_dec" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___remote_followings_inc" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___remote_followings_total" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___local_followers_dec" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___local_followers_inc" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___local_followers_total" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___local_followings_dec" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___local_followings_inc" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___local_followings_total" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_reaction" ALTER COLUMN "___remote_count" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_reaction" ALTER COLUMN "___local_count" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_reaction" ALTER COLUMN "___remote_count" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_reaction" ALTER COLUMN "___local_count" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart_day__drive" ALTER COLUMN "___remote_decSize" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart_day__drive" ALTER COLUMN "___remote_decCount" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart_day__drive" ALTER COLUMN "___remote_incSize" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart_day__drive" ALTER COLUMN "___remote_incCount" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart_day__drive" ALTER COLUMN "___local_decSize" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart_day__drive" ALTER COLUMN "___local_decCount" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart_day__drive" ALTER COLUMN "___local_incSize" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart_day__drive" ALTER COLUMN "___local_incCount" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart__drive" ALTER COLUMN "___remote_decSize" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart__drive" ALTER COLUMN "___remote_decCount" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart__drive" ALTER COLUMN "___remote_incSize" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart__drive" ALTER COLUMN "___remote_incCount" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart__drive" ALTER COLUMN "___local_decSize" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart__drive" ALTER COLUMN "___local_decCount" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart__drive" ALTER COLUMN "___local_incSize" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart__drive" ALTER COLUMN "___local_incCount" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_notes" ALTER COLUMN "___diffs_renote" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_notes" ALTER COLUMN "___diffs_reply" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_notes" ALTER COLUMN "___diffs_normal" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_notes" ALTER COLUMN "___dec" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_notes" ALTER COLUMN "___inc" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_notes" ALTER COLUMN "___total" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_notes" ALTER COLUMN "___diffs_renote" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_notes" ALTER COLUMN "___diffs_reply" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_notes" ALTER COLUMN "___diffs_normal" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_notes" ALTER COLUMN "___dec" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_notes" ALTER COLUMN "___inc" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_notes" ALTER COLUMN "___total" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ALTER COLUMN "___drive_decUsage" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ALTER COLUMN "___drive_incUsage" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ALTER COLUMN "___drive_decFiles" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ALTER COLUMN "___drive_incFiles" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ALTER COLUMN "___drive_totalFiles" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ALTER COLUMN "___followers_dec" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ALTER COLUMN "___followers_inc" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ALTER COLUMN "___followers_total" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ALTER COLUMN "___following_dec" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ALTER COLUMN "___following_inc" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ALTER COLUMN "___following_total" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ALTER COLUMN "___users_dec" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ALTER COLUMN "___users_inc" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ALTER COLUMN "___users_total" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ALTER COLUMN "___notes_diffs_renote" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ALTER COLUMN "___notes_diffs_reply" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ALTER COLUMN "___notes_diffs_normal" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ALTER COLUMN "___notes_dec" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ALTER COLUMN "___notes_inc" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ALTER COLUMN "___notes_total" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ALTER COLUMN "___requests_received" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ALTER COLUMN "___requests_succeeded" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ALTER COLUMN "___requests_failed" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "___drive_decUsage" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "___drive_incUsage" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "___drive_decFiles" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "___drive_incFiles" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "___drive_totalFiles" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "___followers_dec" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "___followers_inc" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "___followers_total" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "___following_dec" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "___following_inc" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "___following_total" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "___users_dec" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "___users_inc" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "___users_total" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "___notes_diffs_renote" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "___notes_diffs_reply" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "___notes_diffs_normal" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "___notes_dec" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "___notes_inc" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "___notes_total" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "___requests_received" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "___requests_succeeded" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "___requests_failed" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart_day__network" ALTER COLUMN "___outgoingBytes" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart_day__network" ALTER COLUMN "___incomingBytes" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart_day__network" ALTER COLUMN "___totalTime" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart_day__network" ALTER COLUMN "___outgoingRequests" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart_day__network" ALTER COLUMN "___incomingRequests" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart__network" ALTER COLUMN "___outgoingBytes" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart__network" ALTER COLUMN "___incomingBytes" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart__network" ALTER COLUMN "___totalTime" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart__network" ALTER COLUMN "___outgoingRequests" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart__network" ALTER COLUMN "___incomingRequests" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart_day__users" ALTER COLUMN "___remote_dec" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart_day__users" ALTER COLUMN "___remote_inc" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart_day__users" ALTER COLUMN "___remote_total" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart_day__users" ALTER COLUMN "___local_dec" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart_day__users" ALTER COLUMN "___local_inc" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart_day__users" ALTER COLUMN "___local_total" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart__users" ALTER COLUMN "___remote_dec" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart__users" ALTER COLUMN "___remote_inc" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart__users" ALTER COLUMN "___remote_total" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart__users" ALTER COLUMN "___local_dec" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart__users" ALTER COLUMN "___local_inc" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart__users" ALTER COLUMN "___local_total" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart_day__notes" ALTER COLUMN "___remote_diffs_renote" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart_day__notes" ALTER COLUMN "___remote_diffs_reply" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart_day__notes" ALTER COLUMN "___remote_diffs_normal" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart_day__notes" ALTER COLUMN "___remote_dec" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart_day__notes" ALTER COLUMN "___remote_inc" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart_day__notes" ALTER COLUMN "___remote_total" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart_day__notes" ALTER COLUMN "___local_diffs_renote" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart_day__notes" ALTER COLUMN "___local_diffs_reply" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart_day__notes" ALTER COLUMN "___local_diffs_normal" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart_day__notes" ALTER COLUMN "___local_dec" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart_day__notes" ALTER COLUMN "___local_inc" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart_day__notes" ALTER COLUMN "___local_total" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart__notes" ALTER COLUMN "___remote_diffs_renote" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart__notes" ALTER COLUMN "___remote_diffs_reply" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart__notes" ALTER COLUMN "___remote_diffs_normal" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart__notes" ALTER COLUMN "___remote_dec" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart__notes" ALTER COLUMN "___remote_inc" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart__notes" ALTER COLUMN "___remote_total" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart__notes" ALTER COLUMN "___local_diffs_renote" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart__notes" ALTER COLUMN "___local_diffs_reply" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart__notes" ALTER COLUMN "___local_diffs_normal" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart__notes" ALTER COLUMN "___local_dec" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart__notes" ALTER COLUMN "___local_inc" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart__notes" ALTER COLUMN "___local_total" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart_day__federation" ALTER COLUMN "___instance_dec" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart_day__federation" ALTER COLUMN "___instance_inc" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart_day__federation" ALTER COLUMN "___instance_total" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart__federation" ALTER COLUMN "___instance_dec" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart__federation" ALTER COLUMN "___instance_inc" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "__chart__federation" ALTER COLUMN "___instance_total" DROP DEFAULT`); - } + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_drive" ALTER COLUMN "___decSize" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_drive" ALTER COLUMN "___decCount" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_drive" ALTER COLUMN "___incSize" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_drive" ALTER COLUMN "___incCount" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_drive" ALTER COLUMN "___totalSize" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_drive" ALTER COLUMN "___totalCount" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_drive" ALTER COLUMN "___decSize" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_drive" ALTER COLUMN "___decCount" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_drive" ALTER COLUMN "___incSize" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_drive" ALTER COLUMN "___incCount" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_drive" ALTER COLUMN "___totalSize" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_drive" ALTER COLUMN "___totalCount" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___remote_followers_dec" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___remote_followers_inc" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___remote_followers_total" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___remote_followings_dec" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___remote_followings_inc" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___remote_followings_total" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___local_followers_dec" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___local_followers_inc" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___local_followers_total" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___local_followings_dec" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___local_followings_inc" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___local_followings_total" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___remote_followers_dec" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___remote_followers_inc" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___remote_followers_total" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___remote_followings_dec" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___remote_followings_inc" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___remote_followings_total" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___local_followers_dec" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___local_followers_inc" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___local_followers_total" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___local_followings_dec" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___local_followings_inc" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___local_followings_total" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_reaction" ALTER COLUMN "___remote_count" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_reaction" ALTER COLUMN "___local_count" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_reaction" ALTER COLUMN "___remote_count" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_reaction" ALTER COLUMN "___local_count" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__drive" ALTER COLUMN "___remote_decSize" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__drive" ALTER COLUMN "___remote_decCount" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__drive" ALTER COLUMN "___remote_incSize" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__drive" ALTER COLUMN "___remote_incCount" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__drive" ALTER COLUMN "___local_decSize" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__drive" ALTER COLUMN "___local_decCount" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__drive" ALTER COLUMN "___local_incSize" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__drive" ALTER COLUMN "___local_incCount" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__drive" ALTER COLUMN "___remote_decSize" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__drive" ALTER COLUMN "___remote_decCount" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__drive" ALTER COLUMN "___remote_incSize" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__drive" ALTER COLUMN "___remote_incCount" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__drive" ALTER COLUMN "___local_decSize" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__drive" ALTER COLUMN "___local_decCount" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__drive" ALTER COLUMN "___local_incSize" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__drive" ALTER COLUMN "___local_incCount" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_notes" ALTER COLUMN "___diffs_renote" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_notes" ALTER COLUMN "___diffs_reply" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_notes" ALTER COLUMN "___diffs_normal" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_notes" ALTER COLUMN "___dec" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_notes" ALTER COLUMN "___inc" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_notes" ALTER COLUMN "___total" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_notes" ALTER COLUMN "___diffs_renote" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_notes" ALTER COLUMN "___diffs_reply" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_notes" ALTER COLUMN "___diffs_normal" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_notes" ALTER COLUMN "___dec" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_notes" ALTER COLUMN "___inc" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_notes" ALTER COLUMN "___total" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ALTER COLUMN "___drive_decUsage" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ALTER COLUMN "___drive_incUsage" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ALTER COLUMN "___drive_decFiles" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ALTER COLUMN "___drive_incFiles" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ALTER COLUMN "___drive_totalFiles" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ALTER COLUMN "___followers_dec" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ALTER COLUMN "___followers_inc" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ALTER COLUMN "___followers_total" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ALTER COLUMN "___following_dec" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ALTER COLUMN "___following_inc" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ALTER COLUMN "___following_total" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ALTER COLUMN "___users_dec" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ALTER COLUMN "___users_inc" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ALTER COLUMN "___users_total" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ALTER COLUMN "___notes_diffs_renote" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ALTER COLUMN "___notes_diffs_reply" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ALTER COLUMN "___notes_diffs_normal" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ALTER COLUMN "___notes_dec" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ALTER COLUMN "___notes_inc" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ALTER COLUMN "___notes_total" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ALTER COLUMN "___requests_received" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ALTER COLUMN "___requests_succeeded" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ALTER COLUMN "___requests_failed" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "___drive_decUsage" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "___drive_incUsage" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "___drive_decFiles" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "___drive_incFiles" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "___drive_totalFiles" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "___followers_dec" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "___followers_inc" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "___followers_total" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "___following_dec" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "___following_inc" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "___following_total" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "___users_dec" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "___users_inc" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "___users_total" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "___notes_diffs_renote" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "___notes_diffs_reply" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "___notes_diffs_normal" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "___notes_dec" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "___notes_inc" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "___notes_total" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "___requests_received" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "___requests_succeeded" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "___requests_failed" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__network" ALTER COLUMN "___outgoingBytes" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__network" ALTER COLUMN "___incomingBytes" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__network" ALTER COLUMN "___totalTime" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__network" ALTER COLUMN "___outgoingRequests" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__network" ALTER COLUMN "___incomingRequests" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__network" ALTER COLUMN "___outgoingBytes" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__network" ALTER COLUMN "___incomingBytes" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__network" ALTER COLUMN "___totalTime" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__network" ALTER COLUMN "___outgoingRequests" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__network" ALTER COLUMN "___incomingRequests" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__users" ALTER COLUMN "___remote_dec" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__users" ALTER COLUMN "___remote_inc" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__users" ALTER COLUMN "___remote_total" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__users" ALTER COLUMN "___local_dec" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__users" ALTER COLUMN "___local_inc" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__users" ALTER COLUMN "___local_total" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__users" ALTER COLUMN "___remote_dec" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__users" ALTER COLUMN "___remote_inc" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__users" ALTER COLUMN "___remote_total" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__users" ALTER COLUMN "___local_dec" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__users" ALTER COLUMN "___local_inc" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__users" ALTER COLUMN "___local_total" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__notes" ALTER COLUMN "___remote_diffs_renote" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__notes" ALTER COLUMN "___remote_diffs_reply" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__notes" ALTER COLUMN "___remote_diffs_normal" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__notes" ALTER COLUMN "___remote_dec" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__notes" ALTER COLUMN "___remote_inc" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__notes" ALTER COLUMN "___remote_total" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__notes" ALTER COLUMN "___local_diffs_renote" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__notes" ALTER COLUMN "___local_diffs_reply" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__notes" ALTER COLUMN "___local_diffs_normal" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__notes" ALTER COLUMN "___local_dec" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__notes" ALTER COLUMN "___local_inc" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__notes" ALTER COLUMN "___local_total" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__notes" ALTER COLUMN "___remote_diffs_renote" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__notes" ALTER COLUMN "___remote_diffs_reply" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__notes" ALTER COLUMN "___remote_diffs_normal" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__notes" ALTER COLUMN "___remote_dec" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__notes" ALTER COLUMN "___remote_inc" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__notes" ALTER COLUMN "___remote_total" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__notes" ALTER COLUMN "___local_diffs_renote" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__notes" ALTER COLUMN "___local_diffs_reply" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__notes" ALTER COLUMN "___local_diffs_normal" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__notes" ALTER COLUMN "___local_dec" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__notes" ALTER COLUMN "___local_inc" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__notes" ALTER COLUMN "___local_total" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__federation" ALTER COLUMN "___instance_dec" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__federation" ALTER COLUMN "___instance_inc" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__federation" ALTER COLUMN "___instance_total" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__federation" ALTER COLUMN "___instance_dec" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__federation" ALTER COLUMN "___instance_inc" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__federation" ALTER COLUMN "___instance_total" DROP DEFAULT`, + ); + } } diff --git a/packages/backend/migration/1644010796173-convert-hard-mutes.js b/packages/backend/migration/1644010796173-convert-hard-mutes.js index 207a759b8e..39c5b080fd 100644 --- a/packages/backend/migration/1644010796173-convert-hard-mutes.js +++ b/packages/backend/migration/1644010796173-convert-hard-mutes.js @@ -1,65 +1,70 @@ -import RE2 from 're2'; - +import RE2 from "re2"; export class convertHardMutes1644010796173 { - name = 'convertHardMutes1644010796173' + name = "convertHardMutes1644010796173"; - async up(queryRunner) { - let entries = await queryRunner.query(`SELECT "userId", "mutedWords" FROM "user_profile" WHERE "userHost" IS NULL`); - for(let i = 0; i < entries.length; i++) { - let words = entries[i].mutedWords - .map(line => { - if (typeof line === 'string') return []; - const regexp = line.join(" ").match(/^\/(.+)\/(.*)$/); - if (regexp) { - // convert regexp's - try { - new RE2(regexp[1], regexp[2]); - return `/${regexp[1]}/${regexp[2]}`; - } catch (err) { - // invalid regex, ignore it - return []; - } - } else { - // remove empty segments - return line.filter(x => x !== ''); - } - }) - // remove empty lines - .filter(x => !(Array.isArray(x) && x.length === 0)); + async up(queryRunner) { + let entries = await queryRunner.query( + `SELECT "userId", "mutedWords" FROM "user_profile" WHERE "userHost" IS NULL`, + ); + for (let i = 0; i < entries.length; i++) { + let words = entries[i].mutedWords + .map((line) => { + if (typeof line === "string") return []; + const regexp = line.join(" ").match(/^\/(.+)\/(.*)$/); + if (regexp) { + // convert regexp's + try { + new RE2(regexp[1], regexp[2]); + return `/${regexp[1]}/${regexp[2]}`; + } catch (err) { + // invalid regex, ignore it + return []; + } + } else { + // remove empty segments + return line.filter((x) => x !== ""); + } + }) + // remove empty lines + .filter((x) => !(Array.isArray(x) && x.length === 0)); - await queryRunner.connection.createQueryBuilder() - .update('user_profile') - .set({ - mutedWords: words - }) - .where('userId = :id', { id: entries[i].userId }) - .execute(); - } - } + await queryRunner.connection + .createQueryBuilder() + .update("user_profile") + .set({ + mutedWords: words, + }) + .where("userId = :id", { id: entries[i].userId }) + .execute(); + } + } - async down(queryRunner) { - let entries = await queryRunner.query(`SELECT "userId", "mutedWords" FROM "user_profile"`); - for(let i = 0; i < entries.length; i++) { - let words = entries[i].mutedWords - .map(line => { - if (Array.isArray(line)) { - return line; - } else { - // do not split regex at spaces again - return [line]; - } - }) - // remove empty lines - .filter(x => !(Array.isArray(x) && x.length === 0)); + async down(queryRunner) { + let entries = await queryRunner.query( + `SELECT "userId", "mutedWords" FROM "user_profile"`, + ); + for (let i = 0; i < entries.length; i++) { + let words = entries[i].mutedWords + .map((line) => { + if (Array.isArray(line)) { + return line; + } else { + // do not split regex at spaces again + return [line]; + } + }) + // remove empty lines + .filter((x) => !(Array.isArray(x) && x.length === 0)); - await queryRunner.connection.createQueryBuilder() - .update('user_profile') - .set({ - mutedWords: words - }) - .where('userId = :id', { id: entries[i].userId }) - .execute(); - } - } + await queryRunner.connection + .createQueryBuilder() + .update("user_profile") + .set({ + mutedWords: words, + }) + .where("userId = :id", { id: entries[i].userId }) + .execute(); + } + } } diff --git a/packages/backend/migration/1644058404077-chart-v7.js b/packages/backend/migration/1644058404077-chart-v7.js index f05ad003db..ab48c9ab38 100644 --- a/packages/backend/migration/1644058404077-chart-v7.js +++ b/packages/backend/migration/1644058404077-chart-v7.js @@ -1,501 +1,1470 @@ - - export class chartV71644058404077 { - name = 'chartV71644058404077' + name = "chartV71644058404077"; async up(queryRunner) { - await queryRunner.query(`UPDATE "__chart__federation" SET "___instance_total"=2147483647 WHERE "___instance_total" > 2147483647`); - await queryRunner.query(`UPDATE "__chart__federation" SET "___instance_inc"=32767 WHERE "___instance_inc" > 32767`); - await queryRunner.query(`UPDATE "__chart__federation" SET "___instance_dec"=32767 WHERE "___instance_dec" > 32767`); - await queryRunner.query(`UPDATE "__chart_day__federation" SET "___instance_total"=2147483647 WHERE "___instance_total" > 2147483647`); - await queryRunner.query(`UPDATE "__chart_day__federation" SET "___instance_inc"=32767 WHERE "___instance_inc" > 32767`); - await queryRunner.query(`UPDATE "__chart_day__federation" SET "___instance_dec"=32767 WHERE "___instance_dec" > 32767`); - await queryRunner.query(`UPDATE "__chart__notes" SET "___local_total"=2147483647 WHERE "___local_total" > 2147483647`); - await queryRunner.query(`UPDATE "__chart__notes" SET "___local_inc"=2147483647 WHERE "___local_inc" > 2147483647`); - await queryRunner.query(`UPDATE "__chart__notes" SET "___local_dec"=2147483647 WHERE "___local_dec" > 2147483647`); - await queryRunner.query(`UPDATE "__chart__notes" SET "___local_diffs_normal"=2147483647 WHERE "___local_diffs_normal" > 2147483647`); - await queryRunner.query(`UPDATE "__chart__notes" SET "___local_diffs_reply"=2147483647 WHERE "___local_diffs_reply" > 2147483647`); - await queryRunner.query(`UPDATE "__chart__notes" SET "___local_diffs_renote"=2147483647 WHERE "___local_diffs_renote" > 2147483647`); - await queryRunner.query(`UPDATE "__chart__notes" SET "___remote_total"=2147483647 WHERE "___remote_total" > 2147483647`); - await queryRunner.query(`UPDATE "__chart__notes" SET "___remote_inc"=2147483647 WHERE "___remote_inc" > 2147483647`); - await queryRunner.query(`UPDATE "__chart__notes" SET "___remote_dec"=2147483647 WHERE "___remote_dec" > 2147483647`); - await queryRunner.query(`UPDATE "__chart__notes" SET "___remote_diffs_normal"=2147483647 WHERE "___remote_diffs_normal" > 2147483647`); - await queryRunner.query(`UPDATE "__chart__notes" SET "___remote_diffs_reply"=2147483647 WHERE "___remote_diffs_reply" > 2147483647`); - await queryRunner.query(`UPDATE "__chart__notes" SET "___remote_diffs_renote"=2147483647 WHERE "___remote_diffs_renote" > 2147483647`); - await queryRunner.query(`UPDATE "__chart_day__notes" SET "___local_total"=2147483647 WHERE "___local_total" > 2147483647`); - await queryRunner.query(`UPDATE "__chart_day__notes" SET "___local_inc"=2147483647 WHERE "___local_inc" > 2147483647`); - await queryRunner.query(`UPDATE "__chart_day__notes" SET "___local_dec"=2147483647 WHERE "___local_dec" > 2147483647`); - await queryRunner.query(`UPDATE "__chart_day__notes" SET "___local_diffs_normal"=2147483647 WHERE "___local_diffs_normal" > 2147483647`); - await queryRunner.query(`UPDATE "__chart_day__notes" SET "___local_diffs_reply"=2147483647 WHERE "___local_diffs_reply" > 2147483647`); - await queryRunner.query(`UPDATE "__chart_day__notes" SET "___local_diffs_renote"=2147483647 WHERE "___local_diffs_renote" > 2147483647`); - await queryRunner.query(`UPDATE "__chart_day__notes" SET "___remote_total"=2147483647 WHERE "___remote_total" > 2147483647`); - await queryRunner.query(`UPDATE "__chart_day__notes" SET "___remote_inc"=2147483647 WHERE "___remote_inc" > 2147483647`); - await queryRunner.query(`UPDATE "__chart_day__notes" SET "___remote_dec"=2147483647 WHERE "___remote_dec" > 2147483647`); - await queryRunner.query(`UPDATE "__chart_day__notes" SET "___remote_diffs_normal"=2147483647 WHERE "___remote_diffs_normal" > 2147483647`); - await queryRunner.query(`UPDATE "__chart_day__notes" SET "___remote_diffs_reply"=2147483647 WHERE "___remote_diffs_reply" > 2147483647`); - await queryRunner.query(`UPDATE "__chart_day__notes" SET "___remote_diffs_renote"=2147483647 WHERE "___remote_diffs_renote" > 2147483647`); - await queryRunner.query(`UPDATE "__chart__users" SET "___local_total"=2147483647 WHERE "___local_total" > 2147483647`); - await queryRunner.query(`UPDATE "__chart__users" SET "___local_inc"=32767 WHERE "___local_inc" > 32767`); - await queryRunner.query(`UPDATE "__chart__users" SET "___local_dec"=32767 WHERE "___local_dec" > 32767`); - await queryRunner.query(`UPDATE "__chart__users" SET "___remote_total"=2147483647 WHERE "___remote_total" > 2147483647`); - await queryRunner.query(`UPDATE "__chart__users" SET "___remote_inc"=32767 WHERE "___remote_inc" > 32767`); - await queryRunner.query(`UPDATE "__chart__users" SET "___remote_dec"=32767 WHERE "___remote_dec" > 32767`); - await queryRunner.query(`UPDATE "__chart_day__users" SET "___local_total"=2147483647 WHERE "___local_total" > 2147483647`); - await queryRunner.query(`UPDATE "__chart_day__users" SET "___local_inc"=32767 WHERE "___local_inc" > 32767`); - await queryRunner.query(`UPDATE "__chart_day__users" SET "___local_dec"=32767 WHERE "___local_dec" > 32767`); - await queryRunner.query(`UPDATE "__chart_day__users" SET "___remote_total"=2147483647 WHERE "___remote_total" > 2147483647`); - await queryRunner.query(`UPDATE "__chart_day__users" SET "___remote_inc"=32767 WHERE "___remote_inc" > 32767`); - await queryRunner.query(`UPDATE "__chart_day__users" SET "___remote_dec"=32767 WHERE "___remote_dec" > 32767`); - await queryRunner.query(`UPDATE "__chart__network" SET "___incomingRequests"=2147483647 WHERE "___incomingRequests" > 2147483647`); - await queryRunner.query(`UPDATE "__chart__network" SET "___outgoingRequests"=2147483647 WHERE "___outgoingRequests" > 2147483647`); - await queryRunner.query(`UPDATE "__chart__network" SET "___totalTime"=2147483647 WHERE "___totalTime" > 2147483647`); - await queryRunner.query(`UPDATE "__chart__network" SET "___incomingBytes"=2147483647 WHERE "___incomingBytes" > 2147483647`); - await queryRunner.query(`UPDATE "__chart__network" SET "___outgoingBytes"=2147483647 WHERE "___outgoingBytes" > 2147483647`); - await queryRunner.query(`UPDATE "__chart_day__network" SET "___incomingRequests"=2147483647 WHERE "___incomingRequests" > 2147483647`); - await queryRunner.query(`UPDATE "__chart_day__network" SET "___outgoingRequests"=2147483647 WHERE "___outgoingRequests" > 2147483647`); - await queryRunner.query(`UPDATE "__chart_day__network" SET "___totalTime"=2147483647 WHERE "___totalTime" > 2147483647`); - await queryRunner.query(`UPDATE "__chart_day__network" SET "___incomingBytes"=2147483647 WHERE "___incomingBytes" > 2147483647`); - await queryRunner.query(`UPDATE "__chart_day__network" SET "___outgoingBytes"=2147483647 WHERE "___outgoingBytes" > 2147483647`); - await queryRunner.query(`UPDATE "__chart__instance" SET "___requests_failed"=32767 WHERE "___requests_failed" > 32767`); - await queryRunner.query(`UPDATE "__chart__instance" SET "___requests_succeeded"=32767 WHERE "___requests_succeeded" > 32767`); - await queryRunner.query(`UPDATE "__chart__instance" SET "___requests_received"=32767 WHERE "___requests_received" > 32767`); - await queryRunner.query(`UPDATE "__chart__instance" SET "___notes_total"=2147483647 WHERE "___notes_total" > 2147483647`); - await queryRunner.query(`UPDATE "__chart__instance" SET "___notes_inc"=2147483647 WHERE "___notes_inc" > 2147483647`); - await queryRunner.query(`UPDATE "__chart__instance" SET "___notes_dec"=2147483647 WHERE "___notes_dec" > 2147483647`); - await queryRunner.query(`UPDATE "__chart__instance" SET "___notes_diffs_normal"=2147483647 WHERE "___notes_diffs_normal" > 2147483647`); - await queryRunner.query(`UPDATE "__chart__instance" SET "___notes_diffs_reply"=2147483647 WHERE "___notes_diffs_reply" > 2147483647`); - await queryRunner.query(`UPDATE "__chart__instance" SET "___notes_diffs_renote"=2147483647 WHERE "___notes_diffs_renote" > 2147483647`); - await queryRunner.query(`UPDATE "__chart__instance" SET "___users_total"=2147483647 WHERE "___users_total" > 2147483647`); - await queryRunner.query(`UPDATE "__chart__instance" SET "___users_inc"=32767 WHERE "___users_inc" > 32767`); - await queryRunner.query(`UPDATE "__chart__instance" SET "___users_dec"=32767 WHERE "___users_dec" > 32767`); - await queryRunner.query(`UPDATE "__chart__instance" SET "___following_total"=2147483647 WHERE "___following_total" > 2147483647`); - await queryRunner.query(`UPDATE "__chart__instance" SET "___following_inc"=32767 WHERE "___following_inc" > 32767`); - await queryRunner.query(`UPDATE "__chart__instance" SET "___following_dec"=32767 WHERE "___following_dec" > 32767`); - await queryRunner.query(`UPDATE "__chart__instance" SET "___followers_total"=2147483647 WHERE "___followers_total" > 2147483647`); - await queryRunner.query(`UPDATE "__chart__instance" SET "___followers_inc"=32767 WHERE "___followers_inc" > 32767`); - await queryRunner.query(`UPDATE "__chart__instance" SET "___followers_dec"=32767 WHERE "___followers_dec" > 32767`); - await queryRunner.query(`UPDATE "__chart__instance" SET "___drive_totalFiles"=2147483647 WHERE "___drive_totalFiles" > 2147483647`); - await queryRunner.query(`UPDATE "__chart__instance" SET "___drive_incFiles"=2147483647 WHERE "___drive_incFiles" > 2147483647`); - await queryRunner.query(`UPDATE "__chart__instance" SET "___drive_decFiles"=2147483647 WHERE "___drive_decFiles" > 2147483647`); - await queryRunner.query(`UPDATE "__chart__instance" SET "___drive_incUsage"=2147483647 WHERE "___drive_incUsage" > 2147483647`); - await queryRunner.query(`UPDATE "__chart__instance" SET "___drive_decUsage"=2147483647 WHERE "___drive_decUsage" > 2147483647`); - await queryRunner.query(`UPDATE "__chart_day__instance" SET "___requests_failed"=32767 WHERE "___requests_failed" > 32767`); - await queryRunner.query(`UPDATE "__chart_day__instance" SET "___requests_succeeded"=32767 WHERE "___requests_succeeded" > 32767`); - await queryRunner.query(`UPDATE "__chart_day__instance" SET "___requests_received"=32767 WHERE "___requests_received" > 32767`); - await queryRunner.query(`UPDATE "__chart_day__instance" SET "___notes_total"=2147483647 WHERE "___notes_total" > 2147483647`); - await queryRunner.query(`UPDATE "__chart_day__instance" SET "___notes_inc"=2147483647 WHERE "___notes_inc" > 2147483647`); - await queryRunner.query(`UPDATE "__chart_day__instance" SET "___notes_dec"=2147483647 WHERE "___notes_dec" > 2147483647`); - await queryRunner.query(`UPDATE "__chart_day__instance" SET "___notes_diffs_normal"=2147483647 WHERE "___notes_diffs_normal" > 2147483647`); - await queryRunner.query(`UPDATE "__chart_day__instance" SET "___notes_diffs_reply"=2147483647 WHERE "___notes_diffs_reply" > 2147483647`); - await queryRunner.query(`UPDATE "__chart_day__instance" SET "___notes_diffs_renote"=2147483647 WHERE "___notes_diffs_renote" > 2147483647`); - await queryRunner.query(`UPDATE "__chart_day__instance" SET "___users_total"=2147483647 WHERE "___users_total" > 2147483647`); - await queryRunner.query(`UPDATE "__chart_day__instance" SET "___users_inc"=32767 WHERE "___users_inc" > 32767`); - await queryRunner.query(`UPDATE "__chart_day__instance" SET "___users_dec"=32767 WHERE "___users_dec" > 32767`); - await queryRunner.query(`UPDATE "__chart_day__instance" SET "___following_total"=2147483647 WHERE "___following_total" > 2147483647`); - await queryRunner.query(`UPDATE "__chart_day__instance" SET "___following_inc"=32767 WHERE "___following_inc" > 32767`); - await queryRunner.query(`UPDATE "__chart_day__instance" SET "___following_dec"=32767 WHERE "___following_dec" > 32767`); - await queryRunner.query(`UPDATE "__chart_day__instance" SET "___followers_total"=2147483647 WHERE "___followers_total" > 2147483647`); - await queryRunner.query(`UPDATE "__chart_day__instance" SET "___followers_inc"=32767 WHERE "___followers_inc" > 32767`); - await queryRunner.query(`UPDATE "__chart_day__instance" SET "___followers_dec"=32767 WHERE "___followers_dec" > 32767`); - await queryRunner.query(`UPDATE "__chart_day__instance" SET "___drive_totalFiles"=2147483647 WHERE "___drive_totalFiles" > 2147483647`); - await queryRunner.query(`UPDATE "__chart_day__instance" SET "___drive_incFiles"=2147483647 WHERE "___drive_incFiles" > 2147483647`); - await queryRunner.query(`UPDATE "__chart_day__instance" SET "___drive_decFiles"=2147483647 WHERE "___drive_decFiles" > 2147483647`); - await queryRunner.query(`UPDATE "__chart_day__instance" SET "___drive_incUsage"=2147483647 WHERE "___drive_incUsage" > 2147483647`); - await queryRunner.query(`UPDATE "__chart_day__instance" SET "___drive_decUsage"=2147483647 WHERE "___drive_decUsage" > 2147483647`); - await queryRunner.query(`UPDATE "__chart__per_user_notes" SET "___total"=2147483647 WHERE "___total" > 2147483647`); - await queryRunner.query(`UPDATE "__chart__per_user_notes" SET "___inc"=32767 WHERE "___inc" > 32767`); - await queryRunner.query(`UPDATE "__chart__per_user_notes" SET "___dec"=32767 WHERE "___dec" > 32767`); - await queryRunner.query(`UPDATE "__chart__per_user_notes" SET "___diffs_normal"=32767 WHERE "___diffs_normal" > 32767`); - await queryRunner.query(`UPDATE "__chart__per_user_notes" SET "___diffs_reply"=32767 WHERE "___diffs_reply" > 32767`); - await queryRunner.query(`UPDATE "__chart__per_user_notes" SET "___diffs_renote"=32767 WHERE "___diffs_renote" > 32767`); - await queryRunner.query(`UPDATE "__chart_day__per_user_notes" SET "___total"=2147483647 WHERE "___total" > 2147483647`); - await queryRunner.query(`UPDATE "__chart_day__per_user_notes" SET "___inc"=32767 WHERE "___inc" > 32767`); - await queryRunner.query(`UPDATE "__chart_day__per_user_notes" SET "___dec"=32767 WHERE "___dec" > 32767`); - await queryRunner.query(`UPDATE "__chart_day__per_user_notes" SET "___diffs_normal"=32767 WHERE "___diffs_normal" > 32767`); - await queryRunner.query(`UPDATE "__chart_day__per_user_notes" SET "___diffs_reply"=32767 WHERE "___diffs_reply" > 32767`); - await queryRunner.query(`UPDATE "__chart_day__per_user_notes" SET "___diffs_renote"=32767 WHERE "___diffs_renote" > 32767`); - await queryRunner.query(`UPDATE "__chart__drive" SET "___local_incCount"=2147483647 WHERE "___local_incCount" > 2147483647`); - await queryRunner.query(`UPDATE "__chart__drive" SET "___local_incSize"=2147483647 WHERE "___local_incSize" > 2147483647`); - await queryRunner.query(`UPDATE "__chart__drive" SET "___local_decCount"=2147483647 WHERE "___local_decCount" > 2147483647`); - await queryRunner.query(`UPDATE "__chart__drive" SET "___local_decSize"=2147483647 WHERE "___local_decSize" > 2147483647`); - await queryRunner.query(`UPDATE "__chart__drive" SET "___remote_incCount"=2147483647 WHERE "___remote_incCount" > 2147483647`); - await queryRunner.query(`UPDATE "__chart__drive" SET "___remote_incSize"=2147483647 WHERE "___remote_incSize" > 2147483647`); - await queryRunner.query(`UPDATE "__chart__drive" SET "___remote_decCount"=2147483647 WHERE "___remote_decCount" > 2147483647`); - await queryRunner.query(`UPDATE "__chart__drive" SET "___remote_decSize"=2147483647 WHERE "___remote_decSize" > 2147483647`); - await queryRunner.query(`UPDATE "__chart_day__drive" SET "___local_incCount"=2147483647 WHERE "___local_incCount" > 2147483647`); - await queryRunner.query(`UPDATE "__chart_day__drive" SET "___local_incSize"=2147483647 WHERE "___local_incSize" > 2147483647`); - await queryRunner.query(`UPDATE "__chart_day__drive" SET "___local_decCount"=2147483647 WHERE "___local_decCount" > 2147483647`); - await queryRunner.query(`UPDATE "__chart_day__drive" SET "___local_decSize"=2147483647 WHERE "___local_decSize" > 2147483647`); - await queryRunner.query(`UPDATE "__chart_day__drive" SET "___remote_incCount"=2147483647 WHERE "___remote_incCount" > 2147483647`); - await queryRunner.query(`UPDATE "__chart_day__drive" SET "___remote_incSize"=2147483647 WHERE "___remote_incSize" > 2147483647`); - await queryRunner.query(`UPDATE "__chart_day__drive" SET "___remote_decCount"=2147483647 WHERE "___remote_decCount" > 2147483647`); - await queryRunner.query(`UPDATE "__chart_day__drive" SET "___remote_decSize"=2147483647 WHERE "___remote_decSize" > 2147483647`); - await queryRunner.query(`UPDATE "__chart__per_user_reaction" SET "___local_count"=32767 WHERE "___local_count" > 32767`); - await queryRunner.query(`UPDATE "__chart__per_user_reaction" SET "___remote_count"=32767 WHERE "___remote_count" > 32767`); - await queryRunner.query(`UPDATE "__chart_day__per_user_reaction" SET "___local_count"=32767 WHERE "___local_count" > 32767`); - await queryRunner.query(`UPDATE "__chart_day__per_user_reaction" SET "___remote_count"=32767 WHERE "___remote_count" > 32767`); - await queryRunner.query(`UPDATE "__chart__per_user_following" SET "___local_followings_total"=2147483647 WHERE "___local_followings_total" > 2147483647`); - await queryRunner.query(`UPDATE "__chart__per_user_following" SET "___local_followings_inc"=32767 WHERE "___local_followings_inc" > 32767`); - await queryRunner.query(`UPDATE "__chart__per_user_following" SET "___local_followings_dec"=32767 WHERE "___local_followings_dec" > 32767`); - await queryRunner.query(`UPDATE "__chart__per_user_following" SET "___local_followers_total"=2147483647 WHERE "___local_followers_total" > 2147483647`); - await queryRunner.query(`UPDATE "__chart__per_user_following" SET "___local_followers_inc"=32767 WHERE "___local_followers_inc" > 32767`); - await queryRunner.query(`UPDATE "__chart__per_user_following" SET "___local_followers_dec"=32767 WHERE "___local_followers_dec" > 32767`); - await queryRunner.query(`UPDATE "__chart__per_user_following" SET "___remote_followings_total"=2147483647 WHERE "___remote_followings_total" > 2147483647`); - await queryRunner.query(`UPDATE "__chart__per_user_following" SET "___remote_followings_inc"=32767 WHERE "___remote_followings_inc" > 32767`); - await queryRunner.query(`UPDATE "__chart__per_user_following" SET "___remote_followings_dec"=32767 WHERE "___remote_followings_dec" > 32767`); - await queryRunner.query(`UPDATE "__chart__per_user_following" SET "___remote_followers_total"=2147483647 WHERE "___remote_followers_total" > 2147483647`); - await queryRunner.query(`UPDATE "__chart__per_user_following" SET "___remote_followers_inc"=32767 WHERE "___remote_followers_inc" > 32767`); - await queryRunner.query(`UPDATE "__chart__per_user_following" SET "___remote_followers_dec"=32767 WHERE "___remote_followers_dec" > 32767`); - await queryRunner.query(`UPDATE "__chart_day__per_user_following" SET "___local_followings_total"=2147483647 WHERE "___local_followings_total" > 2147483647`); - await queryRunner.query(`UPDATE "__chart_day__per_user_following" SET "___local_followings_inc"=32767 WHERE "___local_followings_inc" > 32767`); - await queryRunner.query(`UPDATE "__chart_day__per_user_following" SET "___local_followings_dec"=32767 WHERE "___local_followings_dec" > 32767`); - await queryRunner.query(`UPDATE "__chart_day__per_user_following" SET "___local_followers_total"=2147483647 WHERE "___local_followers_total" > 2147483647`); - await queryRunner.query(`UPDATE "__chart_day__per_user_following" SET "___local_followers_inc"=32767 WHERE "___local_followers_inc" > 32767`); - await queryRunner.query(`UPDATE "__chart_day__per_user_following" SET "___local_followers_dec"=32767 WHERE "___local_followers_dec" > 32767`); - await queryRunner.query(`UPDATE "__chart_day__per_user_following" SET "___remote_followings_total"=2147483647 WHERE "___remote_followings_total" > 2147483647`); - await queryRunner.query(`UPDATE "__chart_day__per_user_following" SET "___remote_followings_inc"=32767 WHERE "___remote_followings_inc" > 32767`); - await queryRunner.query(`UPDATE "__chart_day__per_user_following" SET "___remote_followings_dec"=32767 WHERE "___remote_followings_dec" > 32767`); - await queryRunner.query(`UPDATE "__chart_day__per_user_following" SET "___remote_followers_total"=2147483647 WHERE "___remote_followers_total" > 2147483647`); - await queryRunner.query(`UPDATE "__chart_day__per_user_following" SET "___remote_followers_inc"=32767 WHERE "___remote_followers_inc" > 32767`); - await queryRunner.query(`UPDATE "__chart_day__per_user_following" SET "___remote_followers_dec"=32767 WHERE "___remote_followers_dec" > 32767`); + await queryRunner.query( + `UPDATE "__chart__federation" SET "___instance_total"=2147483647 WHERE "___instance_total" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart__federation" SET "___instance_inc"=32767 WHERE "___instance_inc" > 32767`, + ); + await queryRunner.query( + `UPDATE "__chart__federation" SET "___instance_dec"=32767 WHERE "___instance_dec" > 32767`, + ); + await queryRunner.query( + `UPDATE "__chart_day__federation" SET "___instance_total"=2147483647 WHERE "___instance_total" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart_day__federation" SET "___instance_inc"=32767 WHERE "___instance_inc" > 32767`, + ); + await queryRunner.query( + `UPDATE "__chart_day__federation" SET "___instance_dec"=32767 WHERE "___instance_dec" > 32767`, + ); + await queryRunner.query( + `UPDATE "__chart__notes" SET "___local_total"=2147483647 WHERE "___local_total" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart__notes" SET "___local_inc"=2147483647 WHERE "___local_inc" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart__notes" SET "___local_dec"=2147483647 WHERE "___local_dec" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart__notes" SET "___local_diffs_normal"=2147483647 WHERE "___local_diffs_normal" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart__notes" SET "___local_diffs_reply"=2147483647 WHERE "___local_diffs_reply" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart__notes" SET "___local_diffs_renote"=2147483647 WHERE "___local_diffs_renote" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart__notes" SET "___remote_total"=2147483647 WHERE "___remote_total" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart__notes" SET "___remote_inc"=2147483647 WHERE "___remote_inc" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart__notes" SET "___remote_dec"=2147483647 WHERE "___remote_dec" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart__notes" SET "___remote_diffs_normal"=2147483647 WHERE "___remote_diffs_normal" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart__notes" SET "___remote_diffs_reply"=2147483647 WHERE "___remote_diffs_reply" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart__notes" SET "___remote_diffs_renote"=2147483647 WHERE "___remote_diffs_renote" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart_day__notes" SET "___local_total"=2147483647 WHERE "___local_total" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart_day__notes" SET "___local_inc"=2147483647 WHERE "___local_inc" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart_day__notes" SET "___local_dec"=2147483647 WHERE "___local_dec" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart_day__notes" SET "___local_diffs_normal"=2147483647 WHERE "___local_diffs_normal" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart_day__notes" SET "___local_diffs_reply"=2147483647 WHERE "___local_diffs_reply" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart_day__notes" SET "___local_diffs_renote"=2147483647 WHERE "___local_diffs_renote" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart_day__notes" SET "___remote_total"=2147483647 WHERE "___remote_total" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart_day__notes" SET "___remote_inc"=2147483647 WHERE "___remote_inc" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart_day__notes" SET "___remote_dec"=2147483647 WHERE "___remote_dec" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart_day__notes" SET "___remote_diffs_normal"=2147483647 WHERE "___remote_diffs_normal" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart_day__notes" SET "___remote_diffs_reply"=2147483647 WHERE "___remote_diffs_reply" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart_day__notes" SET "___remote_diffs_renote"=2147483647 WHERE "___remote_diffs_renote" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart__users" SET "___local_total"=2147483647 WHERE "___local_total" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart__users" SET "___local_inc"=32767 WHERE "___local_inc" > 32767`, + ); + await queryRunner.query( + `UPDATE "__chart__users" SET "___local_dec"=32767 WHERE "___local_dec" > 32767`, + ); + await queryRunner.query( + `UPDATE "__chart__users" SET "___remote_total"=2147483647 WHERE "___remote_total" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart__users" SET "___remote_inc"=32767 WHERE "___remote_inc" > 32767`, + ); + await queryRunner.query( + `UPDATE "__chart__users" SET "___remote_dec"=32767 WHERE "___remote_dec" > 32767`, + ); + await queryRunner.query( + `UPDATE "__chart_day__users" SET "___local_total"=2147483647 WHERE "___local_total" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart_day__users" SET "___local_inc"=32767 WHERE "___local_inc" > 32767`, + ); + await queryRunner.query( + `UPDATE "__chart_day__users" SET "___local_dec"=32767 WHERE "___local_dec" > 32767`, + ); + await queryRunner.query( + `UPDATE "__chart_day__users" SET "___remote_total"=2147483647 WHERE "___remote_total" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart_day__users" SET "___remote_inc"=32767 WHERE "___remote_inc" > 32767`, + ); + await queryRunner.query( + `UPDATE "__chart_day__users" SET "___remote_dec"=32767 WHERE "___remote_dec" > 32767`, + ); + await queryRunner.query( + `UPDATE "__chart__network" SET "___incomingRequests"=2147483647 WHERE "___incomingRequests" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart__network" SET "___outgoingRequests"=2147483647 WHERE "___outgoingRequests" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart__network" SET "___totalTime"=2147483647 WHERE "___totalTime" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart__network" SET "___incomingBytes"=2147483647 WHERE "___incomingBytes" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart__network" SET "___outgoingBytes"=2147483647 WHERE "___outgoingBytes" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart_day__network" SET "___incomingRequests"=2147483647 WHERE "___incomingRequests" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart_day__network" SET "___outgoingRequests"=2147483647 WHERE "___outgoingRequests" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart_day__network" SET "___totalTime"=2147483647 WHERE "___totalTime" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart_day__network" SET "___incomingBytes"=2147483647 WHERE "___incomingBytes" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart_day__network" SET "___outgoingBytes"=2147483647 WHERE "___outgoingBytes" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart__instance" SET "___requests_failed"=32767 WHERE "___requests_failed" > 32767`, + ); + await queryRunner.query( + `UPDATE "__chart__instance" SET "___requests_succeeded"=32767 WHERE "___requests_succeeded" > 32767`, + ); + await queryRunner.query( + `UPDATE "__chart__instance" SET "___requests_received"=32767 WHERE "___requests_received" > 32767`, + ); + await queryRunner.query( + `UPDATE "__chart__instance" SET "___notes_total"=2147483647 WHERE "___notes_total" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart__instance" SET "___notes_inc"=2147483647 WHERE "___notes_inc" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart__instance" SET "___notes_dec"=2147483647 WHERE "___notes_dec" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart__instance" SET "___notes_diffs_normal"=2147483647 WHERE "___notes_diffs_normal" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart__instance" SET "___notes_diffs_reply"=2147483647 WHERE "___notes_diffs_reply" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart__instance" SET "___notes_diffs_renote"=2147483647 WHERE "___notes_diffs_renote" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart__instance" SET "___users_total"=2147483647 WHERE "___users_total" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart__instance" SET "___users_inc"=32767 WHERE "___users_inc" > 32767`, + ); + await queryRunner.query( + `UPDATE "__chart__instance" SET "___users_dec"=32767 WHERE "___users_dec" > 32767`, + ); + await queryRunner.query( + `UPDATE "__chart__instance" SET "___following_total"=2147483647 WHERE "___following_total" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart__instance" SET "___following_inc"=32767 WHERE "___following_inc" > 32767`, + ); + await queryRunner.query( + `UPDATE "__chart__instance" SET "___following_dec"=32767 WHERE "___following_dec" > 32767`, + ); + await queryRunner.query( + `UPDATE "__chart__instance" SET "___followers_total"=2147483647 WHERE "___followers_total" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart__instance" SET "___followers_inc"=32767 WHERE "___followers_inc" > 32767`, + ); + await queryRunner.query( + `UPDATE "__chart__instance" SET "___followers_dec"=32767 WHERE "___followers_dec" > 32767`, + ); + await queryRunner.query( + `UPDATE "__chart__instance" SET "___drive_totalFiles"=2147483647 WHERE "___drive_totalFiles" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart__instance" SET "___drive_incFiles"=2147483647 WHERE "___drive_incFiles" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart__instance" SET "___drive_decFiles"=2147483647 WHERE "___drive_decFiles" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart__instance" SET "___drive_incUsage"=2147483647 WHERE "___drive_incUsage" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart__instance" SET "___drive_decUsage"=2147483647 WHERE "___drive_decUsage" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart_day__instance" SET "___requests_failed"=32767 WHERE "___requests_failed" > 32767`, + ); + await queryRunner.query( + `UPDATE "__chart_day__instance" SET "___requests_succeeded"=32767 WHERE "___requests_succeeded" > 32767`, + ); + await queryRunner.query( + `UPDATE "__chart_day__instance" SET "___requests_received"=32767 WHERE "___requests_received" > 32767`, + ); + await queryRunner.query( + `UPDATE "__chart_day__instance" SET "___notes_total"=2147483647 WHERE "___notes_total" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart_day__instance" SET "___notes_inc"=2147483647 WHERE "___notes_inc" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart_day__instance" SET "___notes_dec"=2147483647 WHERE "___notes_dec" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart_day__instance" SET "___notes_diffs_normal"=2147483647 WHERE "___notes_diffs_normal" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart_day__instance" SET "___notes_diffs_reply"=2147483647 WHERE "___notes_diffs_reply" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart_day__instance" SET "___notes_diffs_renote"=2147483647 WHERE "___notes_diffs_renote" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart_day__instance" SET "___users_total"=2147483647 WHERE "___users_total" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart_day__instance" SET "___users_inc"=32767 WHERE "___users_inc" > 32767`, + ); + await queryRunner.query( + `UPDATE "__chart_day__instance" SET "___users_dec"=32767 WHERE "___users_dec" > 32767`, + ); + await queryRunner.query( + `UPDATE "__chart_day__instance" SET "___following_total"=2147483647 WHERE "___following_total" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart_day__instance" SET "___following_inc"=32767 WHERE "___following_inc" > 32767`, + ); + await queryRunner.query( + `UPDATE "__chart_day__instance" SET "___following_dec"=32767 WHERE "___following_dec" > 32767`, + ); + await queryRunner.query( + `UPDATE "__chart_day__instance" SET "___followers_total"=2147483647 WHERE "___followers_total" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart_day__instance" SET "___followers_inc"=32767 WHERE "___followers_inc" > 32767`, + ); + await queryRunner.query( + `UPDATE "__chart_day__instance" SET "___followers_dec"=32767 WHERE "___followers_dec" > 32767`, + ); + await queryRunner.query( + `UPDATE "__chart_day__instance" SET "___drive_totalFiles"=2147483647 WHERE "___drive_totalFiles" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart_day__instance" SET "___drive_incFiles"=2147483647 WHERE "___drive_incFiles" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart_day__instance" SET "___drive_decFiles"=2147483647 WHERE "___drive_decFiles" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart_day__instance" SET "___drive_incUsage"=2147483647 WHERE "___drive_incUsage" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart_day__instance" SET "___drive_decUsage"=2147483647 WHERE "___drive_decUsage" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart__per_user_notes" SET "___total"=2147483647 WHERE "___total" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart__per_user_notes" SET "___inc"=32767 WHERE "___inc" > 32767`, + ); + await queryRunner.query( + `UPDATE "__chart__per_user_notes" SET "___dec"=32767 WHERE "___dec" > 32767`, + ); + await queryRunner.query( + `UPDATE "__chart__per_user_notes" SET "___diffs_normal"=32767 WHERE "___diffs_normal" > 32767`, + ); + await queryRunner.query( + `UPDATE "__chart__per_user_notes" SET "___diffs_reply"=32767 WHERE "___diffs_reply" > 32767`, + ); + await queryRunner.query( + `UPDATE "__chart__per_user_notes" SET "___diffs_renote"=32767 WHERE "___diffs_renote" > 32767`, + ); + await queryRunner.query( + `UPDATE "__chart_day__per_user_notes" SET "___total"=2147483647 WHERE "___total" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart_day__per_user_notes" SET "___inc"=32767 WHERE "___inc" > 32767`, + ); + await queryRunner.query( + `UPDATE "__chart_day__per_user_notes" SET "___dec"=32767 WHERE "___dec" > 32767`, + ); + await queryRunner.query( + `UPDATE "__chart_day__per_user_notes" SET "___diffs_normal"=32767 WHERE "___diffs_normal" > 32767`, + ); + await queryRunner.query( + `UPDATE "__chart_day__per_user_notes" SET "___diffs_reply"=32767 WHERE "___diffs_reply" > 32767`, + ); + await queryRunner.query( + `UPDATE "__chart_day__per_user_notes" SET "___diffs_renote"=32767 WHERE "___diffs_renote" > 32767`, + ); + await queryRunner.query( + `UPDATE "__chart__drive" SET "___local_incCount"=2147483647 WHERE "___local_incCount" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart__drive" SET "___local_incSize"=2147483647 WHERE "___local_incSize" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart__drive" SET "___local_decCount"=2147483647 WHERE "___local_decCount" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart__drive" SET "___local_decSize"=2147483647 WHERE "___local_decSize" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart__drive" SET "___remote_incCount"=2147483647 WHERE "___remote_incCount" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart__drive" SET "___remote_incSize"=2147483647 WHERE "___remote_incSize" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart__drive" SET "___remote_decCount"=2147483647 WHERE "___remote_decCount" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart__drive" SET "___remote_decSize"=2147483647 WHERE "___remote_decSize" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart_day__drive" SET "___local_incCount"=2147483647 WHERE "___local_incCount" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart_day__drive" SET "___local_incSize"=2147483647 WHERE "___local_incSize" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart_day__drive" SET "___local_decCount"=2147483647 WHERE "___local_decCount" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart_day__drive" SET "___local_decSize"=2147483647 WHERE "___local_decSize" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart_day__drive" SET "___remote_incCount"=2147483647 WHERE "___remote_incCount" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart_day__drive" SET "___remote_incSize"=2147483647 WHERE "___remote_incSize" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart_day__drive" SET "___remote_decCount"=2147483647 WHERE "___remote_decCount" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart_day__drive" SET "___remote_decSize"=2147483647 WHERE "___remote_decSize" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart__per_user_reaction" SET "___local_count"=32767 WHERE "___local_count" > 32767`, + ); + await queryRunner.query( + `UPDATE "__chart__per_user_reaction" SET "___remote_count"=32767 WHERE "___remote_count" > 32767`, + ); + await queryRunner.query( + `UPDATE "__chart_day__per_user_reaction" SET "___local_count"=32767 WHERE "___local_count" > 32767`, + ); + await queryRunner.query( + `UPDATE "__chart_day__per_user_reaction" SET "___remote_count"=32767 WHERE "___remote_count" > 32767`, + ); + await queryRunner.query( + `UPDATE "__chart__per_user_following" SET "___local_followings_total"=2147483647 WHERE "___local_followings_total" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart__per_user_following" SET "___local_followings_inc"=32767 WHERE "___local_followings_inc" > 32767`, + ); + await queryRunner.query( + `UPDATE "__chart__per_user_following" SET "___local_followings_dec"=32767 WHERE "___local_followings_dec" > 32767`, + ); + await queryRunner.query( + `UPDATE "__chart__per_user_following" SET "___local_followers_total"=2147483647 WHERE "___local_followers_total" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart__per_user_following" SET "___local_followers_inc"=32767 WHERE "___local_followers_inc" > 32767`, + ); + await queryRunner.query( + `UPDATE "__chart__per_user_following" SET "___local_followers_dec"=32767 WHERE "___local_followers_dec" > 32767`, + ); + await queryRunner.query( + `UPDATE "__chart__per_user_following" SET "___remote_followings_total"=2147483647 WHERE "___remote_followings_total" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart__per_user_following" SET "___remote_followings_inc"=32767 WHERE "___remote_followings_inc" > 32767`, + ); + await queryRunner.query( + `UPDATE "__chart__per_user_following" SET "___remote_followings_dec"=32767 WHERE "___remote_followings_dec" > 32767`, + ); + await queryRunner.query( + `UPDATE "__chart__per_user_following" SET "___remote_followers_total"=2147483647 WHERE "___remote_followers_total" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart__per_user_following" SET "___remote_followers_inc"=32767 WHERE "___remote_followers_inc" > 32767`, + ); + await queryRunner.query( + `UPDATE "__chart__per_user_following" SET "___remote_followers_dec"=32767 WHERE "___remote_followers_dec" > 32767`, + ); + await queryRunner.query( + `UPDATE "__chart_day__per_user_following" SET "___local_followings_total"=2147483647 WHERE "___local_followings_total" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart_day__per_user_following" SET "___local_followings_inc"=32767 WHERE "___local_followings_inc" > 32767`, + ); + await queryRunner.query( + `UPDATE "__chart_day__per_user_following" SET "___local_followings_dec"=32767 WHERE "___local_followings_dec" > 32767`, + ); + await queryRunner.query( + `UPDATE "__chart_day__per_user_following" SET "___local_followers_total"=2147483647 WHERE "___local_followers_total" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart_day__per_user_following" SET "___local_followers_inc"=32767 WHERE "___local_followers_inc" > 32767`, + ); + await queryRunner.query( + `UPDATE "__chart_day__per_user_following" SET "___local_followers_dec"=32767 WHERE "___local_followers_dec" > 32767`, + ); + await queryRunner.query( + `UPDATE "__chart_day__per_user_following" SET "___remote_followings_total"=2147483647 WHERE "___remote_followings_total" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart_day__per_user_following" SET "___remote_followings_inc"=32767 WHERE "___remote_followings_inc" > 32767`, + ); + await queryRunner.query( + `UPDATE "__chart_day__per_user_following" SET "___remote_followings_dec"=32767 WHERE "___remote_followings_dec" > 32767`, + ); + await queryRunner.query( + `UPDATE "__chart_day__per_user_following" SET "___remote_followers_total"=2147483647 WHERE "___remote_followers_total" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart_day__per_user_following" SET "___remote_followers_inc"=32767 WHERE "___remote_followers_inc" > 32767`, + ); + await queryRunner.query( + `UPDATE "__chart_day__per_user_following" SET "___remote_followers_dec"=32767 WHERE "___remote_followers_dec" > 32767`, + ); await queryRunner.query(`TRUNCATE TABLE "__chart__per_user_drive"`); await queryRunner.query(`TRUNCATE TABLE "__chart_day__per_user_drive"`); - await queryRunner.query(`ALTER TABLE "__chart__federation" ALTER COLUMN "___instance_total" TYPE integer USING "___instance_total"::integer`); - await queryRunner.query(`ALTER TABLE "__chart__federation" ALTER COLUMN "___instance_inc" TYPE smallint USING "___instance_inc"::smallint`); - await queryRunner.query(`ALTER TABLE "__chart__federation" ALTER COLUMN "___instance_dec" TYPE smallint USING "___instance_dec"::smallint`); - await queryRunner.query(`ALTER TABLE "__chart_day__federation" ALTER COLUMN "___instance_total" TYPE integer USING "___instance_total"::integer`); - await queryRunner.query(`ALTER TABLE "__chart_day__federation" ALTER COLUMN "___instance_inc" TYPE smallint USING "___instance_inc"::smallint`); - await queryRunner.query(`ALTER TABLE "__chart_day__federation" ALTER COLUMN "___instance_dec" TYPE smallint USING "___instance_dec"::smallint`); - await queryRunner.query(`ALTER TABLE "__chart__notes" ALTER COLUMN "___local_total" TYPE integer USING "___local_total"::integer`); - await queryRunner.query(`ALTER TABLE "__chart__notes" ALTER COLUMN "___local_inc" TYPE integer USING "___local_inc"::integer`); - await queryRunner.query(`ALTER TABLE "__chart__notes" ALTER COLUMN "___local_dec" TYPE integer USING "___local_dec"::integer`); - await queryRunner.query(`ALTER TABLE "__chart__notes" ALTER COLUMN "___local_diffs_normal" TYPE integer USING "___local_diffs_normal"::integer`); - await queryRunner.query(`ALTER TABLE "__chart__notes" ALTER COLUMN "___local_diffs_reply" TYPE integer USING "___local_diffs_reply"::integer`); - await queryRunner.query(`ALTER TABLE "__chart__notes" ALTER COLUMN "___local_diffs_renote" TYPE integer USING "___local_diffs_renote"::integer`); - await queryRunner.query(`ALTER TABLE "__chart__notes" ALTER COLUMN "___remote_total" TYPE integer USING "___remote_total"::integer`); - await queryRunner.query(`ALTER TABLE "__chart__notes" ALTER COLUMN "___remote_inc" TYPE integer USING "___remote_inc"::integer`); - await queryRunner.query(`ALTER TABLE "__chart__notes" ALTER COLUMN "___remote_dec" TYPE integer USING "___remote_dec"::integer`); - await queryRunner.query(`ALTER TABLE "__chart__notes" ALTER COLUMN "___remote_diffs_normal" TYPE integer USING "___remote_diffs_normal"::integer`); - await queryRunner.query(`ALTER TABLE "__chart__notes" ALTER COLUMN "___remote_diffs_reply" TYPE integer USING "___remote_diffs_reply"::integer`); - await queryRunner.query(`ALTER TABLE "__chart__notes" ALTER COLUMN "___remote_diffs_renote" TYPE integer USING "___remote_diffs_renote"::integer`); - await queryRunner.query(`ALTER TABLE "__chart_day__notes" ALTER COLUMN "___local_total" TYPE integer USING "___local_total"::integer`); - await queryRunner.query(`ALTER TABLE "__chart_day__notes" ALTER COLUMN "___local_inc" TYPE integer USING "___local_inc"::integer`); - await queryRunner.query(`ALTER TABLE "__chart_day__notes" ALTER COLUMN "___local_dec" TYPE integer USING "___local_dec"::integer`); - await queryRunner.query(`ALTER TABLE "__chart_day__notes" ALTER COLUMN "___local_diffs_normal" TYPE integer USING "___local_diffs_normal"::integer`); - await queryRunner.query(`ALTER TABLE "__chart_day__notes" ALTER COLUMN "___local_diffs_reply" TYPE integer USING "___local_diffs_reply"::integer`); - await queryRunner.query(`ALTER TABLE "__chart_day__notes" ALTER COLUMN "___local_diffs_renote" TYPE integer USING "___local_diffs_renote"::integer`); - await queryRunner.query(`ALTER TABLE "__chart_day__notes" ALTER COLUMN "___remote_total" TYPE integer USING "___remote_total"::integer`); - await queryRunner.query(`ALTER TABLE "__chart_day__notes" ALTER COLUMN "___remote_inc" TYPE integer USING "___remote_inc"::integer`); - await queryRunner.query(`ALTER TABLE "__chart_day__notes" ALTER COLUMN "___remote_dec" TYPE integer USING "___remote_dec"::integer`); - await queryRunner.query(`ALTER TABLE "__chart_day__notes" ALTER COLUMN "___remote_diffs_normal" TYPE integer USING "___remote_diffs_normal"::integer`); - await queryRunner.query(`ALTER TABLE "__chart_day__notes" ALTER COLUMN "___remote_diffs_reply" TYPE integer USING "___remote_diffs_reply"::integer`); - await queryRunner.query(`ALTER TABLE "__chart_day__notes" ALTER COLUMN "___remote_diffs_renote" TYPE integer USING "___remote_diffs_renote"::integer`); - await queryRunner.query(`ALTER TABLE "__chart__users" ALTER COLUMN "___local_total" TYPE integer USING "___local_total"::integer`); - await queryRunner.query(`ALTER TABLE "__chart__users" ALTER COLUMN "___local_inc" TYPE smallint USING "___local_inc"::smallint`); - await queryRunner.query(`ALTER TABLE "__chart__users" ALTER COLUMN "___local_dec" TYPE smallint USING "___local_dec"::smallint`); - await queryRunner.query(`ALTER TABLE "__chart__users" ALTER COLUMN "___remote_total" TYPE integer USING "___remote_total"::integer`); - await queryRunner.query(`ALTER TABLE "__chart__users" ALTER COLUMN "___remote_inc" TYPE smallint USING "___remote_inc"::smallint`); - await queryRunner.query(`ALTER TABLE "__chart__users" ALTER COLUMN "___remote_dec" TYPE smallint USING "___remote_dec"::smallint`); - await queryRunner.query(`ALTER TABLE "__chart_day__users" ALTER COLUMN "___local_total" TYPE integer USING "___local_total"::integer`); - await queryRunner.query(`ALTER TABLE "__chart_day__users" ALTER COLUMN "___local_inc" TYPE smallint USING "___local_inc"::smallint`); - await queryRunner.query(`ALTER TABLE "__chart_day__users" ALTER COLUMN "___local_dec" TYPE smallint USING "___local_dec"::smallint`); - await queryRunner.query(`ALTER TABLE "__chart_day__users" ALTER COLUMN "___remote_total" TYPE integer USING "___remote_total"::integer`); - await queryRunner.query(`ALTER TABLE "__chart_day__users" ALTER COLUMN "___remote_inc" TYPE smallint USING "___remote_inc"::smallint`); - await queryRunner.query(`ALTER TABLE "__chart_day__users" ALTER COLUMN "___remote_dec" TYPE smallint USING "___remote_dec"::smallint`); - await queryRunner.query(`ALTER TABLE "__chart__network" ALTER COLUMN "___incomingRequests" TYPE integer USING "___incomingRequests"::integer`); - await queryRunner.query(`ALTER TABLE "__chart__network" ALTER COLUMN "___outgoingRequests" TYPE integer USING "___outgoingRequests"::integer`); - await queryRunner.query(`ALTER TABLE "__chart__network" ALTER COLUMN "___totalTime" TYPE integer USING "___totalTime"::integer`); - await queryRunner.query(`ALTER TABLE "__chart__network" ALTER COLUMN "___incomingBytes" TYPE integer USING "___incomingBytes"::integer`); - await queryRunner.query(`ALTER TABLE "__chart__network" ALTER COLUMN "___outgoingBytes" TYPE integer USING "___outgoingBytes"::integer`); - await queryRunner.query(`ALTER TABLE "__chart_day__network" ALTER COLUMN "___incomingRequests" TYPE integer USING "___incomingRequests"::integer`); - await queryRunner.query(`ALTER TABLE "__chart_day__network" ALTER COLUMN "___outgoingRequests" TYPE integer USING "___outgoingRequests"::integer`); - await queryRunner.query(`ALTER TABLE "__chart_day__network" ALTER COLUMN "___totalTime" TYPE integer USING "___totalTime"::integer`); - await queryRunner.query(`ALTER TABLE "__chart_day__network" ALTER COLUMN "___incomingBytes" TYPE integer USING "___incomingBytes"::integer`); - await queryRunner.query(`ALTER TABLE "__chart_day__network" ALTER COLUMN "___outgoingBytes" TYPE integer USING "___outgoingBytes"::integer`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "___requests_failed" TYPE smallint USING "___requests_failed"::smallint`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "___requests_succeeded" TYPE smallint USING "___requests_succeeded"::smallint`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "___requests_received" TYPE smallint USING "___requests_received"::smallint`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "___notes_total" TYPE integer USING "___notes_total"::integer`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "___notes_inc" TYPE integer USING "___notes_inc"::integer`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "___notes_dec" TYPE integer USING "___notes_dec"::integer`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "___notes_diffs_normal" TYPE integer USING "___notes_diffs_normal"::integer`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "___notes_diffs_reply" TYPE integer USING "___notes_diffs_reply"::integer`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "___notes_diffs_renote" TYPE integer USING "___notes_diffs_renote"::integer`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "___users_total" TYPE integer USING "___users_total"::integer`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "___users_inc" TYPE smallint USING "___users_inc"::smallint`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "___users_dec" TYPE smallint USING "___users_dec"::smallint`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "___following_total" TYPE integer USING "___following_total"::integer`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "___following_inc" TYPE smallint USING "___following_inc"::smallint`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "___following_dec" TYPE smallint USING "___following_dec"::smallint`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "___followers_total" TYPE integer USING "___followers_total"::integer`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "___followers_inc" TYPE smallint USING "___followers_inc"::smallint`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "___followers_dec" TYPE smallint USING "___followers_dec"::smallint`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "___drive_totalFiles" TYPE integer USING "___drive_totalFiles"::integer`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "___drive_incFiles" TYPE integer USING "___drive_incFiles"::integer`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "___drive_decFiles" TYPE integer USING "___drive_decFiles"::integer`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "___drive_incUsage" TYPE integer USING "___drive_incUsage"::integer`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "___drive_decUsage" TYPE integer USING "___drive_decUsage"::integer`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ALTER COLUMN "___requests_failed" TYPE smallint USING "___requests_failed"::smallint`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ALTER COLUMN "___requests_succeeded" TYPE smallint USING "___requests_succeeded"::smallint`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ALTER COLUMN "___requests_received" TYPE smallint USING "___requests_received"::smallint`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ALTER COLUMN "___notes_total" TYPE integer USING "___notes_total"::integer`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ALTER COLUMN "___notes_inc" TYPE integer USING "___notes_inc"::integer`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ALTER COLUMN "___notes_dec" TYPE integer USING "___notes_dec"::integer`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ALTER COLUMN "___notes_diffs_normal" TYPE integer USING "___notes_diffs_normal"::integer`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ALTER COLUMN "___notes_diffs_reply" TYPE integer USING "___notes_diffs_reply"::integer`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ALTER COLUMN "___notes_diffs_renote" TYPE integer USING "___notes_diffs_renote"::integer`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ALTER COLUMN "___users_total" TYPE integer USING "___users_total"::integer`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ALTER COLUMN "___users_inc" TYPE smallint USING "___users_inc"::smallint`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ALTER COLUMN "___users_dec" TYPE smallint USING "___users_dec"::smallint`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ALTER COLUMN "___following_total" TYPE integer USING "___following_total"::integer`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ALTER COLUMN "___following_inc" TYPE smallint USING "___following_inc"::smallint`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ALTER COLUMN "___following_dec" TYPE smallint USING "___following_dec"::smallint`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ALTER COLUMN "___followers_total" TYPE integer USING "___followers_total"::integer`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ALTER COLUMN "___followers_inc" TYPE smallint USING "___followers_inc"::smallint`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ALTER COLUMN "___followers_dec" TYPE smallint USING "___followers_dec"::smallint`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ALTER COLUMN "___drive_totalFiles" TYPE integer USING "___drive_totalFiles"::integer`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ALTER COLUMN "___drive_incFiles" TYPE integer USING "___drive_incFiles"::integer`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ALTER COLUMN "___drive_decFiles" TYPE integer USING "___drive_decFiles"::integer`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ALTER COLUMN "___drive_incUsage" TYPE integer USING "___drive_incUsage"::integer`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ALTER COLUMN "___drive_decUsage" TYPE integer USING "___drive_decUsage"::integer`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_notes" ALTER COLUMN "___total" TYPE integer USING "___total"::integer`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_notes" ALTER COLUMN "___inc" TYPE smallint USING "___inc"::smallint`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_notes" ALTER COLUMN "___dec" TYPE smallint USING "___dec"::smallint`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_notes" ALTER COLUMN "___diffs_normal" TYPE smallint USING "___diffs_normal"::smallint`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_notes" ALTER COLUMN "___diffs_reply" TYPE smallint USING "___diffs_reply"::smallint`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_notes" ALTER COLUMN "___diffs_renote" TYPE smallint USING "___diffs_renote"::smallint`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_notes" ALTER COLUMN "___total" TYPE integer USING "___total"::integer`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_notes" ALTER COLUMN "___inc" TYPE smallint USING "___inc"::smallint`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_notes" ALTER COLUMN "___dec" TYPE smallint USING "___dec"::smallint`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_notes" ALTER COLUMN "___diffs_normal" TYPE smallint USING "___diffs_normal"::smallint`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_notes" ALTER COLUMN "___diffs_reply" TYPE smallint USING "___diffs_reply"::smallint`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_notes" ALTER COLUMN "___diffs_renote" TYPE smallint USING "___diffs_renote"::smallint`); - await queryRunner.query(`ALTER TABLE "__chart__drive" ALTER COLUMN "___local_incCount" TYPE integer USING "___local_incCount"::integer`); - await queryRunner.query(`ALTER TABLE "__chart__drive" ALTER COLUMN "___local_incSize" TYPE integer USING "___local_incSize"::integer`); - await queryRunner.query(`ALTER TABLE "__chart__drive" ALTER COLUMN "___local_decCount" TYPE integer USING "___local_decCount"::integer`); - await queryRunner.query(`ALTER TABLE "__chart__drive" ALTER COLUMN "___local_decSize" TYPE integer USING "___local_decSize"::integer`); - await queryRunner.query(`ALTER TABLE "__chart__drive" ALTER COLUMN "___remote_incCount" TYPE integer USING "___remote_incCount"::integer`); - await queryRunner.query(`ALTER TABLE "__chart__drive" ALTER COLUMN "___remote_incSize" TYPE integer USING "___remote_incSize"::integer`); - await queryRunner.query(`ALTER TABLE "__chart__drive" ALTER COLUMN "___remote_decCount" TYPE integer USING "___remote_decCount"::integer`); - await queryRunner.query(`ALTER TABLE "__chart__drive" ALTER COLUMN "___remote_decSize" TYPE integer USING "___remote_decSize"::integer`); - await queryRunner.query(`ALTER TABLE "__chart_day__drive" ALTER COLUMN "___local_incCount" TYPE integer USING "___local_incCount"::integer`); - await queryRunner.query(`ALTER TABLE "__chart_day__drive" ALTER COLUMN "___local_incSize" TYPE integer USING "___local_incSize"::integer`); - await queryRunner.query(`ALTER TABLE "__chart_day__drive" ALTER COLUMN "___local_decCount" TYPE integer USING "___local_decCount"::integer`); - await queryRunner.query(`ALTER TABLE "__chart_day__drive" ALTER COLUMN "___local_decSize" TYPE integer USING "___local_decSize"::integer`); - await queryRunner.query(`ALTER TABLE "__chart_day__drive" ALTER COLUMN "___remote_incCount" TYPE integer USING "___remote_incCount"::integer`); - await queryRunner.query(`ALTER TABLE "__chart_day__drive" ALTER COLUMN "___remote_incSize" TYPE integer USING "___remote_incSize"::integer`); - await queryRunner.query(`ALTER TABLE "__chart_day__drive" ALTER COLUMN "___remote_decCount" TYPE integer USING "___remote_decCount"::integer`); - await queryRunner.query(`ALTER TABLE "__chart_day__drive" ALTER COLUMN "___remote_decSize" TYPE integer USING "___remote_decSize"::integer`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_reaction" ALTER COLUMN "___local_count" TYPE smallint USING "___local_count"::smallint`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_reaction" ALTER COLUMN "___remote_count" TYPE smallint USING "___remote_count"::smallint`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_reaction" ALTER COLUMN "___local_count" TYPE smallint USING "___local_count"::smallint`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_reaction" ALTER COLUMN "___remote_count" TYPE smallint USING "___remote_count"::smallint`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___local_followings_total" TYPE integer USING "___local_followings_total"::integer`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___local_followings_inc" TYPE smallint USING "___local_followings_inc"::smallint`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___local_followings_dec" TYPE smallint USING "___local_followings_dec"::smallint`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___local_followers_total" TYPE integer USING "___local_followers_total"::integer`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___local_followers_inc" TYPE smallint USING "___local_followers_inc"::smallint`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___local_followers_dec" TYPE smallint USING "___local_followers_dec"::smallint`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___remote_followings_total" TYPE integer USING "___remote_followings_total"::integer`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___remote_followings_inc" TYPE smallint USING "___remote_followings_inc"::smallint`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___remote_followings_dec" TYPE smallint USING "___remote_followings_dec"::smallint`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___remote_followers_total" TYPE integer USING "___remote_followers_total"::integer`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___remote_followers_inc" TYPE smallint USING "___remote_followers_inc"::smallint`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___remote_followers_dec" TYPE smallint USING "___remote_followers_dec"::smallint`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___local_followings_total" TYPE integer USING "___local_followings_total"::integer`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___local_followings_inc" TYPE smallint USING "___local_followings_inc"::smallint`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___local_followings_dec" TYPE smallint USING "___local_followings_dec"::smallint`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___local_followers_total" TYPE integer USING "___local_followers_total"::integer`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___local_followers_inc" TYPE smallint USING "___local_followers_inc"::smallint`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___local_followers_dec" TYPE smallint USING "___local_followers_dec"::smallint`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___remote_followings_total" TYPE integer USING "___remote_followings_total"::integer`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___remote_followings_inc" TYPE smallint USING "___remote_followings_inc"::smallint`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___remote_followings_dec" TYPE smallint USING "___remote_followings_dec"::smallint`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___remote_followers_total" TYPE integer USING "___remote_followers_total"::integer`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___remote_followers_inc" TYPE smallint USING "___remote_followers_inc"::smallint`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___remote_followers_dec" TYPE smallint USING "___remote_followers_dec"::smallint`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_drive" ALTER COLUMN "___totalCount" TYPE integer USING "___totalCount"::integer`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_drive" ALTER COLUMN "___totalSize" TYPE integer USING "___totalSize"::integer`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_drive" ALTER COLUMN "___incCount" TYPE smallint USING "___incCount"::smallint`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_drive" ALTER COLUMN "___incSize" TYPE integer USING "___incSize"::integer`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_drive" ALTER COLUMN "___decCount" TYPE smallint USING "___decCount"::smallint`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_drive" ALTER COLUMN "___decSize" TYPE integer USING "___decSize"::integer`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_drive" ALTER COLUMN "___totalCount" TYPE integer USING "___totalCount"::integer`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_drive" ALTER COLUMN "___totalSize" TYPE integer USING "___totalSize"::integer`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_drive" ALTER COLUMN "___incCount" TYPE smallint USING "___incCount"::smallint`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_drive" ALTER COLUMN "___incSize" TYPE integer USING "___incSize"::integer`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_drive" ALTER COLUMN "___decCount" TYPE smallint USING "___decCount"::smallint`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_drive" ALTER COLUMN "___decSize" TYPE integer USING "___decSize"::integer`); + await queryRunner.query( + `ALTER TABLE "__chart__federation" ALTER COLUMN "___instance_total" TYPE integer USING "___instance_total"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__federation" ALTER COLUMN "___instance_inc" TYPE smallint USING "___instance_inc"::smallint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__federation" ALTER COLUMN "___instance_dec" TYPE smallint USING "___instance_dec"::smallint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__federation" ALTER COLUMN "___instance_total" TYPE integer USING "___instance_total"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__federation" ALTER COLUMN "___instance_inc" TYPE smallint USING "___instance_inc"::smallint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__federation" ALTER COLUMN "___instance_dec" TYPE smallint USING "___instance_dec"::smallint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__notes" ALTER COLUMN "___local_total" TYPE integer USING "___local_total"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__notes" ALTER COLUMN "___local_inc" TYPE integer USING "___local_inc"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__notes" ALTER COLUMN "___local_dec" TYPE integer USING "___local_dec"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__notes" ALTER COLUMN "___local_diffs_normal" TYPE integer USING "___local_diffs_normal"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__notes" ALTER COLUMN "___local_diffs_reply" TYPE integer USING "___local_diffs_reply"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__notes" ALTER COLUMN "___local_diffs_renote" TYPE integer USING "___local_diffs_renote"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__notes" ALTER COLUMN "___remote_total" TYPE integer USING "___remote_total"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__notes" ALTER COLUMN "___remote_inc" TYPE integer USING "___remote_inc"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__notes" ALTER COLUMN "___remote_dec" TYPE integer USING "___remote_dec"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__notes" ALTER COLUMN "___remote_diffs_normal" TYPE integer USING "___remote_diffs_normal"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__notes" ALTER COLUMN "___remote_diffs_reply" TYPE integer USING "___remote_diffs_reply"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__notes" ALTER COLUMN "___remote_diffs_renote" TYPE integer USING "___remote_diffs_renote"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__notes" ALTER COLUMN "___local_total" TYPE integer USING "___local_total"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__notes" ALTER COLUMN "___local_inc" TYPE integer USING "___local_inc"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__notes" ALTER COLUMN "___local_dec" TYPE integer USING "___local_dec"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__notes" ALTER COLUMN "___local_diffs_normal" TYPE integer USING "___local_diffs_normal"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__notes" ALTER COLUMN "___local_diffs_reply" TYPE integer USING "___local_diffs_reply"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__notes" ALTER COLUMN "___local_diffs_renote" TYPE integer USING "___local_diffs_renote"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__notes" ALTER COLUMN "___remote_total" TYPE integer USING "___remote_total"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__notes" ALTER COLUMN "___remote_inc" TYPE integer USING "___remote_inc"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__notes" ALTER COLUMN "___remote_dec" TYPE integer USING "___remote_dec"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__notes" ALTER COLUMN "___remote_diffs_normal" TYPE integer USING "___remote_diffs_normal"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__notes" ALTER COLUMN "___remote_diffs_reply" TYPE integer USING "___remote_diffs_reply"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__notes" ALTER COLUMN "___remote_diffs_renote" TYPE integer USING "___remote_diffs_renote"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__users" ALTER COLUMN "___local_total" TYPE integer USING "___local_total"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__users" ALTER COLUMN "___local_inc" TYPE smallint USING "___local_inc"::smallint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__users" ALTER COLUMN "___local_dec" TYPE smallint USING "___local_dec"::smallint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__users" ALTER COLUMN "___remote_total" TYPE integer USING "___remote_total"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__users" ALTER COLUMN "___remote_inc" TYPE smallint USING "___remote_inc"::smallint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__users" ALTER COLUMN "___remote_dec" TYPE smallint USING "___remote_dec"::smallint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__users" ALTER COLUMN "___local_total" TYPE integer USING "___local_total"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__users" ALTER COLUMN "___local_inc" TYPE smallint USING "___local_inc"::smallint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__users" ALTER COLUMN "___local_dec" TYPE smallint USING "___local_dec"::smallint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__users" ALTER COLUMN "___remote_total" TYPE integer USING "___remote_total"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__users" ALTER COLUMN "___remote_inc" TYPE smallint USING "___remote_inc"::smallint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__users" ALTER COLUMN "___remote_dec" TYPE smallint USING "___remote_dec"::smallint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__network" ALTER COLUMN "___incomingRequests" TYPE integer USING "___incomingRequests"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__network" ALTER COLUMN "___outgoingRequests" TYPE integer USING "___outgoingRequests"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__network" ALTER COLUMN "___totalTime" TYPE integer USING "___totalTime"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__network" ALTER COLUMN "___incomingBytes" TYPE integer USING "___incomingBytes"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__network" ALTER COLUMN "___outgoingBytes" TYPE integer USING "___outgoingBytes"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__network" ALTER COLUMN "___incomingRequests" TYPE integer USING "___incomingRequests"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__network" ALTER COLUMN "___outgoingRequests" TYPE integer USING "___outgoingRequests"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__network" ALTER COLUMN "___totalTime" TYPE integer USING "___totalTime"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__network" ALTER COLUMN "___incomingBytes" TYPE integer USING "___incomingBytes"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__network" ALTER COLUMN "___outgoingBytes" TYPE integer USING "___outgoingBytes"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "___requests_failed" TYPE smallint USING "___requests_failed"::smallint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "___requests_succeeded" TYPE smallint USING "___requests_succeeded"::smallint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "___requests_received" TYPE smallint USING "___requests_received"::smallint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "___notes_total" TYPE integer USING "___notes_total"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "___notes_inc" TYPE integer USING "___notes_inc"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "___notes_dec" TYPE integer USING "___notes_dec"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "___notes_diffs_normal" TYPE integer USING "___notes_diffs_normal"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "___notes_diffs_reply" TYPE integer USING "___notes_diffs_reply"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "___notes_diffs_renote" TYPE integer USING "___notes_diffs_renote"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "___users_total" TYPE integer USING "___users_total"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "___users_inc" TYPE smallint USING "___users_inc"::smallint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "___users_dec" TYPE smallint USING "___users_dec"::smallint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "___following_total" TYPE integer USING "___following_total"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "___following_inc" TYPE smallint USING "___following_inc"::smallint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "___following_dec" TYPE smallint USING "___following_dec"::smallint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "___followers_total" TYPE integer USING "___followers_total"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "___followers_inc" TYPE smallint USING "___followers_inc"::smallint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "___followers_dec" TYPE smallint USING "___followers_dec"::smallint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "___drive_totalFiles" TYPE integer USING "___drive_totalFiles"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "___drive_incFiles" TYPE integer USING "___drive_incFiles"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "___drive_decFiles" TYPE integer USING "___drive_decFiles"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "___drive_incUsage" TYPE integer USING "___drive_incUsage"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "___drive_decUsage" TYPE integer USING "___drive_decUsage"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ALTER COLUMN "___requests_failed" TYPE smallint USING "___requests_failed"::smallint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ALTER COLUMN "___requests_succeeded" TYPE smallint USING "___requests_succeeded"::smallint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ALTER COLUMN "___requests_received" TYPE smallint USING "___requests_received"::smallint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ALTER COLUMN "___notes_total" TYPE integer USING "___notes_total"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ALTER COLUMN "___notes_inc" TYPE integer USING "___notes_inc"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ALTER COLUMN "___notes_dec" TYPE integer USING "___notes_dec"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ALTER COLUMN "___notes_diffs_normal" TYPE integer USING "___notes_diffs_normal"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ALTER COLUMN "___notes_diffs_reply" TYPE integer USING "___notes_diffs_reply"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ALTER COLUMN "___notes_diffs_renote" TYPE integer USING "___notes_diffs_renote"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ALTER COLUMN "___users_total" TYPE integer USING "___users_total"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ALTER COLUMN "___users_inc" TYPE smallint USING "___users_inc"::smallint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ALTER COLUMN "___users_dec" TYPE smallint USING "___users_dec"::smallint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ALTER COLUMN "___following_total" TYPE integer USING "___following_total"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ALTER COLUMN "___following_inc" TYPE smallint USING "___following_inc"::smallint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ALTER COLUMN "___following_dec" TYPE smallint USING "___following_dec"::smallint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ALTER COLUMN "___followers_total" TYPE integer USING "___followers_total"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ALTER COLUMN "___followers_inc" TYPE smallint USING "___followers_inc"::smallint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ALTER COLUMN "___followers_dec" TYPE smallint USING "___followers_dec"::smallint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ALTER COLUMN "___drive_totalFiles" TYPE integer USING "___drive_totalFiles"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ALTER COLUMN "___drive_incFiles" TYPE integer USING "___drive_incFiles"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ALTER COLUMN "___drive_decFiles" TYPE integer USING "___drive_decFiles"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ALTER COLUMN "___drive_incUsage" TYPE integer USING "___drive_incUsage"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ALTER COLUMN "___drive_decUsage" TYPE integer USING "___drive_decUsage"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_notes" ALTER COLUMN "___total" TYPE integer USING "___total"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_notes" ALTER COLUMN "___inc" TYPE smallint USING "___inc"::smallint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_notes" ALTER COLUMN "___dec" TYPE smallint USING "___dec"::smallint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_notes" ALTER COLUMN "___diffs_normal" TYPE smallint USING "___diffs_normal"::smallint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_notes" ALTER COLUMN "___diffs_reply" TYPE smallint USING "___diffs_reply"::smallint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_notes" ALTER COLUMN "___diffs_renote" TYPE smallint USING "___diffs_renote"::smallint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_notes" ALTER COLUMN "___total" TYPE integer USING "___total"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_notes" ALTER COLUMN "___inc" TYPE smallint USING "___inc"::smallint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_notes" ALTER COLUMN "___dec" TYPE smallint USING "___dec"::smallint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_notes" ALTER COLUMN "___diffs_normal" TYPE smallint USING "___diffs_normal"::smallint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_notes" ALTER COLUMN "___diffs_reply" TYPE smallint USING "___diffs_reply"::smallint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_notes" ALTER COLUMN "___diffs_renote" TYPE smallint USING "___diffs_renote"::smallint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__drive" ALTER COLUMN "___local_incCount" TYPE integer USING "___local_incCount"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__drive" ALTER COLUMN "___local_incSize" TYPE integer USING "___local_incSize"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__drive" ALTER COLUMN "___local_decCount" TYPE integer USING "___local_decCount"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__drive" ALTER COLUMN "___local_decSize" TYPE integer USING "___local_decSize"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__drive" ALTER COLUMN "___remote_incCount" TYPE integer USING "___remote_incCount"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__drive" ALTER COLUMN "___remote_incSize" TYPE integer USING "___remote_incSize"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__drive" ALTER COLUMN "___remote_decCount" TYPE integer USING "___remote_decCount"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__drive" ALTER COLUMN "___remote_decSize" TYPE integer USING "___remote_decSize"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__drive" ALTER COLUMN "___local_incCount" TYPE integer USING "___local_incCount"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__drive" ALTER COLUMN "___local_incSize" TYPE integer USING "___local_incSize"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__drive" ALTER COLUMN "___local_decCount" TYPE integer USING "___local_decCount"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__drive" ALTER COLUMN "___local_decSize" TYPE integer USING "___local_decSize"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__drive" ALTER COLUMN "___remote_incCount" TYPE integer USING "___remote_incCount"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__drive" ALTER COLUMN "___remote_incSize" TYPE integer USING "___remote_incSize"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__drive" ALTER COLUMN "___remote_decCount" TYPE integer USING "___remote_decCount"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__drive" ALTER COLUMN "___remote_decSize" TYPE integer USING "___remote_decSize"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_reaction" ALTER COLUMN "___local_count" TYPE smallint USING "___local_count"::smallint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_reaction" ALTER COLUMN "___remote_count" TYPE smallint USING "___remote_count"::smallint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_reaction" ALTER COLUMN "___local_count" TYPE smallint USING "___local_count"::smallint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_reaction" ALTER COLUMN "___remote_count" TYPE smallint USING "___remote_count"::smallint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___local_followings_total" TYPE integer USING "___local_followings_total"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___local_followings_inc" TYPE smallint USING "___local_followings_inc"::smallint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___local_followings_dec" TYPE smallint USING "___local_followings_dec"::smallint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___local_followers_total" TYPE integer USING "___local_followers_total"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___local_followers_inc" TYPE smallint USING "___local_followers_inc"::smallint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___local_followers_dec" TYPE smallint USING "___local_followers_dec"::smallint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___remote_followings_total" TYPE integer USING "___remote_followings_total"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___remote_followings_inc" TYPE smallint USING "___remote_followings_inc"::smallint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___remote_followings_dec" TYPE smallint USING "___remote_followings_dec"::smallint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___remote_followers_total" TYPE integer USING "___remote_followers_total"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___remote_followers_inc" TYPE smallint USING "___remote_followers_inc"::smallint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___remote_followers_dec" TYPE smallint USING "___remote_followers_dec"::smallint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___local_followings_total" TYPE integer USING "___local_followings_total"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___local_followings_inc" TYPE smallint USING "___local_followings_inc"::smallint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___local_followings_dec" TYPE smallint USING "___local_followings_dec"::smallint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___local_followers_total" TYPE integer USING "___local_followers_total"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___local_followers_inc" TYPE smallint USING "___local_followers_inc"::smallint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___local_followers_dec" TYPE smallint USING "___local_followers_dec"::smallint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___remote_followings_total" TYPE integer USING "___remote_followings_total"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___remote_followings_inc" TYPE smallint USING "___remote_followings_inc"::smallint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___remote_followings_dec" TYPE smallint USING "___remote_followings_dec"::smallint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___remote_followers_total" TYPE integer USING "___remote_followers_total"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___remote_followers_inc" TYPE smallint USING "___remote_followers_inc"::smallint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___remote_followers_dec" TYPE smallint USING "___remote_followers_dec"::smallint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_drive" ALTER COLUMN "___totalCount" TYPE integer USING "___totalCount"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_drive" ALTER COLUMN "___totalSize" TYPE integer USING "___totalSize"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_drive" ALTER COLUMN "___incCount" TYPE smallint USING "___incCount"::smallint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_drive" ALTER COLUMN "___incSize" TYPE integer USING "___incSize"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_drive" ALTER COLUMN "___decCount" TYPE smallint USING "___decCount"::smallint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_drive" ALTER COLUMN "___decSize" TYPE integer USING "___decSize"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_drive" ALTER COLUMN "___totalCount" TYPE integer USING "___totalCount"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_drive" ALTER COLUMN "___totalSize" TYPE integer USING "___totalSize"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_drive" ALTER COLUMN "___incCount" TYPE smallint USING "___incCount"::smallint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_drive" ALTER COLUMN "___incSize" TYPE integer USING "___incSize"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_drive" ALTER COLUMN "___decCount" TYPE smallint USING "___decCount"::smallint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_drive" ALTER COLUMN "___decSize" TYPE integer USING "___decSize"::integer`, + ); } async down(queryRunner) { - - await queryRunner.query(`ALTER TABLE "__chart__federation" ALTER COLUMN "___instance_total" TYPE bigint USING "___instance_total"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart__federation" ALTER COLUMN "___instance_inc" TYPE bigint USING "___instance_inc"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart__federation" ALTER COLUMN "___instance_dec" TYPE bigint USING "___instance_dec"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart_day__federation" ALTER COLUMN "___instance_total" TYPE bigint USING "___instance_total"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart_day__federation" ALTER COLUMN "___instance_inc" TYPE bigint USING "___instance_inc"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart_day__federation" ALTER COLUMN "___instance_dec" TYPE bigint USING "___instance_dec"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart__notes" ALTER COLUMN "___local_total" TYPE bigint USING "___local_total"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart__notes" ALTER COLUMN "___local_inc" TYPE bigint USING "___local_inc"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart__notes" ALTER COLUMN "___local_dec" TYPE bigint USING "___local_dec"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart__notes" ALTER COLUMN "___local_diffs_normal" TYPE bigint USING "___local_diffs_normal"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart__notes" ALTER COLUMN "___local_diffs_reply" TYPE bigint USING "___local_diffs_reply"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart__notes" ALTER COLUMN "___local_diffs_renote" TYPE bigint USING "___local_diffs_renote"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart__notes" ALTER COLUMN "___remote_total" TYPE bigint USING "___remote_total"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart__notes" ALTER COLUMN "___remote_inc" TYPE bigint USING "___remote_inc"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart__notes" ALTER COLUMN "___remote_dec" TYPE bigint USING "___remote_dec"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart__notes" ALTER COLUMN "___remote_diffs_normal" TYPE bigint USING "___remote_diffs_normal"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart__notes" ALTER COLUMN "___remote_diffs_reply" TYPE bigint USING "___remote_diffs_reply"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart__notes" ALTER COLUMN "___remote_diffs_renote" TYPE bigint USING "___remote_diffs_renote"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart_day__notes" ALTER COLUMN "___local_total" TYPE bigint USING "___local_total"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart_day__notes" ALTER COLUMN "___local_inc" TYPE bigint USING "___local_inc"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart_day__notes" ALTER COLUMN "___local_dec" TYPE bigint USING "___local_dec"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart_day__notes" ALTER COLUMN "___local_diffs_normal" TYPE bigint USING "___local_diffs_normal"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart_day__notes" ALTER COLUMN "___local_diffs_reply" TYPE bigint USING "___local_diffs_reply"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart_day__notes" ALTER COLUMN "___local_diffs_renote" TYPE bigint USING "___local_diffs_renote"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart_day__notes" ALTER COLUMN "___remote_total" TYPE bigint USING "___remote_total"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart_day__notes" ALTER COLUMN "___remote_inc" TYPE bigint USING "___remote_inc"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart_day__notes" ALTER COLUMN "___remote_dec" TYPE bigint USING "___remote_dec"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart_day__notes" ALTER COLUMN "___remote_diffs_normal" TYPE bigint USING "___remote_diffs_normal"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart_day__notes" ALTER COLUMN "___remote_diffs_reply" TYPE bigint USING "___remote_diffs_reply"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart_day__notes" ALTER COLUMN "___remote_diffs_renote" TYPE bigint USING "___remote_diffs_renote"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart__users" ALTER COLUMN "___local_total" TYPE bigint USING "___local_total"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart__users" ALTER COLUMN "___local_inc" TYPE bigint USING "___local_inc"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart__users" ALTER COLUMN "___local_dec" TYPE bigint USING "___local_dec"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart__users" ALTER COLUMN "___remote_total" TYPE bigint USING "___remote_total"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart__users" ALTER COLUMN "___remote_inc" TYPE bigint USING "___remote_inc"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart__users" ALTER COLUMN "___remote_dec" TYPE bigint USING "___remote_dec"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart_day__users" ALTER COLUMN "___local_total" TYPE bigint USING "___local_total"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart_day__users" ALTER COLUMN "___local_inc" TYPE bigint USING "___local_inc"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart_day__users" ALTER COLUMN "___local_dec" TYPE bigint USING "___local_dec"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart_day__users" ALTER COLUMN "___remote_total" TYPE bigint USING "___remote_total"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart_day__users" ALTER COLUMN "___remote_inc" TYPE bigint USING "___remote_inc"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart_day__users" ALTER COLUMN "___remote_dec" TYPE bigint USING "___remote_dec"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart__network" ALTER COLUMN "___incomingRequests" TYPE bigint USING "___incomingRequests"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart__network" ALTER COLUMN "___outgoingRequests" TYPE bigint USING "___outgoingRequests"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart__network" ALTER COLUMN "___totalTime" TYPE bigint USING "___totalTime"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart__network" ALTER COLUMN "___incomingBytes" TYPE bigint USING "___incomingBytes"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart__network" ALTER COLUMN "___outgoingBytes" TYPE bigint USING "___outgoingBytes"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart_day__network" ALTER COLUMN "___incomingRequests" TYPE bigint USING "___incomingRequests"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart_day__network" ALTER COLUMN "___outgoingRequests" TYPE bigint USING "___outgoingRequests"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart_day__network" ALTER COLUMN "___totalTime" TYPE bigint USING "___totalTime"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart_day__network" ALTER COLUMN "___incomingBytes" TYPE bigint USING "___incomingBytes"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart_day__network" ALTER COLUMN "___outgoingBytes" TYPE bigint USING "___outgoingBytes"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "___requests_failed" TYPE bigint USING "___requests_failed"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "___requests_succeeded" TYPE bigint USING "___requests_succeeded"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "___requests_received" TYPE bigint USING "___requests_received"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "___notes_total" TYPE bigint USING "___notes_total"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "___notes_inc" TYPE bigint USING "___notes_inc"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "___notes_dec" TYPE bigint USING "___notes_dec"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "___notes_diffs_normal" TYPE bigint USING "___notes_diffs_normal"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "___notes_diffs_reply" TYPE bigint USING "___notes_diffs_reply"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "___notes_diffs_renote" TYPE bigint USING "___notes_diffs_renote"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "___users_total" TYPE bigint USING "___users_total"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "___users_inc" TYPE bigint USING "___users_inc"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "___users_dec" TYPE bigint USING "___users_dec"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "___following_total" TYPE bigint USING "___following_total"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "___following_inc" TYPE bigint USING "___following_inc"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "___following_dec" TYPE bigint USING "___following_dec"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "___followers_total" TYPE bigint USING "___followers_total"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "___followers_inc" TYPE bigint USING "___followers_inc"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "___followers_dec" TYPE bigint USING "___followers_dec"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "___drive_totalFiles" TYPE bigint USING "___drive_totalFiles"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "___drive_incFiles" TYPE bigint USING "___drive_incFiles"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "___drive_decFiles" TYPE bigint USING "___drive_decFiles"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "___drive_incUsage" TYPE bigint USING "___drive_incUsage"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ALTER COLUMN "___drive_decUsage" TYPE bigint USING "___drive_decUsage"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ALTER COLUMN "___requests_failed" TYPE bigint USING "___requests_failed"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ALTER COLUMN "___requests_succeeded" TYPE bigint USING "___requests_succeeded"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ALTER COLUMN "___requests_received" TYPE bigint USING "___requests_received"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ALTER COLUMN "___notes_total" TYPE bigint USING "___notes_total"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ALTER COLUMN "___notes_inc" TYPE bigint USING "___notes_inc"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ALTER COLUMN "___notes_dec" TYPE bigint USING "___notes_dec"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ALTER COLUMN "___notes_diffs_normal" TYPE bigint USING "___notes_diffs_normal"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ALTER COLUMN "___notes_diffs_reply" TYPE bigint USING "___notes_diffs_reply"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ALTER COLUMN "___notes_diffs_renote" TYPE bigint USING "___notes_diffs_renote"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ALTER COLUMN "___users_total" TYPE bigint USING "___users_total"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ALTER COLUMN "___users_inc" TYPE bigint USING "___users_inc"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ALTER COLUMN "___users_dec" TYPE bigint USING "___users_dec"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ALTER COLUMN "___following_total" TYPE bigint USING "___following_total"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ALTER COLUMN "___following_inc" TYPE bigint USING "___following_inc"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ALTER COLUMN "___following_dec" TYPE bigint USING "___following_dec"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ALTER COLUMN "___followers_total" TYPE bigint USING "___followers_total"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ALTER COLUMN "___followers_inc" TYPE bigint USING "___followers_inc"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ALTER COLUMN "___followers_dec" TYPE bigint USING "___followers_dec"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ALTER COLUMN "___drive_totalFiles" TYPE bigint USING "___drive_totalFiles"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ALTER COLUMN "___drive_incFiles" TYPE bigint USING "___drive_incFiles"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ALTER COLUMN "___drive_decFiles" TYPE bigint USING "___drive_decFiles"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ALTER COLUMN "___drive_incUsage" TYPE bigint USING "___drive_incUsage"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ALTER COLUMN "___drive_decUsage" TYPE bigint USING "___drive_decUsage"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_notes" ALTER COLUMN "___total" TYPE bigint USING "___total"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_notes" ALTER COLUMN "___inc" TYPE bigint USING "___inc"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_notes" ALTER COLUMN "___dec" TYPE bigint USING "___dec"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_notes" ALTER COLUMN "___diffs_normal" TYPE bigint USING "___diffs_normal"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_notes" ALTER COLUMN "___diffs_reply" TYPE bigint USING "___diffs_reply"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_notes" ALTER COLUMN "___diffs_renote" TYPE bigint USING "___diffs_renote"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_notes" ALTER COLUMN "___total" TYPE bigint USING "___total"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_notes" ALTER COLUMN "___inc" TYPE bigint USING "___inc"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_notes" ALTER COLUMN "___dec" TYPE bigint USING "___dec"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_notes" ALTER COLUMN "___diffs_normal" TYPE bigint USING "___diffs_normal"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_notes" ALTER COLUMN "___diffs_reply" TYPE bigint USING "___diffs_reply"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_notes" ALTER COLUMN "___diffs_renote" TYPE bigint USING "___diffs_renote"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart__drive" ALTER COLUMN "___local_incCount" TYPE bigint USING "___local_incCount"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart__drive" ALTER COLUMN "___local_incSize" TYPE bigint USING "___local_incSize"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart__drive" ALTER COLUMN "___local_decCount" TYPE bigint USING "___local_decCount"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart__drive" ALTER COLUMN "___local_decSize" TYPE bigint USING "___local_decSize"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart__drive" ALTER COLUMN "___remote_incCount" TYPE bigint USING "___remote_incCount"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart__drive" ALTER COLUMN "___remote_incSize" TYPE bigint USING "___remote_incSize"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart__drive" ALTER COLUMN "___remote_decCount" TYPE bigint USING "___remote_decCount"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart__drive" ALTER COLUMN "___remote_decSize" TYPE bigint USING "___remote_decSize"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart_day__drive" ALTER COLUMN "___local_incCount" TYPE bigint USING "___local_incCount"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart_day__drive" ALTER COLUMN "___local_incSize" TYPE bigint USING "___local_incSize"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart_day__drive" ALTER COLUMN "___local_decCount" TYPE bigint USING "___local_decCount"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart_day__drive" ALTER COLUMN "___local_decSize" TYPE bigint USING "___local_decSize"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart_day__drive" ALTER COLUMN "___remote_incCount" TYPE bigint USING "___remote_incCount"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart_day__drive" ALTER COLUMN "___remote_incSize" TYPE bigint USING "___remote_incSize"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart_day__drive" ALTER COLUMN "___remote_decCount" TYPE bigint USING "___remote_decCount"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart_day__drive" ALTER COLUMN "___remote_decSize" TYPE bigint USING "___remote_decSize"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_reaction" ALTER COLUMN "___local_count" TYPE bigint USING "___local_count"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_reaction" ALTER COLUMN "___remote_count" TYPE bigint USING "___remote_count"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_reaction" ALTER COLUMN "___local_count" TYPE bigint USING "___local_count"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_reaction" ALTER COLUMN "___remote_count" TYPE bigint USING "___remote_count"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___local_followings_total" TYPE bigint USING "___local_followings_total"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___local_followings_inc" TYPE bigint USING "___local_followings_inc"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___local_followings_dec" TYPE bigint USING "___local_followings_dec"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___local_followers_total" TYPE bigint USING "___local_followers_total"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___local_followers_inc" TYPE bigint USING "___local_followers_inc"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___local_followers_dec" TYPE bigint USING "___local_followers_dec"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___remote_followings_total" TYPE bigint USING "___remote_followings_total"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___remote_followings_inc" TYPE bigint USING "___remote_followings_inc"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___remote_followings_dec" TYPE bigint USING "___remote_followings_dec"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___remote_followers_total" TYPE bigint USING "___remote_followers_total"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___remote_followers_inc" TYPE bigint USING "___remote_followers_inc"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___remote_followers_dec" TYPE bigint USING "___remote_followers_dec"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___local_followings_total" TYPE bigint USING "___local_followings_total"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___local_followings_inc" TYPE bigint USING "___local_followings_inc"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___local_followings_dec" TYPE bigint USING "___local_followings_dec"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___local_followers_total" TYPE bigint USING "___local_followers_total"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___local_followers_inc" TYPE bigint USING "___local_followers_inc"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___local_followers_dec" TYPE bigint USING "___local_followers_dec"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___remote_followings_total" TYPE bigint USING "___remote_followings_total"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___remote_followings_inc" TYPE bigint USING "___remote_followings_inc"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___remote_followings_dec" TYPE bigint USING "___remote_followings_dec"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___remote_followers_total" TYPE bigint USING "___remote_followers_total"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___remote_followers_inc" TYPE bigint USING "___remote_followers_inc"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___remote_followers_dec" TYPE bigint USING "___remote_followers_dec"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_drive" ALTER COLUMN "___totalCount" TYPE bigint USING "___totalCount"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_drive" ALTER COLUMN "___totalSize" TYPE bigint USING "___totalSize"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_drive" ALTER COLUMN "___incCount" TYPE bigint USING "___incCount"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_drive" ALTER COLUMN "___incSize" TYPE bigint USING "___incSize"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_drive" ALTER COLUMN "___decCount" TYPE bigint USING "___decCount"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_drive" ALTER COLUMN "___decSize" TYPE bigint USING "___decSize"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_drive" ALTER COLUMN "___totalCount" TYPE bigint USING "___totalCount"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_drive" ALTER COLUMN "___totalSize" TYPE bigint USING "___totalSize"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_drive" ALTER COLUMN "___incCount" TYPE bigint USING "___incCount"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_drive" ALTER COLUMN "___incSize" TYPE bigint USING "___incSize"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_drive" ALTER COLUMN "___decCount" TYPE bigint USING "___decCount"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_drive" ALTER COLUMN "___decSize" TYPE bigint USING "___decSize"::bigint`); + await queryRunner.query( + `ALTER TABLE "__chart__federation" ALTER COLUMN "___instance_total" TYPE bigint USING "___instance_total"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__federation" ALTER COLUMN "___instance_inc" TYPE bigint USING "___instance_inc"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__federation" ALTER COLUMN "___instance_dec" TYPE bigint USING "___instance_dec"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__federation" ALTER COLUMN "___instance_total" TYPE bigint USING "___instance_total"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__federation" ALTER COLUMN "___instance_inc" TYPE bigint USING "___instance_inc"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__federation" ALTER COLUMN "___instance_dec" TYPE bigint USING "___instance_dec"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__notes" ALTER COLUMN "___local_total" TYPE bigint USING "___local_total"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__notes" ALTER COLUMN "___local_inc" TYPE bigint USING "___local_inc"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__notes" ALTER COLUMN "___local_dec" TYPE bigint USING "___local_dec"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__notes" ALTER COLUMN "___local_diffs_normal" TYPE bigint USING "___local_diffs_normal"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__notes" ALTER COLUMN "___local_diffs_reply" TYPE bigint USING "___local_diffs_reply"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__notes" ALTER COLUMN "___local_diffs_renote" TYPE bigint USING "___local_diffs_renote"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__notes" ALTER COLUMN "___remote_total" TYPE bigint USING "___remote_total"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__notes" ALTER COLUMN "___remote_inc" TYPE bigint USING "___remote_inc"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__notes" ALTER COLUMN "___remote_dec" TYPE bigint USING "___remote_dec"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__notes" ALTER COLUMN "___remote_diffs_normal" TYPE bigint USING "___remote_diffs_normal"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__notes" ALTER COLUMN "___remote_diffs_reply" TYPE bigint USING "___remote_diffs_reply"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__notes" ALTER COLUMN "___remote_diffs_renote" TYPE bigint USING "___remote_diffs_renote"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__notes" ALTER COLUMN "___local_total" TYPE bigint USING "___local_total"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__notes" ALTER COLUMN "___local_inc" TYPE bigint USING "___local_inc"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__notes" ALTER COLUMN "___local_dec" TYPE bigint USING "___local_dec"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__notes" ALTER COLUMN "___local_diffs_normal" TYPE bigint USING "___local_diffs_normal"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__notes" ALTER COLUMN "___local_diffs_reply" TYPE bigint USING "___local_diffs_reply"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__notes" ALTER COLUMN "___local_diffs_renote" TYPE bigint USING "___local_diffs_renote"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__notes" ALTER COLUMN "___remote_total" TYPE bigint USING "___remote_total"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__notes" ALTER COLUMN "___remote_inc" TYPE bigint USING "___remote_inc"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__notes" ALTER COLUMN "___remote_dec" TYPE bigint USING "___remote_dec"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__notes" ALTER COLUMN "___remote_diffs_normal" TYPE bigint USING "___remote_diffs_normal"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__notes" ALTER COLUMN "___remote_diffs_reply" TYPE bigint USING "___remote_diffs_reply"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__notes" ALTER COLUMN "___remote_diffs_renote" TYPE bigint USING "___remote_diffs_renote"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__users" ALTER COLUMN "___local_total" TYPE bigint USING "___local_total"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__users" ALTER COLUMN "___local_inc" TYPE bigint USING "___local_inc"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__users" ALTER COLUMN "___local_dec" TYPE bigint USING "___local_dec"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__users" ALTER COLUMN "___remote_total" TYPE bigint USING "___remote_total"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__users" ALTER COLUMN "___remote_inc" TYPE bigint USING "___remote_inc"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__users" ALTER COLUMN "___remote_dec" TYPE bigint USING "___remote_dec"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__users" ALTER COLUMN "___local_total" TYPE bigint USING "___local_total"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__users" ALTER COLUMN "___local_inc" TYPE bigint USING "___local_inc"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__users" ALTER COLUMN "___local_dec" TYPE bigint USING "___local_dec"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__users" ALTER COLUMN "___remote_total" TYPE bigint USING "___remote_total"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__users" ALTER COLUMN "___remote_inc" TYPE bigint USING "___remote_inc"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__users" ALTER COLUMN "___remote_dec" TYPE bigint USING "___remote_dec"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__network" ALTER COLUMN "___incomingRequests" TYPE bigint USING "___incomingRequests"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__network" ALTER COLUMN "___outgoingRequests" TYPE bigint USING "___outgoingRequests"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__network" ALTER COLUMN "___totalTime" TYPE bigint USING "___totalTime"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__network" ALTER COLUMN "___incomingBytes" TYPE bigint USING "___incomingBytes"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__network" ALTER COLUMN "___outgoingBytes" TYPE bigint USING "___outgoingBytes"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__network" ALTER COLUMN "___incomingRequests" TYPE bigint USING "___incomingRequests"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__network" ALTER COLUMN "___outgoingRequests" TYPE bigint USING "___outgoingRequests"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__network" ALTER COLUMN "___totalTime" TYPE bigint USING "___totalTime"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__network" ALTER COLUMN "___incomingBytes" TYPE bigint USING "___incomingBytes"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__network" ALTER COLUMN "___outgoingBytes" TYPE bigint USING "___outgoingBytes"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "___requests_failed" TYPE bigint USING "___requests_failed"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "___requests_succeeded" TYPE bigint USING "___requests_succeeded"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "___requests_received" TYPE bigint USING "___requests_received"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "___notes_total" TYPE bigint USING "___notes_total"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "___notes_inc" TYPE bigint USING "___notes_inc"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "___notes_dec" TYPE bigint USING "___notes_dec"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "___notes_diffs_normal" TYPE bigint USING "___notes_diffs_normal"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "___notes_diffs_reply" TYPE bigint USING "___notes_diffs_reply"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "___notes_diffs_renote" TYPE bigint USING "___notes_diffs_renote"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "___users_total" TYPE bigint USING "___users_total"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "___users_inc" TYPE bigint USING "___users_inc"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "___users_dec" TYPE bigint USING "___users_dec"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "___following_total" TYPE bigint USING "___following_total"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "___following_inc" TYPE bigint USING "___following_inc"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "___following_dec" TYPE bigint USING "___following_dec"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "___followers_total" TYPE bigint USING "___followers_total"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "___followers_inc" TYPE bigint USING "___followers_inc"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "___followers_dec" TYPE bigint USING "___followers_dec"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "___drive_totalFiles" TYPE bigint USING "___drive_totalFiles"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "___drive_incFiles" TYPE bigint USING "___drive_incFiles"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "___drive_decFiles" TYPE bigint USING "___drive_decFiles"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "___drive_incUsage" TYPE bigint USING "___drive_incUsage"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ALTER COLUMN "___drive_decUsage" TYPE bigint USING "___drive_decUsage"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ALTER COLUMN "___requests_failed" TYPE bigint USING "___requests_failed"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ALTER COLUMN "___requests_succeeded" TYPE bigint USING "___requests_succeeded"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ALTER COLUMN "___requests_received" TYPE bigint USING "___requests_received"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ALTER COLUMN "___notes_total" TYPE bigint USING "___notes_total"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ALTER COLUMN "___notes_inc" TYPE bigint USING "___notes_inc"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ALTER COLUMN "___notes_dec" TYPE bigint USING "___notes_dec"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ALTER COLUMN "___notes_diffs_normal" TYPE bigint USING "___notes_diffs_normal"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ALTER COLUMN "___notes_diffs_reply" TYPE bigint USING "___notes_diffs_reply"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ALTER COLUMN "___notes_diffs_renote" TYPE bigint USING "___notes_diffs_renote"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ALTER COLUMN "___users_total" TYPE bigint USING "___users_total"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ALTER COLUMN "___users_inc" TYPE bigint USING "___users_inc"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ALTER COLUMN "___users_dec" TYPE bigint USING "___users_dec"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ALTER COLUMN "___following_total" TYPE bigint USING "___following_total"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ALTER COLUMN "___following_inc" TYPE bigint USING "___following_inc"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ALTER COLUMN "___following_dec" TYPE bigint USING "___following_dec"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ALTER COLUMN "___followers_total" TYPE bigint USING "___followers_total"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ALTER COLUMN "___followers_inc" TYPE bigint USING "___followers_inc"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ALTER COLUMN "___followers_dec" TYPE bigint USING "___followers_dec"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ALTER COLUMN "___drive_totalFiles" TYPE bigint USING "___drive_totalFiles"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ALTER COLUMN "___drive_incFiles" TYPE bigint USING "___drive_incFiles"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ALTER COLUMN "___drive_decFiles" TYPE bigint USING "___drive_decFiles"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ALTER COLUMN "___drive_incUsage" TYPE bigint USING "___drive_incUsage"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ALTER COLUMN "___drive_decUsage" TYPE bigint USING "___drive_decUsage"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_notes" ALTER COLUMN "___total" TYPE bigint USING "___total"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_notes" ALTER COLUMN "___inc" TYPE bigint USING "___inc"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_notes" ALTER COLUMN "___dec" TYPE bigint USING "___dec"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_notes" ALTER COLUMN "___diffs_normal" TYPE bigint USING "___diffs_normal"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_notes" ALTER COLUMN "___diffs_reply" TYPE bigint USING "___diffs_reply"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_notes" ALTER COLUMN "___diffs_renote" TYPE bigint USING "___diffs_renote"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_notes" ALTER COLUMN "___total" TYPE bigint USING "___total"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_notes" ALTER COLUMN "___inc" TYPE bigint USING "___inc"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_notes" ALTER COLUMN "___dec" TYPE bigint USING "___dec"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_notes" ALTER COLUMN "___diffs_normal" TYPE bigint USING "___diffs_normal"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_notes" ALTER COLUMN "___diffs_reply" TYPE bigint USING "___diffs_reply"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_notes" ALTER COLUMN "___diffs_renote" TYPE bigint USING "___diffs_renote"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__drive" ALTER COLUMN "___local_incCount" TYPE bigint USING "___local_incCount"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__drive" ALTER COLUMN "___local_incSize" TYPE bigint USING "___local_incSize"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__drive" ALTER COLUMN "___local_decCount" TYPE bigint USING "___local_decCount"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__drive" ALTER COLUMN "___local_decSize" TYPE bigint USING "___local_decSize"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__drive" ALTER COLUMN "___remote_incCount" TYPE bigint USING "___remote_incCount"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__drive" ALTER COLUMN "___remote_incSize" TYPE bigint USING "___remote_incSize"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__drive" ALTER COLUMN "___remote_decCount" TYPE bigint USING "___remote_decCount"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__drive" ALTER COLUMN "___remote_decSize" TYPE bigint USING "___remote_decSize"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__drive" ALTER COLUMN "___local_incCount" TYPE bigint USING "___local_incCount"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__drive" ALTER COLUMN "___local_incSize" TYPE bigint USING "___local_incSize"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__drive" ALTER COLUMN "___local_decCount" TYPE bigint USING "___local_decCount"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__drive" ALTER COLUMN "___local_decSize" TYPE bigint USING "___local_decSize"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__drive" ALTER COLUMN "___remote_incCount" TYPE bigint USING "___remote_incCount"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__drive" ALTER COLUMN "___remote_incSize" TYPE bigint USING "___remote_incSize"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__drive" ALTER COLUMN "___remote_decCount" TYPE bigint USING "___remote_decCount"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__drive" ALTER COLUMN "___remote_decSize" TYPE bigint USING "___remote_decSize"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_reaction" ALTER COLUMN "___local_count" TYPE bigint USING "___local_count"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_reaction" ALTER COLUMN "___remote_count" TYPE bigint USING "___remote_count"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_reaction" ALTER COLUMN "___local_count" TYPE bigint USING "___local_count"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_reaction" ALTER COLUMN "___remote_count" TYPE bigint USING "___remote_count"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___local_followings_total" TYPE bigint USING "___local_followings_total"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___local_followings_inc" TYPE bigint USING "___local_followings_inc"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___local_followings_dec" TYPE bigint USING "___local_followings_dec"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___local_followers_total" TYPE bigint USING "___local_followers_total"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___local_followers_inc" TYPE bigint USING "___local_followers_inc"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___local_followers_dec" TYPE bigint USING "___local_followers_dec"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___remote_followings_total" TYPE bigint USING "___remote_followings_total"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___remote_followings_inc" TYPE bigint USING "___remote_followings_inc"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___remote_followings_dec" TYPE bigint USING "___remote_followings_dec"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___remote_followers_total" TYPE bigint USING "___remote_followers_total"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___remote_followers_inc" TYPE bigint USING "___remote_followers_inc"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_following" ALTER COLUMN "___remote_followers_dec" TYPE bigint USING "___remote_followers_dec"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___local_followings_total" TYPE bigint USING "___local_followings_total"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___local_followings_inc" TYPE bigint USING "___local_followings_inc"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___local_followings_dec" TYPE bigint USING "___local_followings_dec"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___local_followers_total" TYPE bigint USING "___local_followers_total"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___local_followers_inc" TYPE bigint USING "___local_followers_inc"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___local_followers_dec" TYPE bigint USING "___local_followers_dec"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___remote_followings_total" TYPE bigint USING "___remote_followings_total"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___remote_followings_inc" TYPE bigint USING "___remote_followings_inc"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___remote_followings_dec" TYPE bigint USING "___remote_followings_dec"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___remote_followers_total" TYPE bigint USING "___remote_followers_total"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___remote_followers_inc" TYPE bigint USING "___remote_followers_inc"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_following" ALTER COLUMN "___remote_followers_dec" TYPE bigint USING "___remote_followers_dec"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_drive" ALTER COLUMN "___totalCount" TYPE bigint USING "___totalCount"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_drive" ALTER COLUMN "___totalSize" TYPE bigint USING "___totalSize"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_drive" ALTER COLUMN "___incCount" TYPE bigint USING "___incCount"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_drive" ALTER COLUMN "___incSize" TYPE bigint USING "___incSize"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_drive" ALTER COLUMN "___decCount" TYPE bigint USING "___decCount"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_drive" ALTER COLUMN "___decSize" TYPE bigint USING "___decSize"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_drive" ALTER COLUMN "___totalCount" TYPE bigint USING "___totalCount"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_drive" ALTER COLUMN "___totalSize" TYPE bigint USING "___totalSize"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_drive" ALTER COLUMN "___incCount" TYPE bigint USING "___incCount"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_drive" ALTER COLUMN "___incSize" TYPE bigint USING "___incSize"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_drive" ALTER COLUMN "___decCount" TYPE bigint USING "___decCount"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_drive" ALTER COLUMN "___decSize" TYPE bigint USING "___decSize"::bigint`, + ); } } diff --git a/packages/backend/migration/1644059847460-chart-v8.js b/packages/backend/migration/1644059847460-chart-v8.js index a5339c0ebd..f9357c8ca1 100644 --- a/packages/backend/migration/1644059847460-chart-v8.js +++ b/packages/backend/migration/1644059847460-chart-v8.js @@ -1,25 +1,46 @@ - - export class chartV81644059847460 { - name = 'chartV81644059847460' + name = "chartV81644059847460"; - async up(queryRunner) { - await queryRunner.query(`UPDATE "__chart__active_users" SET "___local_users"=2147483647 WHERE "___local_users" > 2147483647`); - await queryRunner.query(`UPDATE "__chart__active_users" SET "___remote_users"=2147483647 WHERE "___remote_users" > 2147483647`); - await queryRunner.query(`UPDATE "__chart_day__active_users" SET "___local_users"=2147483647 WHERE "___local_users" > 2147483647`); - await queryRunner.query(`UPDATE "__chart_day__active_users" SET "___remote_users"=2147483647 WHERE "___remote_users" > 2147483647`); + async up(queryRunner) { + await queryRunner.query( + `UPDATE "__chart__active_users" SET "___local_users"=2147483647 WHERE "___local_users" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart__active_users" SET "___remote_users"=2147483647 WHERE "___remote_users" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart_day__active_users" SET "___local_users"=2147483647 WHERE "___local_users" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart_day__active_users" SET "___remote_users"=2147483647 WHERE "___remote_users" > 2147483647`, + ); - await queryRunner.query(`ALTER TABLE "__chart__active_users" ALTER COLUMN "___local_users" TYPE integer USING "___local_users"::integer`); - await queryRunner.query(`ALTER TABLE "__chart__active_users" ALTER COLUMN "___remote_users" TYPE integer USING "___remote_users"::integer`); - await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ALTER COLUMN "___local_users" TYPE integer USING "___local_users"::integer`); - await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ALTER COLUMN "___remote_users" TYPE integer USING "___remote_users"::integer`); - } - - async down(queryRunner) { - - await queryRunner.query(`ALTER TABLE "__chart__active_users" ALTER COLUMN "___local_users" TYPE bigint USING "___local_users"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart__active_users" ALTER COLUMN "___remote_users" TYPE bigint USING "___remote_users"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ALTER COLUMN "___local_users" TYPE bigint USING "___local_users"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ALTER COLUMN "___remote_users" TYPE bigint USING "___remote_users"::bigint`); - } + await queryRunner.query( + `ALTER TABLE "__chart__active_users" ALTER COLUMN "___local_users" TYPE integer USING "___local_users"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__active_users" ALTER COLUMN "___remote_users" TYPE integer USING "___remote_users"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__active_users" ALTER COLUMN "___local_users" TYPE integer USING "___local_users"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__active_users" ALTER COLUMN "___remote_users" TYPE integer USING "___remote_users"::integer`, + ); + } + + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "__chart__active_users" ALTER COLUMN "___local_users" TYPE bigint USING "___local_users"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__active_users" ALTER COLUMN "___remote_users" TYPE bigint USING "___remote_users"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__active_users" ALTER COLUMN "___local_users" TYPE bigint USING "___local_users"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__active_users" ALTER COLUMN "___remote_users" TYPE bigint USING "___remote_users"::bigint`, + ); + } } diff --git a/packages/backend/migration/1644060125705-chart-v9.js b/packages/backend/migration/1644060125705-chart-v9.js index da35d42315..7d7934e319 100644 --- a/packages/backend/migration/1644060125705-chart-v9.js +++ b/packages/backend/migration/1644060125705-chart-v9.js @@ -1,25 +1,46 @@ - - export class chartV91644060125705 { - name = 'chartV91644060125705' + name = "chartV91644060125705"; - async up(queryRunner) { - await queryRunner.query(`UPDATE "__chart__hashtag" SET "___local_users"=2147483647 WHERE "___local_users" > 2147483647`); - await queryRunner.query(`UPDATE "__chart__hashtag" SET "___remote_users"=2147483647 WHERE "___remote_users" > 2147483647`); - await queryRunner.query(`UPDATE "__chart_day__hashtag" SET "___local_users"=2147483647 WHERE "___local_users" > 2147483647`); - await queryRunner.query(`UPDATE "__chart_day__hashtag" SET "___remote_users"=2147483647 WHERE "___remote_users" > 2147483647`); + async up(queryRunner) { + await queryRunner.query( + `UPDATE "__chart__hashtag" SET "___local_users"=2147483647 WHERE "___local_users" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart__hashtag" SET "___remote_users"=2147483647 WHERE "___remote_users" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart_day__hashtag" SET "___local_users"=2147483647 WHERE "___local_users" > 2147483647`, + ); + await queryRunner.query( + `UPDATE "__chart_day__hashtag" SET "___remote_users"=2147483647 WHERE "___remote_users" > 2147483647`, + ); - await queryRunner.query(`ALTER TABLE "__chart__hashtag" ALTER COLUMN "___local_users" TYPE integer USING "___local_users"::integer`); - await queryRunner.query(`ALTER TABLE "__chart__hashtag" ALTER COLUMN "___remote_users" TYPE integer USING "___remote_users"::integer`); - await queryRunner.query(`ALTER TABLE "__chart_day__hashtag" ALTER COLUMN "___local_users" TYPE integer USING "___local_users"::integer`); - await queryRunner.query(`ALTER TABLE "__chart_day__hashtag" ALTER COLUMN "___remote_users" TYPE integer USING "___remote_users"::integer`); - } - - async down(queryRunner) { - - await queryRunner.query(`ALTER TABLE "__chart__hashtag" ALTER COLUMN "___local_users" TYPE bigint USING "___local_users"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart__hashtag" ALTER COLUMN "___remote_users" TYPE bigint USING "___remote_users"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart_day__hashtag" ALTER COLUMN "___local_users" TYPE bigint USING "___local_users"::bigint`); - await queryRunner.query(`ALTER TABLE "__chart_day__hashtag" ALTER COLUMN "___remote_users" TYPE bigint USING "___remote_users"::bigint`); - } + await queryRunner.query( + `ALTER TABLE "__chart__hashtag" ALTER COLUMN "___local_users" TYPE integer USING "___local_users"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__hashtag" ALTER COLUMN "___remote_users" TYPE integer USING "___remote_users"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__hashtag" ALTER COLUMN "___local_users" TYPE integer USING "___local_users"::integer`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__hashtag" ALTER COLUMN "___remote_users" TYPE integer USING "___remote_users"::integer`, + ); + } + + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "__chart__hashtag" ALTER COLUMN "___local_users" TYPE bigint USING "___local_users"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__hashtag" ALTER COLUMN "___remote_users" TYPE bigint USING "___remote_users"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__hashtag" ALTER COLUMN "___local_users" TYPE bigint USING "___local_users"::bigint`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__hashtag" ALTER COLUMN "___remote_users" TYPE bigint USING "___remote_users"::bigint`, + ); + } } diff --git a/packages/backend/migration/1644073149413-chart-v10.js b/packages/backend/migration/1644073149413-chart-v10.js index 7260bbeca4..8038dad156 100644 --- a/packages/backend/migration/1644073149413-chart-v10.js +++ b/packages/backend/migration/1644073149413-chart-v10.js @@ -1,35 +1,77 @@ - - export class chartV101644073149413 { - name = 'chartV101644073149413' + name = "chartV101644073149413"; - async up(queryRunner) { - await queryRunner.query(`CREATE TABLE "__chart__ap_request" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "___deliverFailed" integer NOT NULL DEFAULT '0', "___deliverSucceeded" integer NOT NULL DEFAULT '0', "___inboxReceived" integer NOT NULL DEFAULT '0', CONSTRAINT "UQ_e56f4beac5746d44bc3e19c80d0" UNIQUE ("date"), CONSTRAINT "PK_56a25cd447c7ee08876b3baf8d8" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_e56f4beac5746d44bc3e19c80d" ON "__chart__ap_request" ("date") `); - await queryRunner.query(`CREATE TABLE "__chart_day__ap_request" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "___deliverFailed" integer NOT NULL DEFAULT '0', "___deliverSucceeded" integer NOT NULL DEFAULT '0', "___inboxReceived" integer NOT NULL DEFAULT '0', CONSTRAINT "UQ_a848f66d6cec11980a5dd595822" UNIQUE ("date"), CONSTRAINT "PK_9318b49daee320194e23f712e69" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_a848f66d6cec11980a5dd59582" ON "__chart_day__ap_request" ("date") `); - await queryRunner.query(`ALTER TABLE "__chart__federation" ADD "unique_temp___deliveredInstances" character varying array NOT NULL DEFAULT '{}'`); - await queryRunner.query(`ALTER TABLE "__chart__federation" ADD "___deliveredInstances" smallint NOT NULL DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__federation" ADD "unique_temp___inboxInstances" character varying array NOT NULL DEFAULT '{}'`); - await queryRunner.query(`ALTER TABLE "__chart__federation" ADD "___inboxInstances" smallint NOT NULL DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__federation" ADD "unique_temp___deliveredInstances" character varying array NOT NULL DEFAULT '{}'`); - await queryRunner.query(`ALTER TABLE "__chart_day__federation" ADD "___deliveredInstances" smallint NOT NULL DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__federation" ADD "unique_temp___inboxInstances" character varying array NOT NULL DEFAULT '{}'`); - await queryRunner.query(`ALTER TABLE "__chart_day__federation" ADD "___inboxInstances" smallint NOT NULL DEFAULT '0'`); - } + async up(queryRunner) { + await queryRunner.query( + `CREATE TABLE "__chart__ap_request" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "___deliverFailed" integer NOT NULL DEFAULT '0', "___deliverSucceeded" integer NOT NULL DEFAULT '0', "___inboxReceived" integer NOT NULL DEFAULT '0', CONSTRAINT "UQ_e56f4beac5746d44bc3e19c80d0" UNIQUE ("date"), CONSTRAINT "PK_56a25cd447c7ee08876b3baf8d8" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_e56f4beac5746d44bc3e19c80d" ON "__chart__ap_request" ("date") `, + ); + await queryRunner.query( + `CREATE TABLE "__chart_day__ap_request" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "___deliverFailed" integer NOT NULL DEFAULT '0', "___deliverSucceeded" integer NOT NULL DEFAULT '0', "___inboxReceived" integer NOT NULL DEFAULT '0', CONSTRAINT "UQ_a848f66d6cec11980a5dd595822" UNIQUE ("date"), CONSTRAINT "PK_9318b49daee320194e23f712e69" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_a848f66d6cec11980a5dd59582" ON "__chart_day__ap_request" ("date") `, + ); + await queryRunner.query( + `ALTER TABLE "__chart__federation" ADD "unique_temp___deliveredInstances" character varying array NOT NULL DEFAULT '{}'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__federation" ADD "___deliveredInstances" smallint NOT NULL DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__federation" ADD "unique_temp___inboxInstances" character varying array NOT NULL DEFAULT '{}'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__federation" ADD "___inboxInstances" smallint NOT NULL DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__federation" ADD "unique_temp___deliveredInstances" character varying array NOT NULL DEFAULT '{}'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__federation" ADD "___deliveredInstances" smallint NOT NULL DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__federation" ADD "unique_temp___inboxInstances" character varying array NOT NULL DEFAULT '{}'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__federation" ADD "___inboxInstances" smallint NOT NULL DEFAULT '0'`, + ); + } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "__chart_day__federation" DROP COLUMN "___inboxInstances"`); - await queryRunner.query(`ALTER TABLE "__chart_day__federation" DROP COLUMN "unique_temp___inboxInstances"`); - await queryRunner.query(`ALTER TABLE "__chart_day__federation" DROP COLUMN "___deliveredInstances"`); - await queryRunner.query(`ALTER TABLE "__chart_day__federation" DROP COLUMN "unique_temp___deliveredInstances"`); - await queryRunner.query(`ALTER TABLE "__chart__federation" DROP COLUMN "___inboxInstances"`); - await queryRunner.query(`ALTER TABLE "__chart__federation" DROP COLUMN "unique_temp___inboxInstances"`); - await queryRunner.query(`ALTER TABLE "__chart__federation" DROP COLUMN "___deliveredInstances"`); - await queryRunner.query(`ALTER TABLE "__chart__federation" DROP COLUMN "unique_temp___deliveredInstances"`); - await queryRunner.query(`DROP INDEX "public"."IDX_a848f66d6cec11980a5dd59582"`); - await queryRunner.query(`DROP TABLE "__chart_day__ap_request"`); - await queryRunner.query(`DROP INDEX "public"."IDX_e56f4beac5746d44bc3e19c80d"`); - await queryRunner.query(`DROP TABLE "__chart__ap_request"`); - } + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "__chart_day__federation" DROP COLUMN "___inboxInstances"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__federation" DROP COLUMN "unique_temp___inboxInstances"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__federation" DROP COLUMN "___deliveredInstances"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__federation" DROP COLUMN "unique_temp___deliveredInstances"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__federation" DROP COLUMN "___inboxInstances"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__federation" DROP COLUMN "unique_temp___inboxInstances"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__federation" DROP COLUMN "___deliveredInstances"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__federation" DROP COLUMN "unique_temp___deliveredInstances"`, + ); + await queryRunner.query( + `DROP INDEX "public"."IDX_a848f66d6cec11980a5dd59582"`, + ); + await queryRunner.query(`DROP TABLE "__chart_day__ap_request"`); + await queryRunner.query( + `DROP INDEX "public"."IDX_e56f4beac5746d44bc3e19c80d"`, + ); + await queryRunner.query(`DROP TABLE "__chart__ap_request"`); + } } diff --git a/packages/backend/migration/1644095659741-chart-v11.js b/packages/backend/migration/1644095659741-chart-v11.js index 309fff1d9a..8db122d4f1 100644 --- a/packages/backend/migration/1644095659741-chart-v11.js +++ b/packages/backend/migration/1644095659741-chart-v11.js @@ -1,91 +1,249 @@ - - export class chartV111644095659741 { - name = 'chartV111644095659741' + name = "chartV111644095659741"; - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "___local_users"`); - await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "___remote_users"`); - await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "unique_temp___local_users"`); - await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "unique_temp___remote_users"`); - await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "___local_users"`); - await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "___remote_users"`); - await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "unique_temp___local_users"`); - await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "unique_temp___remote_users"`); - await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "unique_temp___users" character varying array NOT NULL DEFAULT '{}'`); - await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "___users" integer NOT NULL DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "unique_temp___notedUsers" character varying array NOT NULL DEFAULT '{}'`); - await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "___notedUsers" smallint NOT NULL DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "unique_temp___registeredWithinWeek" character varying array NOT NULL DEFAULT '{}'`); - await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "___registeredWithinWeek" smallint NOT NULL DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "unique_temp___registeredWithinMonth" character varying array NOT NULL DEFAULT '{}'`); - await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "___registeredWithinMonth" smallint NOT NULL DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "unique_temp___registeredWithinYear" character varying array NOT NULL DEFAULT '{}'`); - await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "___registeredWithinYear" smallint NOT NULL DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "unique_temp___registeredOutsideWeek" character varying array NOT NULL DEFAULT '{}'`); - await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "___registeredOutsideWeek" smallint NOT NULL DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "unique_temp___registeredOutsideMonth" character varying array NOT NULL DEFAULT '{}'`); - await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "___registeredOutsideMonth" smallint NOT NULL DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "unique_temp___registeredOutsideYear" character varying array NOT NULL DEFAULT '{}'`); - await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "___registeredOutsideYear" smallint NOT NULL DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "unique_temp___users" character varying array NOT NULL DEFAULT '{}'`); - await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "___users" integer NOT NULL DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "unique_temp___notedUsers" character varying array NOT NULL DEFAULT '{}'`); - await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "___notedUsers" smallint NOT NULL DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "unique_temp___registeredWithinWeek" character varying array NOT NULL DEFAULT '{}'`); - await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "___registeredWithinWeek" smallint NOT NULL DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "unique_temp___registeredWithinMonth" character varying array NOT NULL DEFAULT '{}'`); - await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "___registeredWithinMonth" smallint NOT NULL DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "unique_temp___registeredWithinYear" character varying array NOT NULL DEFAULT '{}'`); - await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "___registeredWithinYear" smallint NOT NULL DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "unique_temp___registeredOutsideWeek" character varying array NOT NULL DEFAULT '{}'`); - await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "___registeredOutsideWeek" smallint NOT NULL DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "unique_temp___registeredOutsideMonth" character varying array NOT NULL DEFAULT '{}'`); - await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "___registeredOutsideMonth" smallint NOT NULL DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "unique_temp___registeredOutsideYear" character varying array NOT NULL DEFAULT '{}'`); - await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "___registeredOutsideYear" smallint NOT NULL DEFAULT '0'`); - } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "__chart__active_users" DROP COLUMN "___local_users"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__active_users" DROP COLUMN "___remote_users"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__active_users" DROP COLUMN "unique_temp___local_users"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__active_users" DROP COLUMN "unique_temp___remote_users"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__active_users" DROP COLUMN "___local_users"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__active_users" DROP COLUMN "___remote_users"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__active_users" DROP COLUMN "unique_temp___local_users"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__active_users" DROP COLUMN "unique_temp___remote_users"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__active_users" ADD "unique_temp___users" character varying array NOT NULL DEFAULT '{}'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__active_users" ADD "___users" integer NOT NULL DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__active_users" ADD "unique_temp___notedUsers" character varying array NOT NULL DEFAULT '{}'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__active_users" ADD "___notedUsers" smallint NOT NULL DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__active_users" ADD "unique_temp___registeredWithinWeek" character varying array NOT NULL DEFAULT '{}'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__active_users" ADD "___registeredWithinWeek" smallint NOT NULL DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__active_users" ADD "unique_temp___registeredWithinMonth" character varying array NOT NULL DEFAULT '{}'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__active_users" ADD "___registeredWithinMonth" smallint NOT NULL DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__active_users" ADD "unique_temp___registeredWithinYear" character varying array NOT NULL DEFAULT '{}'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__active_users" ADD "___registeredWithinYear" smallint NOT NULL DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__active_users" ADD "unique_temp___registeredOutsideWeek" character varying array NOT NULL DEFAULT '{}'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__active_users" ADD "___registeredOutsideWeek" smallint NOT NULL DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__active_users" ADD "unique_temp___registeredOutsideMonth" character varying array NOT NULL DEFAULT '{}'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__active_users" ADD "___registeredOutsideMonth" smallint NOT NULL DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__active_users" ADD "unique_temp___registeredOutsideYear" character varying array NOT NULL DEFAULT '{}'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__active_users" ADD "___registeredOutsideYear" smallint NOT NULL DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__active_users" ADD "unique_temp___users" character varying array NOT NULL DEFAULT '{}'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__active_users" ADD "___users" integer NOT NULL DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__active_users" ADD "unique_temp___notedUsers" character varying array NOT NULL DEFAULT '{}'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__active_users" ADD "___notedUsers" smallint NOT NULL DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__active_users" ADD "unique_temp___registeredWithinWeek" character varying array NOT NULL DEFAULT '{}'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__active_users" ADD "___registeredWithinWeek" smallint NOT NULL DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__active_users" ADD "unique_temp___registeredWithinMonth" character varying array NOT NULL DEFAULT '{}'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__active_users" ADD "___registeredWithinMonth" smallint NOT NULL DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__active_users" ADD "unique_temp___registeredWithinYear" character varying array NOT NULL DEFAULT '{}'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__active_users" ADD "___registeredWithinYear" smallint NOT NULL DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__active_users" ADD "unique_temp___registeredOutsideWeek" character varying array NOT NULL DEFAULT '{}'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__active_users" ADD "___registeredOutsideWeek" smallint NOT NULL DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__active_users" ADD "unique_temp___registeredOutsideMonth" character varying array NOT NULL DEFAULT '{}'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__active_users" ADD "___registeredOutsideMonth" smallint NOT NULL DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__active_users" ADD "unique_temp___registeredOutsideYear" character varying array NOT NULL DEFAULT '{}'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__active_users" ADD "___registeredOutsideYear" smallint NOT NULL DEFAULT '0'`, + ); + } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "___registeredOutsideYear"`); - await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "unique_temp___registeredOutsideYear"`); - await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "___registeredOutsideMonth"`); - await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "unique_temp___registeredOutsideMonth"`); - await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "___registeredOutsideWeek"`); - await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "unique_temp___registeredOutsideWeek"`); - await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "___registeredWithinYear"`); - await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "unique_temp___registeredWithinYear"`); - await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "___registeredWithinMonth"`); - await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "unique_temp___registeredWithinMonth"`); - await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "___registeredWithinWeek"`); - await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "unique_temp___registeredWithinWeek"`); - await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "___notedUsers"`); - await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "unique_temp___notedUsers"`); - await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "___users"`); - await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "unique_temp___users"`); - await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "___registeredOutsideYear"`); - await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "unique_temp___registeredOutsideYear"`); - await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "___registeredOutsideMonth"`); - await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "unique_temp___registeredOutsideMonth"`); - await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "___registeredOutsideWeek"`); - await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "unique_temp___registeredOutsideWeek"`); - await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "___registeredWithinYear"`); - await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "unique_temp___registeredWithinYear"`); - await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "___registeredWithinMonth"`); - await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "unique_temp___registeredWithinMonth"`); - await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "___registeredWithinWeek"`); - await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "unique_temp___registeredWithinWeek"`); - await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "___notedUsers"`); - await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "unique_temp___notedUsers"`); - await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "___users"`); - await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "unique_temp___users"`); - await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "unique_temp___remote_users" character varying array NOT NULL DEFAULT '{}'`); - await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "unique_temp___local_users" character varying array NOT NULL DEFAULT '{}'`); - await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "___remote_users" integer NOT NULL DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "___local_users" integer NOT NULL DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "unique_temp___remote_users" character varying array NOT NULL DEFAULT '{}'`); - await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "unique_temp___local_users" character varying array NOT NULL DEFAULT '{}'`); - await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "___remote_users" integer NOT NULL DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "___local_users" integer NOT NULL DEFAULT '0'`); - } + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "__chart_day__active_users" DROP COLUMN "___registeredOutsideYear"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__active_users" DROP COLUMN "unique_temp___registeredOutsideYear"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__active_users" DROP COLUMN "___registeredOutsideMonth"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__active_users" DROP COLUMN "unique_temp___registeredOutsideMonth"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__active_users" DROP COLUMN "___registeredOutsideWeek"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__active_users" DROP COLUMN "unique_temp___registeredOutsideWeek"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__active_users" DROP COLUMN "___registeredWithinYear"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__active_users" DROP COLUMN "unique_temp___registeredWithinYear"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__active_users" DROP COLUMN "___registeredWithinMonth"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__active_users" DROP COLUMN "unique_temp___registeredWithinMonth"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__active_users" DROP COLUMN "___registeredWithinWeek"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__active_users" DROP COLUMN "unique_temp___registeredWithinWeek"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__active_users" DROP COLUMN "___notedUsers"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__active_users" DROP COLUMN "unique_temp___notedUsers"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__active_users" DROP COLUMN "___users"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__active_users" DROP COLUMN "unique_temp___users"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__active_users" DROP COLUMN "___registeredOutsideYear"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__active_users" DROP COLUMN "unique_temp___registeredOutsideYear"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__active_users" DROP COLUMN "___registeredOutsideMonth"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__active_users" DROP COLUMN "unique_temp___registeredOutsideMonth"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__active_users" DROP COLUMN "___registeredOutsideWeek"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__active_users" DROP COLUMN "unique_temp___registeredOutsideWeek"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__active_users" DROP COLUMN "___registeredWithinYear"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__active_users" DROP COLUMN "unique_temp___registeredWithinYear"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__active_users" DROP COLUMN "___registeredWithinMonth"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__active_users" DROP COLUMN "unique_temp___registeredWithinMonth"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__active_users" DROP COLUMN "___registeredWithinWeek"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__active_users" DROP COLUMN "unique_temp___registeredWithinWeek"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__active_users" DROP COLUMN "___notedUsers"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__active_users" DROP COLUMN "unique_temp___notedUsers"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__active_users" DROP COLUMN "___users"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__active_users" DROP COLUMN "unique_temp___users"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__active_users" ADD "unique_temp___remote_users" character varying array NOT NULL DEFAULT '{}'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__active_users" ADD "unique_temp___local_users" character varying array NOT NULL DEFAULT '{}'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__active_users" ADD "___remote_users" integer NOT NULL DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__active_users" ADD "___local_users" integer NOT NULL DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__active_users" ADD "unique_temp___remote_users" character varying array NOT NULL DEFAULT '{}'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__active_users" ADD "unique_temp___local_users" character varying array NOT NULL DEFAULT '{}'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__active_users" ADD "___remote_users" integer NOT NULL DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__active_users" ADD "___local_users" integer NOT NULL DEFAULT '0'`, + ); + } } diff --git a/packages/backend/migration/1644328606241-chart-v12.js b/packages/backend/migration/1644328606241-chart-v12.js index c3c7e44f95..ea363e12fd 100644 --- a/packages/backend/migration/1644328606241-chart-v12.js +++ b/packages/backend/migration/1644328606241-chart-v12.js @@ -1,27 +1,57 @@ - - export class chartV121644328606241 { - name = 'chartV121644328606241' + name = "chartV121644328606241"; - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "__chart__notes" ADD "___local_diffs_withFile" integer NOT NULL DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__notes" ADD "___remote_diffs_withFile" integer NOT NULL DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__notes" ADD "___local_diffs_withFile" integer NOT NULL DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__notes" ADD "___remote_diffs_withFile" integer NOT NULL DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__instance" ADD "___notes_diffs_withFile" integer NOT NULL DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" ADD "___notes_diffs_withFile" integer NOT NULL DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_notes" ADD "___diffs_withFile" smallint NOT NULL DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_notes" ADD "___diffs_withFile" smallint NOT NULL DEFAULT '0'`); - } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "__chart__notes" ADD "___local_diffs_withFile" integer NOT NULL DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__notes" ADD "___remote_diffs_withFile" integer NOT NULL DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__notes" ADD "___local_diffs_withFile" integer NOT NULL DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__notes" ADD "___remote_diffs_withFile" integer NOT NULL DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" ADD "___notes_diffs_withFile" integer NOT NULL DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" ADD "___notes_diffs_withFile" integer NOT NULL DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_notes" ADD "___diffs_withFile" smallint NOT NULL DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_notes" ADD "___diffs_withFile" smallint NOT NULL DEFAULT '0'`, + ); + } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "__chart_day__per_user_notes" DROP COLUMN "___diffs_withFile"`); - await queryRunner.query(`ALTER TABLE "__chart__per_user_notes" DROP COLUMN "___diffs_withFile"`); - await queryRunner.query(`ALTER TABLE "__chart_day__instance" DROP COLUMN "___notes_diffs_withFile"`); - await queryRunner.query(`ALTER TABLE "__chart__instance" DROP COLUMN "___notes_diffs_withFile"`); - await queryRunner.query(`ALTER TABLE "__chart_day__notes" DROP COLUMN "___remote_diffs_withFile"`); - await queryRunner.query(`ALTER TABLE "__chart_day__notes" DROP COLUMN "___local_diffs_withFile"`); - await queryRunner.query(`ALTER TABLE "__chart__notes" DROP COLUMN "___remote_diffs_withFile"`); - await queryRunner.query(`ALTER TABLE "__chart__notes" DROP COLUMN "___local_diffs_withFile"`); - } + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "__chart_day__per_user_notes" DROP COLUMN "___diffs_withFile"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__per_user_notes" DROP COLUMN "___diffs_withFile"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__instance" DROP COLUMN "___notes_diffs_withFile"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__instance" DROP COLUMN "___notes_diffs_withFile"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__notes" DROP COLUMN "___remote_diffs_withFile"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__notes" DROP COLUMN "___local_diffs_withFile"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__notes" DROP COLUMN "___remote_diffs_withFile"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__notes" DROP COLUMN "___local_diffs_withFile"`, + ); + } } diff --git a/packages/backend/migration/1644331238153-chart-v13.js b/packages/backend/migration/1644331238153-chart-v13.js index 639f7b4e20..cbb1830f50 100644 --- a/packages/backend/migration/1644331238153-chart-v13.js +++ b/packages/backend/migration/1644331238153-chart-v13.js @@ -1,19 +1,33 @@ - - export class chartV131644331238153 { - name = 'chartV131644331238153' + name = "chartV131644331238153"; - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "__chart__federation" ADD "unique_temp___stalled" character varying array NOT NULL DEFAULT '{}'`); - await queryRunner.query(`ALTER TABLE "__chart__federation" ADD "___stalled" smallint NOT NULL DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__federation" ADD "unique_temp___stalled" character varying array NOT NULL DEFAULT '{}'`); - await queryRunner.query(`ALTER TABLE "__chart_day__federation" ADD "___stalled" smallint NOT NULL DEFAULT '0'`); - } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "__chart__federation" ADD "unique_temp___stalled" character varying array NOT NULL DEFAULT '{}'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__federation" ADD "___stalled" smallint NOT NULL DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__federation" ADD "unique_temp___stalled" character varying array NOT NULL DEFAULT '{}'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__federation" ADD "___stalled" smallint NOT NULL DEFAULT '0'`, + ); + } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "__chart_day__federation" DROP COLUMN "___stalled"`); - await queryRunner.query(`ALTER TABLE "__chart_day__federation" DROP COLUMN "unique_temp___stalled"`); - await queryRunner.query(`ALTER TABLE "__chart__federation" DROP COLUMN "___stalled"`); - await queryRunner.query(`ALTER TABLE "__chart__federation" DROP COLUMN "unique_temp___stalled"`); - } + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "__chart_day__federation" DROP COLUMN "___stalled"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__federation" DROP COLUMN "unique_temp___stalled"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__federation" DROP COLUMN "___stalled"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__federation" DROP COLUMN "unique_temp___stalled"`, + ); + } } diff --git a/packages/backend/migration/1644344266289-chart-v14.js b/packages/backend/migration/1644344266289-chart-v14.js index a0d9cfc38c..6e6c030833 100644 --- a/packages/backend/migration/1644344266289-chart-v14.js +++ b/packages/backend/migration/1644344266289-chart-v14.js @@ -1,47 +1,117 @@ - - export class chartV141644344266289 { - name = 'chartV141644344266289' + name = "chartV141644344266289"; - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "unique_temp___users"`); - await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "___users"`); - await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "unique_temp___notedUsers"`); - await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "___notedUsers"`); - await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "unique_temp___users"`); - await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "___users"`); - await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "unique_temp___notedUsers"`); - await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "___notedUsers"`); - await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "___readWrite" smallint NOT NULL DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "unique_temp___read" character varying array NOT NULL DEFAULT '{}'`); - await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "___read" smallint NOT NULL DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "unique_temp___write" character varying array NOT NULL DEFAULT '{}'`); - await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "___write" smallint NOT NULL DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "___readWrite" smallint NOT NULL DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "unique_temp___read" character varying array NOT NULL DEFAULT '{}'`); - await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "___read" smallint NOT NULL DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "unique_temp___write" character varying array NOT NULL DEFAULT '{}'`); - await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "___write" smallint NOT NULL DEFAULT '0'`); - } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "__chart__active_users" DROP COLUMN "unique_temp___users"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__active_users" DROP COLUMN "___users"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__active_users" DROP COLUMN "unique_temp___notedUsers"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__active_users" DROP COLUMN "___notedUsers"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__active_users" DROP COLUMN "unique_temp___users"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__active_users" DROP COLUMN "___users"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__active_users" DROP COLUMN "unique_temp___notedUsers"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__active_users" DROP COLUMN "___notedUsers"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__active_users" ADD "___readWrite" smallint NOT NULL DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__active_users" ADD "unique_temp___read" character varying array NOT NULL DEFAULT '{}'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__active_users" ADD "___read" smallint NOT NULL DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__active_users" ADD "unique_temp___write" character varying array NOT NULL DEFAULT '{}'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__active_users" ADD "___write" smallint NOT NULL DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__active_users" ADD "___readWrite" smallint NOT NULL DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__active_users" ADD "unique_temp___read" character varying array NOT NULL DEFAULT '{}'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__active_users" ADD "___read" smallint NOT NULL DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__active_users" ADD "unique_temp___write" character varying array NOT NULL DEFAULT '{}'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__active_users" ADD "___write" smallint NOT NULL DEFAULT '0'`, + ); + } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "___write"`); - await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "unique_temp___write"`); - await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "___read"`); - await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "unique_temp___read"`); - await queryRunner.query(`ALTER TABLE "__chart_day__active_users" DROP COLUMN "___readWrite"`); - await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "___write"`); - await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "unique_temp___write"`); - await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "___read"`); - await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "unique_temp___read"`); - await queryRunner.query(`ALTER TABLE "__chart__active_users" DROP COLUMN "___readWrite"`); - await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "___notedUsers" smallint NOT NULL DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "unique_temp___notedUsers" character varying array NOT NULL DEFAULT '{}'`); - await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "___users" integer NOT NULL DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__active_users" ADD "unique_temp___users" character varying array NOT NULL DEFAULT '{}'`); - await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "___notedUsers" smallint NOT NULL DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "unique_temp___notedUsers" character varying array NOT NULL DEFAULT '{}'`); - await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "___users" integer NOT NULL DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__active_users" ADD "unique_temp___users" character varying array NOT NULL DEFAULT '{}'`); - } + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "__chart_day__active_users" DROP COLUMN "___write"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__active_users" DROP COLUMN "unique_temp___write"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__active_users" DROP COLUMN "___read"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__active_users" DROP COLUMN "unique_temp___read"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__active_users" DROP COLUMN "___readWrite"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__active_users" DROP COLUMN "___write"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__active_users" DROP COLUMN "unique_temp___write"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__active_users" DROP COLUMN "___read"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__active_users" DROP COLUMN "unique_temp___read"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__active_users" DROP COLUMN "___readWrite"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__active_users" ADD "___notedUsers" smallint NOT NULL DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__active_users" ADD "unique_temp___notedUsers" character varying array NOT NULL DEFAULT '{}'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__active_users" ADD "___users" integer NOT NULL DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__active_users" ADD "unique_temp___users" character varying array NOT NULL DEFAULT '{}'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__active_users" ADD "___notedUsers" smallint NOT NULL DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__active_users" ADD "unique_temp___notedUsers" character varying array NOT NULL DEFAULT '{}'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__active_users" ADD "___users" integer NOT NULL DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__active_users" ADD "unique_temp___users" character varying array NOT NULL DEFAULT '{}'`, + ); + } } diff --git a/packages/backend/migration/1644395759931-instance-theme-color.js b/packages/backend/migration/1644395759931-instance-theme-color.js index 8f335ad210..1e0ee04bc2 100644 --- a/packages/backend/migration/1644395759931-instance-theme-color.js +++ b/packages/backend/migration/1644395759931-instance-theme-color.js @@ -1,13 +1,13 @@ - - export class instanceThemeColor1644395759931 { - name = 'instanceThemeColor1644395759931' + name = "instanceThemeColor1644395759931"; - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "meta" ADD "themeColor" character varying(512)`); - } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "meta" ADD "themeColor" character varying(512)`, + ); + } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "themeColor"`); - } + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "themeColor"`); + } } diff --git a/packages/backend/migration/1644481657998-chart-v15.js b/packages/backend/migration/1644481657998-chart-v15.js index b50ca87c40..a8e1b89cb1 100644 --- a/packages/backend/migration/1644481657998-chart-v15.js +++ b/packages/backend/migration/1644481657998-chart-v15.js @@ -1,31 +1,69 @@ - - export class chartV151644481657998 { - name = 'chartV151644481657998' + name = "chartV151644481657998"; - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "__chart__federation" DROP COLUMN "___instance_total"`); - await queryRunner.query(`ALTER TABLE "__chart__federation" DROP COLUMN "___instance_inc"`); - await queryRunner.query(`ALTER TABLE "__chart__federation" DROP COLUMN "___instance_dec"`); - await queryRunner.query(`ALTER TABLE "__chart_day__federation" DROP COLUMN "___instance_total"`); - await queryRunner.query(`ALTER TABLE "__chart_day__federation" DROP COLUMN "___instance_inc"`); - await queryRunner.query(`ALTER TABLE "__chart_day__federation" DROP COLUMN "___instance_dec"`); - await queryRunner.query(`ALTER TABLE "__chart__federation" ADD "___sub" smallint NOT NULL DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__federation" ADD "___pub" smallint NOT NULL DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__federation" ADD "___sub" smallint NOT NULL DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__federation" ADD "___pub" smallint NOT NULL DEFAULT '0'`); - } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "__chart__federation" DROP COLUMN "___instance_total"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__federation" DROP COLUMN "___instance_inc"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__federation" DROP COLUMN "___instance_dec"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__federation" DROP COLUMN "___instance_total"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__federation" DROP COLUMN "___instance_inc"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__federation" DROP COLUMN "___instance_dec"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__federation" ADD "___sub" smallint NOT NULL DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__federation" ADD "___pub" smallint NOT NULL DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__federation" ADD "___sub" smallint NOT NULL DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__federation" ADD "___pub" smallint NOT NULL DEFAULT '0'`, + ); + } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "__chart_day__federation" DROP COLUMN "___pub"`); - await queryRunner.query(`ALTER TABLE "__chart_day__federation" DROP COLUMN "___sub"`); - await queryRunner.query(`ALTER TABLE "__chart__federation" DROP COLUMN "___pub"`); - await queryRunner.query(`ALTER TABLE "__chart__federation" DROP COLUMN "___sub"`); - await queryRunner.query(`ALTER TABLE "__chart_day__federation" ADD "___instance_dec" smallint NOT NULL DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__federation" ADD "___instance_inc" smallint NOT NULL DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__federation" ADD "___instance_total" integer NOT NULL DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__federation" ADD "___instance_dec" smallint NOT NULL DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__federation" ADD "___instance_inc" smallint NOT NULL DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__federation" ADD "___instance_total" integer NOT NULL DEFAULT '0'`); - } + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "__chart_day__federation" DROP COLUMN "___pub"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__federation" DROP COLUMN "___sub"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__federation" DROP COLUMN "___pub"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__federation" DROP COLUMN "___sub"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__federation" ADD "___instance_dec" smallint NOT NULL DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__federation" ADD "___instance_inc" smallint NOT NULL DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__federation" ADD "___instance_total" integer NOT NULL DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__federation" ADD "___instance_dec" smallint NOT NULL DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__federation" ADD "___instance_inc" smallint NOT NULL DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__federation" ADD "___instance_total" integer NOT NULL DEFAULT '0'`, + ); + } } diff --git a/packages/backend/migration/1644551208096-following-indexes.js b/packages/backend/migration/1644551208096-following-indexes.js index 276473ff6c..b06f764e58 100644 --- a/packages/backend/migration/1644551208096-following-indexes.js +++ b/packages/backend/migration/1644551208096-following-indexes.js @@ -1,15 +1,21 @@ - - export class followingIndexes1644551208096 { - name = 'followingIndexes1644551208096' + name = "followingIndexes1644551208096"; - async up(queryRunner) { - await queryRunner.query(`CREATE INDEX "IDX_4ccd2239268ebbd1b35e318754" ON "following" ("followerHost") `); - await queryRunner.query(`CREATE INDEX "IDX_fcdafee716dfe9c3b5fde90f30" ON "following" ("followeeHost") `); - } + async up(queryRunner) { + await queryRunner.query( + `CREATE INDEX "IDX_4ccd2239268ebbd1b35e318754" ON "following" ("followerHost") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_fcdafee716dfe9c3b5fde90f30" ON "following" ("followeeHost") `, + ); + } - async down(queryRunner) { - await queryRunner.query(`DROP INDEX "public"."IDX_fcdafee716dfe9c3b5fde90f30"`); - await queryRunner.query(`DROP INDEX "public"."IDX_4ccd2239268ebbd1b35e318754"`); - } + async down(queryRunner) { + await queryRunner.query( + `DROP INDEX "public"."IDX_fcdafee716dfe9c3b5fde90f30"`, + ); + await queryRunner.query( + `DROP INDEX "public"."IDX_4ccd2239268ebbd1b35e318754"`, + ); + } } diff --git a/packages/backend/migration/1645340161439-remove-max-note-text-length.js b/packages/backend/migration/1645340161439-remove-max-note-text-length.js index c88cb70bfb..d17ef8c124 100644 --- a/packages/backend/migration/1645340161439-remove-max-note-text-length.js +++ b/packages/backend/migration/1645340161439-remove-max-note-text-length.js @@ -1,13 +1,15 @@ - - export class removeMaxNoteTextLength1645340161439 { - name = 'removeMaxNoteTextLength1645340161439' + name = "removeMaxNoteTextLength1645340161439"; - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "maxNoteTextLength"`); - } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "meta" DROP COLUMN "maxNoteTextLength"`, + ); + } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "meta" ADD "maxNoteTextLength" integer NOT NULL DEFAULT '500'`); - } + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "meta" ADD "maxNoteTextLength" integer NOT NULL DEFAULT '500'`, + ); + } } diff --git a/packages/backend/migration/1645599900873-federation-chart-pubsub.js b/packages/backend/migration/1645599900873-federation-chart-pubsub.js index fd7cb6d5a1..58e2b4c00d 100644 --- a/packages/backend/migration/1645599900873-federation-chart-pubsub.js +++ b/packages/backend/migration/1645599900873-federation-chart-pubsub.js @@ -1,15 +1,21 @@ - - export class federationChartPubsub1645599900873 { - name = 'federationChartPubsub1645599900873' + name = "federationChartPubsub1645599900873"; - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "__chart__federation" ADD "___pubsub" smallint NOT NULL DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__federation" ADD "___pubsub" smallint NOT NULL DEFAULT '0'`); - } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "__chart__federation" ADD "___pubsub" smallint NOT NULL DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__federation" ADD "___pubsub" smallint NOT NULL DEFAULT '0'`, + ); + } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "__chart_day__federation" DROP COLUMN "___pubsub"`); - await queryRunner.query(`ALTER TABLE "__chart__federation" DROP COLUMN "___pubsub"`); - } + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "__chart_day__federation" DROP COLUMN "___pubsub"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__federation" DROP COLUMN "___pubsub"`, + ); + } } diff --git a/packages/backend/migration/1646143552768-instance-default-theme.js b/packages/backend/migration/1646143552768-instance-default-theme.js index 029354fd92..d97efcaa6e 100644 --- a/packages/backend/migration/1646143552768-instance-default-theme.js +++ b/packages/backend/migration/1646143552768-instance-default-theme.js @@ -1,13 +1,21 @@ export class instanceDefaultTheme1646143552768 { - name = 'instanceDefaultTheme1646143552768' + name = "instanceDefaultTheme1646143552768"; - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "meta" ADD "defaultLightTheme" character varying(8192)`); - await queryRunner.query(`ALTER TABLE "meta" ADD "defaultDarkTheme" character varying(8192)`); - } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "meta" ADD "defaultLightTheme" character varying(8192)`, + ); + await queryRunner.query( + `ALTER TABLE "meta" ADD "defaultDarkTheme" character varying(8192)`, + ); + } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "defaultDarkTheme"`); - await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "defaultLightTheme"`); - } + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "meta" DROP COLUMN "defaultDarkTheme"`, + ); + await queryRunner.query( + `ALTER TABLE "meta" DROP COLUMN "defaultLightTheme"`, + ); + } } diff --git a/packages/backend/migration/1646387162108-mute-expires-at.js b/packages/backend/migration/1646387162108-mute-expires-at.js index c8be8f3c54..52fe101bd1 100644 --- a/packages/backend/migration/1646387162108-mute-expires-at.js +++ b/packages/backend/migration/1646387162108-mute-expires-at.js @@ -1,13 +1,19 @@ export class muteExpiresAt1646387162108 { - name = 'muteExpiresAt1646387162108' + name = "muteExpiresAt1646387162108"; - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "muting" ADD "expiresAt" TIMESTAMP WITH TIME ZONE`); - await queryRunner.query(`CREATE INDEX "IDX_c1fd1c3dfb0627aa36c253fd14" ON "muting" ("expiresAt") `); - } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "muting" ADD "expiresAt" TIMESTAMP WITH TIME ZONE`, + ); + await queryRunner.query( + `CREATE INDEX "IDX_c1fd1c3dfb0627aa36c253fd14" ON "muting" ("expiresAt") `, + ); + } - async down(queryRunner) { - await queryRunner.query(`DROP INDEX "public"."IDX_c1fd1c3dfb0627aa36c253fd14"`); - await queryRunner.query(`ALTER TABLE "muting" DROP COLUMN "expiresAt"`); - } + async down(queryRunner) { + await queryRunner.query( + `DROP INDEX "public"."IDX_c1fd1c3dfb0627aa36c253fd14"`, + ); + await queryRunner.query(`ALTER TABLE "muting" DROP COLUMN "expiresAt"`); + } } diff --git a/packages/backend/migration/1646549089451-poll-ended-notification.js b/packages/backend/migration/1646549089451-poll-ended-notification.js index 38a38ce64d..cc96a62a7e 100644 --- a/packages/backend/migration/1646549089451-poll-ended-notification.js +++ b/packages/backend/migration/1646549089451-poll-ended-notification.js @@ -1,18 +1,29 @@ - export class pollEndedNotification1646549089451 { - name = 'pollEndedNotification1646549089451' + name = "pollEndedNotification1646549089451"; - async up(queryRunner) { - await queryRunner.query(`ALTER TYPE "public"."notification_type_enum" RENAME TO "notification_type_enum_old"`); - await queryRunner.query(`CREATE TYPE "public"."notification_type_enum" AS ENUM('follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'pollEnded', 'receiveFollowRequest', 'followRequestAccepted', 'groupInvited', 'app')`); - await queryRunner.query(`ALTER TABLE "notification" ALTER COLUMN "type" TYPE "public"."notification_type_enum" USING "type"::"text"::"public"."notification_type_enum"`); - await queryRunner.query(`DROP TYPE "public"."notification_type_enum_old"`); - } + async up(queryRunner) { + await queryRunner.query( + `ALTER TYPE "public"."notification_type_enum" RENAME TO "notification_type_enum_old"`, + ); + await queryRunner.query( + `CREATE TYPE "public"."notification_type_enum" AS ENUM('follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'pollEnded', 'receiveFollowRequest', 'followRequestAccepted', 'groupInvited', 'app')`, + ); + await queryRunner.query( + `ALTER TABLE "notification" ALTER COLUMN "type" TYPE "public"."notification_type_enum" USING "type"::"text"::"public"."notification_type_enum"`, + ); + await queryRunner.query(`DROP TYPE "public"."notification_type_enum_old"`); + } - async down(queryRunner) { - await queryRunner.query(`CREATE TYPE "public"."notification_type_enum_old" AS ENUM('follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'receiveFollowRequest', 'followRequestAccepted', 'groupInvited', 'app')`); - await queryRunner.query(`ALTER TABLE "notification" ALTER COLUMN "type" TYPE "public"."notification_type_enum_old" USING "type"::"text"::"public"."notification_type_enum_old"`); - await queryRunner.query(`DROP TYPE "public"."notification_type_enum"`); - await queryRunner.query(`ALTER TYPE "public"."notification_type_enum_old" RENAME TO "notification_type_enum"`); - } + async down(queryRunner) { + await queryRunner.query( + `CREATE TYPE "public"."notification_type_enum_old" AS ENUM('follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'receiveFollowRequest', 'followRequestAccepted', 'groupInvited', 'app')`, + ); + await queryRunner.query( + `ALTER TABLE "notification" ALTER COLUMN "type" TYPE "public"."notification_type_enum_old" USING "type"::"text"::"public"."notification_type_enum_old"`, + ); + await queryRunner.query(`DROP TYPE "public"."notification_type_enum"`); + await queryRunner.query( + `ALTER TYPE "public"."notification_type_enum_old" RENAME TO "notification_type_enum"`, + ); + } } diff --git a/packages/backend/migration/1646633030285-chart-federation-active.js b/packages/backend/migration/1646633030285-chart-federation-active.js index 952289c8f8..3cb2dffe3f 100644 --- a/packages/backend/migration/1646633030285-chart-federation-active.js +++ b/packages/backend/migration/1646633030285-chart-federation-active.js @@ -1,13 +1,21 @@ export class chartFederationActive1646633030285 { - name = 'chartFederationActive1646633030285' + name = "chartFederationActive1646633030285"; - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "__chart__federation" ADD "___active" smallint NOT NULL DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__federation" ADD "___active" smallint NOT NULL DEFAULT '0'`); - } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "__chart__federation" ADD "___active" smallint NOT NULL DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__federation" ADD "___active" smallint NOT NULL DEFAULT '0'`, + ); + } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "__chart_day__federation" DROP COLUMN "___active"`); - await queryRunner.query(`ALTER TABLE "__chart__federation" DROP COLUMN "___active"`); - } + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "__chart_day__federation" DROP COLUMN "___active"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__federation" DROP COLUMN "___active"`, + ); + } } diff --git a/packages/backend/migration/1646655454495-remove-instance-drive-columns.js b/packages/backend/migration/1646655454495-remove-instance-drive-columns.js index a0ee1b2c43..12f56bc2d7 100644 --- a/packages/backend/migration/1646655454495-remove-instance-drive-columns.js +++ b/packages/backend/migration/1646655454495-remove-instance-drive-columns.js @@ -1,13 +1,17 @@ export class removeInstanceDriveColumns1646655454495 { - name = 'removeInstanceDriveColumns1646655454495' + name = "removeInstanceDriveColumns1646655454495"; - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "instance" DROP COLUMN "driveUsage"`); - await queryRunner.query(`ALTER TABLE "instance" DROP COLUMN "driveFiles"`); - } + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "instance" DROP COLUMN "driveUsage"`); + await queryRunner.query(`ALTER TABLE "instance" DROP COLUMN "driveFiles"`); + } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "instance" ADD "driveFiles" integer NOT NULL DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "instance" ADD "driveUsage" bigint NOT NULL DEFAULT '0'`); - } + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "instance" ADD "driveFiles" integer NOT NULL DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "instance" ADD "driveUsage" bigint NOT NULL DEFAULT '0'`, + ); + } } diff --git a/packages/backend/migration/1646732390560-chart-federation-active-sub-pub.js b/packages/backend/migration/1646732390560-chart-federation-active-sub-pub.js index c9a847cbcf..e34b9a22e9 100644 --- a/packages/backend/migration/1646732390560-chart-federation-active-sub-pub.js +++ b/packages/backend/migration/1646732390560-chart-federation-active-sub-pub.js @@ -1,21 +1,45 @@ export class chartFederationActiveSubPub1646732390560 { - name = 'chartFederationActiveSubPub1646732390560' + name = "chartFederationActiveSubPub1646732390560"; - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "__chart__federation" DROP COLUMN "___active"`); - await queryRunner.query(`ALTER TABLE "__chart_day__federation" DROP COLUMN "___active"`); - await queryRunner.query(`ALTER TABLE "__chart__federation" ADD "___subActive" smallint NOT NULL DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__federation" ADD "___pubActive" smallint NOT NULL DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__federation" ADD "___subActive" smallint NOT NULL DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart_day__federation" ADD "___pubActive" smallint NOT NULL DEFAULT '0'`); - } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "__chart__federation" DROP COLUMN "___active"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__federation" DROP COLUMN "___active"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__federation" ADD "___subActive" smallint NOT NULL DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__federation" ADD "___pubActive" smallint NOT NULL DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__federation" ADD "___subActive" smallint NOT NULL DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__federation" ADD "___pubActive" smallint NOT NULL DEFAULT '0'`, + ); + } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "__chart_day__federation" DROP COLUMN "___pubActive"`); - await queryRunner.query(`ALTER TABLE "__chart_day__federation" DROP COLUMN "___subActive"`); - await queryRunner.query(`ALTER TABLE "__chart__federation" DROP COLUMN "___pubActive"`); - await queryRunner.query(`ALTER TABLE "__chart__federation" DROP COLUMN "___subActive"`); - await queryRunner.query(`ALTER TABLE "__chart_day__federation" ADD "___active" smallint NOT NULL DEFAULT '0'`); - await queryRunner.query(`ALTER TABLE "__chart__federation" ADD "___active" smallint NOT NULL DEFAULT '0'`); - } + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "__chart_day__federation" DROP COLUMN "___pubActive"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__federation" DROP COLUMN "___subActive"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__federation" DROP COLUMN "___pubActive"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__federation" DROP COLUMN "___subActive"`, + ); + await queryRunner.query( + `ALTER TABLE "__chart_day__federation" ADD "___active" smallint NOT NULL DEFAULT '0'`, + ); + await queryRunner.query( + `ALTER TABLE "__chart__federation" ADD "___active" smallint NOT NULL DEFAULT '0'`, + ); + } } diff --git a/packages/backend/migration/1648548247382-webhook.js b/packages/backend/migration/1648548247382-webhook.js index aea369a5cc..3e7160e2b2 100644 --- a/packages/backend/migration/1648548247382-webhook.js +++ b/packages/backend/migration/1648548247382-webhook.js @@ -1,19 +1,37 @@ export class webhook1648548247382 { - name = 'webhook1648548247382' + name = "webhook1648548247382"; - async up(queryRunner) { - await queryRunner.query(`CREATE TABLE "webhook" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "userId" character varying(32) NOT NULL, "name" character varying(128) NOT NULL, "on" character varying(128) array NOT NULL DEFAULT '{}', "url" character varying(1024) NOT NULL, "secret" character varying(1024) NOT NULL, "active" boolean NOT NULL DEFAULT true, CONSTRAINT "PK_e6765510c2d078db49632b59020" PRIMARY KEY ("id")); COMMENT ON COLUMN "webhook"."createdAt" IS 'The created date of the Antenna.'; COMMENT ON COLUMN "webhook"."userId" IS 'The owner ID.'; COMMENT ON COLUMN "webhook"."name" IS 'The name of the Antenna.'`); - await queryRunner.query(`CREATE INDEX "IDX_f272c8c8805969e6a6449c77b3" ON "webhook" ("userId") `); - await queryRunner.query(`CREATE INDEX "IDX_8063a0586ed1dfbe86e982d961" ON "webhook" ("on") `); - await queryRunner.query(`CREATE INDEX "IDX_5a056076f76b2efe08216ba655" ON "webhook" ("active") `); - await queryRunner.query(`ALTER TABLE "webhook" ADD CONSTRAINT "FK_f272c8c8805969e6a6449c77b3c" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); + async up(queryRunner) { + await queryRunner.query( + `CREATE TABLE "webhook" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "userId" character varying(32) NOT NULL, "name" character varying(128) NOT NULL, "on" character varying(128) array NOT NULL DEFAULT '{}', "url" character varying(1024) NOT NULL, "secret" character varying(1024) NOT NULL, "active" boolean NOT NULL DEFAULT true, CONSTRAINT "PK_e6765510c2d078db49632b59020" PRIMARY KEY ("id")); COMMENT ON COLUMN "webhook"."createdAt" IS 'The created date of the Antenna.'; COMMENT ON COLUMN "webhook"."userId" IS 'The owner ID.'; COMMENT ON COLUMN "webhook"."name" IS 'The name of the Antenna.'`, + ); + await queryRunner.query( + `CREATE INDEX "IDX_f272c8c8805969e6a6449c77b3" ON "webhook" ("userId") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_8063a0586ed1dfbe86e982d961" ON "webhook" ("on") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_5a056076f76b2efe08216ba655" ON "webhook" ("active") `, + ); + await queryRunner.query( + `ALTER TABLE "webhook" ADD CONSTRAINT "FK_f272c8c8805969e6a6449c77b3c" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); } async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "webhook" DROP CONSTRAINT "FK_f272c8c8805969e6a6449c77b3c"`); - await queryRunner.query(`DROP INDEX "public"."IDX_5a056076f76b2efe08216ba655"`); - await queryRunner.query(`DROP INDEX "public"."IDX_8063a0586ed1dfbe86e982d961"`); - await queryRunner.query(`DROP INDEX "public"."IDX_f272c8c8805969e6a6449c77b3"`); - await queryRunner.query(`DROP TABLE "webhook"`); + await queryRunner.query( + `ALTER TABLE "webhook" DROP CONSTRAINT "FK_f272c8c8805969e6a6449c77b3c"`, + ); + await queryRunner.query( + `DROP INDEX "public"."IDX_5a056076f76b2efe08216ba655"`, + ); + await queryRunner.query( + `DROP INDEX "public"."IDX_8063a0586ed1dfbe86e982d961"`, + ); + await queryRunner.query( + `DROP INDEX "public"."IDX_f272c8c8805969e6a6449c77b3"`, + ); + await queryRunner.query(`DROP TABLE "webhook"`); } } diff --git a/packages/backend/migration/1648816172177-webhook-2.js b/packages/backend/migration/1648816172177-webhook-2.js index 2feb68d611..a4ab4ef8f8 100644 --- a/packages/backend/migration/1648816172177-webhook-2.js +++ b/packages/backend/migration/1648816172177-webhook-2.js @@ -1,14 +1,15 @@ - export class webhook21648816172177 { - name = 'webhook21648816172177' + name = "webhook21648816172177"; - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "webhook" ADD "latestSentAt" TIMESTAMP WITH TIME ZONE`); - await queryRunner.query(`ALTER TABLE "webhook" ADD "latestStatus" integer`); - } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "webhook" ADD "latestSentAt" TIMESTAMP WITH TIME ZONE`, + ); + await queryRunner.query(`ALTER TABLE "webhook" ADD "latestStatus" integer`); + } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "webhook" DROP COLUMN "latestStatus"`); - await queryRunner.query(`ALTER TABLE "webhook" DROP COLUMN "latestSentAt"`); - } + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "webhook" DROP COLUMN "latestStatus"`); + await queryRunner.query(`ALTER TABLE "webhook" DROP COLUMN "latestSentAt"`); + } } diff --git a/packages/backend/migration/1651224615271-foreign-key.js b/packages/backend/migration/1651224615271-foreign-key.js index 535d21731a..7e6bc79623 100644 --- a/packages/backend/migration/1651224615271-foreign-key.js +++ b/packages/backend/migration/1651224615271-foreign-key.js @@ -1,89 +1,187 @@ export class foreignKeyReports1651224615271 { - name = 'foreignKeyReports1651224615271' + name = "foreignKeyReports1651224615271"; - async up(queryRunner) { - await Promise.all([ - queryRunner.query(`ALTER INDEX "public"."IDX_seoignmeoprigmkpodgrjmkpormg" RENAME TO "IDX_c8cc87bd0f2f4487d17c651fbf"`), - queryRunner.query(`DROP INDEX "public"."IDX_note_on_channelId_and_id_desc"`), + async up(queryRunner) { + await Promise.all([ + queryRunner.query( + `ALTER INDEX "public"."IDX_seoignmeoprigmkpodgrjmkpormg" RENAME TO "IDX_c8cc87bd0f2f4487d17c651fbf"`, + ), + queryRunner.query( + `DROP INDEX "public"."IDX_note_on_channelId_and_id_desc"`, + ), - // remove unnecessary default null, see also down - queryRunner.query(`ALTER TABLE "user" ALTER COLUMN "followersUri" DROP DEFAULT`), - queryRunner.query(`ALTER TABLE "access_token" ALTER COLUMN "session" DROP DEFAULT`), - queryRunner.query(`ALTER TABLE "access_token" ALTER COLUMN "appId" DROP DEFAULT`), - queryRunner.query(`ALTER TABLE "access_token" ALTER COLUMN "name" DROP DEFAULT`), - queryRunner.query(`ALTER TABLE "access_token" ALTER COLUMN "description" DROP DEFAULT`), - queryRunner.query(`ALTER TABLE "access_token" ALTER COLUMN "iconUrl" DROP DEFAULT`), - queryRunner.query(`ALTER TABLE "instance" ALTER COLUMN "softwareName" DROP DEFAULT`), - queryRunner.query(`ALTER TABLE "instance" ALTER COLUMN "softwareVersion" DROP DEFAULT`), - queryRunner.query(`ALTER TABLE "instance" ALTER COLUMN "name" DROP DEFAULT`), - queryRunner.query(`ALTER TABLE "instance" ALTER COLUMN "description" DROP DEFAULT`), - queryRunner.query(`ALTER TABLE "instance" ALTER COLUMN "maintainerName" DROP DEFAULT`), - queryRunner.query(`ALTER TABLE "instance" ALTER COLUMN "maintainerEmail" DROP DEFAULT`), - queryRunner.query(`ALTER TABLE "instance" ALTER COLUMN "iconUrl" DROP DEFAULT`), - queryRunner.query(`ALTER TABLE "instance" ALTER COLUMN "faviconUrl" DROP DEFAULT`), - queryRunner.query(`ALTER TABLE "instance" ALTER COLUMN "themeColor" DROP DEFAULT`), - queryRunner.query(`ALTER TABLE "clip" ALTER COLUMN "description" DROP DEFAULT`), - queryRunner.query(`ALTER TABLE "note" ALTER COLUMN "channelId" DROP DEFAULT`), - queryRunner.query(`ALTER TABLE "abuse_user_report" ALTER COLUMN "comment" DROP DEFAULT`), + // remove unnecessary default null, see also down + queryRunner.query( + `ALTER TABLE "user" ALTER COLUMN "followersUri" DROP DEFAULT`, + ), + queryRunner.query( + `ALTER TABLE "access_token" ALTER COLUMN "session" DROP DEFAULT`, + ), + queryRunner.query( + `ALTER TABLE "access_token" ALTER COLUMN "appId" DROP DEFAULT`, + ), + queryRunner.query( + `ALTER TABLE "access_token" ALTER COLUMN "name" DROP DEFAULT`, + ), + queryRunner.query( + `ALTER TABLE "access_token" ALTER COLUMN "description" DROP DEFAULT`, + ), + queryRunner.query( + `ALTER TABLE "access_token" ALTER COLUMN "iconUrl" DROP DEFAULT`, + ), + queryRunner.query( + `ALTER TABLE "instance" ALTER COLUMN "softwareName" DROP DEFAULT`, + ), + queryRunner.query( + `ALTER TABLE "instance" ALTER COLUMN "softwareVersion" DROP DEFAULT`, + ), + queryRunner.query( + `ALTER TABLE "instance" ALTER COLUMN "name" DROP DEFAULT`, + ), + queryRunner.query( + `ALTER TABLE "instance" ALTER COLUMN "description" DROP DEFAULT`, + ), + queryRunner.query( + `ALTER TABLE "instance" ALTER COLUMN "maintainerName" DROP DEFAULT`, + ), + queryRunner.query( + `ALTER TABLE "instance" ALTER COLUMN "maintainerEmail" DROP DEFAULT`, + ), + queryRunner.query( + `ALTER TABLE "instance" ALTER COLUMN "iconUrl" DROP DEFAULT`, + ), + queryRunner.query( + `ALTER TABLE "instance" ALTER COLUMN "faviconUrl" DROP DEFAULT`, + ), + queryRunner.query( + `ALTER TABLE "instance" ALTER COLUMN "themeColor" DROP DEFAULT`, + ), + queryRunner.query( + `ALTER TABLE "clip" ALTER COLUMN "description" DROP DEFAULT`, + ), + queryRunner.query( + `ALTER TABLE "note" ALTER COLUMN "channelId" DROP DEFAULT`, + ), + queryRunner.query( + `ALTER TABLE "abuse_user_report" ALTER COLUMN "comment" DROP DEFAULT`, + ), - queryRunner.query(`CREATE INDEX "IDX_315c779174fe8247ab324f036e" ON "drive_file" ("isLink")`), - queryRunner.query(`CREATE INDEX "IDX_f22169eb10657bded6d875ac8f" ON "note" ("channelId")`), - //queryRunner.query(`CREATE INDEX "IDX_a9021cc2e1feb5f72d3db6e9f5" ON "abuse_user_report" ("targetUserId")`), + queryRunner.query( + `CREATE INDEX "IDX_315c779174fe8247ab324f036e" ON "drive_file" ("isLink")`, + ), + queryRunner.query( + `CREATE INDEX "IDX_f22169eb10657bded6d875ac8f" ON "note" ("channelId")`, + ), + //queryRunner.query(`CREATE INDEX "IDX_a9021cc2e1feb5f72d3db6e9f5" ON "abuse_user_report" ("targetUserId")`), - //queryRunner.query(`DELETE FROM "abuse_user_report" WHERE "targetUserId" NOT IN (SELECT "id" FROM "user")`).then(() => { - // queryRunner.query(`ALTER TABLE "abuse_user_report" ADD CONSTRAINT "FK_a9021cc2e1feb5f72d3db6e9f5f" FOREIGN KEY ("targetUserId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); - //}), + //queryRunner.query(`DELETE FROM "abuse_user_report" WHERE "targetUserId" NOT IN (SELECT "id" FROM "user")`).then(() => { + // queryRunner.query(`ALTER TABLE "abuse_user_report" ADD CONSTRAINT "FK_a9021cc2e1feb5f72d3db6e9f5f" FOREIGN KEY ("targetUserId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); + //}), - queryRunner.query(`ALTER TABLE "poll" ADD CONSTRAINT "UQ_da851e06d0dfe2ef397d8b1bf1b" UNIQUE ("noteId")`), - queryRunner.query(`ALTER TABLE "user_keypair" ADD CONSTRAINT "UQ_f4853eb41ab722fe05f81cedeb6" UNIQUE ("userId")`), - queryRunner.query(`ALTER TABLE "user_profile" ADD CONSTRAINT "UQ_51cb79b5555effaf7d69ba1cff9" UNIQUE ("userId")`), - queryRunner.query(`ALTER TABLE "user_publickey" ADD CONSTRAINT "UQ_10c146e4b39b443ede016f6736d" UNIQUE ("userId")`), - queryRunner.query(`ALTER TABLE "promo_note" ADD CONSTRAINT "UQ_e263909ca4fe5d57f8d4230dd5c" UNIQUE ("noteId")`), + queryRunner.query( + `ALTER TABLE "poll" ADD CONSTRAINT "UQ_da851e06d0dfe2ef397d8b1bf1b" UNIQUE ("noteId")`, + ), + queryRunner.query( + `ALTER TABLE "user_keypair" ADD CONSTRAINT "UQ_f4853eb41ab722fe05f81cedeb6" UNIQUE ("userId")`, + ), + queryRunner.query( + `ALTER TABLE "user_profile" ADD CONSTRAINT "UQ_51cb79b5555effaf7d69ba1cff9" UNIQUE ("userId")`, + ), + queryRunner.query( + `ALTER TABLE "user_publickey" ADD CONSTRAINT "UQ_10c146e4b39b443ede016f6736d" UNIQUE ("userId")`, + ), + queryRunner.query( + `ALTER TABLE "promo_note" ADD CONSTRAINT "UQ_e263909ca4fe5d57f8d4230dd5c" UNIQUE ("noteId")`, + ), - queryRunner.query(`ALTER TABLE "page" RENAME CONSTRAINT "FK_3126dd7c502c9e4d7597ef7ef10" TO "FK_a9ca79ad939bf06066b81c9d3aa"`), + queryRunner.query( + `ALTER TABLE "page" RENAME CONSTRAINT "FK_3126dd7c502c9e4d7597ef7ef10" TO "FK_a9ca79ad939bf06066b81c9d3aa"`, + ), - queryRunner.query(`ALTER TYPE "public"."user_profile_mutingnotificationtypes_enum" ADD VALUE 'pollEnded' AFTER 'pollVote'`), - ]); - } + queryRunner.query( + `ALTER TYPE "public"."user_profile_mutingnotificationtypes_enum" ADD VALUE 'pollEnded' AFTER 'pollVote'`, + ), + ]); + } - async down(queryRunner) { - await Promise.all([ - // There is no ALTER TYPE REMOVE VALUE query, so the reverse operation is a bit more complex - queryRunner.query(`UPDATE "user_profile" SET "mutingNotificationTypes" = array_remove("mutingNotificationTypes", 'pollEnded')`) - .then(() => - queryRunner.query(`CREATE TYPE "public"."user_profile_mutingnotificationtypes_enum_old" AS ENUM('follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'receiveFollowRequest', 'followRequestAccepted', 'groupInvited', 'app')`) - ).then(() => - queryRunner.query(`ALTER TABLE "user_profile" ALTER COLUMN "mutingNotificationTypes" DROP DEFAULT`) - ).then(() => - queryRunner.query(`ALTER TABLE "user_profile" ALTER COLUMN "mutingNotificationTypes" TYPE "public"."user_profile_mutingnotificationtypes_enum_old"[] USING "mutingNotificationTypes"::"text"::"public"."user_profile_mutingnotificationtypes_enum_old"[]`) - ).then(() => - queryRunner.query(`ALTER TABLE "user_profile" ALTER COLUMN "mutingNotificationTypes" SET DEFAULT '{}'`) - ).then(() => - queryRunner.query(`DROP TYPE "public"."user_profile_mutingnotificationtypes_enum"`) - ).then(() => - queryRunner.query(`ALTER TYPE "public"."user_profile_mutingnotificationtypes_enum_old" RENAME TO "user_profile_mutingnotificationtypes_enum"`) - ), + async down(queryRunner) { + await Promise.all([ + // There is no ALTER TYPE REMOVE VALUE query, so the reverse operation is a bit more complex + queryRunner + .query( + `UPDATE "user_profile" SET "mutingNotificationTypes" = array_remove("mutingNotificationTypes", 'pollEnded')`, + ) + .then(() => + queryRunner.query( + `CREATE TYPE "public"."user_profile_mutingnotificationtypes_enum_old" AS ENUM('follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'receiveFollowRequest', 'followRequestAccepted', 'groupInvited', 'app')`, + ), + ) + .then(() => + queryRunner.query( + `ALTER TABLE "user_profile" ALTER COLUMN "mutingNotificationTypes" DROP DEFAULT`, + ), + ) + .then(() => + queryRunner.query( + `ALTER TABLE "user_profile" ALTER COLUMN "mutingNotificationTypes" TYPE "public"."user_profile_mutingnotificationtypes_enum_old"[] USING "mutingNotificationTypes"::"text"::"public"."user_profile_mutingnotificationtypes_enum_old"[]`, + ), + ) + .then(() => + queryRunner.query( + `ALTER TABLE "user_profile" ALTER COLUMN "mutingNotificationTypes" SET DEFAULT '{}'`, + ), + ) + .then(() => + queryRunner.query( + `DROP TYPE "public"."user_profile_mutingnotificationtypes_enum"`, + ), + ) + .then(() => + queryRunner.query( + `ALTER TYPE "public"."user_profile_mutingnotificationtypes_enum_old" RENAME TO "user_profile_mutingnotificationtypes_enum"`, + ), + ), - queryRunner.query(`ALTER TABLE "page" RENAME CONSTRAINT "FK_a9ca79ad939bf06066b81c9d3aa" TO "FK_3126dd7c502c9e4d7597ef7ef10"`), + queryRunner.query( + `ALTER TABLE "page" RENAME CONSTRAINT "FK_a9ca79ad939bf06066b81c9d3aa" TO "FK_3126dd7c502c9e4d7597ef7ef10"`, + ), - queryRunner.query(`ALTER TABLE "promo_note" DROP CONSTRAINT "UQ_e263909ca4fe5d57f8d4230dd5c"`), - queryRunner.query(`ALTER TABLE "user_publickey" DROP CONSTRAINT "UQ_10c146e4b39b443ede016f6736d"`), - queryRunner.query(`ALTER TABLE "user_profile" DROP CONSTRAINT "UQ_51cb79b5555effaf7d69ba1cff9"`), - queryRunner.query(`ALTER TABLE "user_keypair" DROP CONSTRAINT "UQ_f4853eb41ab722fe05f81cedeb6"`), - queryRunner.query(`ALTER TABLE "poll" DROP CONSTRAINT "UQ_da851e06d0dfe2ef397d8b1bf1b"`), + queryRunner.query( + `ALTER TABLE "promo_note" DROP CONSTRAINT "UQ_e263909ca4fe5d57f8d4230dd5c"`, + ), + queryRunner.query( + `ALTER TABLE "user_publickey" DROP CONSTRAINT "UQ_10c146e4b39b443ede016f6736d"`, + ), + queryRunner.query( + `ALTER TABLE "user_profile" DROP CONSTRAINT "UQ_51cb79b5555effaf7d69ba1cff9"`, + ), + queryRunner.query( + `ALTER TABLE "user_keypair" DROP CONSTRAINT "UQ_f4853eb41ab722fe05f81cedeb6"`, + ), + queryRunner.query( + `ALTER TABLE "poll" DROP CONSTRAINT "UQ_da851e06d0dfe2ef397d8b1bf1b"`, + ), - queryRunner.query(`ALTER TABLE "abuse_user_report" ALTER COLUMN "comment" SET DEFAULT '{}'`), - queryRunner.query(`ALTER TABLE "abuse_user_report" DROP CONSTRAINT "FK_a9021cc2e1feb5f72d3db6e9f5f"`), + queryRunner.query( + `ALTER TABLE "abuse_user_report" ALTER COLUMN "comment" SET DEFAULT '{}'`, + ), + queryRunner.query( + `ALTER TABLE "abuse_user_report" DROP CONSTRAINT "FK_a9021cc2e1feb5f72d3db6e9f5f"`, + ), - queryRunner.query(`DROP INDEX "public"."IDX_a9021cc2e1feb5f72d3db6e9f5"`), - queryRunner.query(`DROP INDEX "public"."IDX_f22169eb10657bded6d875ac8f"`), - queryRunner.query(`DROP INDEX "public"."IDX_315c779174fe8247ab324f036e"`), + queryRunner.query(`DROP INDEX "public"."IDX_a9021cc2e1feb5f72d3db6e9f5"`), + queryRunner.query(`DROP INDEX "public"."IDX_f22169eb10657bded6d875ac8f"`), + queryRunner.query(`DROP INDEX "public"."IDX_315c779174fe8247ab324f036e"`), - /* DEFAULT's are not set again because if the column can be NULL, then DEFAULT NULL is not necessary. + /* DEFAULT's are not set again because if the column can be NULL, then DEFAULT NULL is not necessary. see also https://github.com/typeorm/typeorm/issues/7579#issuecomment-835423615 */ - queryRunner.query(`CREATE INDEX "IDX_note_on_channelId_and_id_desc" ON "note" ("id", "channelId") `), - queryRunner.query(`ALTER INDEX "public"."IDX_c8cc87bd0f2f4487d17c651fbf" RENAME TO "IDX_seoignmeoprigmkpodgrjmkpormg"`), - ]); - } + queryRunner.query( + `CREATE INDEX "IDX_note_on_channelId_and_id_desc" ON "note" ("id", "channelId") `, + ), + queryRunner.query( + `ALTER INDEX "public"."IDX_c8cc87bd0f2f4487d17c651fbf" RENAME TO "IDX_seoignmeoprigmkpodgrjmkpormg"`, + ), + ]); + } } diff --git a/packages/backend/migration/1652859567549-uniform-themecolor.js b/packages/backend/migration/1652859567549-uniform-themecolor.js index 8da1fd7fbb..8c91854d53 100644 --- a/packages/backend/migration/1652859567549-uniform-themecolor.js +++ b/packages/backend/migration/1652859567549-uniform-themecolor.js @@ -1,7 +1,7 @@ -import tinycolor from 'tinycolor2'; +import tinycolor from "tinycolor2"; export class uniformThemecolor1652859567549 { - name = 'uniformThemecolor1652859567549' + name = "uniformThemecolor1652859567549"; async up(queryRunner) { const formatColor = (color) => { @@ -13,20 +13,35 @@ export class uniformThemecolor1652859567549 { } }; - await queryRunner.query('SELECT "id", "themeColor" FROM "instance" WHERE "themeColor" IS NOT NULL') - .then(instances => Promise.all(instances.map(instance => { - // update theme color to uniform format, e.g. #00ff00 - // invalid theme colors get set to null - return queryRunner.query('UPDATE "instance" SET "themeColor" = $1 WHERE "id" = $2', [formatColor(instance.themeColor), instance.id]); - }))); + await queryRunner + .query( + 'SELECT "id", "themeColor" FROM "instance" WHERE "themeColor" IS NOT NULL', + ) + .then((instances) => + Promise.all( + instances.map((instance) => { + // update theme color to uniform format, e.g. #00ff00 + // invalid theme colors get set to null + return queryRunner.query( + 'UPDATE "instance" SET "themeColor" = $1 WHERE "id" = $2', + [formatColor(instance.themeColor), instance.id], + ); + }), + ), + ); // also fix own theme color - await queryRunner.query('SELECT "themeColor" FROM "meta" WHERE "themeColor" IS NOT NULL LIMIT 1') - .then(metas => { - if (metas.length > 0) { - return queryRunner.query('UPDATE "meta" SET "themeColor" = $1', [formatColor(metas[0].themeColor)]); - } - }); + await queryRunner + .query( + 'SELECT "themeColor" FROM "meta" WHERE "themeColor" IS NOT NULL LIMIT 1', + ) + .then((metas) => { + if (metas.length > 0) { + return queryRunner.query('UPDATE "meta" SET "themeColor" = $1', [ + formatColor(metas[0].themeColor), + ]); + } + }); } async down(queryRunner) { diff --git a/packages/backend/migration/1655368940105-nsfw-detection.js b/packages/backend/migration/1655368940105-nsfw-detection.js index 9268f43407..f1bcda50e5 100644 --- a/packages/backend/migration/1655368940105-nsfw-detection.js +++ b/packages/backend/migration/1655368940105-nsfw-detection.js @@ -1,23 +1,51 @@ export class nsfwDetection1655368940105 { - name = 'nsfwDetection1655368940105' + name = "nsfwDetection1655368940105"; - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "drive_file" ADD "forceIsSensitive" boolean NOT NULL DEFAULT false`); - await queryRunner.query(`ALTER TABLE "drive_file" ADD "predictedIsSensitive" boolean NOT NULL DEFAULT false`); - await queryRunner.query(`COMMENT ON COLUMN "drive_file"."predictedIsSensitive" IS 'Whether the DriveFile is NSFW. (predict)'`); - await queryRunner.query(`CREATE TYPE "public"."meta_sensitiveimagedetection_enum" AS ENUM('none', 'all', 'local', 'remote')`); - await queryRunner.query(`ALTER TABLE "meta" ADD "sensitiveImageDetection" "public"."meta_sensitiveimagedetection_enum" NOT NULL DEFAULT 'none'`); - await queryRunner.query(`ALTER TABLE "meta" ADD "forceIsSensitiveWhenPredicted" boolean NOT NULL DEFAULT true`); - await queryRunner.query(`CREATE INDEX "IDX_fc2d74a6d7d8b11292a851d8f8" ON "drive_file" ("predictedIsSensitive") `); - } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "drive_file" ADD "forceIsSensitive" boolean NOT NULL DEFAULT false`, + ); + await queryRunner.query( + `ALTER TABLE "drive_file" ADD "predictedIsSensitive" boolean NOT NULL DEFAULT false`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "drive_file"."predictedIsSensitive" IS 'Whether the DriveFile is NSFW. (predict)'`, + ); + await queryRunner.query( + `CREATE TYPE "public"."meta_sensitiveimagedetection_enum" AS ENUM('none', 'all', 'local', 'remote')`, + ); + await queryRunner.query( + `ALTER TABLE "meta" ADD "sensitiveImageDetection" "public"."meta_sensitiveimagedetection_enum" NOT NULL DEFAULT 'none'`, + ); + await queryRunner.query( + `ALTER TABLE "meta" ADD "forceIsSensitiveWhenPredicted" boolean NOT NULL DEFAULT true`, + ); + await queryRunner.query( + `CREATE INDEX "IDX_fc2d74a6d7d8b11292a851d8f8" ON "drive_file" ("predictedIsSensitive") `, + ); + } - async down(queryRunner) { - await queryRunner.query(`DROP INDEX "public"."IDX_fc2d74a6d7d8b11292a851d8f8"`); - await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "forceIsSensitiveWhenPredicted"`); - await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "sensitiveImageDetection"`); - await queryRunner.query(`DROP TYPE "public"."meta_sensitiveimagedetection_enum"`); - await queryRunner.query(`COMMENT ON COLUMN "drive_file"."predictedIsSensitive" IS 'Whether the DriveFile is NSFW. (predict)'`); - await queryRunner.query(`ALTER TABLE "drive_file" DROP COLUMN "predictedIsSensitive"`); - await queryRunner.query(`ALTER TABLE "drive_file" DROP COLUMN "forceIsSensitive"`); - } + async down(queryRunner) { + await queryRunner.query( + `DROP INDEX "public"."IDX_fc2d74a6d7d8b11292a851d8f8"`, + ); + await queryRunner.query( + `ALTER TABLE "meta" DROP COLUMN "forceIsSensitiveWhenPredicted"`, + ); + await queryRunner.query( + `ALTER TABLE "meta" DROP COLUMN "sensitiveImageDetection"`, + ); + await queryRunner.query( + `DROP TYPE "public"."meta_sensitiveimagedetection_enum"`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "drive_file"."predictedIsSensitive" IS 'Whether the DriveFile is NSFW. (predict)'`, + ); + await queryRunner.query( + `ALTER TABLE "drive_file" DROP COLUMN "predictedIsSensitive"`, + ); + await queryRunner.query( + `ALTER TABLE "drive_file" DROP COLUMN "forceIsSensitive"`, + ); + } } diff --git a/packages/backend/migration/1655371960534-nsfw-detection-2.js b/packages/backend/migration/1655371960534-nsfw-detection-2.js index aac6f37dad..236cd70913 100644 --- a/packages/backend/migration/1655371960534-nsfw-detection-2.js +++ b/packages/backend/migration/1655371960534-nsfw-detection-2.js @@ -1,15 +1,27 @@ export class nsfwDetection21655371960534 { - name = 'nsfwDetection21655371960534' + name = "nsfwDetection21655371960534"; - async up(queryRunner) { - await queryRunner.query(`CREATE TYPE "public"."meta_sensitiveimagedetectionsensitivity_enum" AS ENUM('medium', 'low', 'high')`); - await queryRunner.query(`ALTER TABLE "meta" ADD "sensitiveImageDetectionSensitivity" "public"."meta_sensitiveimagedetectionsensitivity_enum" NOT NULL DEFAULT 'medium'`); - await queryRunner.query(`ALTER TABLE "meta" ADD "disallowUploadWhenPredictedAsPorn" boolean NOT NULL DEFAULT false`); - } + async up(queryRunner) { + await queryRunner.query( + `CREATE TYPE "public"."meta_sensitiveimagedetectionsensitivity_enum" AS ENUM('medium', 'low', 'high')`, + ); + await queryRunner.query( + `ALTER TABLE "meta" ADD "sensitiveImageDetectionSensitivity" "public"."meta_sensitiveimagedetectionsensitivity_enum" NOT NULL DEFAULT 'medium'`, + ); + await queryRunner.query( + `ALTER TABLE "meta" ADD "disallowUploadWhenPredictedAsPorn" boolean NOT NULL DEFAULT false`, + ); + } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "disallowUploadWhenPredictedAsPorn"`); - await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "sensitiveImageDetectionSensitivity"`); - await queryRunner.query(`DROP TYPE "public"."meta_sensitiveimagedetectionsensitivity_enum"`); - } + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "meta" DROP COLUMN "disallowUploadWhenPredictedAsPorn"`, + ); + await queryRunner.query( + `ALTER TABLE "meta" DROP COLUMN "sensitiveImageDetectionSensitivity"`, + ); + await queryRunner.query( + `DROP TYPE "public"."meta_sensitiveimagedetectionsensitivity_enum"`, + ); + } } diff --git a/packages/backend/migration/1655388169582-nsfw-detection-3.js b/packages/backend/migration/1655388169582-nsfw-detection-3.js index a5c80cf968..6a6ceeafba 100644 --- a/packages/backend/migration/1655388169582-nsfw-detection-3.js +++ b/packages/backend/migration/1655388169582-nsfw-detection-3.js @@ -1,21 +1,45 @@ export class nsfwDetection31655388169582 { - name = 'nsfwDetection31655388169582' + name = "nsfwDetection31655388169582"; - async up(queryRunner) { - await queryRunner.query(`ALTER TYPE "public"."meta_sensitiveimagedetectionsensitivity_enum" RENAME TO "meta_sensitiveimagedetectionsensitivity_enum_old"`); - await queryRunner.query(`CREATE TYPE "public"."meta_sensitiveimagedetectionsensitivity_enum" AS ENUM('medium', 'low', 'high', 'veryLow', 'veryHigh')`); - await queryRunner.query(`ALTER TABLE "meta" ALTER COLUMN "sensitiveImageDetectionSensitivity" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "meta" ALTER COLUMN "sensitiveImageDetectionSensitivity" TYPE "public"."meta_sensitiveimagedetectionsensitivity_enum" USING "sensitiveImageDetectionSensitivity"::"text"::"public"."meta_sensitiveimagedetectionsensitivity_enum"`); - await queryRunner.query(`ALTER TABLE "meta" ALTER COLUMN "sensitiveImageDetectionSensitivity" SET DEFAULT 'medium'`); - await queryRunner.query(`DROP TYPE "public"."meta_sensitiveimagedetectionsensitivity_enum_old"`); - } + async up(queryRunner) { + await queryRunner.query( + `ALTER TYPE "public"."meta_sensitiveimagedetectionsensitivity_enum" RENAME TO "meta_sensitiveimagedetectionsensitivity_enum_old"`, + ); + await queryRunner.query( + `CREATE TYPE "public"."meta_sensitiveimagedetectionsensitivity_enum" AS ENUM('medium', 'low', 'high', 'veryLow', 'veryHigh')`, + ); + await queryRunner.query( + `ALTER TABLE "meta" ALTER COLUMN "sensitiveImageDetectionSensitivity" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "meta" ALTER COLUMN "sensitiveImageDetectionSensitivity" TYPE "public"."meta_sensitiveimagedetectionsensitivity_enum" USING "sensitiveImageDetectionSensitivity"::"text"::"public"."meta_sensitiveimagedetectionsensitivity_enum"`, + ); + await queryRunner.query( + `ALTER TABLE "meta" ALTER COLUMN "sensitiveImageDetectionSensitivity" SET DEFAULT 'medium'`, + ); + await queryRunner.query( + `DROP TYPE "public"."meta_sensitiveimagedetectionsensitivity_enum_old"`, + ); + } - async down(queryRunner) { - await queryRunner.query(`CREATE TYPE "public"."meta_sensitiveimagedetectionsensitivity_enum_old" AS ENUM('medium', 'low', 'high')`); - await queryRunner.query(`ALTER TABLE "meta" ALTER COLUMN "sensitiveImageDetectionSensitivity" DROP DEFAULT`); - await queryRunner.query(`ALTER TABLE "meta" ALTER COLUMN "sensitiveImageDetectionSensitivity" TYPE "public"."meta_sensitiveimagedetectionsensitivity_enum_old" USING "sensitiveImageDetectionSensitivity"::"text"::"public"."meta_sensitiveimagedetectionsensitivity_enum_old"`); - await queryRunner.query(`ALTER TABLE "meta" ALTER COLUMN "sensitiveImageDetectionSensitivity" SET DEFAULT 'medium'`); - await queryRunner.query(`DROP TYPE "public"."meta_sensitiveimagedetectionsensitivity_enum"`); - await queryRunner.query(`ALTER TYPE "public"."meta_sensitiveimagedetectionsensitivity_enum_old" RENAME TO "meta_sensitiveimagedetectionsensitivity_enum"`); - } + async down(queryRunner) { + await queryRunner.query( + `CREATE TYPE "public"."meta_sensitiveimagedetectionsensitivity_enum_old" AS ENUM('medium', 'low', 'high')`, + ); + await queryRunner.query( + `ALTER TABLE "meta" ALTER COLUMN "sensitiveImageDetectionSensitivity" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "meta" ALTER COLUMN "sensitiveImageDetectionSensitivity" TYPE "public"."meta_sensitiveimagedetectionsensitivity_enum_old" USING "sensitiveImageDetectionSensitivity"::"text"::"public"."meta_sensitiveimagedetectionsensitivity_enum_old"`, + ); + await queryRunner.query( + `ALTER TABLE "meta" ALTER COLUMN "sensitiveImageDetectionSensitivity" SET DEFAULT 'medium'`, + ); + await queryRunner.query( + `DROP TYPE "public"."meta_sensitiveimagedetectionsensitivity_enum"`, + ); + await queryRunner.query( + `ALTER TYPE "public"."meta_sensitiveimagedetectionsensitivity_enum_old" RENAME TO "meta_sensitiveimagedetectionsensitivity_enum"`, + ); + } } diff --git a/packages/backend/migration/1655393015659-nsfw-detection-4.js b/packages/backend/migration/1655393015659-nsfw-detection-4.js index e780732623..3e45897327 100644 --- a/packages/backend/migration/1655393015659-nsfw-detection-4.js +++ b/packages/backend/migration/1655393015659-nsfw-detection-4.js @@ -1,25 +1,57 @@ export class nsfwDetection41655393015659 { - name = 'nsfwDetection41655393015659' + name = "nsfwDetection41655393015659"; - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "sensitiveImageDetection"`); - await queryRunner.query(`DROP TYPE "public"."meta_sensitiveimagedetection_enum"`); - await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "sensitiveImageDetectionSensitivity"`); - await queryRunner.query(`DROP TYPE "public"."meta_sensitiveimagedetectionsensitivity_enum"`); - await queryRunner.query(`CREATE TYPE "public"."meta_sensitivemediadetection_enum" AS ENUM('none', 'all', 'local', 'remote')`); - await queryRunner.query(`ALTER TABLE "meta" ADD "sensitiveMediaDetection" "public"."meta_sensitivemediadetection_enum" NOT NULL DEFAULT 'none'`); - await queryRunner.query(`CREATE TYPE "public"."meta_sensitivemediadetectionsensitivity_enum" AS ENUM('medium', 'low', 'high', 'veryLow', 'veryHigh')`); - await queryRunner.query(`ALTER TABLE "meta" ADD "sensitiveMediaDetectionSensitivity" "public"."meta_sensitivemediadetectionsensitivity_enum" NOT NULL DEFAULT 'medium'`); - } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "meta" DROP COLUMN "sensitiveImageDetection"`, + ); + await queryRunner.query( + `DROP TYPE "public"."meta_sensitiveimagedetection_enum"`, + ); + await queryRunner.query( + `ALTER TABLE "meta" DROP COLUMN "sensitiveImageDetectionSensitivity"`, + ); + await queryRunner.query( + `DROP TYPE "public"."meta_sensitiveimagedetectionsensitivity_enum"`, + ); + await queryRunner.query( + `CREATE TYPE "public"."meta_sensitivemediadetection_enum" AS ENUM('none', 'all', 'local', 'remote')`, + ); + await queryRunner.query( + `ALTER TABLE "meta" ADD "sensitiveMediaDetection" "public"."meta_sensitivemediadetection_enum" NOT NULL DEFAULT 'none'`, + ); + await queryRunner.query( + `CREATE TYPE "public"."meta_sensitivemediadetectionsensitivity_enum" AS ENUM('medium', 'low', 'high', 'veryLow', 'veryHigh')`, + ); + await queryRunner.query( + `ALTER TABLE "meta" ADD "sensitiveMediaDetectionSensitivity" "public"."meta_sensitivemediadetectionsensitivity_enum" NOT NULL DEFAULT 'medium'`, + ); + } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "sensitiveMediaDetectionSensitivity"`); - await queryRunner.query(`DROP TYPE "public"."meta_sensitivemediadetectionsensitivity_enum"`); - await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "sensitiveMediaDetection"`); - await queryRunner.query(`DROP TYPE "public"."meta_sensitivemediadetection_enum"`); - await queryRunner.query(`CREATE TYPE "public"."meta_sensitiveimagedetectionsensitivity_enum" AS ENUM('medium', 'low', 'high', 'veryLow', 'veryHigh')`); - await queryRunner.query(`ALTER TABLE "meta" ADD "sensitiveImageDetectionSensitivity" "public"."meta_sensitiveimagedetectionsensitivity_enum" NOT NULL DEFAULT 'medium'`); - await queryRunner.query(`CREATE TYPE "public"."meta_sensitiveimagedetection_enum" AS ENUM('none', 'all', 'local', 'remote')`); - await queryRunner.query(`ALTER TABLE "meta" ADD "sensitiveImageDetection" "public"."meta_sensitiveimagedetection_enum" NOT NULL DEFAULT 'none'`); - } + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "meta" DROP COLUMN "sensitiveMediaDetectionSensitivity"`, + ); + await queryRunner.query( + `DROP TYPE "public"."meta_sensitivemediadetectionsensitivity_enum"`, + ); + await queryRunner.query( + `ALTER TABLE "meta" DROP COLUMN "sensitiveMediaDetection"`, + ); + await queryRunner.query( + `DROP TYPE "public"."meta_sensitivemediadetection_enum"`, + ); + await queryRunner.query( + `CREATE TYPE "public"."meta_sensitiveimagedetectionsensitivity_enum" AS ENUM('medium', 'low', 'high', 'veryLow', 'veryHigh')`, + ); + await queryRunner.query( + `ALTER TABLE "meta" ADD "sensitiveImageDetectionSensitivity" "public"."meta_sensitiveimagedetectionsensitivity_enum" NOT NULL DEFAULT 'medium'`, + ); + await queryRunner.query( + `CREATE TYPE "public"."meta_sensitiveimagedetection_enum" AS ENUM('none', 'all', 'local', 'remote')`, + ); + await queryRunner.query( + `ALTER TABLE "meta" ADD "sensitiveImageDetection" "public"."meta_sensitiveimagedetection_enum" NOT NULL DEFAULT 'none'`, + ); + } } diff --git a/packages/backend/migration/1655813815729-driveCapacityOverrideMb.js b/packages/backend/migration/1655813815729-driveCapacityOverrideMb.js index f257cd112f..e43f0af5c1 100644 --- a/packages/backend/migration/1655813815729-driveCapacityOverrideMb.js +++ b/packages/backend/migration/1655813815729-driveCapacityOverrideMb.js @@ -1,13 +1,21 @@ export class driveCapacityOverrideMb1655813815729 { - name = 'driveCapacityOverrideMb1655813815729' + name = "driveCapacityOverrideMb1655813815729"; - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "user" ADD "driveCapacityOverrideMb" integer`); - await queryRunner.query(`COMMENT ON COLUMN "user"."driveCapacityOverrideMb" IS 'Overrides user drive capacity limit'`); - } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "user" ADD "driveCapacityOverrideMb" integer`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "user"."driveCapacityOverrideMb" IS 'Overrides user drive capacity limit'`, + ); + } - async down(queryRunner) { - await queryRunner.query(`COMMENT ON COLUMN "user"."driveCapacityOverrideMb" IS 'Overrides user drive capacity limit'`); - await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "driveCapacityOverrideMb"`); - } + async down(queryRunner) { + await queryRunner.query( + `COMMENT ON COLUMN "user"."driveCapacityOverrideMb" IS 'Overrides user drive capacity limit'`, + ); + await queryRunner.query( + `ALTER TABLE "user" DROP COLUMN "driveCapacityOverrideMb"`, + ); + } } diff --git a/packages/backend/migration/1655918165614-user-ip.js b/packages/backend/migration/1655918165614-user-ip.js index 2294fbaf19..2c5dc1c821 100644 --- a/packages/backend/migration/1655918165614-user-ip.js +++ b/packages/backend/migration/1655918165614-user-ip.js @@ -1,17 +1,31 @@ export class userIp1655918165614 { - name = 'userIp1655918165614' + name = "userIp1655918165614"; - async up(queryRunner) { - await queryRunner.query(`CREATE TABLE "user_ip" ("id" SERIAL NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "userId" character varying(32) NOT NULL, "ip" character varying(128) NOT NULL, CONSTRAINT "PK_2c44ddfbf7c0464d028dcef325e" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE INDEX "IDX_7f7f1c66f48e9a8e18a33bc515" ON "user_ip" ("userId") `); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_361b500e06721013c124b7b6c5" ON "user_ip" ("userId", "ip") `); - await queryRunner.query(`ALTER TABLE "user_ip" ADD CONSTRAINT "FK_7f7f1c66f48e9a8e18a33bc5150" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`); - } + async up(queryRunner) { + await queryRunner.query( + `CREATE TABLE "user_ip" ("id" SERIAL NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "userId" character varying(32) NOT NULL, "ip" character varying(128) NOT NULL, CONSTRAINT "PK_2c44ddfbf7c0464d028dcef325e" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE INDEX "IDX_7f7f1c66f48e9a8e18a33bc515" ON "user_ip" ("userId") `, + ); + await queryRunner.query( + `CREATE UNIQUE INDEX "IDX_361b500e06721013c124b7b6c5" ON "user_ip" ("userId", "ip") `, + ); + await queryRunner.query( + `ALTER TABLE "user_ip" ADD CONSTRAINT "FK_7f7f1c66f48e9a8e18a33bc5150" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, + ); + } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "user_ip" DROP CONSTRAINT "FK_7f7f1c66f48e9a8e18a33bc5150"`); - await queryRunner.query(`DROP INDEX "public"."IDX_361b500e06721013c124b7b6c5"`); - await queryRunner.query(`DROP INDEX "public"."IDX_7f7f1c66f48e9a8e18a33bc515"`); - await queryRunner.query(`DROP TABLE "user_ip"`); - } + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "user_ip" DROP CONSTRAINT "FK_7f7f1c66f48e9a8e18a33bc5150"`, + ); + await queryRunner.query( + `DROP INDEX "public"."IDX_361b500e06721013c124b7b6c5"`, + ); + await queryRunner.query( + `DROP INDEX "public"."IDX_7f7f1c66f48e9a8e18a33bc515"`, + ); + await queryRunner.query(`DROP TABLE "user_ip"`); + } } diff --git a/packages/backend/migration/1656122560740-file-ip.js b/packages/backend/migration/1656122560740-file-ip.js index b59e7a911f..534097adb3 100644 --- a/packages/backend/migration/1656122560740-file-ip.js +++ b/packages/backend/migration/1656122560740-file-ip.js @@ -1,13 +1,19 @@ export class fileIp1656122560740 { - name = 'fileIp1656122560740' + name = "fileIp1656122560740"; - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "drive_file" ADD "requestHeaders" jsonb DEFAULT '{}'`); - await queryRunner.query(`ALTER TABLE "drive_file" ADD "requestIp" character varying(128)`); - } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "drive_file" ADD "requestHeaders" jsonb DEFAULT '{}'`, + ); + await queryRunner.query( + `ALTER TABLE "drive_file" ADD "requestIp" character varying(128)`, + ); + } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "drive_file" DROP COLUMN "requestIp"`); - await queryRunner.query(`ALTER TABLE "drive_file" DROP COLUMN "requestHeaders"`); - } + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "drive_file" DROP COLUMN "requestIp"`); + await queryRunner.query( + `ALTER TABLE "drive_file" DROP COLUMN "requestHeaders"`, + ); + } } diff --git a/packages/backend/migration/1656251734807-nsfw-detection-5.js b/packages/backend/migration/1656251734807-nsfw-detection-5.js index 6f0c536907..7a2206e018 100644 --- a/packages/backend/migration/1656251734807-nsfw-detection-5.js +++ b/packages/backend/migration/1656251734807-nsfw-detection-5.js @@ -1,33 +1,79 @@ export class nsfwDetection51656251734807 { - name = 'nsfwDetection51656251734807' + name = "nsfwDetection51656251734807"; - async up(queryRunner) { - await queryRunner.query(`DROP INDEX "public"."IDX_fc2d74a6d7d8b11292a851d8f8"`); - await queryRunner.query(`ALTER TABLE "drive_file" DROP COLUMN "forceIsSensitive"`); - await queryRunner.query(`ALTER TABLE "drive_file" DROP COLUMN "predictedIsSensitive"`); - await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "forceIsSensitiveWhenPredicted"`); - await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "disallowUploadWhenPredictedAsPorn"`); - await queryRunner.query(`ALTER TABLE "drive_file" ADD "maybeSensitive" boolean NOT NULL DEFAULT false`); - await queryRunner.query(`COMMENT ON COLUMN "drive_file"."maybeSensitive" IS 'Whether the DriveFile is NSFW. (predict)'`); - await queryRunner.query(`ALTER TABLE "drive_file" ADD "maybePorn" boolean NOT NULL DEFAULT false`); - await queryRunner.query(`ALTER TABLE "meta" ADD "setSensitiveFlagAutomatically" boolean NOT NULL DEFAULT false`); - await queryRunner.query(`ALTER TABLE "user_profile" ADD "autoSensitive" boolean NOT NULL DEFAULT false`); - await queryRunner.query(`CREATE INDEX "IDX_3b33dff77bb64b23c88151d23e" ON "drive_file" ("maybeSensitive") `); - await queryRunner.query(`CREATE INDEX "IDX_8bdcd3dd2bddb78014999a16ce" ON "drive_file" ("maybePorn") `); - } + async up(queryRunner) { + await queryRunner.query( + `DROP INDEX "public"."IDX_fc2d74a6d7d8b11292a851d8f8"`, + ); + await queryRunner.query( + `ALTER TABLE "drive_file" DROP COLUMN "forceIsSensitive"`, + ); + await queryRunner.query( + `ALTER TABLE "drive_file" DROP COLUMN "predictedIsSensitive"`, + ); + await queryRunner.query( + `ALTER TABLE "meta" DROP COLUMN "forceIsSensitiveWhenPredicted"`, + ); + await queryRunner.query( + `ALTER TABLE "meta" DROP COLUMN "disallowUploadWhenPredictedAsPorn"`, + ); + await queryRunner.query( + `ALTER TABLE "drive_file" ADD "maybeSensitive" boolean NOT NULL DEFAULT false`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "drive_file"."maybeSensitive" IS 'Whether the DriveFile is NSFW. (predict)'`, + ); + await queryRunner.query( + `ALTER TABLE "drive_file" ADD "maybePorn" boolean NOT NULL DEFAULT false`, + ); + await queryRunner.query( + `ALTER TABLE "meta" ADD "setSensitiveFlagAutomatically" boolean NOT NULL DEFAULT false`, + ); + await queryRunner.query( + `ALTER TABLE "user_profile" ADD "autoSensitive" boolean NOT NULL DEFAULT false`, + ); + await queryRunner.query( + `CREATE INDEX "IDX_3b33dff77bb64b23c88151d23e" ON "drive_file" ("maybeSensitive") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_8bdcd3dd2bddb78014999a16ce" ON "drive_file" ("maybePorn") `, + ); + } - async down(queryRunner) { - await queryRunner.query(`DROP INDEX "public"."IDX_8bdcd3dd2bddb78014999a16ce"`); - await queryRunner.query(`DROP INDEX "public"."IDX_3b33dff77bb64b23c88151d23e"`); - await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "autoSensitive"`); - await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "setSensitiveFlagAutomatically"`); - await queryRunner.query(`ALTER TABLE "drive_file" DROP COLUMN "maybePorn"`); - await queryRunner.query(`COMMENT ON COLUMN "drive_file"."maybeSensitive" IS 'Whether the DriveFile is NSFW. (predict)'`); - await queryRunner.query(`ALTER TABLE "drive_file" DROP COLUMN "maybeSensitive"`); - await queryRunner.query(`ALTER TABLE "meta" ADD "disallowUploadWhenPredictedAsPorn" boolean NOT NULL DEFAULT false`); - await queryRunner.query(`ALTER TABLE "meta" ADD "forceIsSensitiveWhenPredicted" boolean NOT NULL DEFAULT true`); - await queryRunner.query(`ALTER TABLE "drive_file" ADD "predictedIsSensitive" boolean NOT NULL DEFAULT false`); - await queryRunner.query(`ALTER TABLE "drive_file" ADD "forceIsSensitive" boolean NOT NULL DEFAULT false`); - await queryRunner.query(`CREATE INDEX "IDX_fc2d74a6d7d8b11292a851d8f8" ON "drive_file" ("predictedIsSensitive") `); - } + async down(queryRunner) { + await queryRunner.query( + `DROP INDEX "public"."IDX_8bdcd3dd2bddb78014999a16ce"`, + ); + await queryRunner.query( + `DROP INDEX "public"."IDX_3b33dff77bb64b23c88151d23e"`, + ); + await queryRunner.query( + `ALTER TABLE "user_profile" DROP COLUMN "autoSensitive"`, + ); + await queryRunner.query( + `ALTER TABLE "meta" DROP COLUMN "setSensitiveFlagAutomatically"`, + ); + await queryRunner.query(`ALTER TABLE "drive_file" DROP COLUMN "maybePorn"`); + await queryRunner.query( + `COMMENT ON COLUMN "drive_file"."maybeSensitive" IS 'Whether the DriveFile is NSFW. (predict)'`, + ); + await queryRunner.query( + `ALTER TABLE "drive_file" DROP COLUMN "maybeSensitive"`, + ); + await queryRunner.query( + `ALTER TABLE "meta" ADD "disallowUploadWhenPredictedAsPorn" boolean NOT NULL DEFAULT false`, + ); + await queryRunner.query( + `ALTER TABLE "meta" ADD "forceIsSensitiveWhenPredicted" boolean NOT NULL DEFAULT true`, + ); + await queryRunner.query( + `ALTER TABLE "drive_file" ADD "predictedIsSensitive" boolean NOT NULL DEFAULT false`, + ); + await queryRunner.query( + `ALTER TABLE "drive_file" ADD "forceIsSensitive" boolean NOT NULL DEFAULT false`, + ); + await queryRunner.query( + `CREATE INDEX "IDX_fc2d74a6d7d8b11292a851d8f8" ON "drive_file" ("predictedIsSensitive") `, + ); + } } diff --git a/packages/backend/migration/1656328812281-ip-2.js b/packages/backend/migration/1656328812281-ip-2.js index b0ee1ebfc7..ab4096e9c6 100644 --- a/packages/backend/migration/1656328812281-ip-2.js +++ b/packages/backend/migration/1656328812281-ip-2.js @@ -1,13 +1,19 @@ export class ip21656328812281 { - name = 'ip21656328812281' + name = "ip21656328812281"; - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "user_ip" DROP CONSTRAINT "FK_7f7f1c66f48e9a8e18a33bc5150"`); - await queryRunner.query(`ALTER TABLE "meta" ADD "enableIpLogging" boolean NOT NULL DEFAULT false`); - } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "user_ip" DROP CONSTRAINT "FK_7f7f1c66f48e9a8e18a33bc5150"`, + ); + await queryRunner.query( + `ALTER TABLE "meta" ADD "enableIpLogging" boolean NOT NULL DEFAULT false`, + ); + } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "enableIpLogging"`); - await queryRunner.query(`ALTER TABLE "user_ip" ADD CONSTRAINT "FK_7f7f1c66f48e9a8e18a33bc5150" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`); - } + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "enableIpLogging"`); + await queryRunner.query( + `ALTER TABLE "user_ip" ADD CONSTRAINT "FK_7f7f1c66f48e9a8e18a33bc5150" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, + ); + } } diff --git a/packages/backend/migration/1656408772602-nsfw-detection-6.js b/packages/backend/migration/1656408772602-nsfw-detection-6.js index 7ef223a4c6..4ef237308f 100644 --- a/packages/backend/migration/1656408772602-nsfw-detection-6.js +++ b/packages/backend/migration/1656408772602-nsfw-detection-6.js @@ -1,11 +1,15 @@ export class nsfwDetection61656408772602 { - name = 'nsfwDetection61656408772602' + name = "nsfwDetection61656408772602"; - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "meta" ADD "enableSensitiveMediaDetectionForVideos" boolean NOT NULL DEFAULT false`); - } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "meta" ADD "enableSensitiveMediaDetectionForVideos" boolean NOT NULL DEFAULT false`, + ); + } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "enableSensitiveMediaDetectionForVideos"`); - } + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "meta" DROP COLUMN "enableSensitiveMediaDetectionForVideos"`, + ); + } } diff --git a/packages/backend/migration/1656772790599-user-moderation-note.js b/packages/backend/migration/1656772790599-user-moderation-note.js index 133bcffe1a..11d3124aeb 100644 --- a/packages/backend/migration/1656772790599-user-moderation-note.js +++ b/packages/backend/migration/1656772790599-user-moderation-note.js @@ -1,11 +1,15 @@ export class userModerationNote1656772790599 { - name = 'userModerationNote1656772790599' + name = "userModerationNote1656772790599"; - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "user_profile" ADD "moderationNote" character varying(8192) NOT NULL DEFAULT ''`); - } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "user_profile" ADD "moderationNote" character varying(8192) NOT NULL DEFAULT ''`, + ); + } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "moderationNote"`); - } + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "user_profile" DROP COLUMN "moderationNote"`, + ); + } } diff --git a/packages/backend/migration/1657346559800-active-email-validation.js b/packages/backend/migration/1657346559800-active-email-validation.js index f8e03eeb07..4e3fd3f820 100644 --- a/packages/backend/migration/1657346559800-active-email-validation.js +++ b/packages/backend/migration/1657346559800-active-email-validation.js @@ -1,11 +1,15 @@ export class activeEmailValidation1657346559800 { - name = 'activeEmailValidation1657346559800' + name = "activeEmailValidation1657346559800"; - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "meta" ADD "enableActiveEmailValidation" boolean NOT NULL DEFAULT true`); - } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "meta" ADD "enableActiveEmailValidation" boolean NOT NULL DEFAULT true`, + ); + } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "enableActiveEmailValidation"`); - } + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "meta" DROP COLUMN "enableActiveEmailValidation"`, + ); + } } diff --git a/packages/backend/migration/1658203170545calckey.js b/packages/backend/migration/1658203170545calckey.js deleted file mode 100644 index eb5913e811..0000000000 --- a/packages/backend/migration/1658203170545calckey.js +++ /dev/null @@ -1,15 +0,0 @@ -export class calckey1658203170545 { - name = 'calckey1658203170545' - - async up(queryRunner) { - await queryRunner.query(`UPDATE meta SET "useStarForReactionFallback" = TRUE;`); - await queryRunner.query(`UPDATE meta SET "repositoryUrl" = 'https://codeberg/calckey/calckey'`); - await queryRunner.query(`UPDATE meta SET "feedbackUrl" = 'https://codeberg/calckey/calckey/issues'`); - } - - async down(queryRunner) { - await queryRunner.query(`UPDATE meta SET "useStarForReactionFallback" = FALSE;`); - await queryRunner.query(`UPDATE meta SET "repositoryUrl" = 'https://codeberg/calckey/calckey'`); - await queryRunner.query(`UPDATE meta SET "feedbackUrl" = 'https://codeberg/calckey/calckey/issues'`); - } -} diff --git a/packages/backend/migration/1658203170545firefish.js b/packages/backend/migration/1658203170545firefish.js new file mode 100644 index 0000000000..b2f67f56d1 --- /dev/null +++ b/packages/backend/migration/1658203170545firefish.js @@ -0,0 +1,27 @@ +export class calckey1658203170545 { + name = "calckey1658203170545"; + + async up(queryRunner) { + await queryRunner.query( + `UPDATE meta SET "useStarForReactionFallback" = TRUE;`, + ); + await queryRunner.query( + `UPDATE meta SET "repositoryUrl" = 'https://codeberg/firefish/firefish'`, + ); + await queryRunner.query( + `UPDATE meta SET "feedbackUrl" = 'https://codeberg/firefish/firefish/issues'`, + ); + } + + async down(queryRunner) { + await queryRunner.query( + `UPDATE meta SET "useStarForReactionFallback" = FALSE;`, + ); + await queryRunner.query( + `UPDATE meta SET "repositoryUrl" = 'https://codeberg/firefish/firefish'`, + ); + await queryRunner.query( + `UPDATE meta SET "feedbackUrl" = 'https://codeberg/firefish/firefish/issues'`, + ); + } +} diff --git a/packages/backend/migration/1658656633972-note-replies-function.js b/packages/backend/migration/1658656633972-note-replies-function.js index de2e28c6f2..810e238439 100644 --- a/packages/backend/migration/1658656633972-note-replies-function.js +++ b/packages/backend/migration/1658656633972-note-replies-function.js @@ -1,5 +1,5 @@ export class noteRepliesFunction1658656633972 { - name = 'noteRepliesFunction1658656633972' + name = "noteRepliesFunction1658656633972"; async up(queryRunner) { await queryRunner.query(` diff --git a/packages/backend/migration/1658939464003CustomMOTD.js b/packages/backend/migration/1658939464003CustomMOTD.js index eac03451dc..ee1b182e5d 100644 --- a/packages/backend/migration/1658939464003CustomMOTD.js +++ b/packages/backend/migration/1658939464003CustomMOTD.js @@ -1,8 +1,10 @@ export class CustomMOTD1658939464003 { - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "meta" ADD "customMOTD" character varying(256) array NOT NULL DEFAULT '{}'::varchar[]`); - } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "customMOTD"`); - } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "meta" ADD "customMOTD" character varying(256) array NOT NULL DEFAULT '{}'::varchar[]`, + ); + } + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "customMOTD"`); + } } diff --git a/packages/backend/migration/1658941974648CustomSplashIcons.js b/packages/backend/migration/1658941974648CustomSplashIcons.js index fce5eb7671..5c6a874d0f 100644 --- a/packages/backend/migration/1658941974648CustomSplashIcons.js +++ b/packages/backend/migration/1658941974648CustomSplashIcons.js @@ -1,8 +1,12 @@ export class CustomSplashIcons1658941974648 { - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "meta" ADD "customSplashIcons" character varying(256) array NOT NULL DEFAULT '{}'::varchar[]`); - } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "customSplashIcons"`); - } + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "meta" ADD "customSplashIcons" character varying(256) array NOT NULL DEFAULT '{}'::varchar[]`, + ); + } + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "meta" DROP COLUMN "customSplashIcons"`, + ); + } } diff --git a/packages/backend/migration/1658981842728FixCalckey.js b/packages/backend/migration/1658981842728FixCalckey.js index 7d8750634b..96dfd77ac3 100644 --- a/packages/backend/migration/1658981842728FixCalckey.js +++ b/packages/backend/migration/1658981842728FixCalckey.js @@ -1,15 +1,27 @@ -export class FixCalckey1658981842728 { - name = 'FixCalckey1658981842728' +export class FixFirefish1658981842728 { + name = "FixFirefish1658981842728"; - async up(queryRunner) { - await queryRunner.query(`UPDATE "meta" SET "useStarForReactionFallback" = TRUE;`); - await queryRunner.query(`UPDATE "meta" SET "repositoryUrl" = 'https://codeberg/calckey/calckey'`); - await queryRunner.query(`UPDATE "meta" SET "feedbackUrl" = 'https://codeberg/calckey/calckey/issues'`); + async up(queryRunner) { + await queryRunner.query( + `UPDATE "meta" SET "useStarForReactionFallback" = TRUE;`, + ); + await queryRunner.query( + `UPDATE "meta" SET "repositoryUrl" = 'https://codeberg/firefish/firefish'`, + ); + await queryRunner.query( + `UPDATE "meta" SET "feedbackUrl" = 'https://codeberg/firefish/firefish/issues'`, + ); } async down(queryRunner) { - await queryRunner.query(`UPDATE "meta" SET "useStarForReactionFallback" = FALSE;`); - await queryRunner.query(`UPDATE "meta" SET "repositoryUrl" = 'https://codeberg/calckey/calckey'`); - await queryRunner.query(`UPDATE "meta" SET "feedbackUrl" = 'https://codeberg/calckey/calckey/issues'`); + await queryRunner.query( + `UPDATE "meta" SET "useStarForReactionFallback" = FALSE;`, + ); + await queryRunner.query( + `UPDATE "meta" SET "repositoryUrl" = 'https://codeberg/firefish/firefish'`, + ); + await queryRunner.query( + `UPDATE "meta" SET "feedbackUrl" = 'https://codeberg/firefish/firefish/issues'`, + ); } } diff --git a/packages/backend/migration/1659042130648RecommendedTimeline.js b/packages/backend/migration/1659042130648RecommendedTimeline.js index 39c9b41636..01d28b2f38 100644 --- a/packages/backend/migration/1659042130648RecommendedTimeline.js +++ b/packages/backend/migration/1659042130648RecommendedTimeline.js @@ -1,11 +1,19 @@ export class RecommendedTimeline1659042130648 { - name = 'RecommendedTimeline1659042130648' - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "meta" ADD "disableRecommendedTimeline" boolean NOT NULL DEFAULT true`); - await queryRunner.query(`ALTER TABLE "meta" ADD "recommendedInstances" character varying(256) array NOT NULL DEFAULT '{}'::varchar[]`); - } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "disableRecommendedTimeline"`); - await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "recommendedInstances"`); - } + name = "RecommendedTimeline1659042130648"; + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "meta" ADD "disableRecommendedTimeline" boolean NOT NULL DEFAULT true`, + ); + await queryRunner.query( + `ALTER TABLE "meta" ADD "recommendedInstances" character varying(256) array NOT NULL DEFAULT '{}'::varchar[]`, + ); } + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "meta" DROP COLUMN "disableRecommendedTimeline"`, + ); + await queryRunner.query( + `ALTER TABLE "meta" DROP COLUMN "recommendedInstances"`, + ); + } +} diff --git a/packages/backend/migration/1660068273737GuestTimeline.js b/packages/backend/migration/1660068273737GuestTimeline.js index 23d3bc51b0..9a42f602da 100644 --- a/packages/backend/migration/1660068273737GuestTimeline.js +++ b/packages/backend/migration/1660068273737GuestTimeline.js @@ -1,9 +1,13 @@ export class GuestTimeline1660068273737 { - name = 'GuestTimeline1660068273737' - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "meta" ADD "enableGuestTimeline" boolean NOT NULL DEFAULT false`); - } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "enableGuestTimeline"`); - } + name = "GuestTimeline1660068273737"; + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "meta" ADD "enableGuestTimeline" boolean NOT NULL DEFAULT false`, + ); } + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "meta" DROP COLUMN "enableGuestTimeline"`, + ); + } +} diff --git a/packages/backend/migration/1665091090561-add-renote-muting.js b/packages/backend/migration/1665091090561-add-renote-muting.js new file mode 100644 index 0000000000..2c76aaff5f --- /dev/null +++ b/packages/backend/migration/1665091090561-add-renote-muting.js @@ -0,0 +1,22 @@ +export class addRenoteMuting1665091090561 { + constructor() { + this.name = "addRenoteMuting1665091090561"; + } + + async up(queryRunner) { + await queryRunner.query( + `CREATE TABLE "renote_muting" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "muteeId" character varying(32) NOT NULL, "muterId" character varying(32) NOT NULL, CONSTRAINT "PK_renoteMuting_id" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE INDEX "IDX_renote_muting_createdAt" ON "muting" ("createdAt") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_renote_muting_muteeId" ON "muting" ("muteeId") `, + ); + await queryRunner.query( + `CREATE INDEX "IDX_renote_muting_muterId" ON "muting" ("muterId") `, + ); + } + + async down(queryRunner) {} +} diff --git a/packages/backend/migration/1668828368510PageDraft.js b/packages/backend/migration/1668828368510PageDraft.js index 4a68189120..a5cc88b26e 100644 --- a/packages/backend/migration/1668828368510PageDraft.js +++ b/packages/backend/migration/1668828368510PageDraft.js @@ -1,8 +1,10 @@ export class Page1668828368510 { async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "page" ADD "isPublic" boolean NOT NULL DEFAULT true`); + await queryRunner.query( + `ALTER TABLE "page" ADD "isPublic" boolean NOT NULL DEFAULT true`, + ); } async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "page" DROP COLUMN "isPublic"`); + await queryRunner.query(`ALTER TABLE "page" DROP COLUMN "isPublic"`); } } diff --git a/packages/backend/migration/1668831378728FixCalckeyAgain.js b/packages/backend/migration/1668831378728FixCalckeyAgain.js index d5e67a48c3..33db2b15a7 100644 --- a/packages/backend/migration/1668831378728FixCalckeyAgain.js +++ b/packages/backend/migration/1668831378728FixCalckeyAgain.js @@ -1,11 +1,15 @@ -export class FixCalckeyAgain1668831378728 { - name = 'FixCalckeyAgain1668831378728' +export class FixFirefishAgain1668831378728 { + name = "FixFirefishAgain1668831378728"; async up(queryRunner) { - await queryRunner.query(`UPDATE "meta" SET "useStarForReactionFallback" = TRUE`); + await queryRunner.query( + `UPDATE "meta" SET "useStarForReactionFallback" = TRUE`, + ); } async down(queryRunner) { - await queryRunner.query(`UPDATE "meta" SET "useStarForReactionFallback" = FALSE`); + await queryRunner.query( + `UPDATE "meta" SET "useStarForReactionFallback" = FALSE`, + ); } } diff --git a/packages/backend/migration/1669138716634-whetherPushNotifyToSendReadMessage.js b/packages/backend/migration/1669138716634-whetherPushNotifyToSendReadMessage.js new file mode 100644 index 0000000000..101972a68c --- /dev/null +++ b/packages/backend/migration/1669138716634-whetherPushNotifyToSendReadMessage.js @@ -0,0 +1,15 @@ +export class whetherPushNotifyToSendReadMessage1669138716634 { + name = "whetherPushNotifyToSendReadMessage1669138716634"; + + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "sw_subscription" ADD "sendReadMessage" boolean NOT NULL DEFAULT false`, + ); + } + + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "sw_subscription" DROP COLUMN "sendReadMessage"`, + ); + } +} diff --git a/packages/backend/migration/1669288094000-AddMovedToAndKnownAs.js b/packages/backend/migration/1669288094000-AddMovedToAndKnownAs.js index 8b3c770acb..c34764e0b0 100644 --- a/packages/backend/migration/1669288094000-AddMovedToAndKnownAs.js +++ b/packages/backend/migration/1669288094000-AddMovedToAndKnownAs.js @@ -1,16 +1,21 @@ export class addMovedToAndKnownAs1669288094000 { - name = 'addMovedToAndKnownAs1669288094000' + name = "addMovedToAndKnownAs1669288094000"; async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "user" ADD "movedToUri" character varying(512)`); - await queryRunner.query(`ALTER TABLE "user" ADD "alsoKnownAs" TEXT`); - await queryRunner.query(`COMMENT ON COLUMN "user"."movedToUri" IS 'The URI of the new account of the User'`); - await queryRunner.query(`COMMENT ON COLUMN "user"."alsoKnownAs" IS 'URIs the user is known as too'`); + await queryRunner.query( + `ALTER TABLE "user" ADD "movedToUri" character varying(512)`, + ); + await queryRunner.query(`ALTER TABLE "user" ADD "alsoKnownAs" TEXT`); + await queryRunner.query( + `COMMENT ON COLUMN "user"."movedToUri" IS 'The URI of the new account of the User'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "user"."alsoKnownAs" IS 'URIs the user is known as too'`, + ); } async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "movedToUri"`); - await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "alsoKnownAs"`); + await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "movedToUri"`); + await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "alsoKnownAs"`); } - } diff --git a/packages/backend/migration/1671199573000-AddFkAbuseUserReportTargetUserIdToUserId.js b/packages/backend/migration/1671199573000-AddFkAbuseUserReportTargetUserIdToUserId.js index 68f560b478..96ed8e1d6b 100644 --- a/packages/backend/migration/1671199573000-AddFkAbuseUserReportTargetUserIdToUserId.js +++ b/packages/backend/migration/1671199573000-AddFkAbuseUserReportTargetUserIdToUserId.js @@ -1,12 +1,18 @@ export class addFkAbuseUserReportTargetUserIdToUserId1671199573000 { - name = 'addFkAbuseUserReportTargetUserIdToUserId1671199573000' + name = "addFkAbuseUserReportTargetUserIdToUserId1671199573000"; async up(queryRunner) { - await queryRunner.query(`DELETE FROM abuse_user_report WHERE NOT EXISTS (SELECT 1 FROM "user" WHERE "user"."id" = "abuse_user_report"."targetUserId")`); - await queryRunner.query(`ALTER TABLE abuse_user_report ADD CONSTRAINT fk_7f4e851a35d81b64dda28eee0 FOREIGN KEY ("targetUserId") REFERENCES "user"("id") ON DELETE CASCADE`); + await queryRunner.query( + `DELETE FROM abuse_user_report WHERE NOT EXISTS (SELECT 1 FROM "user" WHERE "user"."id" = "abuse_user_report"."targetUserId")`, + ); + await queryRunner.query( + `ALTER TABLE abuse_user_report ADD CONSTRAINT fk_7f4e851a35d81b64dda28eee0 FOREIGN KEY ("targetUserId") REFERENCES "user"("id") ON DELETE CASCADE`, + ); } async down(queryRunner) { - await queryRunner.query(`ALTER TABLE abuse_user_report DROP CONSTRAINT fk_7f4e851a35d81b64dda28eee0`); + await queryRunner.query( + `ALTER TABLE abuse_user_report DROP CONSTRAINT fk_7f4e851a35d81b64dda28eee0`, + ); } } diff --git a/packages/backend/migration/1671388343000-CalckeyRepoMove.js b/packages/backend/migration/1671388343000-CalckeyRepoMove.js index df933bcb97..e11ddcb435 100644 --- a/packages/backend/migration/1671388343000-CalckeyRepoMove.js +++ b/packages/backend/migration/1671388343000-CalckeyRepoMove.js @@ -1,15 +1,23 @@ -/* "CalckeyRepoMove1671388343000" is a class that updates the "useStarForReactionFallback" column in +/* "FirefishRepoMove1671388343000" is a class that updates the "useStarForReactionFallback" column in the "meta" table to TRUE */ -export class CalckeyRepoMove1671388343000 { - name = 'CalckeyRepoMove1671388343000' +export class FirefishRepoMove1671388343000 { + name = "FirefishRepoMove1671388343000"; async up(queryRunner) { - await queryRunner.query(`UPDATE meta SET "repositoryUrl" = 'https://codeberg/calckey/calckey'`); - await queryRunner.query(`UPDATE meta SET "feedbackUrl" = 'https://codeberg/calckey/calckey/issues'`); + await queryRunner.query( + `UPDATE meta SET "repositoryUrl" = 'https://codeberg/firefish/firefish'`, + ); + await queryRunner.query( + `UPDATE meta SET "feedbackUrl" = 'https://codeberg/firefish/firefish/issues'`, + ); } async down(queryRunner) { - await queryRunner.query(`UPDATE meta SET "repositoryUrl" = 'https://codeberg/calckey/calckey'`); - await queryRunner.query(`UPDATE meta SET "feedbackUrl" = 'https://codeberg/calckey/calckey/issues'`); + await queryRunner.query( + `UPDATE meta SET "repositoryUrl" = 'https://codeberg/firefish/firefish'`, + ); + await queryRunner.query( + `UPDATE meta SET "feedbackUrl" = 'https://codeberg/firefish/firefish/issues'`, + ); } } diff --git a/packages/backend/migration/1672882664294-DefaultReaction.js b/packages/backend/migration/1672882664294-DefaultReaction.js index 498a1810d6..6ce1146ecb 100644 --- a/packages/backend/migration/1672882664294-DefaultReaction.js +++ b/packages/backend/migration/1672882664294-DefaultReaction.js @@ -1,9 +1,13 @@ export class DefaultReaction1672882664294 { - name = 'DefaultReaction1672882664294' + name = "DefaultReaction1672882664294"; async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "meta" ADD "defaultReaction" character varying(256) NOT NULL DEFAULT '⭐'`); - await queryRunner.query(`COMMENT ON COLUMN "meta"."defaultReaction" IS 'The fallback reaction for emoji reacts'`); + await queryRunner.query( + `ALTER TABLE "meta" ADD "defaultReaction" character varying(256) NOT NULL DEFAULT '⭐'`, + ); + await queryRunner.query( + `COMMENT ON COLUMN "meta"."defaultReaction" IS 'The fallback reaction for emoji reacts'`, + ); } async down(queryRunner) { diff --git a/packages/backend/migration/1673336077243-PollChoiceLength.js b/packages/backend/migration/1673336077243-PollChoiceLength.js index 13db7dd963..a0e3350162 100644 --- a/packages/backend/migration/1673336077243-PollChoiceLength.js +++ b/packages/backend/migration/1673336077243-PollChoiceLength.js @@ -1,11 +1,15 @@ export class PollChoiceLength1673336077243 { - name = 'PollChoiceLength1673336077243' + name = "PollChoiceLength1673336077243"; async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "poll" ALTER COLUMN "choices" TYPE character varying(256) array`); + await queryRunner.query( + `ALTER TABLE "poll" ALTER COLUMN "choices" TYPE character varying(256) array`, + ); } async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "poll" ALTER COLUMN "choices" TYPE character varying(128) array`); + await queryRunner.query( + `ALTER TABLE "poll" ALTER COLUMN "choices" TYPE character varying(128) array`, + ); } } diff --git a/packages/backend/migration/1676093997212-AntennaInstances.js b/packages/backend/migration/1676093997212-AntennaInstances.js new file mode 100644 index 0000000000..ffd428f7dc --- /dev/null +++ b/packages/backend/migration/1676093997212-AntennaInstances.js @@ -0,0 +1,27 @@ +export class AntennaInstances1676093997212 { + name = "AntennaInstances1676093997212"; + + async up(queryRunner) { + await queryRunner.query( + `ALTER TYPE "antenna_src_enum" ADD VALUE 'instances'`, + ); + await queryRunner.query( + `ALTER TABLE "antenna" ADD "instances" jsonb NOT NULL DEFAULT '[]'`, + ); + } + + async down(queryRunner) { + await queryRunner.query(`DELETE FROM "antenna" WHERE "src" = 'instances'`); + await queryRunner.query(`ALTER TABLE "antenna" DROP COLUMN "instances"`); + await queryRunner.query( + `CREATE TYPE "public"."antenna_src_enum_old" AS ENUM('home', 'all', 'users', 'list', 'group')`, + ); + await queryRunner.query( + `ALTER TABLE "antenna" ALTER COLUMN "src" TYPE "public"."antenna_src_enum_old" USING "src"::"text"::"public"."antenna_src_enum_old"`, + ); + await queryRunner.query(`DROP TYPE "public"."antenna_src_enum"`); + await queryRunner.query( + `ALTER TYPE "public"."antenna_src_enum_old" RENAME TO "antenna_src_enum"`, + ); + } +} diff --git a/packages/backend/migration/1677935903517-DriveComment.js b/packages/backend/migration/1677935903517-DriveComment.js new file mode 100644 index 0000000000..571958f294 --- /dev/null +++ b/packages/backend/migration/1677935903517-DriveComment.js @@ -0,0 +1,15 @@ +export class DriveComment1677935903517 { + name = "DriveComment1677935903517"; + + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "drive_file" ALTER "comment" TYPE character varying(8192)`, + ); + } + + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "drive_file" ALTER "comment" TYPE character varying(512)`, + ); + } +} diff --git a/packages/backend/migration/1678426061773-tweak-varchar-length.js b/packages/backend/migration/1678426061773-tweak-varchar-length.js new file mode 100644 index 0000000000..00ddcaebea --- /dev/null +++ b/packages/backend/migration/1678426061773-tweak-varchar-length.js @@ -0,0 +1,16 @@ +export class 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 down(queryRunner) {} +} diff --git a/packages/backend/migration/1678945242650-add-props-for-custom-emoji.js b/packages/backend/migration/1678945242650-add-props-for-custom-emoji.js new file mode 100644 index 0000000000..f3de7ce278 --- /dev/null +++ b/packages/backend/migration/1678945242650-add-props-for-custom-emoji.js @@ -0,0 +1,13 @@ +export class addPropsForCustomEmoji1678945242650 { + name = "addPropsForCustomEmoji1678945242650"; + + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "emoji" ADD "license" character varying(1024)`, + ); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "emoji" DROP COLUMN "license"`); + } +} diff --git a/packages/backend/migration/1679269929000-fix-repo.js b/packages/backend/migration/1679269929000-fix-repo.js new file mode 100644 index 0000000000..2abcd71bb9 --- /dev/null +++ b/packages/backend/migration/1679269929000-fix-repo.js @@ -0,0 +1,21 @@ +export class FixRepo1679269929000 { + name = "FixRepo1679269929000"; + + async up(queryRunner) { + await queryRunner.query( + `UPDATE meta SET "repositoryUrl" = 'https://codeberg.org/firefish/firefish'`, + ); + await queryRunner.query( + `UPDATE meta SET "feedbackUrl" = 'https://codeberg.org/firefish/firefish/issues'`, + ); + } + + async down(queryRunner) { + await queryRunner.query( + `UPDATE meta SET "repositoryUrl" = 'https://codeberg.org/firefish/firefish'`, + ); + await queryRunner.query( + `UPDATE meta SET "feedbackUrl" = 'https://codeberg.org/firefish/firefish/issues'`, + ); + } +} diff --git a/packages/backend/migration/1680375641101-clean-charts.js b/packages/backend/migration/1680375641101-clean-charts.js new file mode 100644 index 0000000000..bfb8aa9d92 --- /dev/null +++ b/packages/backend/migration/1680375641101-clean-charts.js @@ -0,0 +1,25 @@ +export class CleanCharts1680375641101 { + constructor() { + this.name = "CleanCharts1680375641101"; + } + async up(queryRunner) { + await queryRunner.query( + `delete from __chart__hashtag where ___local_users = 0 and ___remote_users = 0;`, + ); + await queryRunner.query( + `delete from __chart_day__hashtag where ___local_users = 0 and ___remote_users = 0;`, + ); + await queryRunner.query(`COMMIT;`); + await queryRunner.query(`vacuum __chart__hashtag;`); + await queryRunner.query(`vacuum __chart_day__hashtag;`); + await queryRunner.query(`COMMIT;`); + } + async down(queryRunner) { + await queryRunner.query( + `delete from __chart__hashtag where ___local_users = 0 and ___remote_users = 0;`, + ); + await queryRunner.query( + `delete from __chart_day__hashtag where ___local_users = 0 and ___remote_users = 0;`, + ); + } +} diff --git a/packages/backend/migration/1680426269172-SpeakAsCat.js b/packages/backend/migration/1680426269172-SpeakAsCat.js new file mode 100644 index 0000000000..375098542c --- /dev/null +++ b/packages/backend/migration/1680426269172-SpeakAsCat.js @@ -0,0 +1,20 @@ +export class SpeakAsCat1680426269172 { + name = "SpeakAsCat1680426269172"; + + async up(queryRunner) { + await queryRunner.query(` + ALTER TABLE "user" + ADD "speakAsCat" boolean NOT NULL DEFAULT true + `); + await queryRunner.query(` + COMMENT ON COLUMN "user"."speakAsCat" + IS 'Whether to speak as a cat if isCat.' + `); + } + + async down(queryRunner) { + await queryRunner.query(` + ALTER TABLE "user" DROP COLUMN "speakAsCat" + `); + } +} diff --git a/packages/backend/migration/1682753227899-NoteEdit.js b/packages/backend/migration/1682753227899-NoteEdit.js new file mode 100644 index 0000000000..55a0de0206 --- /dev/null +++ b/packages/backend/migration/1682753227899-NoteEdit.js @@ -0,0 +1,53 @@ +export class NoteEdit1682753227899 { + name = "NoteEdit1682753227899"; + + async up(queryRunner) { + await queryRunner.query(` + CREATE TABLE "note_edit" ( + "id" character varying(32) NOT NULL, + "noteId" character varying(32) NOT NULL, + "text" text, + "cw" character varying(512), + "fileIds" character varying(32) array NOT NULL DEFAULT '{}', + "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL, + CONSTRAINT "PK_736fc6e0d4e222ecc6f82058e08" PRIMARY KEY ("id") + ) + `); + await queryRunner.query(` + COMMENT ON COLUMN "note_edit"."noteId" IS 'The ID of note.' + `); + await queryRunner.query(` + COMMENT ON COLUMN "note_edit"."updatedAt" IS 'The updated date of the Note.' + `); + await queryRunner.query(` + CREATE INDEX "IDX_702ad5ae993a672e4fbffbcd38" ON "note_edit" ("noteId") + `); + await queryRunner.query(` + ALTER TABLE "note" + ADD "updatedAt" TIMESTAMP WITH TIME ZONE + `); + await queryRunner.query(` + COMMENT ON COLUMN "note"."updatedAt" IS 'The updated date of the Note.' + `); + await queryRunner.query(` + ALTER TABLE "note_edit" + ADD CONSTRAINT "FK_702ad5ae993a672e4fbffbcd38c" + FOREIGN KEY ("noteId") + REFERENCES "note"("id") + ON DELETE CASCADE + ON UPDATE NO ACTION + `); + } + + async down(queryRunner) { + await queryRunner.query(` + ALTER TABLE "note_edit" DROP CONSTRAINT "FK_702ad5ae993a672e4fbffbcd38c" + `); + await queryRunner.query(` + ALTER TABLE "note" DROP COLUMN "updatedAt" + `); + await queryRunner.query(` + DROP TABLE "note_edit" + `); + } +} diff --git a/packages/backend/migration/1682777547198-LibreTranslate.js b/packages/backend/migration/1682777547198-LibreTranslate.js new file mode 100644 index 0000000000..dbaf483e6c --- /dev/null +++ b/packages/backend/migration/1682777547198-LibreTranslate.js @@ -0,0 +1,23 @@ +export class LibreTranslate1682777547198 { + name = "LibreTranslate1682777547198"; + + async up(queryRunner) { + await queryRunner.query(` + ALTER TABLE "meta" + ADD "libreTranslateApiUrl" character varying(512) + `); + await queryRunner.query(` + ALTER TABLE "meta" + ADD "libreTranslateApiKey" character varying(128) + `); + } + + async down(queryRunner) { + await queryRunner.query(` + ALTER TABLE "meta" DROP COLUMN "libreTranslateApiKey" + `); + await queryRunner.query(` + ALTER TABLE "meta" DROP COLUMN "libreTranslateApiUrl" + `); + } +} diff --git a/packages/backend/migration/1682891890317-InstanceSilence.js b/packages/backend/migration/1682891890317-InstanceSilence.js new file mode 100644 index 0000000000..babe64883a --- /dev/null +++ b/packages/backend/migration/1682891890317-InstanceSilence.js @@ -0,0 +1,13 @@ +export class InstanceSilence1682891890317 { + name = "InstanceSilence1682891890317"; + + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "meta" ADD "silencedHosts" character varying(256) array NOT NULL DEFAULT '{}'`, + ); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "silencedHosts"`); + } +} diff --git a/packages/backend/migration/1682891891317-AddHiddenPosts.js b/packages/backend/migration/1682891891317-AddHiddenPosts.js new file mode 100644 index 0000000000..827470f88e --- /dev/null +++ b/packages/backend/migration/1682891891317-AddHiddenPosts.js @@ -0,0 +1,15 @@ +export class AddHiddenPosts1682891891317 { + name = "AddHiddenPosts1682891891317"; + + async up(queryRunner) { + await queryRunner.query( + `ALTER TYPE note_visibility_enum ADD VALUE IF NOT EXISTS 'hidden'`, + ); + } + + async down(queryRunner) { + await queryRunner.query( + `ALTER TYPE note_visibility_enum REMOVE VALUE IF EXISTS 'hidden'`, + ); + } +} diff --git a/packages/backend/migration/1683682889948-PreventAiLearning.js b/packages/backend/migration/1683682889948-PreventAiLearning.js new file mode 100644 index 0000000000..afb892ed5d --- /dev/null +++ b/packages/backend/migration/1683682889948-PreventAiLearning.js @@ -0,0 +1,15 @@ +export class PreventAiLearning1683682889948 { + name = "PreventAiLearning1683682889948"; + + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "user_profile" ADD "preventAiLearning" boolean NOT NULL DEFAULT true`, + ); + } + + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "user_profile" DROP COLUMN "preventAiLearning"`, + ); + } +} diff --git a/packages/backend/migration/1683980686995-ExperimentalFeatures.js b/packages/backend/migration/1683980686995-ExperimentalFeatures.js new file mode 100644 index 0000000000..a289a9ecdc --- /dev/null +++ b/packages/backend/migration/1683980686995-ExperimentalFeatures.js @@ -0,0 +1,16 @@ +export class ExperimentalFeatures1683980686995 { + name = "ExperimentalFeatures1683980686995"; + + async up(queryRunner) { + await queryRunner.query(` + ALTER TABLE "meta" + ADD "experimentalFeatures" jsonb NOT NULL DEFAULT '{}' + `); + } + + async down(queryRunner) { + await queryRunner.query(` + ALTER TABLE "meta" DROP COLUMN "experimentalFeatures" + `); + } +} diff --git a/packages/backend/migration/1684206886988-remove-showTimelineReplies.js b/packages/backend/migration/1684206886988-remove-showTimelineReplies.js new file mode 100644 index 0000000000..e5f8483c7f --- /dev/null +++ b/packages/backend/migration/1684206886988-remove-showTimelineReplies.js @@ -0,0 +1,15 @@ +export class RemoveShowTimelineReplies1684206886988 { + name = "RemoveShowTimelineReplies1684206886988"; + + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "user" DROP COLUMN "showTimelineReplies"`, + ); + } + + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "user" ADD "showTimelineReplies" boolean NOT NULL DEFAULT false`, + ); + } +} diff --git a/packages/backend/migration/1684494870830-EmojiSize.js b/packages/backend/migration/1684494870830-EmojiSize.js new file mode 100644 index 0000000000..6d42f1a614 --- /dev/null +++ b/packages/backend/migration/1684494870830-EmojiSize.js @@ -0,0 +1,19 @@ +export class EmojiSize1684494870830 { + name = "EmojiSize1684494870830"; + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "emoji" ADD "width" integer`); + await queryRunner.query( + `COMMENT ON COLUMN "emoji"."width" IS 'Image width'`, + ); + await queryRunner.query(`ALTER TABLE "emoji" ADD "height" integer`); + await queryRunner.query( + `COMMENT ON COLUMN "emoji"."height" IS 'Image height'`, + ); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "emoji" DROP COLUMN "height"`); + await queryRunner.query(`ALTER TABLE "emoji" DROP COLUMN "width"`); + } +} diff --git a/packages/backend/migration/1688280713783-add-meta-options.js b/packages/backend/migration/1688280713783-add-meta-options.js new file mode 100644 index 0000000000..e97a95c423 --- /dev/null +++ b/packages/backend/migration/1688280713783-add-meta-options.js @@ -0,0 +1,21 @@ +export class AddMetaOptions1688280713783 { + name = "AddMetaOptions1688280713783"; + + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "meta" ADD "enableServerMachineStats" boolean NOT NULL DEFAULT false`, + ); + await queryRunner.query( + `ALTER TABLE "meta" ADD "enableIdenticonGeneration" boolean NOT NULL DEFAULT true`, + ); + } + + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "meta" DROP COLUMN "enableIdenticonGeneration"`, + ); + await queryRunner.query( + `ALTER TABLE "meta" DROP COLUMN "enableServerMachineStats"`, + ); + } +} diff --git a/packages/backend/migration/1688845537045-announcement-popup.js b/packages/backend/migration/1688845537045-announcement-popup.js new file mode 100644 index 0000000000..196590d3e3 --- /dev/null +++ b/packages/backend/migration/1688845537045-announcement-popup.js @@ -0,0 +1,21 @@ +export class AnnouncementPopup1688845537045 { + name = "AnnouncementPopup1688845537045"; + + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "announcement" ADD "showPopup" boolean NOT NULL DEFAULT false`, + ); + await queryRunner.query( + `ALTER TABLE "announcement" ADD "isGoodNews" boolean NOT NULL DEFAULT false`, + ); + } + + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "announcement" DROP COLUMN "isGoodNews"`, + ); + await queryRunner.query( + `ALTER TABLE "announcement" DROP COLUMN "showPopup"`, + ); + } +} diff --git a/packages/backend/migration/1689136347561-donation-link.js b/packages/backend/migration/1689136347561-donation-link.js new file mode 100644 index 0000000000..dbe0ed7c8e --- /dev/null +++ b/packages/backend/migration/1689136347561-donation-link.js @@ -0,0 +1,15 @@ +export class DonationLink1689136347561 { + name = "DonationLink1689136347561"; + + async up(queryRunner) { + await queryRunner.query( + `ALTER TABLE "meta" ADD "donationLink" character varying(256)`, + ); + } + + async down(queryRunner) { + await queryRunner.query( + `ALTER TABLE "meta" DROP COLUMN "DonationLink1689136347561"`, + ); + } +} diff --git a/packages/backend/migration/1689739513827-firefish-repo.js b/packages/backend/migration/1689739513827-firefish-repo.js new file mode 100644 index 0000000000..a2185fbc69 --- /dev/null +++ b/packages/backend/migration/1689739513827-firefish-repo.js @@ -0,0 +1,21 @@ +export class FirefishRepo1689739513827 { + name = "FirefishRepo1689739513827"; + + async up(queryRunner) { + await queryRunner.query( + `UPDATE meta SET "repositoryUrl" = 'https://gitlab.prometheus.systems/firefish/firefish'`, + ); + await queryRunner.query( + `UPDATE meta SET "feedbackUrl" = 'https://gitlab.prometheus.systems/firefish/firefish/issues'`, + ); + } + + async down(queryRunner) { + await queryRunner.query( + `UPDATE meta SET "repositoryUrl" = 'https://codeberg.org/calckey/calckey'`, + ); + await queryRunner.query( + `UPDATE meta SET "feedbackUrl" = 'https://codeberg.org/calckey/calckey/firefish/firefish/issues'`, + ); + } +} diff --git a/packages/backend/native-utils/.cargo/config.toml b/packages/backend/native-utils/.cargo/config.toml new file mode 100644 index 0000000000..7ede30ee04 --- /dev/null +++ b/packages/backend/native-utils/.cargo/config.toml @@ -0,0 +1,3 @@ +[target.aarch64-unknown-linux-musl] +linker = "aarch64-linux-musl-gcc" +rustflags = ["-C", "target-feature=-crt-static"] \ No newline at end of file diff --git a/packages/backend/native-utils/.editorconfig b/packages/backend/native-utils/.editorconfig new file mode 100644 index 0000000000..889b72e112 --- /dev/null +++ b/packages/backend/native-utils/.editorconfig @@ -0,0 +1,3 @@ +[*.rs] +indent_style = space +indent_size = 4 diff --git a/packages/backend/native-utils/.gitignore b/packages/backend/native-utils/.gitignore new file mode 100644 index 0000000000..0a2a1de408 --- /dev/null +++ b/packages/backend/native-utils/.gitignore @@ -0,0 +1,199 @@ +# Created by https://www.toptal.com/developers/gitignore/api/node +# Edit at https://www.toptal.com/developers/gitignore?templates=node + +### Node ### +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# TypeScript v1 declaration files +typings/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env +.env.test + +# parcel-bundler cache (https://parceljs.org/) +.cache + +# Next.js build output +.next + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# End of https://www.toptal.com/developers/gitignore/api/node + +# Created by https://www.toptal.com/developers/gitignore/api/macos +# Edit at https://www.toptal.com/developers/gitignore?templates=macos + +### macOS ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### macOS Patch ### +# iCloud generated files +*.icloud + +# End of https://www.toptal.com/developers/gitignore/api/macos + +# Created by https://www.toptal.com/developers/gitignore/api/windows +# Edit at https://www.toptal.com/developers/gitignore?templates=windows + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# End of https://www.toptal.com/developers/gitignore/api/windows + +# napi-rs generated files +built/ + +#Added by cargo + +/target + +.pnp.* +.yarn/* +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/sdks +!.yarn/versions + +*.node diff --git a/packages/backend/native-utils/.npmignore b/packages/backend/native-utils/.npmignore new file mode 100644 index 0000000000..ec144db2a7 --- /dev/null +++ b/packages/backend/native-utils/.npmignore @@ -0,0 +1,13 @@ +target +Cargo.lock +.cargo +.github +npm +.eslintrc +.prettierignore +rustfmt.toml +yarn.lock +*.node +.yarn +__test__ +renovate.json diff --git a/packages/backend/native-utils/Cargo.lock b/packages/backend/native-utils/Cargo.lock new file mode 100644 index 0000000000..e5f8af37a7 --- /dev/null +++ b/packages/backend/native-utils/Cargo.lock @@ -0,0 +1,3471 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "Inflector" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" + +[[package]] +name = "ahash" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +dependencies = [ + "getrandom", + "once_cell", + "version_check", +] + +[[package]] +name = "ahash" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" +dependencies = [ + "cfg-if", + "getrandom", + "once_cell", + "serde", + "version_check", +] + +[[package]] +name = "aho-corasick" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" +dependencies = [ + "memchr", +] + +[[package]] +name = "aliasable" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd" + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anstream" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is-terminal", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41ed9a86bf92ae6580e0a31281f65a1b1d867c0cc68d5346e2ae128dddfa6a7d" + +[[package]] +name = "anstyle-parse" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e765fd216e48e067936442276d1d57399e37bce53c264d6fefbe298080cb57ee" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +dependencies = [ + "windows-sys 0.48.0", +] + +[[package]] +name = "anstyle-wincon" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188" +dependencies = [ + "anstyle", + "windows-sys 0.48.0", +] + +[[package]] +name = "anyhow" +version = "1.0.71" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" + +[[package]] +name = "arrayvec" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" + +[[package]] +name = "async-stream" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" +dependencies = [ + "async-stream-impl", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.18", +] + +[[package]] +name = "async-trait" +version = "0.1.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.18", +] + +[[package]] +name = "atoi" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7c57d12312ff59c811c0643f4d80830505833c9ffaebd193d819392b265be8e" +dependencies = [ + "num-traits", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bae" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33b8de67cc41132507eeece2584804efcb15f85ba516e34c944b7667f480397a" +dependencies = [ + "heck 0.3.3", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "base64" +version = "0.21.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" + +[[package]] +name = "bigdecimal" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6773ddc0eafc0e509fb60e48dff7f450f8e674a0686ae8605e8d9901bd5eefa" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", +] + +[[package]] +name = "bit-set" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6776fc96284a0bb647b615056fc496d1fe1644a7ab01829818a6d91cae888b84" + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "borsh" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4114279215a005bc675e386011e594e1d9b800918cea18fcadadcce864a2046b" +dependencies = [ + "borsh-derive", + "hashbrown 0.13.2", +] + +[[package]] +name = "borsh-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0754613691538d51f329cce9af41d7b7ca150bc973056f1156611489475f54f7" +dependencies = [ + "borsh-derive-internal", + "borsh-schema-derive-internal", + "proc-macro-crate", + "proc-macro2", + "syn 1.0.109", +] + +[[package]] +name = "borsh-derive-internal" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afb438156919598d2c7bad7e1c0adf3d26ed3840dbc010db1a882a65583ca2fb" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "borsh-schema-derive-internal" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634205cc43f74a1b9046ef87c4540ebda95696ec0f315024860cad7c5b0f5ccd" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "bumpalo" +version = "3.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" + +[[package]] +name = "bytecheck" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b6372023ac861f6e6dc89c8344a8f398fb42aaba2b5dbc649ca0c0e9dbcb627" +dependencies = [ + "bytecheck_derive", + "ptr_meta", + "simdutf8", +] + +[[package]] +name = "bytecheck_derive" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7ec4c6f261935ad534c0c22dbef2201b45918860eb1c574b972bd213a76af61" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "bytecount" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c676a478f63e9fa2dd5368a42f28bba0d6c560b775f38583c8bbaa7fcd67c9c" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "bytes" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" + +[[package]] +name = "cc" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "serde", + "time 0.1.45", + "wasm-bindgen", + "winapi", +] + +[[package]] +name = "clap" +version = "3.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" +dependencies = [ + "bitflags 1.3.2", + "clap_derive 3.2.25", + "clap_lex 0.2.4", + "indexmap", + "once_cell", + "textwrap", +] + +[[package]] +name = "clap" +version = "4.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "401a4694d2bf92537b6867d94de48c4842089645fdcdf6c71865b175d836e9c2" +dependencies = [ + "clap_builder", + "clap_derive 4.3.2", + "once_cell", +] + +[[package]] +name = "clap_builder" +version = "4.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72394f3339a76daf211e57d4bcb374410f3965dcc606dd0e03738c7888766980" +dependencies = [ + "anstream", + "anstyle", + "bitflags 1.3.2", + "clap_lex 0.5.0", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "3.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae6371b8bdc8b7d3959e9cf7b22d4435ef3e79e138688421ec654acf8c81b008" +dependencies = [ + "heck 0.4.1", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "clap_derive" +version = "4.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8cd2b2a819ad6eec39e8f1d6b53001af1e5469f8c177579cdaeb313115b825f" +dependencies = [ + "heck 0.4.1", + "proc-macro2", + "quote", + "syn 2.0.18", +] + +[[package]] +name = "clap_lex" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" +dependencies = [ + "os_str_bytes", +] + +[[package]] +name = "clap_lex" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" + +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + +[[package]] +name = "combine" +version = "4.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4" +dependencies = [ + "bytes", + "futures-core", + "memchr", + "pin-project-lite", + "tokio", + "tokio-util", +] + +[[package]] +name = "console" +version = "0.15.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c926e00cc70edefdc64d3a5ff31cc65bb97a3460097762bd23afb4d8145fccf8" +dependencies = [ + "encode_unicode", + "lazy_static", + "libc", + "unicode-width", + "windows-sys 0.45.0", +] + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "convert_case" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "core-foundation" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" + +[[package]] +name = "cpufeatures" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e4c1eaa2012c47becbbad2ab175484c2a84d1185b566fb2cc5b8707343dfe58" +dependencies = [ + "libc", +] + +[[package]] +name = "crossbeam-queue" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1cfb3ea8a53f37c40dea2c7bedcbd88bdfae54f5e2175d6ecaff1c988353add" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "ctor" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ctor" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1586fa608b1dab41f667475b4a41faec5ba680aee428bfa5de4ea520fdc6e901" +dependencies = [ + "quote", + "syn 2.0.18", +] + +[[package]] +name = "cuid2" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1debbff0c0f0b54e681296c6f064a78f8ecec8e89e7fcc472443d9f85b98ca9a" +dependencies = [ + "num", + "proptest", + "rand", + "sha3", +] + +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "convert_case 0.4.0", + "proc-macro2", + "quote", + "rustc_version", + "syn 1.0.109", +] + +[[package]] +name = "diff" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", + "subtle", +] + +[[package]] +name = "dirs" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + +[[package]] +name = "dotenvy" +version = "0.15.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" + +[[package]] +name = "dyn-clone" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68b0cf012f1230e43cd00ebb729c6bb58707ecfa8ad08b52ef3a4ccd2697fc30" + +[[package]] +name = "either" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" + +[[package]] +name = "encode_unicode" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" + +[[package]] +name = "encoding_rs" +version = "0.8.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "errno" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" +dependencies = [ + "errno-dragonfly", + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + +[[package]] +name = "fancy-regex" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b95f7c0680e4142284cf8b22c14a476e87d61b004a3a0861872b32ef7ead40a2" +dependencies = [ + "bit-set", + "regex", +] + +[[package]] +name = "fastrand" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" +dependencies = [ + "instant", +] + +[[package]] +name = "flume" +version = "0.10.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1657b4441c3403d9f7b3409e47575237dac27b1b5726df654a6ecbf92f0f7577" +dependencies = [ + "futures-core", + "futures-sink", + "pin-project", + "spin 0.9.8", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "form_urlencoded" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "fraction" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3027ae1df8d41b4bed2241c8fdad4acc1e7af60c8e17743534b545e77182d678" +dependencies = [ + "lazy_static", + "num", +] + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "futures" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" + +[[package]] +name = "futures-executor" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-intrusive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a604f7a68fbf8103337523b1fadc8ade7361ee3f112f7c680ad179651616aed5" +dependencies = [ + "futures-core", + "lock_api", + "parking_lot 0.11.2", +] + +[[package]] +name = "futures-io" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" + +[[package]] +name = "futures-macro" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.18", +] + +[[package]] +name = "futures-sink" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" + +[[package]] +name = "futures-task" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" + +[[package]] +name = "futures-util" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", + "wasm-bindgen", +] + +[[package]] +name = "h2" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d357c7ae988e7d2182f7d7871d0b963962420b0678b0997ce7de72001aeab782" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash 0.7.6", +] + +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash 0.8.3", +] + +[[package]] +name = "hashlink" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0761a1b9491c4f2e3d66aa0f62d0fba0af9a0e2852e4d48ea506632a4b56e6aa" +dependencies = [ + "hashbrown 0.13.2", +] + +[[package]] +name = "heck" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "hermit-abi" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" +dependencies = [ + "libc", +] + +[[package]] +name = "hermit-abi" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hkdf" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437" +dependencies = [ + "hmac", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "http" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" + +[[package]] +name = "hyper" +version = "0.14.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab302d72a6f11a3b910431ff93aae7e773078c769f0a3ef15fb9ec692ed147d4" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0722cd7114b7de04316e7ea5456a0bbb20e4adb46fd27a3697adb812cff0f37c" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "idna" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", + "serde", +] + +[[package]] +name = "indicatif" +version = "0.17.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ff8cc23a7393a397ed1d7f56e6365cba772aba9f9912ab968b03043c395d057" +dependencies = [ + "console", + "instant", + "number_prefix", + "portable-atomic", + "tokio", + "unicode-width", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "io-lifetimes" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" +dependencies = [ + "hermit-abi 0.3.1", + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "ipnet" +version = "2.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12b6ee2129af8d4fb011108c73d99a1b83a85977f23b82460c0ae2e25bb4b57f" + +[[package]] +name = "is-terminal" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" +dependencies = [ + "hermit-abi 0.3.1", + "io-lifetimes", + "rustix", + "windows-sys 0.48.0", +] + +[[package]] +name = "iso8601" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "924e5d73ea28f59011fec52a0d12185d496a9b075d360657aed2a5707f701153" +dependencies = [ + "nom", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" + +[[package]] +name = "js-sys" +version = "0.3.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f37a4a5928311ac501dee68b3c7613a1037d0edb30c8e5427bd832d55d1b790" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "jsonschema" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e48354c4c4f088714424ddf090de1ff84acc82b2f08c192d46d226ae2529a465" +dependencies = [ + "ahash 0.8.3", + "anyhow", + "base64 0.21.2", + "bytecount", + "clap 4.3.2", + "fancy-regex", + "fraction", + "getrandom", + "iso8601", + "itoa", + "memchr", + "num-cmp", + "once_cell", + "parking_lot 0.12.1", + "percent-encoding", + "regex", + "reqwest", + "serde", + "serde_json", + "time 0.3.21", + "url", + "uuid", +] + +[[package]] +name = "keccak" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.145" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc86cde3ff845662b8f4ef6cb50ea0e20c524eb3d29ae048287e06a1b3fa6a81" + +[[package]] +name = "libloading" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +dependencies = [ + "cfg-if", + "winapi", +] + +[[package]] +name = "libm" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4" + +[[package]] +name = "libsqlite3-sys" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "898745e570c7d0453cc1fbc4a701eb6c662ed54e8fec8b7d14be137ebeeb9d14" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "linux-raw-sys" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" + +[[package]] +name = "lock_api" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "518ef76f2f87365916b142844c16d8fefd85039bc5699050210a7778ee1cd1de" + +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata", +] + +[[package]] +name = "md-5" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6365506850d44bff6e2fbcb5176cf63650e48bd45ef2fe2665ae1570e0f4b9ca" +dependencies = [ + "digest", +] + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "migration" +version = "0.1.0" +dependencies = [ + "futures", + "indicatif", + "native-utils", + "redis", + "sea-orm", + "sea-orm-migration", + "serde", + "serde_json", + "serde_yaml", + "tokio", + "url", + "urlencoding", +] + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "mio" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" +dependencies = [ + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys 0.48.0", +] + +[[package]] +name = "napi" +version = "2.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7f0a2e93526dd9c8c522d72a4d0c88678be8966fabe9fb8f2947fde6339b682" +dependencies = [ + "bitflags 2.3.1", + "ctor 0.2.2", + "napi-derive", + "napi-sys", + "once_cell", + "tokio", +] + +[[package]] +name = "napi-build" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "882a73d9ef23e8dc2ebbffb6a6ae2ef467c0f18ac10711e4cc59c5485d41df0e" + +[[package]] +name = "napi-derive" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da1c6a8fa84d549aa8708fcd062372bf8ec6e849de39016ab921067d21bde367" +dependencies = [ + "cfg-if", + "convert_case 0.6.0", + "napi-derive-backend", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "napi-derive-backend" +version = "1.0.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20bbc7c69168d06a848f925ec5f0e0997f98e8c8d4f2cc30157f0da51c009e17" +dependencies = [ + "convert_case 0.6.0", + "once_cell", + "proc-macro2", + "quote", + "regex", + "semver", + "syn 1.0.109", +] + +[[package]] +name = "napi-sys" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "166b5ef52a3ab5575047a9fe8d4a030cdd0f63c96f071cd6907674453b07bae3" +dependencies = [ + "libloading", +] + +[[package]] +name = "native-utils" +version = "0.0.0" +dependencies = [ + "async-trait", + "cfg-if", + "chrono", + "cuid2", + "derive_more", + "jsonschema", + "napi", + "napi-build", + "napi-derive", + "once_cell", + "parse-display", + "pretty_assertions", + "radix_fmt", + "rand", + "schemars", + "sea-orm", + "serde", + "serde_json", + "thiserror", + "tokio", + "utoipa", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "num" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43db66d1170d347f9a065114077f7dccb00c1b9478c89384490a3425279a4606" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + +[[package]] +name = "num-bigint" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-cmp" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63335b2e2c34fae2fb0aa2cecfd9f0832a1e24b3b32ecec612c3426d46dc8aaa" + +[[package]] +name = "num-complex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02e0d21255c828d6f128a1e41534206671e8c3ea0c62f32291e808dc82cff17d" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +dependencies = [ + "autocfg", + "num-bigint", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "num_cpus" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +dependencies = [ + "hermit-abi 0.2.6", + "libc", +] + +[[package]] +name = "number_prefix" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" + +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "os_str_bytes" +version = "6.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ceedf44fb00f2d1984b0bc98102627ce622e083e49a5bacdb3e514fa4238e267" + +[[package]] +name = "ouroboros" +version = "0.15.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1358bd1558bd2a083fed428ffeda486fbfb323e698cdda7794259d592ca72db" +dependencies = [ + "aliasable", + "ouroboros_macro", +] + +[[package]] +name = "ouroboros_macro" +version = "0.15.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f7d21ccd03305a674437ee1248f3ab5d4b1db095cf1caf49f1713ddf61956b7" +dependencies = [ + "Inflector", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "output_vt100" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "628223faebab4e3e40667ee0b2336d34a5b960ff60ea743ddfdbcf7770bcfb66" +dependencies = [ + "winapi", +] + +[[package]] +name = "parking_lot" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" +dependencies = [ + "instant", + "lock_api", + "parking_lot_core 0.8.6", +] + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core 0.9.8", +] + +[[package]] +name = "parking_lot_core" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" +dependencies = [ + "cfg-if", + "instant", + "libc", + "redox_syscall 0.2.16", + "smallvec", + "winapi", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.3.5", + "smallvec", + "windows-targets 0.48.0", +] + +[[package]] +name = "parse-display" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f96cc033d72896bb9a2c239a14e1141c3e2eae6d649e7c10ef4e598d66bc86c" +dependencies = [ + "once_cell", + "parse-display-derive", + "regex", +] + +[[package]] +name = "parse-display-derive" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5587062be441f3d868f7c4c9d13c67f286b03aa679d7f8176ef80bf2ee79e5d" +dependencies = [ + "once_cell", + "proc-macro2", + "quote", + "regex", + "regex-syntax 0.6.29", + "structmeta", + "syn 1.0.109", +] + +[[package]] +name = "paste" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79" + +[[package]] +name = "percent-encoding" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" + +[[package]] +name = "pin-project" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c95a7476719eab1e366eaf73d0260af3021184f18177925b07f54b30089ceead" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39407670928234ebc5e6e580247dd567ad73a3578460c5990f9503df207e8f07" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.18", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" + +[[package]] +name = "portable-atomic" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "767eb9f07d4a5ebcb39bbf2d452058a93c011373abf6832e24194a1c3f004794" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "pretty_assertions" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a25e9bcb20aa780fd0bb16b72403a9064d6b3f22f026946029acb941a50af755" +dependencies = [ + "ctor 0.1.26", + "diff", + "output_vt100", + "yansi", +] + +[[package]] +name = "proc-macro-crate" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" +dependencies = [ + "toml", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.59" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6aeca18b86b413c660b781aa319e4e2648a3e6f9eadc9b47e9038e6fe9f3451b" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "proptest" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e35c06b98bf36aba164cc17cb25f7e232f5c4aeea73baa14b8a9f0d92dbfa65" +dependencies = [ + "bit-set", + "bitflags 1.3.2", + "byteorder", + "lazy_static", + "num-traits", + "rand", + "rand_chacha", + "rand_xorshift", + "regex-syntax 0.6.29", + "rusty-fork", + "tempfile", + "unarray", +] + +[[package]] +name = "ptr_meta" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" +dependencies = [ + "ptr_meta_derive", +] + +[[package]] +name = "ptr_meta_derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + +[[package]] +name = "quote" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "radix_fmt" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce082a9940a7ace2ad4a8b7d0b1eac6aa378895f18be598230c5f2284ac05426" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_xorshift" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" +dependencies = [ + "rand_core", +] + +[[package]] +name = "redis" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ea8c51b5dc1d8e5fd3350ec8167f464ec0995e79f2e90a075b63371500d557f" +dependencies = [ + "async-trait", + "bytes", + "combine", + "futures-util", + "itoa", + "percent-encoding", + "pin-project-lite", + "rustls 0.21.3", + "rustls-native-certs", + "ryu", + "sha1_smol", + "tokio", + "tokio-rustls 0.24.1", + "tokio-util", + "url", +] + +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "redox_users" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +dependencies = [ + "getrandom", + "redox_syscall 0.2.16", + "thiserror", +] + +[[package]] +name = "regex" +version = "1.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0ab3ca65655bb1e41f2a8c8cd662eb4fb035e67c3f78da1d61dffe89d07300f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.7.2", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", +] + +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "regex-syntax" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78" + +[[package]] +name = "rend" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581008d2099240d37fb08d77ad713bcaec2c4d89d50b5b21a8bb1996bbab68ab" +dependencies = [ + "bytecheck", +] + +[[package]] +name = "reqwest" +version = "0.11.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cde824a14b7c14f85caff81225f411faacc04a2013f41670f41443742b1c1c55" +dependencies = [ + "base64 0.21.2", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "ipnet", + "js-sys", + "log", + "mime", + "once_cell", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_json", + "serde_urlencoded", + "tokio", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg", +] + +[[package]] +name = "ring" +version = "0.16.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin 0.5.2", + "untrusted", + "web-sys", + "winapi", +] + +[[package]] +name = "rkyv" +version = "0.7.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0200c8230b013893c0b2d6213d6ec64ed2b9be2e0e016682b7224ff82cff5c58" +dependencies = [ + "bitvec", + "bytecheck", + "hashbrown 0.12.3", + "ptr_meta", + "rend", + "rkyv_derive", + "seahash", + "tinyvec", + "uuid", +] + +[[package]] +name = "rkyv_derive" +version = "0.7.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2e06b915b5c230a17d7a736d1e2e63ee753c256a8614ef3f5147b13a4f5541d" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "rust_decimal" +version = "1.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26bd36b60561ee1fb5ec2817f198b6fd09fa571c897a5e86d1487cfc2b096dfc" +dependencies = [ + "arrayvec", + "borsh", + "bytecheck", + "byteorder", + "bytes", + "num-traits", + "rand", + "rkyv", + "serde", + "serde_json", +] + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "rustix" +version = "0.37.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acf8729d8542766f1b2cf77eb034d52f40d375bb8b615d0b147089946e16613d" +dependencies = [ + "bitflags 1.3.2", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys", + "windows-sys 0.48.0", +] + +[[package]] +name = "rustls" +version = "0.20.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fff78fc74d175294f4e83b28343315ffcfb114b156f0185e9741cb5570f50e2f" +dependencies = [ + "log", + "ring", + "sct", + "webpki", +] + +[[package]] +name = "rustls" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b19faa85ecb5197342b54f987b142fb3e30d0c90da40f80ef4fa9a726e6676ed" +dependencies = [ + "log", + "ring", + "rustls-webpki", + "sct", +] + +[[package]] +name = "rustls-native-certs" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" +dependencies = [ + "openssl-probe", + "rustls-pemfile", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b" +dependencies = [ + "base64 0.21.2", +] + +[[package]] +name = "rustls-webpki" +version = "0.101.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15f36a6828982f422756984e47912a7a51dcbc2a197aa791158f8ca61cd8204e" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f3208ce4d8448b3f3e7d168a73f5e0c43a61e32930de3bceeccedb388b6bf06" + +[[package]] +name = "rusty-fork" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f" +dependencies = [ + "fnv", + "quick-error", + "tempfile", + "wait-timeout", +] + +[[package]] +name = "ryu" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" + +[[package]] +name = "schannel" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" +dependencies = [ + "windows-sys 0.48.0", +] + +[[package]] +name = "schemars" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02c613288622e5f0c3fdc5dbd4db1c5fbe752746b1d1a56a0630b78fd00de44f" +dependencies = [ + "chrono", + "dyn-clone", + "schemars_derive", + "serde", + "serde_json", +] + +[[package]] +name = "schemars_derive" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "109da1e6b197438deb6db99952990c7f959572794b80ff93707d55a232545e7c" +dependencies = [ + "proc-macro2", + "quote", + "serde_derive_internals", + "syn 1.0.109", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "sct" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "sea-orm" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fade86e8d41fd1a4721f84cb834f4ca2783f973cc30e6212b7fafc134f169214" +dependencies = [ + "async-stream", + "async-trait", + "bigdecimal", + "chrono", + "futures", + "log", + "ouroboros", + "rust_decimal", + "sea-orm-macros", + "sea-query", + "sea-query-binder", + "sea-strum", + "serde", + "serde_json", + "sqlx", + "thiserror", + "time 0.3.21", + "tracing", + "url", + "uuid", +] + +[[package]] +name = "sea-orm-cli" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efbf34a2caf70c2e3be9bb1e674e9540f6dfd7c8f40f6f05daf3b9740e476005" +dependencies = [ + "chrono", + "clap 3.2.25", + "dotenvy", + "regex", + "sea-schema", + "tracing", + "tracing-subscriber", + "url", +] + +[[package]] +name = "sea-orm-macros" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28936f26d62234ff0be16f80115dbdeb3237fe9c25cf18fbcd1e3b3592360f20" +dependencies = [ + "bae", + "heck 0.3.3", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "sea-orm-migration" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "278d3adfd0832b6ffc17d3cfbc574d3695a5c1b38814e0bc8ac238d33f3d87cf" +dependencies = [ + "async-trait", + "clap 3.2.25", + "dotenvy", + "futures", + "sea-orm", + "sea-orm-cli", + "sea-schema", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "sea-query" +version = "0.28.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbab99b8cd878ab7786157b7eb8df96333a6807cc6e45e8888c85b51534b401a" +dependencies = [ + "bigdecimal", + "chrono", + "rust_decimal", + "sea-query-derive", + "serde_json", + "time 0.3.21", + "uuid", +] + +[[package]] +name = "sea-query-binder" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cea85029985b40dfbf18318d85fe985c04db7c1b4e5e8e0a0a0cdff5f1e30f9" +dependencies = [ + "bigdecimal", + "chrono", + "rust_decimal", + "sea-query", + "serde_json", + "sqlx", + "time 0.3.21", + "uuid", +] + +[[package]] +name = "sea-query-derive" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63f62030c60f3a691f5fe251713b4e220b306e50a71e1d6f9cce1f24bb781978" +dependencies = [ + "heck 0.4.1", + "proc-macro2", + "quote", + "syn 1.0.109", + "thiserror", +] + +[[package]] +name = "sea-schema" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eeb2940bb5a10bc6cd05b450ce6cd3993e27fddd7eface2becb97fc5af3a040e" +dependencies = [ + "futures", + "sea-query", + "sea-schema-derive", +] + +[[package]] +name = "sea-schema-derive" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56821b7076f5096b8f726e2791ad255a99c82498e08ec477a65a96c461ff1927" +dependencies = [ + "heck 0.3.3", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "sea-strum" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "391d06a6007842cfe79ac6f7f53911b76dfd69fc9a6769f1cf6569d12ce20e1b" +dependencies = [ + "sea-strum_macros", +] + +[[package]] +name = "sea-strum_macros" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69b4397b825df6ccf1e98bcdabef3bbcfc47ff5853983467850eeab878384f21" +dependencies = [ + "heck 0.3.3", + "proc-macro2", + "quote", + "rustversion", + "syn 1.0.109", +] + +[[package]] +name = "seahash" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" + +[[package]] +name = "security-framework" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fc758eb7bffce5b308734e9b0c1468893cae9ff70ebf13e7090be8dcbcc83a8" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f51d0c0d83bec45f16480d0ce0058397a69e48fcdc52d1dc8855fb68acbd31a7" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "semver" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" + +[[package]] +name = "serde" +version = "1.0.163" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2113ab51b87a539ae008b5c6c02dc020ffa39afd2d83cffcb3f4eb2722cebec2" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.163" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c805777e3930c8883389c602315a24224bcc738b63905ef87cd1420353ea93e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.18", +] + +[[package]] +name = "serde_derive_internals" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "serde_json" +version = "1.0.96" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_yaml" +version = "0.9.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9d684e3ec7de3bf5466b32bd75303ac16f0736426e5a4e0d6e489559ce1249c" +dependencies = [ + "indexmap", + "itoa", + "ryu", + "serde", + "unsafe-libyaml", +] + +[[package]] +name = "sha1" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha1_smol" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" + +[[package]] +name = "sha2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest", + "keccak", +] + +[[package]] +name = "sharded-slab" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +dependencies = [ + "libc", +] + +[[package]] +name = "simdutf8" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f27f6278552951f1f2b8cf9da965d10969b2efdea95a6ec47987ab46edfe263a" + +[[package]] +name = "slab" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" + +[[package]] +name = "socket2" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + +[[package]] +name = "sqlformat" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c12bc9199d1db8234678b7051747c07f517cdcf019262d1847b94ec8b1aee3e" +dependencies = [ + "itertools", + "nom", + "unicode_categories", +] + +[[package]] +name = "sqlx" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8de3b03a925878ed54a954f621e64bf55a3c1bd29652d0d1a17830405350188" +dependencies = [ + "sqlx-core", + "sqlx-macros", +] + +[[package]] +name = "sqlx-core" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa8241483a83a3f33aa5fff7e7d9def398ff9990b2752b6c6112b83c6d246029" +dependencies = [ + "ahash 0.7.6", + "atoi", + "base64 0.13.1", + "bigdecimal", + "bitflags 1.3.2", + "byteorder", + "bytes", + "chrono", + "crossbeam-queue", + "dirs", + "dotenvy", + "either", + "event-listener", + "flume", + "futures-channel", + "futures-core", + "futures-executor", + "futures-intrusive", + "futures-util", + "hashlink", + "hex", + "hkdf", + "hmac", + "indexmap", + "itoa", + "libc", + "libsqlite3-sys", + "log", + "md-5", + "memchr", + "num-bigint", + "once_cell", + "paste", + "percent-encoding", + "rand", + "rust_decimal", + "rustls 0.20.8", + "rustls-pemfile", + "serde", + "serde_json", + "sha1", + "sha2", + "smallvec", + "sqlformat", + "sqlx-rt", + "stringprep", + "thiserror", + "time 0.3.21", + "tokio-stream", + "url", + "uuid", + "webpki-roots", + "whoami", +] + +[[package]] +name = "sqlx-macros" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9966e64ae989e7e575b19d7265cb79d7fc3cbbdf179835cb0d716f294c2049c9" +dependencies = [ + "dotenvy", + "either", + "heck 0.4.1", + "once_cell", + "proc-macro2", + "quote", + "serde_json", + "sqlx-core", + "sqlx-rt", + "syn 1.0.109", + "url", +] + +[[package]] +name = "sqlx-rt" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "804d3f245f894e61b1e6263c84b23ca675d96753b5abfd5cc8597d86806e8024" +dependencies = [ + "once_cell", + "tokio", + "tokio-rustls 0.23.4", +] + +[[package]] +name = "stringprep" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ee348cb74b87454fff4b551cbf727025810a004f88aeacae7f85b87f4e9a1c1" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "structmeta" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "104842d6278bf64aa9d2f182ba4bde31e8aec7a131d29b7f444bb9b344a09e2a" +dependencies = [ + "proc-macro2", + "quote", + "structmeta-derive", + "syn 1.0.109", +] + +[[package]] +name = "structmeta-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24420be405b590e2d746d83b01f09af673270cf80e9b003a5fa7b651c58c7d93" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "subtle" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "tempfile" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998" +dependencies = [ + "cfg-if", + "fastrand", + "redox_syscall 0.3.5", + "rustix", + "windows-sys 0.45.0", +] + +[[package]] +name = "textwrap" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" + +[[package]] +name = "thiserror" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.18", +] + +[[package]] +name = "thread_local" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "time" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" +dependencies = [ + "libc", + "wasi 0.10.0+wasi-snapshot-preview1", + "winapi", +] + +[[package]] +name = "time" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f3403384eaacbca9923fa06940178ac13e4edb725486d70e8e15881d0c836cc" +dependencies = [ + "itoa", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" + +[[package]] +name = "time-macros" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "372950940a5f07bf38dbe211d7283c9e6d7327df53794992d293e534c733d09b" +dependencies = [ + "time-core", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.28.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94d7b1cfd2aa4011f2de74c2c4c63665e27a71006b0a192dcd2710272e73dfa2" +dependencies = [ + "autocfg", + "bytes", + "libc", + "mio", + "num_cpus", + "parking_lot 0.12.1", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.48.0", +] + +[[package]] +name = "tokio-macros" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.18", +] + +[[package]] +name = "tokio-rustls" +version = "0.23.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" +dependencies = [ + "rustls 0.20.8", + "tokio", + "webpki", +] + +[[package]] +name = "tokio-rustls" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" +dependencies = [ + "rustls 0.21.3", + "tokio", +] + +[[package]] +name = "tokio-stream" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + +[[package]] +name = "toml" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +dependencies = [ + "serde", +] + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "tracing" +version = "0.1.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +dependencies = [ + "cfg-if", + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f57e3ca2a01450b1a921183a9c9cbfda207fd822cef4ccb00a65402cbba7a74" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.18", +] + +[[package]] +name = "tracing-core" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77" +dependencies = [ + "matchers", + "once_cell", + "regex", + "sharded-slab", + "thread_local", + "tracing", + "tracing-core", +] + +[[package]] +name = "try-lock" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" + +[[package]] +name = "typenum" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" + +[[package]] +name = "unarray" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" + +[[package]] +name = "unicode-bidi" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" + +[[package]] +name = "unicode-ident" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0" + +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-segmentation" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" + +[[package]] +name = "unicode-width" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" + +[[package]] +name = "unicode_categories" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" + +[[package]] +name = "unsafe-libyaml" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1865806a559042e51ab5414598446a5871b561d21b6764f2eabb0dd481d880a6" + +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + +[[package]] +name = "url" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "urlencoding" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8db7427f936968176eaa7cdf81b7f98b980b18495ec28f1b5791ac3bfe3eea9" + +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + +[[package]] +name = "utoipa" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ae74ef183fae36d650f063ae7bde1cacbe1cd7e72b617cbe1e985551878b98" +dependencies = [ + "indexmap", + "serde", + "serde_json", + "utoipa-gen", +] + +[[package]] +name = "utoipa-gen" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ea8ac818da7e746a63285594cce8a96f5e00ee31994e655bd827569cb8b137b" +dependencies = [ + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.18", +] + +[[package]] +name = "uuid" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "345444e32442451b267fc254ae85a209c64be56d2890e601a0c37ff0c3c5ecd2" +dependencies = [ + "serde", +] + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wait-timeout" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" +dependencies = [ + "libc", +] + +[[package]] +name = "want" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +dependencies = [ + "log", + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bba0e8cb82ba49ff4e229459ff22a191bbe9a1cb3a341610c9c33efc27ddf73" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b04bc93f9d6bdee709f6bd2118f57dd6679cf1176a1af464fca3ab0d66d8fb" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.18", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d1985d03709c53167ce907ff394f5316aa22cb4e12761295c5dc57dacb6297e" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14d6b024f1a526bb0234f52840389927257beb670610081360e5a03c5df9c258" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e128beba882dd1eb6200e1dc92ae6c5dbaa4311aa7bb211ca035779e5efc39f8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.18", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed9d5b4305409d1fc9482fee2d7f9bcbf24b3972bf59817ef757e23982242a93" + +[[package]] +name = "web-sys" +version = "0.3.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bdd9ef4e984da1187bf8110c5cf5b845fbc87a23602cdf912386a76fcd3a7c2" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "webpki-roots" +version = "0.22.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87" +dependencies = [ + "webpki", +] + +[[package]] +name = "whoami" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c70234412ca409cc04e864e89523cb0fc37f5e1344ebed5a3ebf4192b6b9f68" +dependencies = [ + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +dependencies = [ + "windows-targets 0.48.0", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.0", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +dependencies = [ + "windows_aarch64_gnullvm 0.48.0", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm 0.48.0", + "windows_x86_64_msvc 0.48.0", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" + +[[package]] +name = "winreg" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" +dependencies = [ + "winapi", +] + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "yansi" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" diff --git a/packages/backend/native-utils/Cargo.toml b/packages/backend/native-utils/Cargo.toml new file mode 100644 index 0000000000..6f4dd91759 --- /dev/null +++ b/packages/backend/native-utils/Cargo.toml @@ -0,0 +1,47 @@ +[package] +edition = "2021" +name = "native-utils" +version = "0.0.0" + +[workspace] +members = ["migration"] + +[features] +default = [] +noarray = [] +napi = ["dep:napi", "dep:napi-derive"] + +[lib] +crate-type = ["cdylib", "lib"] + +[dependencies] +async-trait = "0.1.68" +cfg-if = "1.0.0" +chrono = "0.4.24" +cuid2 = "0.1.0" +derive_more = "0.99.17" +jsonschema = "0.17.0" +once_cell = "1.17.1" +parse-display = "0.8.0" +rand = "0.8.5" +schemars = { version = "0.8.12", features = ["chrono"] } +sea-orm = { version = "0.11.3", features = ["sqlx-postgres", "postgres-array", "sqlx-sqlite", "runtime-tokio-rustls"] } +serde = { version = "1.0.163", features = ["derive"] } +serde_json = "1.0.96" +thiserror = "1.0.40" +tokio = { version = "1.28.1", features = ["full"] } +utoipa = "3.3.0" +radix_fmt = "1.0.0" + +# Default enable napi4 feature, see https://nodejs.org/api/n-api.html#node-api-version-matrix +napi = { version = "2.13.1", default-features = false, features = ["napi6", "tokio_rt"], optional = true } +napi-derive = { version = "2.12.0", optional = true } + +[dev-dependencies] +pretty_assertions = "1.3.0" + +[build-dependencies] +napi-build = "2.0.1" + +[profile.release] +lto = true diff --git a/packages/backend/native-utils/__test__/index.spec.mjs b/packages/backend/native-utils/__test__/index.spec.mjs new file mode 100644 index 0000000000..9ff8ead6c9 --- /dev/null +++ b/packages/backend/native-utils/__test__/index.spec.mjs @@ -0,0 +1,32 @@ +import test from "ava"; + +import { + convertId, + IdConvertType, + nativeInitIdGenerator, + nativeCreateId, + nativeRandomStr, +} from "../built/index.js"; + +test("convert to mastodon id", (t) => { + t.is(convertId("9gf61ehcxv", IdConvertType.MastodonId), "960365976481219"); + t.is( + convertId("9fbr9z0wbrjqyd3u", IdConvertType.MastodonId), + "2083785058661759970208986", + ); + t.is( + convertId("9fbs680oyviiqrol9md73p8g", IdConvertType.MastodonId), + "5878598648988104013828532260828151168", + ); +}); + +test("create cuid2 with timestamp prefix", (t) => { + nativeInitIdGenerator(16, ""); + t.not(nativeCreateId(Date.now()), nativeCreateId(Date.now())); + t.is(nativeCreateId(Date.now()).length, 16); +}); + +test("create random string", (t) => { + t.not(nativeRandomStr(16), nativeRandomStr(16)); + t.is(nativeRandomStr(24).length, 24); +}); diff --git a/packages/backend/native-utils/build.rs b/packages/backend/native-utils/build.rs new file mode 100644 index 0000000000..9fc2367889 --- /dev/null +++ b/packages/backend/native-utils/build.rs @@ -0,0 +1,5 @@ +extern crate napi_build; + +fn main() { + napi_build::setup(); +} diff --git a/packages/backend/native-utils/migration/Cargo.toml b/packages/backend/native-utils/migration/Cargo.toml new file mode 100644 index 0000000000..9bf793e042 --- /dev/null +++ b/packages/backend/native-utils/migration/Cargo.toml @@ -0,0 +1,37 @@ +[package] +name = "migration" +version = "0.1.0" +edition = "2021" +publish = false + +[lib] +name = "migration" +path = "src/lib.rs" + +[features] +default = [] +convert = ["dep:native-utils", "dep:indicatif", "dep:futures"] + +[dependencies] +serde_json = "1.0.96" +native-utils = { path = "../", optional = true } +indicatif = { version = "0.17.4", features = ["tokio"], optional = true } +tokio = { version = "1.28.2", features = ["full"] } +futures = { version = "0.3.28", optional = true } +serde_yaml = "0.9.21" +serde = { version = "1.0.163", features = ["derive"] } +urlencoding = "2.1.2" +redis = { version = "0.23.0", features = ["tokio-rustls-comp"] } +sea-orm = "0.11.3" +url = { version = "2.4.0", features = ["serde"] } + +[dependencies.sea-orm-migration] +version = "0.11.0" +features = [ + # Enable at least one `ASYNC_RUNTIME` and `DATABASE_DRIVER` feature if you want to run migration via CLI. + # View the list of supported features at https://www.sea-ql.org/SeaORM/docs/install-and-config/database-and-async-runtime. + # e.g. + "runtime-tokio-rustls", # `ASYNC_RUNTIME` feature + "sqlx-postgres", # `DATABASE_DRIVER` feature + "sqlx-sqlite", +] diff --git a/packages/backend/native-utils/migration/README.md b/packages/backend/native-utils/migration/README.md new file mode 100644 index 0000000000..1ac338d743 --- /dev/null +++ b/packages/backend/native-utils/migration/README.md @@ -0,0 +1,55 @@ +# Making migrations + +For more information, please read https://www.sea-ql.org/SeaORM/docs/migration/setting-up-migration/ + +- Install `sea-orm-cli` + ```sh + cargo install sea-orm-cli + ``` + +- Generate + ```sh + sea-orm-cli migrate generate **** + ``` + +# Running Migrator CLI + +- Generate a new migration file + ```sh + cargo run -- migrate generate MIGRATION_NAME + ``` +- Apply all pending migrations + ```sh + cargo run + ``` + ```sh + cargo run -- up + ``` +- Apply first 10 pending migrations + ```sh + cargo run -- up -n 10 + ``` +- Rollback last applied migrations + ```sh + cargo run -- down + ``` +- Rollback last 10 applied migrations + ```sh + cargo run -- down -n 10 + ``` +- Drop all tables from the database, then reapply all migrations + ```sh + cargo run -- fresh + ``` +- Rollback all applied migrations, then reapply all migrations + ```sh + cargo run -- refresh + ``` +- Rollback all applied migrations + ```sh + cargo run -- reset + ``` +- Check the status of all migrations + ```sh + cargo run -- status + ``` diff --git a/packages/backend/native-utils/migration/src/lib.rs b/packages/backend/native-utils/migration/src/lib.rs new file mode 100644 index 0000000000..5ad23f1625 --- /dev/null +++ b/packages/backend/native-utils/migration/src/lib.rs @@ -0,0 +1,18 @@ +pub use sea_orm_migration::prelude::*; + +mod m20230531_180824_drop_reversi; +mod m20230627_185451_index_note_url; +mod m20230709_000510_move_antenna_to_cache; + +pub struct Migrator; + +#[async_trait::async_trait] +impl MigratorTrait for Migrator { + fn migrations() -> Vec> { + vec![ + Box::new(m20230531_180824_drop_reversi::Migration), + Box::new(m20230627_185451_index_note_url::Migration), + Box::new(m20230709_000510_move_antenna_to_cache::Migration), + ] + } +} diff --git a/packages/backend/native-utils/migration/src/m20230531_180824_drop_reversi.rs b/packages/backend/native-utils/migration/src/m20230531_180824_drop_reversi.rs new file mode 100644 index 0000000000..32b8dae223 --- /dev/null +++ b/packages/backend/native-utils/migration/src/m20230531_180824_drop_reversi.rs @@ -0,0 +1,51 @@ +use sea_orm_migration::{ + prelude::*, + sea_orm::{DbBackend, Statement}, +}; + +#[derive(DeriveMigrationName)] +pub struct Migration; + +#[async_trait::async_trait] +impl MigrationTrait for Migration { + async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { + if manager.get_database_backend() == DbBackend::Sqlite { + return Ok(()); + } + + let db = manager.get_connection(); + db.query_one(Statement::from_string( + DbBackend::Postgres, + Table::drop() + .table(ReversiGame::Table) + .if_exists() + .to_string(PostgresQueryBuilder), + )) + .await?; + db.query_one(Statement::from_string( + DbBackend::Postgres, + Table::drop() + .table(ReversiMatching::Table) + .if_exists() + .to_string(PostgresQueryBuilder), + )) + .await?; + + Ok(()) + } + + async fn down(&self, _manager: &SchemaManager) -> Result<(), DbErr> { + // Replace the sample below with your own migration scripts + Ok(()) + } +} + +/// Learn more at https://docs.rs/sea-query#iden +#[derive(Iden)] +enum ReversiGame { + Table, +} +#[derive(Iden)] +enum ReversiMatching { + Table, +} diff --git a/packages/backend/native-utils/migration/src/m20230627_185451_index_note_url.rs b/packages/backend/native-utils/migration/src/m20230627_185451_index_note_url.rs new file mode 100644 index 0000000000..ceffed9c41 --- /dev/null +++ b/packages/backend/native-utils/migration/src/m20230627_185451_index_note_url.rs @@ -0,0 +1,38 @@ +use sea_orm_migration::prelude::*; + +#[derive(DeriveMigrationName)] +pub struct Migration; + +#[async_trait::async_trait] +impl MigrationTrait for Migration { + async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .create_index( + Index::create() + .name("IDX_note_url") + .table(Note::Table) + .col(Note::Url) + .if_not_exists() + .to_owned(), + ) + .await + } + + async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .drop_index( + Index::drop() + .name("IDX_note_url") + .table(Note::Table) + .to_owned(), + ) + .await + } +} + +/// Learn more at https://docs.rs/sea-query#iden +#[derive(Iden)] +enum Note { + Table, + Url, +} diff --git a/packages/backend/native-utils/migration/src/m20230709_000510_move_antenna_to_cache.rs b/packages/backend/native-utils/migration/src/m20230709_000510_move_antenna_to_cache.rs new file mode 100644 index 0000000000..83bd0448ab --- /dev/null +++ b/packages/backend/native-utils/migration/src/m20230709_000510_move_antenna_to_cache.rs @@ -0,0 +1,248 @@ +use redis::streams::StreamMaxlen; +use sea_orm::Statement; +use sea_orm_migration::prelude::*; +use std::env; + +#[derive(DeriveMigrationName)] +pub struct Migration; + +#[async_trait::async_trait] +impl MigrationTrait for Migration { + async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { + let cache_url = env::var("CACHE_URL").unwrap(); + let skip_copy = env::var("ANTENNA_MIGRATION_SKIP").unwrap_or_default(); + let copy_limit = env::var("ANTENNA_MIGRATION_COPY_LIMIT").unwrap_or_default(); + let read_limit: u64 = env::var("ANTENNA_MIGRATION_READ_LIMIT") + .unwrap_or("10000".to_string()) + .parse() + .unwrap(); + let copy_limit: i64 = match copy_limit.parse() { + Ok(limit) => limit, + Err(_) => 0, + }; + + if skip_copy == "true" { + println!("Skipped antenna migration"); + } else { + let prefix = env::var("CACHE_PREFIX").unwrap(); + + let db = manager.get_connection(); + let bk = manager.get_database_backend(); + + let count_stmt = + Statement::from_string(bk, "SELECT COUNT(1) FROM antenna_note".to_owned()); + let total_num = db + .query_one(count_stmt) + .await? + .unwrap() + .try_get_by_index::(0)?; + let copy_limit = if copy_limit > 0 { + copy_limit + } else { + total_num + }; + println!( + "Copying {} out of {} entries in antenna_note.", + copy_limit, total_num + ); + + let stmt_base = Query::select() + .column((AntennaNote::Table, AntennaNote::Id)) + .column(AntennaNote::AntennaId) + .column(AntennaNote::NoteId) + .from(AntennaNote::Table) + .order_by((AntennaNote::Table, AntennaNote::Id), Order::Asc) + .limit(read_limit) + .to_owned(); + + let mut stmt = stmt_base.clone(); + + let client = redis::Client::open(cache_url).unwrap(); + let mut redis_conn = client.get_connection().unwrap(); + + let mut remaining = total_num; + let mut pagination: i64 = 0; + + loop { + let res = db.query_all(bk.build(&stmt)).await?; + if res.len() == 0 { + break; + } + let val: Vec<(String, String, String)> = res + .iter() + .filter_map(|q| q.try_get_many_by_index().ok()) + .collect(); + + remaining -= val.len() as i64; + if remaining <= copy_limit { + let mut pipe = redis::pipe(); + for v in &val { + pipe.xadd_maxlen( + format!("{}:antennaTimeline:{}", prefix, v.1), + StreamMaxlen::Approx(200), + "*", + &[("note", v.2.to_owned())], + ) + .ignore(); + } + pipe.query::<()>(&mut redis_conn).unwrap(); + } + + let copied = total_num - remaining; + let copied = std::cmp::min(copied, total_num); + pagination += 1; + if pagination % 10 == 0 { + println!( + "Migrating antenna [{:.2}%]", + (copied as f64 / total_num as f64) * 100_f64, + ); + } + + if let Some((last_id, _, _)) = val.last() { + stmt = stmt_base + .clone() + .and_where( + Expr::col((AntennaNote::Table, AntennaNote::Id)).gt(last_id.to_owned()), + ) + .to_owned(); + } else { + break; + } + } + + println!("Migrating antenna [100.00%]"); + } + + manager + .drop_table( + Table::drop() + .table(AntennaNote::Table) + .if_exists() + .to_owned(), + ) + .await?; + + Ok(()) + } + + async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .create_table( + Table::create() + .table(AntennaNote::Table) + .if_not_exists() + .col( + ColumnDef::new(AntennaNote::Id) + .string_len(32) + .not_null() + .primary_key(), + ) + .col( + ColumnDef::new(AntennaNote::NoteId) + .string_len(32) + .not_null(), + ) + .col( + ColumnDef::new(AntennaNote::AntennaId) + .string_len(32) + .not_null(), + ) + .col( + ColumnDef::new(AntennaNote::Read) + .boolean() + .default(false) + .not_null(), + ) + .to_owned(), + ) + .await?; + manager + .create_index( + Index::create() + .name("IDX_0d775946662d2575dfd2068a5f") + .table(AntennaNote::Table) + .col(AntennaNote::AntennaId) + .if_not_exists() + .to_owned(), + ) + .await?; + manager + .create_index( + Index::create() + .name("IDX_bd0397be22147e17210940e125") + .table(AntennaNote::Table) + .col(AntennaNote::NoteId) + .if_not_exists() + .to_owned(), + ) + .await?; + manager + .create_index( + Index::create() + .name("IDX_335a0bf3f904406f9ef3dd51c2") + .table(AntennaNote::Table) + .col(AntennaNote::NoteId) + .col(AntennaNote::AntennaId) + .unique() + .if_not_exists() + .to_owned(), + ) + .await?; + manager + .create_index( + Index::create() + .name("IDX_9937ea48d7ae97ffb4f3f063a4") + .table(AntennaNote::Table) + .col(AntennaNote::Read) + .if_not_exists() + .to_owned(), + ) + .await?; + manager + .create_foreign_key( + ForeignKey::create() + .name("FK_0d775946662d2575dfd2068a5f5") + .from(AntennaNote::Table, AntennaNote::AntennaId) + .to(Antenna::Table, Antenna::Id) + .on_delete(ForeignKeyAction::Cascade) + .to_owned(), + ) + .await?; + manager + .create_foreign_key( + ForeignKey::create() + .name("FK_bd0397be22147e17210940e125b") + .from(AntennaNote::Table, AntennaNote::NoteId) + .to(Note::Table, Note::Id) + .on_delete(ForeignKeyAction::Cascade) + .to_owned(), + ) + .await?; + + Ok(()) + } +} + +/// Learn more at https://docs.rs/sea-query#iden +#[derive(Iden)] +enum AntennaNote { + Table, + Id, + #[iden = "noteId"] + NoteId, + #[iden = "antennaId"] + AntennaId, + Read, +} + +#[derive(Iden)] +enum Antenna { + Table, + Id, +} + +#[derive(Iden)] +enum Note { + Table, + Id, +} diff --git a/packages/backend/native-utils/migration/src/main.rs b/packages/backend/native-utils/migration/src/main.rs new file mode 100644 index 0000000000..896f1ed597 --- /dev/null +++ b/packages/backend/native-utils/migration/src/main.rs @@ -0,0 +1,107 @@ +use serde::Deserialize; +use std::env; +use std::fs; +use urlencoding::encode; + +use sea_orm_migration::prelude::*; + +const DB_URL_ENV: &str = "DATABASE_URL"; +const CACHE_URL_ENV: &str = "CACHE_URL"; +const CACHE_PREFIX_ENV: &str = "CACHE_PREFIX"; + +#[cfg(feature = "convert")] +mod vec_to_json; + +#[tokio::main] +async fn main() { + let cwd = env::current_dir().unwrap(); + let yml = fs::File::open(cwd.join("../../.config/default.yml")) + .expect("Failed to open '.config/default.yml'"); + let config: Config = serde_yaml::from_reader(yml).expect("Failed to parse yaml"); + + if env::var_os(DB_URL_ENV).is_none() { + env::set_var( + DB_URL_ENV, + format!( + "postgres://{}:{}@{}:{}/{}", + config.db.user, + encode(&config.db.pass), + config.db.host, + config.db.port, + config.db.db, + ), + ); + }; + + if env::var_os(CACHE_URL_ENV).is_none() { + let redis_conf = match config.cache_server { + None => config.redis, + Some(conf) => conf, + }; + let redis_proto = match redis_conf.tls { + None => "redis", + Some(_) => "rediss", + }; + let redis_uri_userpass = match redis_conf.user { + None => "".to_string(), + Some(user) => format!("{}:{}@", user, encode(&redis_conf.pass.unwrap_or_default())), + }; + let redis_uri_hostport = format!("{}:{}", redis_conf.host, redis_conf.port); + let redis_uri = format!( + "{}://{}{}/{}", + redis_proto, redis_uri_userpass, redis_uri_hostport, redis_conf.db + ); + env::set_var(CACHE_URL_ENV, redis_uri); + env::set_var( + CACHE_PREFIX_ENV, + if redis_conf.prefix.is_empty() { + config.url.host_str().unwrap() + } else { + &redis_conf.prefix + }, + ); + } + + cli::run_cli(migration::Migrator).await; + + #[cfg(feature = "convert")] + vec_to_json::convert().await; +} + +#[derive(Debug, PartialEq, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct Config { + pub url: url::Url, + pub db: DbConfig, + pub redis: RedisConfig, + pub cache_server: Option, +} + +#[derive(Debug, PartialEq, Deserialize)] +pub struct DbConfig { + pub host: String, + pub port: u32, + pub db: String, + pub user: String, + pub pass: String, +} + +#[derive(Debug, PartialEq, Deserialize)] +pub struct RedisConfig { + pub host: String, + pub port: u32, + pub user: Option, + pub pass: Option, + pub tls: Option, + #[serde(default)] + pub db: u32, + #[serde(default)] + pub prefix: String, +} + +#[derive(Debug, PartialEq, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct TlsConfig { + pub host: String, + pub reject_unauthorized: bool, +} diff --git a/packages/backend/native-utils/migration/src/vec_to_json.rs b/packages/backend/native-utils/migration/src/vec_to_json.rs new file mode 100644 index 0000000000..104357a496 --- /dev/null +++ b/packages/backend/native-utils/migration/src/vec_to_json.rs @@ -0,0 +1,498 @@ +use indicatif::{MultiProgress, ProgressBar, ProgressStyle}; +use native_utils::model::entity::newtype::{I32Vec, StringVec}; +use sea_orm_migration::{ + prelude::*, + sea_orm::{Database, DbBackend, DbConn, Statement, TryGetable}, +}; +use serde_json::json; +use std::env; +use std::time::Duration; + +pub async fn convert() { + let uri = env::var("DATABASE_URL").expect("Environment variable 'DATABASE_URL' not set"); + + let db = Database::connect(uri).await.expect("Unable to connect"); + let mp = MultiProgress::new(); + + let handlers = vec![ + tokio::spawn(to_json::, StringVec>( + db.clone(), + mp.clone(), + AccessToken::Table, + AccessToken::Id, + AccessToken::Permission, + )), + tokio::spawn(to_json::, StringVec>( + db.clone(), + mp.clone(), + Antenna::Table, + Antenna::Id, + Antenna::Users, + )), + tokio::spawn(to_json::, StringVec>( + db.clone(), + mp.clone(), + App::Table, + App::Id, + App::Permission, + )), + tokio::spawn(to_json::, StringVec>( + db.clone(), + mp.clone(), + Emoji::Table, + Emoji::Id, + Emoji::Aliases, + )), + tokio::spawn(to_json::, StringVec>( + db.clone(), + mp.clone(), + GalleryPost::Table, + GalleryPost::Id, + GalleryPost::FileIds, + )), + tokio::spawn(to_json::, StringVec>( + db.clone(), + mp.clone(), + GalleryPost::Table, + GalleryPost::Id, + GalleryPost::Tags, + )), + tokio::spawn(to_json::, StringVec>( + db.clone(), + mp.clone(), + Hashtag::Table, + Hashtag::Id, + Hashtag::MentionedUserIds, + )), + tokio::spawn(to_json::, StringVec>( + db.clone(), + mp.clone(), + Hashtag::Table, + Hashtag::Id, + Hashtag::MentionedLocalUserIds, + )), + tokio::spawn(to_json::, StringVec>( + db.clone(), + mp.clone(), + Hashtag::Table, + Hashtag::Id, + Hashtag::MentionedRemoteUserIds, + )), + tokio::spawn(to_json::, StringVec>( + db.clone(), + mp.clone(), + Hashtag::Table, + Hashtag::Id, + Hashtag::AttachedUserIds, + )), + tokio::spawn(to_json::, StringVec>( + db.clone(), + mp.clone(), + Hashtag::Table, + Hashtag::Id, + Hashtag::AttachedLocalUserIds, + )), + tokio::spawn(to_json::, StringVec>( + db.clone(), + mp.clone(), + Hashtag::Table, + Hashtag::Id, + Hashtag::AttachedRemoteUserIds, + )), + tokio::spawn(to_json::, StringVec>( + db.clone(), + mp.clone(), + MessagingMessage::Table, + MessagingMessage::Id, + MessagingMessage::Reads, + )), + tokio::spawn(to_json::, StringVec>( + db.clone(), + mp.clone(), + Meta::Table, + Meta::Id, + Meta::Langs, + )), + tokio::spawn(to_json::, StringVec>( + db.clone(), + mp.clone(), + Meta::Table, + Meta::Id, + Meta::BlockedHosts, + )), + tokio::spawn(to_json::, StringVec>( + db.clone(), + mp.clone(), + Meta::Table, + Meta::Id, + Meta::HiddenTags, + )), + tokio::spawn(to_json::, StringVec>( + db.clone(), + mp.clone(), + Meta::Table, + Meta::Id, + Meta::PinnedUsers, + )), + tokio::spawn(to_json::, StringVec>( + db.clone(), + mp.clone(), + Meta::Table, + Meta::Id, + Meta::PinnedPages, + )), + tokio::spawn(to_json::, StringVec>( + db.clone(), + mp.clone(), + Meta::Table, + Meta::Id, + Meta::RecommendedInstances, + )), + tokio::spawn(to_json::, StringVec>( + db.clone(), + mp.clone(), + Meta::Table, + Meta::Id, + Meta::SilencedHosts, + )), + tokio::spawn(to_json::, StringVec>( + db.clone(), + mp.clone(), + Note::Table, + Note::Id, + Note::FileIds, + )), + tokio::spawn(to_json::, StringVec>( + db.clone(), + mp.clone(), + Note::Table, + Note::Id, + Note::AttachedFileTypes, + )), + tokio::spawn(to_json::, StringVec>( + db.clone(), + mp.clone(), + Note::Table, + Note::Id, + Note::VisibleUserIds, + )), + tokio::spawn(to_json::, StringVec>( + db.clone(), + mp.clone(), + Note::Table, + Note::Id, + Note::Mentions, + )), + tokio::spawn(to_json::, StringVec>( + db.clone(), + mp.clone(), + Note::Table, + Note::Id, + Note::Emojis, + )), + tokio::spawn(to_json::, StringVec>( + db.clone(), + mp.clone(), + Note::Table, + Note::Id, + Note::Tags, + )), + tokio::spawn(to_json::, StringVec>( + db.clone(), + mp.clone(), + NoteEdit::Table, + NoteEdit::Id, + NoteEdit::FileIds, + )), + tokio::spawn(to_json::, StringVec>( + db.clone(), + mp.clone(), + Page::Table, + Page::Id, + Page::VisibleUserIds, + )), + tokio::spawn(to_json::, StringVec>( + db.clone(), + mp.clone(), + RegistryItem::Table, + RegistryItem::Id, + RegistryItem::Scope, + )), + tokio::spawn(to_json::, StringVec>( + db.clone(), + mp.clone(), + User::Table, + User::Id, + User::Tags, + )), + tokio::spawn(to_json::, StringVec>( + db.clone(), + mp.clone(), + User::Table, + User::Id, + User::Emojis, + )), + tokio::spawn(to_json::, StringVec>( + db.clone(), + mp.clone(), + Webhook::Table, + Webhook::Id, + Webhook::On, + )), + tokio::spawn(to_json::, StringVec>( + db.clone(), + mp.clone(), + Poll::Table, + Poll::NoteId, + Poll::Choices, + )), + tokio::spawn(to_json::, I32Vec>( + db.clone(), + mp.clone(), + Poll::Table, + Poll::NoteId, + Poll::Votes, + )), + tokio::spawn(to_json::, StringVec>( + db.clone(), + mp.clone(), + UserProfile::Table, + UserProfile::UserId, + UserProfile::MutingNotificationTypes, + )), + ]; + + futures::future::join_all(handlers).await; +} + +fn select_query(table: T, id: T, col: T) -> String { + Query::select() + .column(id) + .column(col) + .from(table) + .to_string(PostgresQueryBuilder) +} + +async fn get_vec(db: &DbConn, query: String) -> Result, DbErr> { + let res: Vec<(String, T)> = db + .query_all(Statement::from_string(DbBackend::Postgres, query)) + .await? + .iter() + .filter_map(|r| r.try_get_many_by_index().ok()) + .collect(); + Ok(res) +} + +async fn convert_col( + db: &DbConn, + table: T, + col: T, +) -> Result<(), DbErr> { + let stmt = Table::alter() + .table(table) + .drop_column(col.to_owned()) + .add_column( + ColumnDef::new(col.to_owned()) + .json_binary() + .not_null() + .default(json!([])), + ) + .to_string(PostgresQueryBuilder); + db.query_one(Statement::from_string(DbBackend::Postgres, stmt)) + .await?; + Ok(()) +} + +async fn to_json( + db: DbConn, + mp: MultiProgress, + table: T, + id: T, + col: T, +) -> Result<(), DbErr> +where + T: Iden + Clone + 'static, + U: TryGetable + IntoIterator + Clone, + V: From + Into, +{ + let query = select_query(table.clone(), id.clone(), col.clone()); + let loading = ProgressBar::new_spinner() + .with_style(ProgressStyle::with_template("{prefix} {msg} {spinner}").unwrap()) + .with_prefix("[-]") + .with_message(format!( + "Loading data from {}.{}", + table.to_string(), + col.to_string() + )); + let loading = mp.add(loading); + loading.enable_steady_tick(Duration::from_millis(100)); + let res = get_vec::(&db, query).await?; + let models: Vec<(String, V)> = res + .iter() + .filter(|(_, r)| r.clone().into_iter().count() > 0) + .map(|(id, r)| (id.clone(), ::from(r.clone()))) + .collect(); + loading.finish_and_clear(); + convert_col(&db, table.clone(), col.clone()).await?; + + let progress = ProgressBar::new(models.len() as u64) + .with_style( + ProgressStyle::with_template("{prefix} {msg} {wide_bar} {pos}/{len}") + .unwrap() + .progress_chars("##-"), + ) + .with_prefix("[*]") + .with_message(format!("Copying {}.{}", table.to_string(), col.to_string())); + let progress = mp.add(progress); + + for model in models { + progress.inc(1); + let q = Query::update() + .table(table.clone()) + .values([(col.clone(), model.1.into())]) + .and_where(Expr::col(id.clone()).eq(model.0)) + .to_string(PostgresQueryBuilder); + db.query_one(Statement::from_string(DbBackend::Postgres, q)) + .await?; + } + progress.finish_with_message(format!("Done {}.{}", table.to_string(), col.to_string())); + + Ok(()) +} + +#[derive(Iden, Clone)] +enum AccessToken { + Table, + Id, + Permission, +} +#[derive(Iden, Clone)] +enum Antenna { + Table, + Id, + Users, +} +#[derive(Iden, Clone)] +enum App { + Table, + Id, + Permission, +} +#[derive(Iden, Clone)] +enum Emoji { + Table, + Id, + Aliases, +} +#[derive(Iden, Clone)] +enum GalleryPost { + Table, + Id, + #[iden = "fileIds"] + FileIds, + Tags, +} +#[derive(Iden, Clone)] +enum Hashtag { + Table, + Id, + #[iden = "mentionedUserIds"] + MentionedUserIds, + #[iden = "mentionedLocalUserIds"] + MentionedLocalUserIds, + #[iden = "mentionedRemoteUserIds"] + MentionedRemoteUserIds, + #[iden = "attachedUserIds"] + AttachedUserIds, + #[iden = "attachedLocalUserIds"] + AttachedLocalUserIds, + #[iden = "attachedRemoteUserIds"] + AttachedRemoteUserIds, +} +#[derive(Iden, Clone)] +enum MessagingMessage { + Table, + Id, + Reads, +} +#[derive(Iden, Clone)] +enum Meta { + Table, + Id, + Langs, + #[iden = "hiddenTags"] + HiddenTags, + #[iden = "blockedHosts"] + BlockedHosts, + #[iden = "pinnedUsers"] + PinnedUsers, + #[iden = "pinnedPages"] + PinnedPages, + #[iden = "recommendedInstances"] + RecommendedInstances, + #[iden = "silencedHosts"] + SilencedHosts, +} +#[derive(Iden, Clone)] +enum Note { + Table, + Id, + #[iden = "fileIds"] + FileIds, + #[iden = "attachedFileTypes"] + AttachedFileTypes, + #[iden = "visibleUserIds"] + VisibleUserIds, + Mentions, + Emojis, + Tags, +} +#[derive(Iden, Clone)] +enum NoteEdit { + Table, + Id, + #[iden = "fileIds"] + FileIds, +} +#[derive(Iden, Clone)] +enum Page { + Table, + Id, + #[iden = "visibleUserIds"] + VisibleUserIds, +} +#[derive(Iden, Clone)] +enum Poll { + Table, + #[iden = "noteId"] + NoteId, + Choices, + Votes, // I32Vec +} +#[derive(Iden, Clone)] +enum RegistryItem { + Table, + Id, + Scope, +} +#[derive(Iden, Clone)] +enum User { + Table, + Id, + Tags, + Emojis, +} +#[derive(Iden, Clone)] +enum UserProfile { + Table, + #[iden = "userId"] + UserId, + #[iden = "mutingNotificationTypes"] + MutingNotificationTypes, +} +#[derive(Iden, Clone)] +enum Webhook { + Table, + Id, + On, +} diff --git a/packages/backend/native-utils/npm/android-arm-eabi/README.md b/packages/backend/native-utils/npm/android-arm-eabi/README.md new file mode 100644 index 0000000000..10199cb8ec --- /dev/null +++ b/packages/backend/native-utils/npm/android-arm-eabi/README.md @@ -0,0 +1,3 @@ +# `native-utils-android-arm-eabi` + +This is the **armv7-linux-androideabi** binary for `native-utils` diff --git a/packages/backend/native-utils/npm/android-arm-eabi/package.json b/packages/backend/native-utils/npm/android-arm-eabi/package.json new file mode 100644 index 0000000000..b4404c410a --- /dev/null +++ b/packages/backend/native-utils/npm/android-arm-eabi/package.json @@ -0,0 +1,18 @@ +{ + "name": "native-utils-android-arm-eabi", + "version": "0.0.0", + "os": [ + "android" + ], + "cpu": [ + "arm" + ], + "main": "native-utils.android-arm-eabi.node", + "files": [ + "native-utils.android-arm-eabi.node" + ], + "license": "MIT", + "engines": { + "node": ">= 10" + } +} \ No newline at end of file diff --git a/packages/backend/native-utils/npm/android-arm64/README.md b/packages/backend/native-utils/npm/android-arm64/README.md new file mode 100644 index 0000000000..c32c2fe710 --- /dev/null +++ b/packages/backend/native-utils/npm/android-arm64/README.md @@ -0,0 +1,3 @@ +# `native-utils-android-arm64` + +This is the **aarch64-linux-android** binary for `native-utils` diff --git a/packages/backend/native-utils/npm/android-arm64/package.json b/packages/backend/native-utils/npm/android-arm64/package.json new file mode 100644 index 0000000000..9050ef37bd --- /dev/null +++ b/packages/backend/native-utils/npm/android-arm64/package.json @@ -0,0 +1,18 @@ +{ + "name": "native-utils-android-arm64", + "version": "0.0.0", + "os": [ + "android" + ], + "cpu": [ + "arm64" + ], + "main": "native-utils.android-arm64.node", + "files": [ + "native-utils.android-arm64.node" + ], + "license": "MIT", + "engines": { + "node": ">= 10" + } +} \ No newline at end of file diff --git a/packages/backend/native-utils/npm/darwin-arm64/README.md b/packages/backend/native-utils/npm/darwin-arm64/README.md new file mode 100644 index 0000000000..8703902223 --- /dev/null +++ b/packages/backend/native-utils/npm/darwin-arm64/README.md @@ -0,0 +1,3 @@ +# `native-utils-darwin-arm64` + +This is the **aarch64-apple-darwin** binary for `native-utils` diff --git a/packages/backend/native-utils/npm/darwin-arm64/package.json b/packages/backend/native-utils/npm/darwin-arm64/package.json new file mode 100644 index 0000000000..a7fcef289f --- /dev/null +++ b/packages/backend/native-utils/npm/darwin-arm64/package.json @@ -0,0 +1,18 @@ +{ + "name": "native-utils-darwin-arm64", + "version": "0.0.0", + "os": [ + "darwin" + ], + "cpu": [ + "arm64" + ], + "main": "native-utils.darwin-arm64.node", + "files": [ + "native-utils.darwin-arm64.node" + ], + "license": "MIT", + "engines": { + "node": ">= 10" + } +} \ No newline at end of file diff --git a/packages/backend/native-utils/npm/darwin-universal/README.md b/packages/backend/native-utils/npm/darwin-universal/README.md new file mode 100644 index 0000000000..098bb35906 --- /dev/null +++ b/packages/backend/native-utils/npm/darwin-universal/README.md @@ -0,0 +1,3 @@ +# `native-utils-darwin-universal` + +This is the **universal-apple-darwin** binary for `native-utils` diff --git a/packages/backend/native-utils/npm/darwin-universal/package.json b/packages/backend/native-utils/npm/darwin-universal/package.json new file mode 100644 index 0000000000..a46061d421 --- /dev/null +++ b/packages/backend/native-utils/npm/darwin-universal/package.json @@ -0,0 +1,15 @@ +{ + "name": "native-utils-darwin-universal", + "version": "0.0.0", + "os": [ + "darwin" + ], + "main": "native-utils.darwin-universal.node", + "files": [ + "native-utils.darwin-universal.node" + ], + "license": "MIT", + "engines": { + "node": ">= 10" + } +} \ No newline at end of file diff --git a/packages/backend/native-utils/npm/darwin-x64/README.md b/packages/backend/native-utils/npm/darwin-x64/README.md new file mode 100644 index 0000000000..0acf363352 --- /dev/null +++ b/packages/backend/native-utils/npm/darwin-x64/README.md @@ -0,0 +1,3 @@ +# `native-utils-darwin-x64` + +This is the **x86_64-apple-darwin** binary for `native-utils` diff --git a/packages/backend/native-utils/npm/darwin-x64/package.json b/packages/backend/native-utils/npm/darwin-x64/package.json new file mode 100644 index 0000000000..6bbcf1d232 --- /dev/null +++ b/packages/backend/native-utils/npm/darwin-x64/package.json @@ -0,0 +1,18 @@ +{ + "name": "native-utils-darwin-x64", + "version": "0.0.0", + "os": [ + "darwin" + ], + "cpu": [ + "x64" + ], + "main": "native-utils.darwin-x64.node", + "files": [ + "native-utils.darwin-x64.node" + ], + "license": "MIT", + "engines": { + "node": ">= 10" + } +} \ No newline at end of file diff --git a/packages/backend/native-utils/npm/freebsd-x64/README.md b/packages/backend/native-utils/npm/freebsd-x64/README.md new file mode 100644 index 0000000000..2b74996de7 --- /dev/null +++ b/packages/backend/native-utils/npm/freebsd-x64/README.md @@ -0,0 +1,3 @@ +# `native-utils-freebsd-x64` + +This is the **x86_64-unknown-freebsd** binary for `native-utils` diff --git a/packages/backend/native-utils/npm/freebsd-x64/package.json b/packages/backend/native-utils/npm/freebsd-x64/package.json new file mode 100644 index 0000000000..654b8abf38 --- /dev/null +++ b/packages/backend/native-utils/npm/freebsd-x64/package.json @@ -0,0 +1,18 @@ +{ + "name": "native-utils-freebsd-x64", + "version": "0.0.0", + "os": [ + "freebsd" + ], + "cpu": [ + "x64" + ], + "main": "native-utils.freebsd-x64.node", + "files": [ + "native-utils.freebsd-x64.node" + ], + "license": "MIT", + "engines": { + "node": ">= 10" + } +} \ No newline at end of file diff --git a/packages/backend/native-utils/npm/linux-arm-gnueabihf/README.md b/packages/backend/native-utils/npm/linux-arm-gnueabihf/README.md new file mode 100644 index 0000000000..2203036de0 --- /dev/null +++ b/packages/backend/native-utils/npm/linux-arm-gnueabihf/README.md @@ -0,0 +1,3 @@ +# `native-utils-linux-arm-gnueabihf` + +This is the **armv7-unknown-linux-gnueabihf** binary for `native-utils` diff --git a/packages/backend/native-utils/npm/linux-arm-gnueabihf/package.json b/packages/backend/native-utils/npm/linux-arm-gnueabihf/package.json new file mode 100644 index 0000000000..1e206c078d --- /dev/null +++ b/packages/backend/native-utils/npm/linux-arm-gnueabihf/package.json @@ -0,0 +1,18 @@ +{ + "name": "native-utils-linux-arm-gnueabihf", + "version": "0.0.0", + "os": [ + "linux" + ], + "cpu": [ + "arm" + ], + "main": "native-utils.linux-arm-gnueabihf.node", + "files": [ + "native-utils.linux-arm-gnueabihf.node" + ], + "license": "MIT", + "engines": { + "node": ">= 10" + } +} \ No newline at end of file diff --git a/packages/backend/native-utils/npm/linux-arm64-gnu/README.md b/packages/backend/native-utils/npm/linux-arm64-gnu/README.md new file mode 100644 index 0000000000..ad3a9333f5 --- /dev/null +++ b/packages/backend/native-utils/npm/linux-arm64-gnu/README.md @@ -0,0 +1,3 @@ +# `native-utils-linux-arm64-gnu` + +This is the **aarch64-unknown-linux-gnu** binary for `native-utils` diff --git a/packages/backend/native-utils/npm/linux-arm64-gnu/package.json b/packages/backend/native-utils/npm/linux-arm64-gnu/package.json new file mode 100644 index 0000000000..aa0b2a805f --- /dev/null +++ b/packages/backend/native-utils/npm/linux-arm64-gnu/package.json @@ -0,0 +1,21 @@ +{ + "name": "native-utils-linux-arm64-gnu", + "version": "0.0.0", + "os": [ + "linux" + ], + "cpu": [ + "arm64" + ], + "main": "native-utils.linux-arm64-gnu.node", + "files": [ + "native-utils.linux-arm64-gnu.node" + ], + "license": "MIT", + "engines": { + "node": ">= 10" + }, + "libc": [ + "glibc" + ] +} \ No newline at end of file diff --git a/packages/backend/native-utils/npm/linux-arm64-musl/README.md b/packages/backend/native-utils/npm/linux-arm64-musl/README.md new file mode 100644 index 0000000000..df282532ff --- /dev/null +++ b/packages/backend/native-utils/npm/linux-arm64-musl/README.md @@ -0,0 +1,3 @@ +# `native-utils-linux-arm64-musl` + +This is the **aarch64-unknown-linux-musl** binary for `native-utils` diff --git a/packages/backend/native-utils/npm/linux-arm64-musl/package.json b/packages/backend/native-utils/npm/linux-arm64-musl/package.json new file mode 100644 index 0000000000..99e9387ee6 --- /dev/null +++ b/packages/backend/native-utils/npm/linux-arm64-musl/package.json @@ -0,0 +1,21 @@ +{ + "name": "native-utils-linux-arm64-musl", + "version": "0.0.0", + "os": [ + "linux" + ], + "cpu": [ + "arm64" + ], + "main": "native-utils.linux-arm64-musl.node", + "files": [ + "native-utils.linux-arm64-musl.node" + ], + "license": "MIT", + "engines": { + "node": ">= 10" + }, + "libc": [ + "musl" + ] +} \ No newline at end of file diff --git a/packages/backend/native-utils/npm/linux-x64-gnu/README.md b/packages/backend/native-utils/npm/linux-x64-gnu/README.md new file mode 100644 index 0000000000..52eea85aab --- /dev/null +++ b/packages/backend/native-utils/npm/linux-x64-gnu/README.md @@ -0,0 +1,3 @@ +# `native-utils-linux-x64-gnu` + +This is the **x86_64-unknown-linux-gnu** binary for `native-utils` diff --git a/packages/backend/native-utils/npm/linux-x64-gnu/package.json b/packages/backend/native-utils/npm/linux-x64-gnu/package.json new file mode 100644 index 0000000000..f99a5f664e --- /dev/null +++ b/packages/backend/native-utils/npm/linux-x64-gnu/package.json @@ -0,0 +1,21 @@ +{ + "name": "native-utils-linux-x64-gnu", + "version": "0.0.0", + "os": [ + "linux" + ], + "cpu": [ + "x64" + ], + "main": "native-utils.linux-x64-gnu.node", + "files": [ + "native-utils.linux-x64-gnu.node" + ], + "license": "MIT", + "engines": { + "node": ">= 10" + }, + "libc": [ + "glibc" + ] +} \ No newline at end of file diff --git a/packages/backend/native-utils/npm/linux-x64-musl/README.md b/packages/backend/native-utils/npm/linux-x64-musl/README.md new file mode 100644 index 0000000000..6664b23783 --- /dev/null +++ b/packages/backend/native-utils/npm/linux-x64-musl/README.md @@ -0,0 +1,3 @@ +# `native-utils-linux-x64-musl` + +This is the **x86_64-unknown-linux-musl** binary for `native-utils` diff --git a/packages/backend/native-utils/npm/linux-x64-musl/package.json b/packages/backend/native-utils/npm/linux-x64-musl/package.json new file mode 100644 index 0000000000..56b520fff4 --- /dev/null +++ b/packages/backend/native-utils/npm/linux-x64-musl/package.json @@ -0,0 +1,21 @@ +{ + "name": "native-utils-linux-x64-musl", + "version": "0.0.0", + "os": [ + "linux" + ], + "cpu": [ + "x64" + ], + "main": "native-utils.linux-x64-musl.node", + "files": [ + "native-utils.linux-x64-musl.node" + ], + "license": "MIT", + "engines": { + "node": ">= 10" + }, + "libc": [ + "musl" + ] +} \ No newline at end of file diff --git a/packages/backend/native-utils/npm/win32-arm64-msvc/README.md b/packages/backend/native-utils/npm/win32-arm64-msvc/README.md new file mode 100644 index 0000000000..7aec7e0a55 --- /dev/null +++ b/packages/backend/native-utils/npm/win32-arm64-msvc/README.md @@ -0,0 +1,3 @@ +# `native-utils-win32-arm64-msvc` + +This is the **aarch64-pc-windows-msvc** binary for `native-utils` diff --git a/packages/backend/native-utils/npm/win32-arm64-msvc/package.json b/packages/backend/native-utils/npm/win32-arm64-msvc/package.json new file mode 100644 index 0000000000..865a771052 --- /dev/null +++ b/packages/backend/native-utils/npm/win32-arm64-msvc/package.json @@ -0,0 +1,18 @@ +{ + "name": "native-utils-win32-arm64-msvc", + "version": "0.0.0", + "os": [ + "win32" + ], + "cpu": [ + "arm64" + ], + "main": "native-utils.win32-arm64-msvc.node", + "files": [ + "native-utils.win32-arm64-msvc.node" + ], + "license": "MIT", + "engines": { + "node": ">= 10" + } +} \ No newline at end of file diff --git a/packages/backend/native-utils/npm/win32-ia32-msvc/README.md b/packages/backend/native-utils/npm/win32-ia32-msvc/README.md new file mode 100644 index 0000000000..690de1975d --- /dev/null +++ b/packages/backend/native-utils/npm/win32-ia32-msvc/README.md @@ -0,0 +1,3 @@ +# `native-utils-win32-ia32-msvc` + +This is the **i686-pc-windows-msvc** binary for `native-utils` diff --git a/packages/backend/native-utils/npm/win32-ia32-msvc/package.json b/packages/backend/native-utils/npm/win32-ia32-msvc/package.json new file mode 100644 index 0000000000..994eff12fd --- /dev/null +++ b/packages/backend/native-utils/npm/win32-ia32-msvc/package.json @@ -0,0 +1,18 @@ +{ + "name": "native-utils-win32-ia32-msvc", + "version": "0.0.0", + "os": [ + "win32" + ], + "cpu": [ + "ia32" + ], + "main": "native-utils.win32-ia32-msvc.node", + "files": [ + "native-utils.win32-ia32-msvc.node" + ], + "license": "MIT", + "engines": { + "node": ">= 10" + } +} \ No newline at end of file diff --git a/packages/backend/native-utils/npm/win32-x64-msvc/README.md b/packages/backend/native-utils/npm/win32-x64-msvc/README.md new file mode 100644 index 0000000000..e34a5ff172 --- /dev/null +++ b/packages/backend/native-utils/npm/win32-x64-msvc/README.md @@ -0,0 +1,3 @@ +# `native-utils-win32-x64-msvc` + +This is the **x86_64-pc-windows-msvc** binary for `native-utils` diff --git a/packages/backend/native-utils/npm/win32-x64-msvc/package.json b/packages/backend/native-utils/npm/win32-x64-msvc/package.json new file mode 100644 index 0000000000..33b259b132 --- /dev/null +++ b/packages/backend/native-utils/npm/win32-x64-msvc/package.json @@ -0,0 +1,18 @@ +{ + "name": "native-utils-win32-x64-msvc", + "version": "0.0.0", + "os": [ + "win32" + ], + "cpu": [ + "x64" + ], + "main": "native-utils.win32-x64-msvc.node", + "files": [ + "native-utils.win32-x64-msvc.node" + ], + "license": "MIT", + "engines": { + "node": ">= 10" + } +} \ No newline at end of file diff --git a/packages/backend/native-utils/package.json b/packages/backend/native-utils/package.json new file mode 100644 index 0000000000..962b4bc4c4 --- /dev/null +++ b/packages/backend/native-utils/package.json @@ -0,0 +1,51 @@ +{ + "name": "native-utils", + "version": "0.0.0", + "main": "built/index.js", + "types": "built/index.d.ts", + "napi": { + "name": "native-utils", + "triples": { + "additional": [ + "aarch64-apple-darwin", + "aarch64-linux-android", + "aarch64-unknown-linux-gnu", + "aarch64-unknown-linux-musl", + "aarch64-pc-windows-msvc", + "armv7-unknown-linux-gnueabihf", + "x86_64-unknown-linux-musl", + "x86_64-unknown-freebsd", + "i686-pc-windows-msvc", + "armv7-linux-androideabi", + "universal-apple-darwin" + ] + } + }, + "license": "MIT", + "devDependencies": { + "@napi-rs/cli": "2.16.1", + "ava": "5.1.1" + }, + "ava": { + "timeout": "3m" + }, + "engines": { + "node": ">= 10" + }, + "scripts": { + "artifacts": "napi artifacts", + "build": "pnpm run build:napi && pnpm run build:migration", + "build:napi": "napi build --features napi --platform --release ./built/", + "build:migration": "cargo build --locked --release --manifest-path ./migration/Cargo.toml && cp ./target/release/migration ./built/migration", + "build:debug": "napi build --platform ./built/ && cargo build --manifest-path ./migration/Cargo.toml", + "prepublishOnly": "napi prepublish -t npm", + "test": "pnpm run cargo:test && pnpm run build:napi && ava", + "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/database/error.rs b/packages/backend/native-utils/src/database/error.rs new file mode 100644 index 0000000000..68e959e0af --- /dev/null +++ b/packages/backend/native-utils/src/database/error.rs @@ -0,0 +1,13 @@ +use sea_orm::error::DbErr; + +use crate::impl_into_napi_error; + +#[derive(thiserror::Error, Debug, PartialEq, Eq)] +pub enum Error { + #[error("The database connections have not been initialized yet")] + Uninitialized, + #[error("ORM error: {0}")] + OrmError(#[from] DbErr), +} + +impl_into_napi_error!(Error); diff --git a/packages/backend/native-utils/src/database/mod.rs b/packages/backend/native-utils/src/database/mod.rs new file mode 100644 index 0000000000..80189a8135 --- /dev/null +++ b/packages/backend/native-utils/src/database/mod.rs @@ -0,0 +1,38 @@ +pub mod error; + +use cfg_if::cfg_if; +use error::Error; +use sea_orm::{Database, DbConn}; + +static DB_CONN: once_cell::sync::OnceCell = once_cell::sync::OnceCell::new(); + +pub async fn init_database(conn_uri: impl Into) -> Result<(), Error> { + let conn = Database::connect(conn_uri.into()).await?; + DB_CONN.get_or_init(move || conn); + Ok(()) +} + +pub fn get_database() -> Result<&'static DbConn, Error> { + DB_CONN.get().ok_or(Error::Uninitialized) +} + +cfg_if! { + if #[cfg(feature = "napi")] { + use napi_derive::napi; + + #[napi] + pub async fn native_init_database(conn_uri: String) -> napi::Result<()> { + init_database(conn_uri).await.map_err(Into::into) + } + } +} + +#[cfg(test)] +mod unit_test { + use super::{error::Error, get_database}; + + #[test] + fn error_uninitialized() { + assert_eq!(get_database().unwrap_err(), Error::Uninitialized); + } +} diff --git a/packages/backend/native-utils/src/lib.rs b/packages/backend/native-utils/src/lib.rs new file mode 100644 index 0000000000..f18e69a48f --- /dev/null +++ b/packages/backend/native-utils/src/lib.rs @@ -0,0 +1,7 @@ +pub mod database; +pub mod macros; +pub mod model; +pub mod util; + +#[cfg(feature = "napi")] +pub mod mastodon_api; diff --git a/packages/backend/native-utils/src/macros.rs b/packages/backend/native-utils/src/macros.rs new file mode 100644 index 0000000000..49ab826329 --- /dev/null +++ b/packages/backend/native-utils/src/macros.rs @@ -0,0 +1,11 @@ +#[macro_export] +macro_rules! impl_into_napi_error { + ($a:ty) => { + #[cfg(feature = "napi")] + impl Into for $a { + fn into(self) -> napi::Error { + napi::Error::from_reason(self.to_string()) + } + } + }; +} diff --git a/packages/backend/native-utils/src/mastodon_api.rs b/packages/backend/native-utils/src/mastodon_api.rs new file mode 100644 index 0000000000..150a762455 --- /dev/null +++ b/packages/backend/native-utils/src/mastodon_api.rs @@ -0,0 +1,70 @@ +use napi::{bindgen_prelude::*, Error, Status}; +use napi_derive::napi; + +static CHAR_COLLECTION: &str = "0123456789abcdefghijklmnopqrstuvwxyz"; + +// -- NAPI exports -- + +#[napi] +pub enum IdConvertType { + MastodonId, + FirefishId, +} + +#[napi] +pub fn convert_id(in_id: String, id_convert_type: IdConvertType) -> napi::Result { + use IdConvertType::*; + match id_convert_type { + MastodonId => { + let mut out: i128 = 0; + for (i, c) in in_id.to_lowercase().chars().rev().enumerate() { + out += num_from_char(c)? as i128 * 36_i128.pow(i as u32); + } + + Ok(out.to_string()) + } + FirefishId => { + let mut input: i128 = match in_id.parse() { + Ok(s) => s, + Err(_) => { + return Err(Error::new( + Status::InvalidArg, + "Unable to parse ID as MastodonId", + )) + } + }; + let mut out = String::new(); + + while input != 0 { + out.insert(0, char_from_num((input % 36) as u8)?); + input /= 36; + } + + Ok(out) + } + } +} + +// -- end -- + +#[inline(always)] +fn num_from_char(character: char) -> napi::Result { + for (i, c) in CHAR_COLLECTION.chars().enumerate() { + if c == character { + return Ok(i as u8); + } + } + + Err(Error::new( + Status::InvalidArg, + "Invalid character in parsed base36 id", + )) +} + +#[inline(always)] +fn char_from_num(number: u8) -> napi::Result { + CHAR_COLLECTION + .chars() + .nth(number as usize) + .ok_or(Error::from_status(Status::Unknown)) +} diff --git a/packages/backend/native-utils/src/model/entity.rs b/packages/backend/native-utils/src/model/entity.rs new file mode 100644 index 0000000000..ccf3f00601 --- /dev/null +++ b/packages/backend/native-utils/src/model/entity.rs @@ -0,0 +1,73 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3 + +pub mod prelude; + +pub mod abuse_user_report; +pub mod access_token; +pub mod ad; +pub mod announcement; +pub mod announcement_read; +pub mod antenna; +pub mod app; +pub mod attestation_challenge; +pub mod auth_session; +pub mod blocking; +pub mod channel; +pub mod channel_following; +pub mod channel_note_pining; +pub mod clip; +pub mod clip_note; +pub mod drive_file; +pub mod drive_folder; +pub mod emoji; +pub mod follow_request; +pub mod following; +pub mod gallery_like; +pub mod gallery_post; +pub mod hashtag; +pub mod instance; +pub mod messaging_message; +pub mod meta; +pub mod migrations; +pub mod moderation_log; +pub mod muted_note; +pub mod muting; +pub mod newtype; +pub mod note; +pub mod note_edit; +pub mod note_favorite; +pub mod note_reaction; +pub mod note_thread_muting; +pub mod note_unread; +pub mod note_watching; +pub mod notification; +pub mod page; +pub mod page_like; +pub mod password_reset_request; +pub mod poll; +pub mod poll_vote; +pub mod promo_note; +pub mod promo_read; +pub mod registration_ticket; +pub mod registry_item; +pub mod relay; +pub mod renote_muting; +pub mod sea_orm_active_enums; +pub mod signin; +pub mod sw_subscription; +pub mod used_username; +pub mod user; +pub mod user_group; +pub mod user_group_invitation; +pub mod user_group_invite; +pub mod user_group_joining; +pub mod user_ip; +pub mod user_keypair; +pub mod user_list; +pub mod user_list_joining; +pub mod user_note_pining; +pub mod user_pending; +pub mod user_profile; +pub mod user_publickey; +pub mod user_security_key; +pub mod webhook; diff --git a/packages/backend/native-utils/src/model/entity/abuse_user_report.rs b/packages/backend/native-utils/src/model/entity/abuse_user_report.rs new file mode 100644 index 0000000000..24230b3949 --- /dev/null +++ b/packages/backend/native-utils/src/model/entity/abuse_user_report.rs @@ -0,0 +1,55 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3 + +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Default)] +#[sea_orm(table_name = "abuse_user_report")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub id: String, + #[sea_orm(column_name = "createdAt")] + pub created_at: DateTimeWithTimeZone, + #[sea_orm(column_name = "targetUserId")] + pub target_user_id: String, + #[sea_orm(column_name = "reporterId")] + pub reporter_id: String, + #[sea_orm(column_name = "assigneeId")] + pub assignee_id: Option, + pub resolved: bool, + pub comment: String, + #[sea_orm(column_name = "targetUserHost")] + pub target_user_host: Option, + #[sea_orm(column_name = "reporterHost")] + pub reporter_host: Option, + pub forwarded: bool, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::user::Entity", + from = "Column::ReporterId", + to = "super::user::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + User3, + #[sea_orm( + belongs_to = "super::user::Entity", + from = "Column::AssigneeId", + to = "super::user::Column::Id", + on_update = "NoAction", + on_delete = "SetNull" + )] + User2, + #[sea_orm( + belongs_to = "super::user::Entity", + from = "Column::TargetUserId", + to = "super::user::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + User1, +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/packages/backend/native-utils/src/model/entity/access_token.rs b/packages/backend/native-utils/src/model/entity/access_token.rs new file mode 100644 index 0000000000..dd9289224f --- /dev/null +++ b/packages/backend/native-utils/src/model/entity/access_token.rs @@ -0,0 +1,71 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3 + +use sea_orm::entity::prelude::*; + +use super::newtype::StringVec; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Default)] +#[sea_orm(table_name = "access_token")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub id: String, + #[sea_orm(column_name = "createdAt")] + pub created_at: DateTimeWithTimeZone, + pub token: String, + pub hash: String, + #[sea_orm(column_name = "userId")] + pub user_id: String, + #[sea_orm(column_name = "appId")] + pub app_id: Option, + #[sea_orm(column_name = "lastUsedAt")] + pub last_used_at: Option, + pub session: Option, + pub name: Option, + pub description: Option, + #[sea_orm(column_name = "iconUrl")] + pub icon_url: Option, + pub permission: StringVec, + pub fetched: bool, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::app::Entity", + from = "Column::AppId", + to = "super::app::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + App, + #[sea_orm(has_many = "super::notification::Entity")] + Notification, + #[sea_orm( + belongs_to = "super::user::Entity", + from = "Column::UserId", + to = "super::user::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + User, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::App.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Notification.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::User.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/packages/backend/native-utils/src/model/entity/ad.rs b/packages/backend/native-utils/src/model/entity/ad.rs new file mode 100644 index 0000000000..2cf7a6fc87 --- /dev/null +++ b/packages/backend/native-utils/src/model/entity/ad.rs @@ -0,0 +1,26 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3 + +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Default)] +#[sea_orm(table_name = "ad")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub id: String, + #[sea_orm(column_name = "createdAt")] + pub created_at: DateTimeWithTimeZone, + #[sea_orm(column_name = "expiresAt")] + pub expires_at: DateTimeWithTimeZone, + pub place: String, + pub priority: String, + pub url: String, + #[sea_orm(column_name = "imageUrl")] + pub image_url: String, + pub memo: String, + pub ratio: i32, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation {} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/packages/backend/native-utils/src/model/entity/announcement.rs b/packages/backend/native-utils/src/model/entity/announcement.rs new file mode 100644 index 0000000000..5cdb690d23 --- /dev/null +++ b/packages/backend/native-utils/src/model/entity/announcement.rs @@ -0,0 +1,36 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3 + +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Default)] +#[sea_orm(table_name = "announcement")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub id: String, + #[sea_orm(column_name = "createdAt")] + pub created_at: DateTimeWithTimeZone, + pub text: String, + pub title: String, + #[sea_orm(column_name = "imageUrl")] + pub image_url: Option, + #[sea_orm(column_name = "updatedAt")] + pub updated_at: Option, + #[sea_orm(column_name = "showPopup")] + pub show_popup: bool, + #[sea_orm(column_name = "isGoodNews")] + pub is_good_news: bool, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm(has_many = "super::announcement_read::Entity")] + AnnouncementRead, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::AnnouncementRead.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/packages/backend/native-utils/src/model/entity/announcement_read.rs b/packages/backend/native-utils/src/model/entity/announcement_read.rs new file mode 100644 index 0000000000..53ff8d6cef --- /dev/null +++ b/packages/backend/native-utils/src/model/entity/announcement_read.rs @@ -0,0 +1,50 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3 + +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Default)] +#[sea_orm(table_name = "announcement_read")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub id: String, + #[sea_orm(column_name = "userId")] + pub user_id: String, + #[sea_orm(column_name = "announcementId")] + pub announcement_id: String, + #[sea_orm(column_name = "createdAt")] + pub created_at: DateTimeWithTimeZone, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::announcement::Entity", + from = "Column::AnnouncementId", + to = "super::announcement::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + Announcement, + #[sea_orm( + belongs_to = "super::user::Entity", + from = "Column::UserId", + to = "super::user::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + User, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Announcement.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::User.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/packages/backend/native-utils/src/model/entity/antenna.rs b/packages/backend/native-utils/src/model/entity/antenna.rs new file mode 100644 index 0000000000..9d5a64f82f --- /dev/null +++ b/packages/backend/native-utils/src/model/entity/antenna.rs @@ -0,0 +1,84 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3 + +use super::{newtype, sea_orm_active_enums::AntennaSrcEnum}; +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Default)] +#[sea_orm(table_name = "antenna")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub id: String, + #[sea_orm(column_name = "createdAt")] + pub created_at: DateTimeWithTimeZone, + #[sea_orm(column_name = "userId")] + pub user_id: String, + pub name: String, + pub src: AntennaSrcEnum, + #[sea_orm(column_name = "userListId")] + pub user_list_id: Option, + #[sea_orm(column_type = "JsonBinary")] + pub keywords: newtype::JsonKeyword, + #[sea_orm(column_name = "withFile")] + pub with_file: bool, + pub expression: Option, + pub notify: bool, + #[sea_orm(column_name = "caseSensitive")] + pub case_sensitive: bool, + #[sea_orm(column_name = "withReplies")] + pub with_replies: bool, + #[sea_orm(column_name = "userGroupJoiningId")] + pub user_group_joining_id: Option, + pub users: newtype::StringVec, + #[sea_orm(column_name = "excludeKeywords", column_type = "JsonBinary")] + pub exclude_keywords: newtype::JsonKeyword, + #[sea_orm(column_type = "JsonBinary")] + pub instances: newtype::JsonStringVec, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::user::Entity", + from = "Column::UserId", + to = "super::user::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + User, + #[sea_orm( + belongs_to = "super::user_group_joining::Entity", + from = "Column::UserGroupJoiningId", + to = "super::user_group_joining::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + UserGroupJoining, + #[sea_orm( + belongs_to = "super::user_list::Entity", + from = "Column::UserListId", + to = "super::user_list::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + UserList, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::User.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::UserGroupJoining.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::UserList.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/packages/backend/native-utils/src/model/entity/antenna_note.rs b/packages/backend/native-utils/src/model/entity/antenna_note.rs new file mode 100644 index 0000000000..c86fb349d4 --- /dev/null +++ b/packages/backend/native-utils/src/model/entity/antenna_note.rs @@ -0,0 +1,49 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3 + +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Default)] +#[sea_orm(table_name = "antenna_note")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub id: String, + #[sea_orm(column_name = "noteId")] + pub note_id: String, + #[sea_orm(column_name = "antennaId")] + pub antenna_id: String, + pub read: bool, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::antenna::Entity", + from = "Column::AntennaId", + to = "super::antenna::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + Antenna, + #[sea_orm( + belongs_to = "super::note::Entity", + from = "Column::NoteId", + to = "super::note::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + Note, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Antenna.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Note.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/packages/backend/native-utils/src/model/entity/app.rs b/packages/backend/native-utils/src/model/entity/app.rs new file mode 100644 index 0000000000..6400d0b24e --- /dev/null +++ b/packages/backend/native-utils/src/model/entity/app.rs @@ -0,0 +1,58 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3 + +use sea_orm::entity::prelude::*; + +use super::newtype::StringVec; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Default)] +#[sea_orm(table_name = "app")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub id: String, + #[sea_orm(column_name = "createdAt")] + pub created_at: DateTimeWithTimeZone, + #[sea_orm(column_name = "userId")] + pub user_id: Option, + pub secret: String, + pub name: String, + pub description: String, + pub permission: StringVec, + #[sea_orm(column_name = "callbackUrl")] + pub callback_url: Option, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm(has_many = "super::access_token::Entity")] + AccessToken, + #[sea_orm(has_many = "super::auth_session::Entity")] + AuthSession, + #[sea_orm( + belongs_to = "super::user::Entity", + from = "Column::UserId", + to = "super::user::Column::Id", + on_update = "NoAction", + on_delete = "SetNull" + )] + User, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::AccessToken.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::AuthSession.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::User.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/packages/backend/native-utils/src/model/entity/attestation_challenge.rs b/packages/backend/native-utils/src/model/entity/attestation_challenge.rs new file mode 100644 index 0000000000..5217b2796d --- /dev/null +++ b/packages/backend/native-utils/src/model/entity/attestation_challenge.rs @@ -0,0 +1,37 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3 + +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Default)] +#[sea_orm(table_name = "attestation_challenge")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub id: String, + #[sea_orm(column_name = "userId", primary_key, auto_increment = false)] + pub user_id: String, + pub challenge: String, + #[sea_orm(column_name = "createdAt")] + pub created_at: DateTimeWithTimeZone, + #[sea_orm(column_name = "registrationChallenge")] + pub registration_challenge: bool, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::user::Entity", + from = "Column::UserId", + to = "super::user::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + User, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::User.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/packages/backend/native-utils/src/model/entity/auth_session.rs b/packages/backend/native-utils/src/model/entity/auth_session.rs new file mode 100644 index 0000000000..8ced191c3b --- /dev/null +++ b/packages/backend/native-utils/src/model/entity/auth_session.rs @@ -0,0 +1,51 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3 + +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Default)] +#[sea_orm(table_name = "auth_session")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub id: String, + #[sea_orm(column_name = "createdAt")] + pub created_at: DateTimeWithTimeZone, + pub token: String, + #[sea_orm(column_name = "userId")] + pub user_id: Option, + #[sea_orm(column_name = "appId")] + pub app_id: String, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::app::Entity", + from = "Column::AppId", + to = "super::app::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + App, + #[sea_orm( + belongs_to = "super::user::Entity", + from = "Column::UserId", + to = "super::user::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + User, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::App.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::User.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/packages/backend/native-utils/src/model/entity/blocking.rs b/packages/backend/native-utils/src/model/entity/blocking.rs new file mode 100644 index 0000000000..4f326f6faa --- /dev/null +++ b/packages/backend/native-utils/src/model/entity/blocking.rs @@ -0,0 +1,38 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3 + +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Default)] +#[sea_orm(table_name = "blocking")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub id: String, + #[sea_orm(column_name = "createdAt")] + pub created_at: DateTimeWithTimeZone, + #[sea_orm(column_name = "blockeeId")] + pub blockee_id: String, + #[sea_orm(column_name = "blockerId")] + pub blocker_id: String, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::user::Entity", + from = "Column::BlockerId", + to = "super::user::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + User2, + #[sea_orm( + belongs_to = "super::user::Entity", + from = "Column::BlockeeId", + to = "super::user::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + User1, +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/packages/backend/native-utils/src/model/entity/channel.rs b/packages/backend/native-utils/src/model/entity/channel.rs new file mode 100644 index 0000000000..abc79b4f57 --- /dev/null +++ b/packages/backend/native-utils/src/model/entity/channel.rs @@ -0,0 +1,82 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3 + +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Default)] +#[sea_orm(table_name = "channel")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub id: String, + #[sea_orm(column_name = "createdAt")] + pub created_at: DateTimeWithTimeZone, + #[sea_orm(column_name = "lastNotedAt")] + pub last_noted_at: Option, + #[sea_orm(column_name = "userId")] + pub user_id: Option, + pub name: String, + pub description: Option, + #[sea_orm(column_name = "bannerId")] + pub banner_id: Option, + #[sea_orm(column_name = "notesCount")] + pub notes_count: i32, + #[sea_orm(column_name = "usersCount")] + pub users_count: i32, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm(has_many = "super::channel_following::Entity")] + ChannelFollowing, + #[sea_orm(has_many = "super::channel_note_pining::Entity")] + ChannelNotePining, + #[sea_orm( + belongs_to = "super::drive_file::Entity", + from = "Column::BannerId", + to = "super::drive_file::Column::Id", + on_update = "NoAction", + on_delete = "SetNull" + )] + DriveFile, + #[sea_orm(has_many = "super::note::Entity")] + Note, + #[sea_orm( + belongs_to = "super::user::Entity", + from = "Column::UserId", + to = "super::user::Column::Id", + on_update = "NoAction", + on_delete = "SetNull" + )] + User, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::ChannelFollowing.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::ChannelNotePining.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::DriveFile.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Note.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::User.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/packages/backend/native-utils/src/model/entity/channel_following.rs b/packages/backend/native-utils/src/model/entity/channel_following.rs new file mode 100644 index 0000000000..93739459aa --- /dev/null +++ b/packages/backend/native-utils/src/model/entity/channel_following.rs @@ -0,0 +1,50 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3 + +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Default)] +#[sea_orm(table_name = "channel_following")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub id: String, + #[sea_orm(column_name = "createdAt")] + pub created_at: DateTimeWithTimeZone, + #[sea_orm(column_name = "followeeId")] + pub followee_id: String, + #[sea_orm(column_name = "followerId")] + pub follower_id: String, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::channel::Entity", + from = "Column::FolloweeId", + to = "super::channel::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + Channel, + #[sea_orm( + belongs_to = "super::user::Entity", + from = "Column::FollowerId", + to = "super::user::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + User, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Channel.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::User.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/packages/backend/native-utils/src/model/entity/channel_note_pining.rs b/packages/backend/native-utils/src/model/entity/channel_note_pining.rs new file mode 100644 index 0000000000..50ec1ecefb --- /dev/null +++ b/packages/backend/native-utils/src/model/entity/channel_note_pining.rs @@ -0,0 +1,50 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3 + +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Default)] +#[sea_orm(table_name = "channel_note_pining")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub id: String, + #[sea_orm(column_name = "createdAt")] + pub created_at: DateTimeWithTimeZone, + #[sea_orm(column_name = "channelId")] + pub channel_id: String, + #[sea_orm(column_name = "noteId")] + pub note_id: String, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::channel::Entity", + from = "Column::ChannelId", + to = "super::channel::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + Channel, + #[sea_orm( + belongs_to = "super::note::Entity", + from = "Column::NoteId", + to = "super::note::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + Note, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Channel.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Note.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/packages/backend/native-utils/src/model/entity/clip.rs b/packages/backend/native-utils/src/model/entity/clip.rs new file mode 100644 index 0000000000..a51ef720ed --- /dev/null +++ b/packages/backend/native-utils/src/model/entity/clip.rs @@ -0,0 +1,46 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3 + +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Default)] +#[sea_orm(table_name = "clip")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub id: String, + #[sea_orm(column_name = "createdAt")] + pub created_at: DateTimeWithTimeZone, + #[sea_orm(column_name = "userId")] + pub user_id: String, + pub name: String, + #[sea_orm(column_name = "isPublic")] + pub is_public: bool, + pub description: Option, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm(has_many = "super::clip_note::Entity")] + ClipNote, + #[sea_orm( + belongs_to = "super::user::Entity", + from = "Column::UserId", + to = "super::user::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + User, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::ClipNote.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::User.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/packages/backend/native-utils/src/model/entity/clip_note.rs b/packages/backend/native-utils/src/model/entity/clip_note.rs new file mode 100644 index 0000000000..a8bfd4564c --- /dev/null +++ b/packages/backend/native-utils/src/model/entity/clip_note.rs @@ -0,0 +1,48 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3 + +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Default)] +#[sea_orm(table_name = "clip_note")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub id: String, + #[sea_orm(column_name = "noteId")] + pub note_id: String, + #[sea_orm(column_name = "clipId")] + pub clip_id: String, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::clip::Entity", + from = "Column::ClipId", + to = "super::clip::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + Clip, + #[sea_orm( + belongs_to = "super::note::Entity", + from = "Column::NoteId", + to = "super::note::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + Note, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Clip.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Note.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/packages/backend/native-utils/src/model/entity/drive_file.rs b/packages/backend/native-utils/src/model/entity/drive_file.rs new file mode 100644 index 0000000000..7c42b98815 --- /dev/null +++ b/packages/backend/native-utils/src/model/entity/drive_file.rs @@ -0,0 +1,113 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3 + +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Default)] +#[sea_orm(table_name = "drive_file")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub id: String, + #[sea_orm(column_name = "createdAt")] + pub created_at: DateTimeWithTimeZone, + #[sea_orm(column_name = "userId")] + pub user_id: Option, + #[sea_orm(column_name = "userHost")] + pub user_host: Option, + pub md5: String, + pub name: String, + pub r#type: String, + pub size: i32, + pub comment: Option, + #[sea_orm(column_type = "JsonBinary")] + pub properties: Json, + #[sea_orm(column_name = "storedInternal")] + pub stored_internal: bool, + pub url: String, + #[sea_orm(column_name = "thumbnailUrl")] + pub thumbnail_url: Option, + #[sea_orm(column_name = "webpublicUrl")] + pub webpublic_url: Option, + #[sea_orm(column_name = "accessKey")] + pub access_key: Option, + #[sea_orm(column_name = "thumbnailAccessKey")] + pub thumbnail_access_key: Option, + #[sea_orm(column_name = "webpublicAccessKey")] + pub webpublic_access_key: Option, + pub uri: Option, + pub src: Option, + #[sea_orm(column_name = "folderId")] + pub folder_id: Option, + #[sea_orm(column_name = "isSensitive")] + pub is_sensitive: bool, + #[sea_orm(column_name = "isLink")] + pub is_link: bool, + pub blurhash: Option, + #[sea_orm(column_name = "webpublicType")] + pub webpublic_type: Option, + #[sea_orm(column_name = "requestHeaders", column_type = "JsonBinary", nullable)] + pub request_headers: Option, + #[sea_orm(column_name = "requestIp")] + pub request_ip: Option, + #[sea_orm(column_name = "maybeSensitive")] + pub maybe_sensitive: bool, + #[sea_orm(column_name = "maybePorn")] + pub maybe_porn: bool, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm(has_many = "super::channel::Entity")] + Channel, + #[sea_orm( + belongs_to = "super::drive_folder::Entity", + from = "Column::FolderId", + to = "super::drive_folder::Column::Id", + on_update = "NoAction", + on_delete = "SetNull" + )] + DriveFolder, + #[sea_orm(has_many = "super::messaging_message::Entity")] + MessagingMessage, + #[sea_orm(has_many = "super::page::Entity")] + Page, + #[sea_orm( + belongs_to = "super::user::Entity", + from = "Column::UserId", + to = "super::user::Column::Id", + on_update = "NoAction", + on_delete = "SetNull" + )] + User, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Channel.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::DriveFolder.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::MessagingMessage.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Page.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::User.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/packages/backend/native-utils/src/model/entity/drive_folder.rs b/packages/backend/native-utils/src/model/entity/drive_folder.rs new file mode 100644 index 0000000000..98a9f89011 --- /dev/null +++ b/packages/backend/native-utils/src/model/entity/drive_folder.rs @@ -0,0 +1,53 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3 + +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Default)] +#[sea_orm(table_name = "drive_folder")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub id: String, + #[sea_orm(column_name = "createdAt")] + pub created_at: DateTimeWithTimeZone, + pub name: String, + #[sea_orm(column_name = "userId")] + pub user_id: Option, + #[sea_orm(column_name = "parentId")] + pub parent_id: Option, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm(has_many = "super::drive_file::Entity")] + DriveFile, + #[sea_orm( + belongs_to = "Entity", + from = "Column::ParentId", + to = "Column::Id", + on_update = "NoAction", + on_delete = "SetNull" + )] + SelfRef, + #[sea_orm( + belongs_to = "super::user::Entity", + from = "Column::UserId", + to = "super::user::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + User, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::DriveFile.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::User.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/packages/backend/native-utils/src/model/entity/emoji.rs b/packages/backend/native-utils/src/model/entity/emoji.rs new file mode 100644 index 0000000000..00fc6184a8 --- /dev/null +++ b/packages/backend/native-utils/src/model/entity/emoji.rs @@ -0,0 +1,32 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3 + +use sea_orm::entity::prelude::*; + +use super::newtype::StringVec; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Default)] +#[sea_orm(table_name = "emoji")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub id: String, + #[sea_orm(column_name = "updatedAt")] + pub updated_at: Option, + pub name: String, + pub host: Option, + #[sea_orm(column_name = "originalUrl")] + pub original_url: String, + pub uri: Option, + pub r#type: Option, + pub aliases: StringVec, + pub category: Option, + #[sea_orm(column_name = "publicUrl")] + pub public_url: String, + pub license: Option, + pub width: Option, + pub height: Option, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation {} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/packages/backend/native-utils/src/model/entity/follow_request.rs b/packages/backend/native-utils/src/model/entity/follow_request.rs new file mode 100644 index 0000000000..6f8b00b791 --- /dev/null +++ b/packages/backend/native-utils/src/model/entity/follow_request.rs @@ -0,0 +1,60 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3 + +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Default)] +#[sea_orm(table_name = "follow_request")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub id: String, + #[sea_orm(column_name = "createdAt")] + pub created_at: DateTimeWithTimeZone, + #[sea_orm(column_name = "followeeId")] + pub followee_id: String, + #[sea_orm(column_name = "followerId")] + pub follower_id: String, + #[sea_orm(column_name = "requestId")] + pub request_id: Option, + #[sea_orm(column_name = "followerHost")] + pub follower_host: Option, + #[sea_orm(column_name = "followerInbox")] + pub follower_inbox: Option, + #[sea_orm(column_name = "followerSharedInbox")] + pub follower_shared_inbox: Option, + #[sea_orm(column_name = "followeeHost")] + pub followee_host: Option, + #[sea_orm(column_name = "followeeInbox")] + pub followee_inbox: Option, + #[sea_orm(column_name = "followeeSharedInbox")] + pub followee_shared_inbox: Option, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm(has_many = "super::notification::Entity")] + Notification, + #[sea_orm( + belongs_to = "super::user::Entity", + from = "Column::FolloweeId", + to = "super::user::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + User2, + #[sea_orm( + belongs_to = "super::user::Entity", + from = "Column::FollowerId", + to = "super::user::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + User1, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Notification.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/packages/backend/native-utils/src/model/entity/following.rs b/packages/backend/native-utils/src/model/entity/following.rs new file mode 100644 index 0000000000..641e415300 --- /dev/null +++ b/packages/backend/native-utils/src/model/entity/following.rs @@ -0,0 +1,50 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3 + +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Default)] +#[sea_orm(table_name = "following")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub id: String, + #[sea_orm(column_name = "createdAt")] + pub created_at: DateTimeWithTimeZone, + #[sea_orm(column_name = "followeeId")] + pub followee_id: String, + #[sea_orm(column_name = "followerId")] + pub follower_id: String, + #[sea_orm(column_name = "followerHost")] + pub follower_host: Option, + #[sea_orm(column_name = "followerInbox")] + pub follower_inbox: Option, + #[sea_orm(column_name = "followerSharedInbox")] + pub follower_shared_inbox: Option, + #[sea_orm(column_name = "followeeHost")] + pub followee_host: Option, + #[sea_orm(column_name = "followeeInbox")] + pub followee_inbox: Option, + #[sea_orm(column_name = "followeeSharedInbox")] + pub followee_shared_inbox: Option, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::user::Entity", + from = "Column::FolloweeId", + to = "super::user::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + User2, + #[sea_orm( + belongs_to = "super::user::Entity", + from = "Column::FollowerId", + to = "super::user::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + User1, +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/packages/backend/native-utils/src/model/entity/gallery_like.rs b/packages/backend/native-utils/src/model/entity/gallery_like.rs new file mode 100644 index 0000000000..e90dfedb36 --- /dev/null +++ b/packages/backend/native-utils/src/model/entity/gallery_like.rs @@ -0,0 +1,50 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3 + +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Default)] +#[sea_orm(table_name = "gallery_like")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub id: String, + #[sea_orm(column_name = "createdAt")] + pub created_at: DateTimeWithTimeZone, + #[sea_orm(column_name = "userId")] + pub user_id: String, + #[sea_orm(column_name = "postId")] + pub post_id: String, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::gallery_post::Entity", + from = "Column::PostId", + to = "super::gallery_post::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + GalleryPost, + #[sea_orm( + belongs_to = "super::user::Entity", + from = "Column::UserId", + to = "super::user::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + User, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::GalleryPost.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::User.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/packages/backend/native-utils/src/model/entity/gallery_post.rs b/packages/backend/native-utils/src/model/entity/gallery_post.rs new file mode 100644 index 0000000000..7e53e6bf39 --- /dev/null +++ b/packages/backend/native-utils/src/model/entity/gallery_post.rs @@ -0,0 +1,55 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3 + +use sea_orm::entity::prelude::*; + +use super::newtype::StringVec; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Default)] +#[sea_orm(table_name = "gallery_post")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub id: String, + #[sea_orm(column_name = "createdAt")] + pub created_at: DateTimeWithTimeZone, + #[sea_orm(column_name = "updatedAt")] + pub updated_at: DateTimeWithTimeZone, + pub title: String, + pub description: Option, + #[sea_orm(column_name = "userId")] + pub user_id: String, + #[sea_orm(column_name = "fileIds")] + pub file_ids: StringVec, + #[sea_orm(column_name = "isSensitive")] + pub is_sensitive: bool, + #[sea_orm(column_name = "likedCount")] + pub liked_count: i32, + pub tags: StringVec, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm(has_many = "super::gallery_like::Entity")] + GalleryLike, + #[sea_orm( + belongs_to = "super::user::Entity", + from = "Column::UserId", + to = "super::user::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + User, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::GalleryLike.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::User.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/packages/backend/native-utils/src/model/entity/hashtag.rs b/packages/backend/native-utils/src/model/entity/hashtag.rs new file mode 100644 index 0000000000..7a8722a5fd --- /dev/null +++ b/packages/backend/native-utils/src/model/entity/hashtag.rs @@ -0,0 +1,42 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3 + +use sea_orm::entity::prelude::*; + +use super::newtype::StringVec; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Default)] +#[sea_orm(table_name = "hashtag")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub id: String, + pub name: String, + #[sea_orm(column_name = "mentionedUserIds")] + pub mentioned_user_ids: StringVec, + #[sea_orm(column_name = "mentionedUsersCount")] + pub mentioned_users_count: i32, + #[sea_orm(column_name = "mentionedLocalUserIds")] + pub mentioned_local_user_ids: StringVec, + #[sea_orm(column_name = "mentionedLocalUsersCount")] + pub mentioned_local_users_count: i32, + #[sea_orm(column_name = "mentionedRemoteUserIds")] + pub mentioned_remote_user_ids: StringVec, + #[sea_orm(column_name = "mentionedRemoteUsersCount")] + pub mentioned_remote_users_count: i32, + #[sea_orm(column_name = "attachedUserIds")] + pub attached_user_ids: StringVec, + #[sea_orm(column_name = "attachedUsersCount")] + pub attached_users_count: i32, + #[sea_orm(column_name = "attachedLocalUserIds")] + pub attached_local_user_ids: StringVec, + #[sea_orm(column_name = "attachedLocalUsersCount")] + pub attached_local_users_count: i32, + #[sea_orm(column_name = "attachedRemoteUserIds")] + pub attached_remote_user_ids: StringVec, + #[sea_orm(column_name = "attachedRemoteUsersCount")] + pub attached_remote_users_count: i32, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation {} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/packages/backend/native-utils/src/model/entity/instance.rs b/packages/backend/native-utils/src/model/entity/instance.rs new file mode 100644 index 0000000000..fc9c5bf8b4 --- /dev/null +++ b/packages/backend/native-utils/src/model/entity/instance.rs @@ -0,0 +1,58 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3 + +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Default)] +#[sea_orm(table_name = "instance")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub id: String, + #[sea_orm(column_name = "caughtAt")] + pub caught_at: DateTimeWithTimeZone, + pub host: String, + #[sea_orm(column_name = "usersCount")] + pub users_count: i32, + #[sea_orm(column_name = "notesCount")] + pub notes_count: i32, + #[sea_orm(column_name = "followingCount")] + pub following_count: i32, + #[sea_orm(column_name = "followersCount")] + pub followers_count: i32, + #[sea_orm(column_name = "latestRequestSentAt")] + pub latest_request_sent_at: Option, + #[sea_orm(column_name = "latestStatus")] + pub latest_status: Option, + #[sea_orm(column_name = "latestRequestReceivedAt")] + pub latest_request_received_at: Option, + #[sea_orm(column_name = "lastCommunicatedAt")] + pub last_communicated_at: DateTimeWithTimeZone, + #[sea_orm(column_name = "isNotResponding")] + pub is_not_responding: bool, + #[sea_orm(column_name = "softwareName")] + pub software_name: Option, + #[sea_orm(column_name = "softwareVersion")] + pub software_version: Option, + #[sea_orm(column_name = "openRegistrations")] + pub open_registrations: Option, + pub name: Option, + pub description: Option, + #[sea_orm(column_name = "maintainerName")] + pub maintainer_name: Option, + #[sea_orm(column_name = "maintainerEmail")] + pub maintainer_email: Option, + #[sea_orm(column_name = "infoUpdatedAt")] + pub info_updated_at: Option, + #[sea_orm(column_name = "isSuspended")] + pub is_suspended: bool, + #[sea_orm(column_name = "iconUrl")] + pub icon_url: Option, + #[sea_orm(column_name = "themeColor")] + pub theme_color: Option, + #[sea_orm(column_name = "faviconUrl")] + pub favicon_url: Option, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation {} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/packages/backend/native-utils/src/model/entity/messaging_message.rs b/packages/backend/native-utils/src/model/entity/messaging_message.rs new file mode 100644 index 0000000000..8d7c7b8cc4 --- /dev/null +++ b/packages/backend/native-utils/src/model/entity/messaging_message.rs @@ -0,0 +1,77 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3 + +use sea_orm::entity::prelude::*; + +use super::newtype::StringVec; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Default)] +#[sea_orm(table_name = "messaging_message")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub id: String, + #[sea_orm(column_name = "createdAt")] + pub created_at: DateTimeWithTimeZone, + #[sea_orm(column_name = "userId")] + pub user_id: String, + #[sea_orm(column_name = "recipientId")] + pub recipient_id: Option, + pub text: Option, + #[sea_orm(column_name = "isRead")] + pub is_read: bool, + #[sea_orm(column_name = "fileId")] + pub file_id: Option, + #[sea_orm(column_name = "groupId")] + pub group_id: Option, + pub reads: StringVec, + pub uri: Option, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::drive_file::Entity", + from = "Column::FileId", + to = "super::drive_file::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + DriveFile, + #[sea_orm( + belongs_to = "super::user::Entity", + from = "Column::UserId", + to = "super::user::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + User2, + #[sea_orm( + belongs_to = "super::user::Entity", + from = "Column::RecipientId", + to = "super::user::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + User1, + #[sea_orm( + belongs_to = "super::user_group::Entity", + from = "Column::GroupId", + to = "super::user_group::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + UserGroup, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::DriveFile.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::UserGroup.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/packages/backend/native-utils/src/model/entity/meta.rs b/packages/backend/native-utils/src/model/entity/meta.rs new file mode 100644 index 0000000000..ac43a78427 --- /dev/null +++ b/packages/backend/native-utils/src/model/entity/meta.rs @@ -0,0 +1,216 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3 + +use super::sea_orm_active_enums::MetaSensitivemediadetectionEnum; +use super::sea_orm_active_enums::MetaSensitivemediadetectionsensitivityEnum; +use sea_orm::entity::prelude::*; + +use super::newtype::StringVec; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Default)] +#[sea_orm(table_name = "meta")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub id: String, + pub name: Option, + pub description: Option, + #[sea_orm(column_name = "maintainerName")] + pub maintainer_name: Option, + #[sea_orm(column_name = "maintainerEmail")] + pub maintainer_email: Option, + #[sea_orm(column_name = "disableRegistration")] + pub disable_registration: bool, + #[sea_orm(column_name = "disableLocalTimeline")] + pub disable_local_timeline: bool, + #[sea_orm(column_name = "disableGlobalTimeline")] + pub disable_global_timeline: bool, + #[sea_orm(column_name = "useStarForReactionFallback")] + pub use_star_for_reaction_fallback: bool, + pub langs: StringVec, + #[sea_orm(column_name = "hiddenTags")] + pub hidden_tags: StringVec, + #[sea_orm(column_name = "blockedHosts")] + pub blocked_hosts: StringVec, + #[sea_orm(column_name = "mascotImageUrl")] + pub mascot_image_url: Option, + #[sea_orm(column_name = "bannerUrl")] + pub banner_url: Option, + #[sea_orm(column_name = "errorImageUrl")] + pub error_image_url: Option, + #[sea_orm(column_name = "iconUrl")] + pub icon_url: Option, + #[sea_orm(column_name = "cacheRemoteFiles")] + pub cache_remote_files: bool, + #[sea_orm(column_name = "enableRecaptcha")] + pub enable_recaptcha: bool, + #[sea_orm(column_name = "recaptchaSiteKey")] + pub recaptcha_site_key: Option, + #[sea_orm(column_name = "recaptchaSecretKey")] + pub recaptcha_secret_key: Option, + #[sea_orm(column_name = "localDriveCapacityMb")] + pub local_drive_capacity_mb: i32, + #[sea_orm(column_name = "remoteDriveCapacityMb")] + pub remote_drive_capacity_mb: i32, + #[sea_orm(column_name = "summalyProxy")] + pub summaly_proxy: Option, + #[sea_orm(column_name = "enableEmail")] + pub enable_email: bool, + pub email: Option, + #[sea_orm(column_name = "smtpSecure")] + pub smtp_secure: bool, + #[sea_orm(column_name = "smtpHost")] + pub smtp_host: Option, + #[sea_orm(column_name = "smtpPort")] + pub smtp_port: Option, + #[sea_orm(column_name = "smtpUser")] + pub smtp_user: Option, + #[sea_orm(column_name = "smtpPass")] + pub smtp_pass: Option, + #[sea_orm(column_name = "enableServiceWorker")] + pub enable_service_worker: bool, + #[sea_orm(column_name = "swPublicKey")] + pub sw_public_key: Option, + #[sea_orm(column_name = "swPrivateKey")] + pub sw_private_key: Option, + #[sea_orm(column_name = "enableTwitterIntegration")] + pub enable_twitter_integration: bool, + #[sea_orm(column_name = "twitterConsumerKey")] + pub twitter_consumer_key: Option, + #[sea_orm(column_name = "twitterConsumerSecret")] + pub twitter_consumer_secret: Option, + #[sea_orm(column_name = "enableGithubIntegration")] + pub enable_github_integration: bool, + #[sea_orm(column_name = "githubClientId")] + pub github_client_id: Option, + #[sea_orm(column_name = "githubClientSecret")] + pub github_client_secret: Option, + #[sea_orm(column_name = "enableDiscordIntegration")] + pub enable_discord_integration: bool, + #[sea_orm(column_name = "discordClientId")] + pub discord_client_id: Option, + #[sea_orm(column_name = "discordClientSecret")] + pub discord_client_secret: Option, + #[sea_orm(column_name = "pinnedUsers")] + pub pinned_users: StringVec, + #[sea_orm(column_name = "ToSUrl")] + pub to_s_url: Option, + #[sea_orm(column_name = "repositoryUrl")] + pub repository_url: String, + #[sea_orm(column_name = "feedbackUrl")] + pub feedback_url: Option, + #[sea_orm(column_name = "useObjectStorage")] + pub use_object_storage: bool, + #[sea_orm(column_name = "objectStorageBucket")] + pub object_storage_bucket: Option, + #[sea_orm(column_name = "objectStoragePrefix")] + pub object_storage_prefix: Option, + #[sea_orm(column_name = "objectStorageBaseUrl")] + pub object_storage_base_url: Option, + #[sea_orm(column_name = "objectStorageEndpoint")] + pub object_storage_endpoint: Option, + #[sea_orm(column_name = "objectStorageRegion")] + pub object_storage_region: Option, + #[sea_orm(column_name = "objectStorageAccessKey")] + pub object_storage_access_key: Option, + #[sea_orm(column_name = "objectStorageSecretKey")] + pub object_storage_secret_key: Option, + #[sea_orm(column_name = "objectStoragePort")] + pub object_storage_port: Option, + #[sea_orm(column_name = "objectStorageUseSSL")] + pub object_storage_use_ssl: bool, + #[sea_orm(column_name = "proxyAccountId")] + pub proxy_account_id: Option, + #[sea_orm(column_name = "objectStorageUseProxy")] + pub object_storage_use_proxy: bool, + #[sea_orm(column_name = "enableHcaptcha")] + pub enable_hcaptcha: bool, + #[sea_orm(column_name = "hcaptchaSiteKey")] + pub hcaptcha_site_key: Option, + #[sea_orm(column_name = "hcaptchaSecretKey")] + pub hcaptcha_secret_key: Option, + #[sea_orm(column_name = "objectStorageSetPublicRead")] + pub object_storage_set_public_read: bool, + #[sea_orm(column_name = "pinnedPages")] + pub pinned_pages: StringVec, + #[sea_orm(column_name = "backgroundImageUrl")] + pub background_image_url: Option, + #[sea_orm(column_name = "logoImageUrl")] + pub logo_image_url: Option, + #[sea_orm(column_name = "pinnedClipId")] + pub pinned_clip_id: Option, + #[sea_orm(column_name = "objectStorageS3ForcePathStyle")] + pub object_storage_s3_force_path_style: bool, + #[sea_orm(column_name = "allowedHosts")] + pub allowed_hosts: Option, + #[sea_orm(column_name = "secureMode")] + pub secure_mode: Option, + #[sea_orm(column_name = "privateMode")] + pub private_mode: Option, + #[sea_orm(column_name = "deeplAuthKey")] + pub deepl_auth_key: Option, + #[sea_orm(column_name = "deeplIsPro")] + pub deepl_is_pro: bool, + #[sea_orm(column_name = "emailRequiredForSignup")] + pub email_required_for_signup: bool, + #[sea_orm(column_name = "themeColor")] + pub theme_color: Option, + #[sea_orm(column_name = "defaultLightTheme")] + pub default_light_theme: Option, + #[sea_orm(column_name = "defaultDarkTheme")] + pub default_dark_theme: Option, + #[sea_orm(column_name = "sensitiveMediaDetection")] + pub sensitive_media_detection: MetaSensitivemediadetectionEnum, + #[sea_orm(column_name = "sensitiveMediaDetectionSensitivity")] + pub sensitive_media_detection_sensitivity: MetaSensitivemediadetectionsensitivityEnum, + #[sea_orm(column_name = "setSensitiveFlagAutomatically")] + pub set_sensitive_flag_automatically: bool, + #[sea_orm(column_name = "enableIpLogging")] + pub enable_ip_logging: bool, + #[sea_orm(column_name = "enableSensitiveMediaDetectionForVideos")] + pub enable_sensitive_media_detection_for_videos: bool, + #[sea_orm(column_name = "enableActiveEmailValidation")] + pub enable_active_email_validation: bool, + #[sea_orm(column_name = "customMOTD")] + pub custom_motd: StringVec, + #[sea_orm(column_name = "customSplashIcons")] + pub custom_splash_icons: StringVec, + #[sea_orm(column_name = "disableRecommendedTimeline")] + pub disable_recommended_timeline: bool, + #[sea_orm(column_name = "recommendedInstances")] + pub recommended_instances: StringVec, + #[sea_orm(column_name = "enableGuestTimeline")] + pub enable_guest_timeline: bool, + #[sea_orm(column_name = "defaultReaction")] + pub default_reaction: String, + #[sea_orm(column_name = "libreTranslateApiUrl")] + pub libre_translate_api_url: Option, + #[sea_orm(column_name = "libreTranslateApiKey")] + pub libre_translate_api_key: Option, + #[sea_orm(column_name = "silencedHosts")] + pub silenced_hosts: StringVec, + #[sea_orm(column_name = "experimentalFeatures", column_type = "JsonBinary")] + pub experimental_features: Json, + #[sea_orm(column_name = "enableServerMachineStats")] + pub enable_server_machine_stats: bool, + #[sea_orm(column_name = "enableIdenticonGeneration")] + pub enable_identicon_generation: bool, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::user::Entity", + from = "Column::ProxyAccountId", + to = "super::user::Column::Id", + on_update = "NoAction", + on_delete = "SetNull" + )] + User, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::User.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/packages/backend/native-utils/src/model/entity/migrations.rs b/packages/backend/native-utils/src/model/entity/migrations.rs new file mode 100644 index 0000000000..54e44f2fdb --- /dev/null +++ b/packages/backend/native-utils/src/model/entity/migrations.rs @@ -0,0 +1,17 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3 + +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Default)] +#[sea_orm(table_name = "migrations")] +pub struct Model { + #[sea_orm(primary_key)] + pub id: i32, + pub timestamp: i64, + pub name: String, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation {} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/packages/backend/native-utils/src/model/entity/moderation_log.rs b/packages/backend/native-utils/src/model/entity/moderation_log.rs new file mode 100644 index 0000000000..eb882b8964 --- /dev/null +++ b/packages/backend/native-utils/src/model/entity/moderation_log.rs @@ -0,0 +1,37 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3 + +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Default)] +#[sea_orm(table_name = "moderation_log")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub id: String, + #[sea_orm(column_name = "createdAt")] + pub created_at: DateTimeWithTimeZone, + #[sea_orm(column_name = "userId")] + pub user_id: String, + pub r#type: String, + #[sea_orm(column_type = "JsonBinary")] + pub info: Json, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::user::Entity", + from = "Column::UserId", + to = "super::user::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + User, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::User.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/packages/backend/native-utils/src/model/entity/muted_note.rs b/packages/backend/native-utils/src/model/entity/muted_note.rs new file mode 100644 index 0000000000..2388985497 --- /dev/null +++ b/packages/backend/native-utils/src/model/entity/muted_note.rs @@ -0,0 +1,50 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3 + +use super::sea_orm_active_enums::MutedNoteReasonEnum; +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Default)] +#[sea_orm(table_name = "muted_note")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub id: String, + #[sea_orm(column_name = "noteId")] + pub note_id: String, + #[sea_orm(column_name = "userId")] + pub user_id: String, + pub reason: MutedNoteReasonEnum, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::note::Entity", + from = "Column::NoteId", + to = "super::note::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + Note, + #[sea_orm( + belongs_to = "super::user::Entity", + from = "Column::UserId", + to = "super::user::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + User, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Note.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::User.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/packages/backend/native-utils/src/model/entity/muting.rs b/packages/backend/native-utils/src/model/entity/muting.rs new file mode 100644 index 0000000000..7b46a0b246 --- /dev/null +++ b/packages/backend/native-utils/src/model/entity/muting.rs @@ -0,0 +1,40 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3 + +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Default)] +#[sea_orm(table_name = "muting")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub id: String, + #[sea_orm(column_name = "createdAt")] + pub created_at: DateTimeWithTimeZone, + #[sea_orm(column_name = "muteeId")] + pub mutee_id: String, + #[sea_orm(column_name = "muterId")] + pub muter_id: String, + #[sea_orm(column_name = "expiresAt")] + pub expires_at: Option, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::user::Entity", + from = "Column::MuterId", + to = "super::user::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + User2, + #[sea_orm( + belongs_to = "super::user::Entity", + from = "Column::MuteeId", + to = "super::user::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + User1, +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/packages/backend/native-utils/src/model/entity/newtype/macros.rs b/packages/backend/native-utils/src/model/entity/newtype/macros.rs new file mode 100644 index 0000000000..4b05c2c99c --- /dev/null +++ b/packages/backend/native-utils/src/model/entity/newtype/macros.rs @@ -0,0 +1,51 @@ +#[macro_export] +macro_rules! impl_json_newtype { + ($a:tt) => { + impl From<$a> for Value { + fn from(source: $a) -> Self { + Value::Json(serde_json::to_value(source).ok().map(Box::new)) + } + } + + impl TryGetable for $a { + fn try_get_by( + res: &QueryResult, + idx: I, + ) -> Result { + let json_value: serde_json::Value = + res.try_get_by(idx).map_err(TryGetError::DbErr)?; + serde_json::from_value(json_value) + .map_err(|e| TryGetError::DbErr(DbErr::Json(e.to_string()))) + } + } + + impl sea_query::ValueType for $a { + fn try_from(v: Value) -> Result { + match v { + Value::Json(Some(x)) => Ok($a( + serde_json::from_value(*x).map_err(|_| sea_query::ValueTypeErr)? + )), + _ => Err(sea_query::ValueTypeErr), + } + } + + fn type_name() -> String { + stringify!($a).to_owned() + } + + fn array_type() -> sea_query::ArrayType { + sea_query::ArrayType::Json + } + + fn column_type() -> sea_query::ColumnType { + sea_query::ColumnType::JsonBinary + } + } + + impl sea_query::Nullable for $a { + fn null() -> Value { + Value::Json(None) + } + } + }; +} diff --git a/packages/backend/native-utils/src/model/entity/newtype/mod.rs b/packages/backend/native-utils/src/model/entity/newtype/mod.rs new file mode 100644 index 0000000000..3dc2d7553d --- /dev/null +++ b/packages/backend/native-utils/src/model/entity/newtype/mod.rs @@ -0,0 +1,30 @@ +mod macros; + +use cfg_if::cfg_if; +use derive_more::{From, Into}; +use sea_orm::{sea_query, DbErr, QueryResult, TryGetError, TryGetable, Value}; +use serde::{Deserialize, Serialize}; + +use crate::impl_json_newtype; + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, From, Into, Default)] +pub struct JsonKeyword(pub Vec>); +impl_json_newtype!(JsonKeyword); + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, From, Into, Default)] +pub struct JsonStringVec(pub Vec); +impl_json_newtype!(JsonStringVec); + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, From, Into, Default)] +pub struct JsonI32Vec(pub Vec); +impl_json_newtype!(JsonI32Vec); + +cfg_if! { + if #[cfg(feature = "noarray")] { + pub type StringVec = JsonStringVec; + pub type I32Vec = JsonI32Vec; + } else { + pub type StringVec = Vec; + pub type I32Vec = Vec; + } +} diff --git a/packages/backend/native-utils/src/model/entity/note.rs b/packages/backend/native-utils/src/model/entity/note.rs new file mode 100644 index 0000000000..5c97a20cf9 --- /dev/null +++ b/packages/backend/native-utils/src/model/entity/note.rs @@ -0,0 +1,228 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3 + +use super::sea_orm_active_enums::NoteVisibilityEnum; +use sea_orm::entity::prelude::*; + +use super::newtype::StringVec; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Default)] +#[sea_orm(table_name = "note")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub id: String, + #[sea_orm(column_name = "createdAt")] + pub created_at: DateTimeWithTimeZone, + #[sea_orm(column_name = "replyId")] + pub reply_id: Option, + #[sea_orm(column_name = "renoteId")] + pub renote_id: Option, + #[sea_orm(column_type = "Text", nullable)] + pub text: Option, + pub name: Option, + pub cw: Option, + #[sea_orm(column_name = "userId")] + pub user_id: String, + #[sea_orm(column_name = "localOnly")] + pub local_only: bool, + #[sea_orm(column_name = "renoteCount")] + pub renote_count: i16, + #[sea_orm(column_name = "repliesCount")] + pub replies_count: i16, + #[sea_orm(column_type = "JsonBinary")] + pub reactions: Json, + pub visibility: NoteVisibilityEnum, + pub uri: Option, + pub score: i32, + #[sea_orm(column_name = "fileIds")] + pub file_ids: StringVec, + #[sea_orm(column_name = "attachedFileTypes")] + pub attached_file_types: StringVec, + #[sea_orm(column_name = "visibleUserIds")] + pub visible_user_ids: StringVec, + pub mentions: StringVec, + #[sea_orm(column_name = "mentionedRemoteUsers", column_type = "Text")] + pub mentioned_remote_users: String, + pub emojis: StringVec, + pub tags: StringVec, + #[sea_orm(column_name = "hasPoll")] + pub has_poll: bool, + #[sea_orm(column_name = "userHost")] + pub user_host: Option, + #[sea_orm(column_name = "replyUserId")] + pub reply_user_id: Option, + #[sea_orm(column_name = "replyUserHost")] + pub reply_user_host: Option, + #[sea_orm(column_name = "renoteUserId")] + pub renote_user_id: Option, + #[sea_orm(column_name = "renoteUserHost")] + pub renote_user_host: Option, + pub url: Option, + #[sea_orm(column_name = "channelId")] + pub channel_id: Option, + #[sea_orm(column_name = "threadId")] + pub thread_id: Option, + #[sea_orm(column_name = "updatedAt")] + pub updated_at: Option, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::channel::Entity", + from = "Column::ChannelId", + to = "super::channel::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + Channel, + #[sea_orm(has_many = "super::channel_note_pining::Entity")] + ChannelNotePining, + #[sea_orm(has_many = "super::clip_note::Entity")] + ClipNote, + #[sea_orm(has_many = "super::muted_note::Entity")] + MutedNote, + #[sea_orm( + belongs_to = "Entity", + from = "Column::ReplyId", + to = "Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + SelfRef2, + #[sea_orm( + belongs_to = "Entity", + from = "Column::RenoteId", + to = "Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + SelfRef1, + #[sea_orm(has_many = "super::note_edit::Entity")] + NoteEdit, + #[sea_orm(has_many = "super::note_favorite::Entity")] + NoteFavorite, + #[sea_orm(has_many = "super::note_reaction::Entity")] + NoteReaction, + #[sea_orm(has_many = "super::note_unread::Entity")] + NoteUnread, + #[sea_orm(has_many = "super::note_watching::Entity")] + NoteWatching, + #[sea_orm(has_many = "super::notification::Entity")] + Notification, + #[sea_orm(has_one = "super::poll::Entity")] + Poll, + #[sea_orm(has_many = "super::poll_vote::Entity")] + PollVote, + #[sea_orm(has_one = "super::promo_note::Entity")] + PromoNote, + #[sea_orm(has_many = "super::promo_read::Entity")] + PromoRead, + #[sea_orm( + belongs_to = "super::user::Entity", + from = "Column::UserId", + to = "super::user::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + User, + #[sea_orm(has_many = "super::user_note_pining::Entity")] + UserNotePining, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Channel.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::ChannelNotePining.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::ClipNote.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::MutedNote.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::NoteEdit.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::NoteFavorite.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::NoteReaction.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::NoteUnread.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::NoteWatching.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Notification.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Poll.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::PollVote.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::PromoNote.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::PromoRead.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::User.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::UserNotePining.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/packages/backend/native-utils/src/model/entity/note_edit.rs b/packages/backend/native-utils/src/model/entity/note_edit.rs new file mode 100644 index 0000000000..ea9b9eabd5 --- /dev/null +++ b/packages/backend/native-utils/src/model/entity/note_edit.rs @@ -0,0 +1,41 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3 + +use sea_orm::entity::prelude::*; + +use super::newtype::StringVec; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Default)] +#[sea_orm(table_name = "note_edit")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub id: String, + #[sea_orm(column_name = "noteId")] + pub note_id: String, + #[sea_orm(column_type = "Text", nullable)] + pub text: Option, + pub cw: Option, + #[sea_orm(column_name = "fileIds")] + pub file_ids: StringVec, + #[sea_orm(column_name = "updatedAt")] + pub updated_at: DateTimeWithTimeZone, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::note::Entity", + from = "Column::NoteId", + to = "super::note::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + Note, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Note.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/packages/backend/native-utils/src/model/entity/note_favorite.rs b/packages/backend/native-utils/src/model/entity/note_favorite.rs new file mode 100644 index 0000000000..470ad55d26 --- /dev/null +++ b/packages/backend/native-utils/src/model/entity/note_favorite.rs @@ -0,0 +1,50 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3 + +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Default)] +#[sea_orm(table_name = "note_favorite")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub id: String, + #[sea_orm(column_name = "createdAt")] + pub created_at: DateTimeWithTimeZone, + #[sea_orm(column_name = "userId")] + pub user_id: String, + #[sea_orm(column_name = "noteId")] + pub note_id: String, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::note::Entity", + from = "Column::NoteId", + to = "super::note::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + Note, + #[sea_orm( + belongs_to = "super::user::Entity", + from = "Column::UserId", + to = "super::user::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + User, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Note.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::User.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/packages/backend/native-utils/src/model/entity/note_reaction.rs b/packages/backend/native-utils/src/model/entity/note_reaction.rs new file mode 100644 index 0000000000..a4e9f490dc --- /dev/null +++ b/packages/backend/native-utils/src/model/entity/note_reaction.rs @@ -0,0 +1,51 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3 + +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Default)] +#[sea_orm(table_name = "note_reaction")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub id: String, + #[sea_orm(column_name = "createdAt")] + pub created_at: DateTimeWithTimeZone, + #[sea_orm(column_name = "userId")] + pub user_id: String, + #[sea_orm(column_name = "noteId")] + pub note_id: String, + pub reaction: String, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::note::Entity", + from = "Column::NoteId", + to = "super::note::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + Note, + #[sea_orm( + belongs_to = "super::user::Entity", + from = "Column::UserId", + to = "super::user::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + User, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Note.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::User.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/packages/backend/native-utils/src/model/entity/note_thread_muting.rs b/packages/backend/native-utils/src/model/entity/note_thread_muting.rs new file mode 100644 index 0000000000..51688a0889 --- /dev/null +++ b/packages/backend/native-utils/src/model/entity/note_thread_muting.rs @@ -0,0 +1,36 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3 + +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Default)] +#[sea_orm(table_name = "note_thread_muting")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub id: String, + #[sea_orm(column_name = "createdAt")] + pub created_at: DateTimeWithTimeZone, + #[sea_orm(column_name = "userId")] + pub user_id: String, + #[sea_orm(column_name = "threadId")] + pub thread_id: String, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::user::Entity", + from = "Column::UserId", + to = "super::user::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + User, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::User.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/packages/backend/native-utils/src/model/entity/note_unread.rs b/packages/backend/native-utils/src/model/entity/note_unread.rs new file mode 100644 index 0000000000..a444eb35dd --- /dev/null +++ b/packages/backend/native-utils/src/model/entity/note_unread.rs @@ -0,0 +1,56 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3 + +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Default)] +#[sea_orm(table_name = "note_unread")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub id: String, + #[sea_orm(column_name = "userId")] + pub user_id: String, + #[sea_orm(column_name = "noteId")] + pub note_id: String, + #[sea_orm(column_name = "noteUserId")] + pub note_user_id: String, + #[sea_orm(column_name = "isSpecified")] + pub is_specified: bool, + #[sea_orm(column_name = "isMentioned")] + pub is_mentioned: bool, + #[sea_orm(column_name = "noteChannelId")] + pub note_channel_id: Option, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::note::Entity", + from = "Column::NoteId", + to = "super::note::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + Note, + #[sea_orm( + belongs_to = "super::user::Entity", + from = "Column::UserId", + to = "super::user::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + User, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Note.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::User.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/packages/backend/native-utils/src/model/entity/note_watching.rs b/packages/backend/native-utils/src/model/entity/note_watching.rs new file mode 100644 index 0000000000..962ef081e0 --- /dev/null +++ b/packages/backend/native-utils/src/model/entity/note_watching.rs @@ -0,0 +1,52 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3 + +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Default)] +#[sea_orm(table_name = "note_watching")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub id: String, + #[sea_orm(column_name = "createdAt")] + pub created_at: DateTimeWithTimeZone, + #[sea_orm(column_name = "userId")] + pub user_id: String, + #[sea_orm(column_name = "noteId")] + pub note_id: String, + #[sea_orm(column_name = "noteUserId")] + pub note_user_id: String, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::note::Entity", + from = "Column::NoteId", + to = "super::note::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + Note, + #[sea_orm( + belongs_to = "super::user::Entity", + from = "Column::UserId", + to = "super::user::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + User, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Note.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::User.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/packages/backend/native-utils/src/model/entity/notification.rs b/packages/backend/native-utils/src/model/entity/notification.rs new file mode 100644 index 0000000000..896b6c2da3 --- /dev/null +++ b/packages/backend/native-utils/src/model/entity/notification.rs @@ -0,0 +1,114 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3 + +use super::sea_orm_active_enums::NotificationTypeEnum; +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Default)] +#[sea_orm(table_name = "notification")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub id: String, + #[sea_orm(column_name = "createdAt")] + pub created_at: DateTimeWithTimeZone, + #[sea_orm(column_name = "notifieeId")] + pub notifiee_id: String, + #[sea_orm(column_name = "notifierId")] + pub notifier_id: Option, + #[sea_orm(column_name = "isRead")] + pub is_read: bool, + #[sea_orm(column_name = "noteId")] + pub note_id: Option, + pub reaction: Option, + pub choice: Option, + #[sea_orm(column_name = "followRequestId")] + pub follow_request_id: Option, + pub r#type: NotificationTypeEnum, + #[sea_orm(column_name = "userGroupInvitationId")] + pub user_group_invitation_id: Option, + #[sea_orm(column_name = "customBody")] + pub custom_body: Option, + #[sea_orm(column_name = "customHeader")] + pub custom_header: Option, + #[sea_orm(column_name = "customIcon")] + pub custom_icon: Option, + #[sea_orm(column_name = "appAccessTokenId")] + pub app_access_token_id: Option, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::access_token::Entity", + from = "Column::AppAccessTokenId", + to = "super::access_token::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + AccessToken, + #[sea_orm( + belongs_to = "super::follow_request::Entity", + from = "Column::FollowRequestId", + to = "super::follow_request::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + FollowRequest, + #[sea_orm( + belongs_to = "super::note::Entity", + from = "Column::NoteId", + to = "super::note::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + Note, + #[sea_orm( + belongs_to = "super::user::Entity", + from = "Column::NotifierId", + to = "super::user::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + User2, + #[sea_orm( + belongs_to = "super::user::Entity", + from = "Column::NotifieeId", + to = "super::user::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + User1, + #[sea_orm( + belongs_to = "super::user_group_invitation::Entity", + from = "Column::UserGroupInvitationId", + to = "super::user_group_invitation::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + UserGroupInvitation, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::AccessToken.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::FollowRequest.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Note.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::UserGroupInvitation.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/packages/backend/native-utils/src/model/entity/page.rs b/packages/backend/native-utils/src/model/entity/page.rs new file mode 100644 index 0000000000..dabb5c9f06 --- /dev/null +++ b/packages/backend/native-utils/src/model/entity/page.rs @@ -0,0 +1,91 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3 + +use super::sea_orm_active_enums::PageVisibilityEnum; +use sea_orm::entity::prelude::*; + +use super::newtype::StringVec; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Default)] +#[sea_orm(table_name = "page")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub id: String, + #[sea_orm(column_name = "createdAt")] + pub created_at: DateTimeWithTimeZone, + #[sea_orm(column_name = "updatedAt")] + pub updated_at: DateTimeWithTimeZone, + pub title: String, + pub name: String, + pub summary: Option, + #[sea_orm(column_name = "alignCenter")] + pub align_center: bool, + pub font: String, + #[sea_orm(column_name = "userId")] + pub user_id: String, + #[sea_orm(column_name = "eyeCatchingImageId")] + pub eye_catching_image_id: Option, + #[sea_orm(column_type = "JsonBinary")] + pub content: Json, + #[sea_orm(column_type = "JsonBinary")] + pub variables: Json, + pub visibility: PageVisibilityEnum, + #[sea_orm(column_name = "visibleUserIds")] + pub visible_user_ids: StringVec, + #[sea_orm(column_name = "likedCount")] + pub liked_count: i32, + #[sea_orm(column_name = "hideTitleWhenPinned")] + pub hide_title_when_pinned: bool, + pub script: String, + #[sea_orm(column_name = "isPublic")] + pub is_public: bool, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::drive_file::Entity", + from = "Column::EyeCatchingImageId", + to = "super::drive_file::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + DriveFile, + #[sea_orm(has_many = "super::page_like::Entity")] + PageLike, + #[sea_orm( + belongs_to = "super::user::Entity", + from = "Column::UserId", + to = "super::user::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + User, + #[sea_orm(has_one = "super::user_profile::Entity")] + UserProfile, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::DriveFile.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::PageLike.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::User.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::UserProfile.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/packages/backend/native-utils/src/model/entity/page_like.rs b/packages/backend/native-utils/src/model/entity/page_like.rs new file mode 100644 index 0000000000..108b6b929f --- /dev/null +++ b/packages/backend/native-utils/src/model/entity/page_like.rs @@ -0,0 +1,50 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3 + +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Default)] +#[sea_orm(table_name = "page_like")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub id: String, + #[sea_orm(column_name = "createdAt")] + pub created_at: DateTimeWithTimeZone, + #[sea_orm(column_name = "userId")] + pub user_id: String, + #[sea_orm(column_name = "pageId")] + pub page_id: String, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::page::Entity", + from = "Column::PageId", + to = "super::page::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + Page, + #[sea_orm( + belongs_to = "super::user::Entity", + from = "Column::UserId", + to = "super::user::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + User, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Page.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::User.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/packages/backend/native-utils/src/model/entity/password_reset_request.rs b/packages/backend/native-utils/src/model/entity/password_reset_request.rs new file mode 100644 index 0000000000..45cc3de107 --- /dev/null +++ b/packages/backend/native-utils/src/model/entity/password_reset_request.rs @@ -0,0 +1,35 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3 + +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Default)] +#[sea_orm(table_name = "password_reset_request")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub id: String, + #[sea_orm(column_name = "createdAt")] + pub created_at: DateTimeWithTimeZone, + pub token: String, + #[sea_orm(column_name = "userId")] + pub user_id: String, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::user::Entity", + from = "Column::UserId", + to = "super::user::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + User, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::User.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/packages/backend/native-utils/src/model/entity/poll.rs b/packages/backend/native-utils/src/model/entity/poll.rs new file mode 100644 index 0000000000..4d64594c76 --- /dev/null +++ b/packages/backend/native-utils/src/model/entity/poll.rs @@ -0,0 +1,44 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3 + +use super::sea_orm_active_enums::PollNotevisibilityEnum; +use sea_orm::entity::prelude::*; + +use super::newtype::{I32Vec, StringVec}; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Default)] +#[sea_orm(table_name = "poll")] +pub struct Model { + #[sea_orm(column_name = "noteId", primary_key, auto_increment = false, unique)] + pub note_id: String, + #[sea_orm(column_name = "expiresAt")] + pub expires_at: Option, + pub multiple: bool, + pub choices: StringVec, + pub votes: I32Vec, + #[sea_orm(column_name = "noteVisibility")] + pub note_visibility: PollNotevisibilityEnum, + #[sea_orm(column_name = "userId")] + pub user_id: String, + #[sea_orm(column_name = "userHost")] + pub user_host: Option, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::note::Entity", + from = "Column::NoteId", + to = "super::note::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + Note, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Note.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/packages/backend/native-utils/src/model/entity/poll_vote.rs b/packages/backend/native-utils/src/model/entity/poll_vote.rs new file mode 100644 index 0000000000..bf26bf5dd5 --- /dev/null +++ b/packages/backend/native-utils/src/model/entity/poll_vote.rs @@ -0,0 +1,51 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3 + +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Default)] +#[sea_orm(table_name = "poll_vote")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub id: String, + #[sea_orm(column_name = "createdAt")] + pub created_at: DateTimeWithTimeZone, + #[sea_orm(column_name = "userId")] + pub user_id: String, + #[sea_orm(column_name = "noteId")] + pub note_id: String, + pub choice: i32, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::note::Entity", + from = "Column::NoteId", + to = "super::note::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + Note, + #[sea_orm( + belongs_to = "super::user::Entity", + from = "Column::UserId", + to = "super::user::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + User, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Note.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::User.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/packages/backend/native-utils/src/model/entity/prelude.rs b/packages/backend/native-utils/src/model/entity/prelude.rs new file mode 100644 index 0000000000..a859a9cc37 --- /dev/null +++ b/packages/backend/native-utils/src/model/entity/prelude.rs @@ -0,0 +1,69 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3 + +pub use super::abuse_user_report::Entity as AbuseUserReport; +pub use super::access_token::Entity as AccessToken; +pub use super::ad::Entity as Ad; +pub use super::announcement::Entity as Announcement; +pub use super::announcement_read::Entity as AnnouncementRead; +pub use super::antenna::Entity as Antenna; +pub use super::app::Entity as App; +pub use super::attestation_challenge::Entity as AttestationChallenge; +pub use super::auth_session::Entity as AuthSession; +pub use super::blocking::Entity as Blocking; +pub use super::channel::Entity as Channel; +pub use super::channel_following::Entity as ChannelFollowing; +pub use super::channel_note_pining::Entity as ChannelNotePining; +pub use super::clip::Entity as Clip; +pub use super::clip_note::Entity as ClipNote; +pub use super::drive_file::Entity as DriveFile; +pub use super::drive_folder::Entity as DriveFolder; +pub use super::emoji::Entity as Emoji; +pub use super::follow_request::Entity as FollowRequest; +pub use super::following::Entity as Following; +pub use super::gallery_like::Entity as GalleryLike; +pub use super::gallery_post::Entity as GalleryPost; +pub use super::hashtag::Entity as Hashtag; +pub use super::instance::Entity as Instance; +pub use super::messaging_message::Entity as MessagingMessage; +pub use super::meta::Entity as Meta; +pub use super::migrations::Entity as Migrations; +pub use super::moderation_log::Entity as ModerationLog; +pub use super::muted_note::Entity as MutedNote; +pub use super::muting::Entity as Muting; +pub use super::note::Entity as Note; +pub use super::note_edit::Entity as NoteEdit; +pub use super::note_favorite::Entity as NoteFavorite; +pub use super::note_reaction::Entity as NoteReaction; +pub use super::note_thread_muting::Entity as NoteThreadMuting; +pub use super::note_unread::Entity as NoteUnread; +pub use super::note_watching::Entity as NoteWatching; +pub use super::notification::Entity as Notification; +pub use super::page::Entity as Page; +pub use super::page_like::Entity as PageLike; +pub use super::password_reset_request::Entity as PasswordResetRequest; +pub use super::poll::Entity as Poll; +pub use super::poll_vote::Entity as PollVote; +pub use super::promo_note::Entity as PromoNote; +pub use super::promo_read::Entity as PromoRead; +pub use super::registration_ticket::Entity as RegistrationTicket; +pub use super::registry_item::Entity as RegistryItem; +pub use super::relay::Entity as Relay; +pub use super::renote_muting::Entity as RenoteMuting; +pub use super::signin::Entity as Signin; +pub use super::sw_subscription::Entity as SwSubscription; +pub use super::used_username::Entity as UsedUsername; +pub use super::user::Entity as User; +pub use super::user_group::Entity as UserGroup; +pub use super::user_group_invitation::Entity as UserGroupInvitation; +pub use super::user_group_invite::Entity as UserGroupInvite; +pub use super::user_group_joining::Entity as UserGroupJoining; +pub use super::user_ip::Entity as UserIp; +pub use super::user_keypair::Entity as UserKeypair; +pub use super::user_list::Entity as UserList; +pub use super::user_list_joining::Entity as UserListJoining; +pub use super::user_note_pining::Entity as UserNotePining; +pub use super::user_pending::Entity as UserPending; +pub use super::user_profile::Entity as UserProfile; +pub use super::user_publickey::Entity as UserPublickey; +pub use super::user_security_key::Entity as UserSecurityKey; +pub use super::webhook::Entity as Webhook; diff --git a/packages/backend/native-utils/src/model/entity/promo_note.rs b/packages/backend/native-utils/src/model/entity/promo_note.rs new file mode 100644 index 0000000000..288a0ea812 --- /dev/null +++ b/packages/backend/native-utils/src/model/entity/promo_note.rs @@ -0,0 +1,34 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3 + +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Default)] +#[sea_orm(table_name = "promo_note")] +pub struct Model { + #[sea_orm(column_name = "noteId", primary_key, auto_increment = false, unique)] + pub note_id: String, + #[sea_orm(column_name = "expiresAt")] + pub expires_at: DateTimeWithTimeZone, + #[sea_orm(column_name = "userId")] + pub user_id: String, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::note::Entity", + from = "Column::NoteId", + to = "super::note::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + Note, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Note.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/packages/backend/native-utils/src/model/entity/promo_read.rs b/packages/backend/native-utils/src/model/entity/promo_read.rs new file mode 100644 index 0000000000..4e6224cf29 --- /dev/null +++ b/packages/backend/native-utils/src/model/entity/promo_read.rs @@ -0,0 +1,50 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3 + +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Default)] +#[sea_orm(table_name = "promo_read")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub id: String, + #[sea_orm(column_name = "createdAt")] + pub created_at: DateTimeWithTimeZone, + #[sea_orm(column_name = "userId")] + pub user_id: String, + #[sea_orm(column_name = "noteId")] + pub note_id: String, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::note::Entity", + from = "Column::NoteId", + to = "super::note::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + Note, + #[sea_orm( + belongs_to = "super::user::Entity", + from = "Column::UserId", + to = "super::user::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + User, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Note.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::User.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/packages/backend/native-utils/src/model/entity/registration_ticket.rs b/packages/backend/native-utils/src/model/entity/registration_ticket.rs new file mode 100644 index 0000000000..798f19586f --- /dev/null +++ b/packages/backend/native-utils/src/model/entity/registration_ticket.rs @@ -0,0 +1,18 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3 + +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Default)] +#[sea_orm(table_name = "registration_ticket")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub id: String, + #[sea_orm(column_name = "createdAt")] + pub created_at: DateTimeWithTimeZone, + pub code: String, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation {} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/packages/backend/native-utils/src/model/entity/registry_item.rs b/packages/backend/native-utils/src/model/entity/registry_item.rs new file mode 100644 index 0000000000..904c43abfe --- /dev/null +++ b/packages/backend/native-utils/src/model/entity/registry_item.rs @@ -0,0 +1,43 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3 + +use sea_orm::entity::prelude::*; + +use super::newtype::StringVec; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Default)] +#[sea_orm(table_name = "registry_item")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub id: String, + #[sea_orm(column_name = "createdAt")] + pub created_at: DateTimeWithTimeZone, + #[sea_orm(column_name = "updatedAt")] + pub updated_at: DateTimeWithTimeZone, + #[sea_orm(column_name = "userId")] + pub user_id: String, + pub key: String, + pub scope: StringVec, + pub domain: Option, + #[sea_orm(column_type = "JsonBinary", nullable)] + pub value: Option, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::user::Entity", + from = "Column::UserId", + to = "super::user::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + User, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::User.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/packages/backend/native-utils/src/model/entity/relay.rs b/packages/backend/native-utils/src/model/entity/relay.rs new file mode 100644 index 0000000000..bed89c849f --- /dev/null +++ b/packages/backend/native-utils/src/model/entity/relay.rs @@ -0,0 +1,18 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3 + +use super::sea_orm_active_enums::RelayStatusEnum; +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Default)] +#[sea_orm(table_name = "relay")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub id: String, + pub inbox: String, + pub status: RelayStatusEnum, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation {} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/packages/backend/native-utils/src/model/entity/renote_muting.rs b/packages/backend/native-utils/src/model/entity/renote_muting.rs new file mode 100644 index 0000000000..44751c14c5 --- /dev/null +++ b/packages/backend/native-utils/src/model/entity/renote_muting.rs @@ -0,0 +1,21 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3 + +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Default)] +#[sea_orm(table_name = "renote_muting")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub id: String, + #[sea_orm(column_name = "createdAt")] + pub created_at: DateTimeWithTimeZone, + #[sea_orm(column_name = "muteeId")] + pub mutee_id: String, + #[sea_orm(column_name = "muterId")] + pub muter_id: String, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation {} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/packages/backend/native-utils/src/model/entity/sea_orm_active_enums.rs b/packages/backend/native-utils/src/model/entity/sea_orm_active_enums.rs new file mode 100644 index 0000000000..f269952246 --- /dev/null +++ b/packages/backend/native-utils/src/model/entity/sea_orm_active_enums.rs @@ -0,0 +1,184 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3 + +use sea_orm::entity::prelude::*; + +#[derive(Debug, Clone, PartialEq, Eq, EnumIter, DeriveActiveEnum, Default)] +#[sea_orm(rs_type = "String", db_type = "Enum", enum_name = "antenna_src_enum")] +pub enum AntennaSrcEnum { + #[default] + #[sea_orm(string_value = "all")] + All, + #[sea_orm(string_value = "group")] + Group, + #[sea_orm(string_value = "home")] + Home, + #[sea_orm(string_value = "instances")] + Instances, + #[sea_orm(string_value = "list")] + List, + #[sea_orm(string_value = "users")] + Users, +} +#[derive(Debug, Clone, PartialEq, Eq, EnumIter, DeriveActiveEnum, Default)] +#[sea_orm( + rs_type = "String", + db_type = "Enum", + enum_name = "meta_sensitivemediadetection_enum" +)] +pub enum MetaSensitivemediadetectionEnum { + #[default] + #[sea_orm(string_value = "all")] + All, + #[sea_orm(string_value = "local")] + Local, + #[sea_orm(string_value = "none")] + None, + #[sea_orm(string_value = "remote")] + Remote, +} +#[derive(Debug, Clone, PartialEq, Eq, EnumIter, DeriveActiveEnum, Default)] +#[sea_orm( + rs_type = "String", + db_type = "Enum", + enum_name = "meta_sensitivemediadetectionsensitivity_enum" +)] +pub enum MetaSensitivemediadetectionsensitivityEnum { + #[sea_orm(string_value = "high")] + High, + #[sea_orm(string_value = "low")] + Low, + #[default] + #[sea_orm(string_value = "medium")] + Medium, + #[sea_orm(string_value = "veryHigh")] + VeryHigh, + #[sea_orm(string_value = "veryLow")] + VeryLow, +} +#[derive(Debug, Clone, PartialEq, Eq, EnumIter, DeriveActiveEnum, Default)] +#[sea_orm( + rs_type = "String", + db_type = "Enum", + enum_name = "muted_note_reason_enum" +)] +pub enum MutedNoteReasonEnum { + #[default] + #[sea_orm(string_value = "manual")] + Manual, + #[sea_orm(string_value = "other")] + Other, + #[sea_orm(string_value = "spam")] + Spam, + #[sea_orm(string_value = "word")] + Word, +} +#[derive(Debug, Clone, PartialEq, Eq, EnumIter, DeriveActiveEnum, Default)] +#[sea_orm( + rs_type = "String", + db_type = "Enum", + enum_name = "note_visibility_enum" +)] +pub enum NoteVisibilityEnum { + #[sea_orm(string_value = "followers")] + Followers, + #[sea_orm(string_value = "hidden")] + Hidden, + #[sea_orm(string_value = "home")] + Home, + #[default] + #[sea_orm(string_value = "public")] + Public, + #[sea_orm(string_value = "specified")] + Specified, +} +#[derive(Debug, Clone, PartialEq, Eq, EnumIter, DeriveActiveEnum, Default)] +#[sea_orm( + rs_type = "String", + db_type = "Enum", + enum_name = "notification_type_enum" +)] +pub enum NotificationTypeEnum { + #[sea_orm(string_value = "app")] + App, + #[sea_orm(string_value = "follow")] + Follow, + #[sea_orm(string_value = "followRequestAccepted")] + FollowRequestAccepted, + #[sea_orm(string_value = "groupInvited")] + GroupInvited, + #[sea_orm(string_value = "mention")] + Mention, + #[sea_orm(string_value = "pollEnded")] + PollEnded, + #[sea_orm(string_value = "pollVote")] + PollVote, + #[sea_orm(string_value = "quote")] + Quote, + #[sea_orm(string_value = "reaction")] + Reaction, + #[sea_orm(string_value = "receiveFollowRequest")] + ReceiveFollowRequest, + #[sea_orm(string_value = "renote")] + Renote, + #[default] + #[sea_orm(string_value = "reply")] + Reply, +} +#[derive(Debug, Clone, PartialEq, Eq, EnumIter, DeriveActiveEnum, Default)] +#[sea_orm( + rs_type = "String", + db_type = "Enum", + enum_name = "page_visibility_enum" +)] +pub enum PageVisibilityEnum { + #[sea_orm(string_value = "followers")] + Followers, + #[default] + #[sea_orm(string_value = "public")] + Public, + #[sea_orm(string_value = "specified")] + Specified, +} +#[derive(Debug, Clone, PartialEq, Eq, EnumIter, DeriveActiveEnum, Default)] +#[sea_orm( + rs_type = "String", + db_type = "Enum", + enum_name = "poll_notevisibility_enum" +)] +pub enum PollNotevisibilityEnum { + #[sea_orm(string_value = "followers")] + Followers, + #[sea_orm(string_value = "home")] + Home, + #[default] + #[sea_orm(string_value = "public")] + Public, + #[sea_orm(string_value = "specified")] + Specified, +} +#[derive(Debug, Clone, PartialEq, Eq, EnumIter, DeriveActiveEnum, Default)] +#[sea_orm(rs_type = "String", db_type = "Enum", enum_name = "relay_status_enum")] +pub enum RelayStatusEnum { + #[sea_orm(string_value = "accepted")] + Accepted, + #[sea_orm(string_value = "rejected")] + Rejected, + #[default] + #[sea_orm(string_value = "requesting")] + Requesting, +} +#[derive(Debug, Clone, PartialEq, Eq, EnumIter, DeriveActiveEnum, Default)] +#[sea_orm( + rs_type = "String", + db_type = "Enum", + enum_name = "user_profile_ffvisibility_enum" +)] +pub enum UserProfileFfvisibilityEnum { + #[sea_orm(string_value = "followers")] + Followers, + #[sea_orm(string_value = "private")] + Private, + #[default] + #[sea_orm(string_value = "public")] + Public, +} diff --git a/packages/backend/native-utils/src/model/entity/signin.rs b/packages/backend/native-utils/src/model/entity/signin.rs new file mode 100644 index 0000000000..60bbc33d20 --- /dev/null +++ b/packages/backend/native-utils/src/model/entity/signin.rs @@ -0,0 +1,38 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3 + +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Default)] +#[sea_orm(table_name = "signin")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub id: String, + #[sea_orm(column_name = "createdAt")] + pub created_at: DateTimeWithTimeZone, + #[sea_orm(column_name = "userId")] + pub user_id: String, + pub ip: String, + #[sea_orm(column_type = "JsonBinary")] + pub headers: Json, + pub success: bool, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::user::Entity", + from = "Column::UserId", + to = "super::user::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + User, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::User.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/packages/backend/native-utils/src/model/entity/sw_subscription.rs b/packages/backend/native-utils/src/model/entity/sw_subscription.rs new file mode 100644 index 0000000000..1be9e046a0 --- /dev/null +++ b/packages/backend/native-utils/src/model/entity/sw_subscription.rs @@ -0,0 +1,39 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3 + +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Default)] +#[sea_orm(table_name = "sw_subscription")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub id: String, + #[sea_orm(column_name = "createdAt")] + pub created_at: DateTimeWithTimeZone, + #[sea_orm(column_name = "userId")] + pub user_id: String, + pub endpoint: String, + pub auth: String, + pub publickey: String, + #[sea_orm(column_name = "sendReadMessage")] + pub send_read_message: bool, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::user::Entity", + from = "Column::UserId", + to = "super::user::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + User, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::User.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/packages/backend/native-utils/src/model/entity/used_username.rs b/packages/backend/native-utils/src/model/entity/used_username.rs new file mode 100644 index 0000000000..620950b643 --- /dev/null +++ b/packages/backend/native-utils/src/model/entity/used_username.rs @@ -0,0 +1,17 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3 + +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Default)] +#[sea_orm(table_name = "used_username")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub username: String, + #[sea_orm(column_name = "createdAt")] + pub created_at: DateTimeWithTimeZone, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation {} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/packages/backend/native-utils/src/model/entity/user.rs b/packages/backend/native-utils/src/model/entity/user.rs new file mode 100644 index 0000000000..e76ae08c7f --- /dev/null +++ b/packages/backend/native-utils/src/model/entity/user.rs @@ -0,0 +1,424 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3 + +use sea_orm::entity::prelude::*; + +use super::newtype::StringVec; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Default)] +#[sea_orm(table_name = "user")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub id: String, + #[sea_orm(column_name = "createdAt")] + pub created_at: DateTimeWithTimeZone, + #[sea_orm(column_name = "updatedAt")] + pub updated_at: Option, + #[sea_orm(column_name = "lastFetchedAt")] + pub last_fetched_at: Option, + pub username: String, + #[sea_orm(column_name = "usernameLower")] + pub username_lower: String, + pub name: Option, + #[sea_orm(column_name = "followersCount")] + pub followers_count: i32, + #[sea_orm(column_name = "followingCount")] + pub following_count: i32, + #[sea_orm(column_name = "notesCount")] + pub notes_count: i32, + #[sea_orm(column_name = "avatarId", unique)] + pub avatar_id: Option, + #[sea_orm(column_name = "bannerId", unique)] + pub banner_id: Option, + pub tags: StringVec, + #[sea_orm(column_name = "isSuspended")] + pub is_suspended: bool, + #[sea_orm(column_name = "isSilenced")] + pub is_silenced: bool, + #[sea_orm(column_name = "isLocked")] + pub is_locked: bool, + #[sea_orm(column_name = "isBot")] + pub is_bot: bool, + #[sea_orm(column_name = "isCat")] + pub is_cat: bool, + #[sea_orm(column_name = "isAdmin")] + pub is_admin: bool, + #[sea_orm(column_name = "isModerator")] + pub is_moderator: bool, + pub emojis: StringVec, + pub host: Option, + pub inbox: Option, + #[sea_orm(column_name = "sharedInbox")] + pub shared_inbox: Option, + pub featured: Option, + pub uri: Option, + #[sea_orm(unique)] + pub token: Option, + #[sea_orm(column_name = "isExplorable")] + pub is_explorable: bool, + #[sea_orm(column_name = "followersUri")] + pub followers_uri: Option, + #[sea_orm(column_name = "lastActiveDate")] + pub last_active_date: Option, + #[sea_orm(column_name = "hideOnlineStatus")] + pub hide_online_status: bool, + #[sea_orm(column_name = "isDeleted")] + pub is_deleted: bool, + #[sea_orm(column_name = "driveCapacityOverrideMb")] + pub drive_capacity_override_mb: Option, + #[sea_orm(column_name = "movedToUri")] + pub moved_to_uri: Option, + #[sea_orm(column_name = "alsoKnownAs", column_type = "Text", nullable)] + pub also_known_as: Option, + #[sea_orm(column_name = "speakAsCat")] + pub speak_as_cat: bool, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm(has_many = "super::access_token::Entity")] + AccessToken, + #[sea_orm(has_many = "super::announcement_read::Entity")] + AnnouncementRead, + #[sea_orm(has_many = "super::antenna::Entity")] + Antenna, + #[sea_orm(has_many = "super::app::Entity")] + App, + #[sea_orm(has_many = "super::attestation_challenge::Entity")] + AttestationChallenge, + #[sea_orm(has_many = "super::auth_session::Entity")] + AuthSession, + #[sea_orm(has_many = "super::channel::Entity")] + Channel, + #[sea_orm(has_many = "super::channel_following::Entity")] + ChannelFollowing, + #[sea_orm(has_many = "super::clip::Entity")] + Clip, + #[sea_orm( + belongs_to = "super::drive_file::Entity", + from = "Column::AvatarId", + to = "super::drive_file::Column::Id", + on_update = "NoAction", + on_delete = "SetNull" + )] + DriveFile2, + #[sea_orm( + belongs_to = "super::drive_file::Entity", + from = "Column::BannerId", + to = "super::drive_file::Column::Id", + on_update = "NoAction", + on_delete = "SetNull" + )] + DriveFile1, + #[sea_orm(has_many = "super::drive_folder::Entity")] + DriveFolder, + #[sea_orm(has_many = "super::gallery_like::Entity")] + GalleryLike, + #[sea_orm(has_many = "super::gallery_post::Entity")] + GalleryPost, + #[sea_orm(has_many = "super::meta::Entity")] + Meta, + #[sea_orm(has_many = "super::moderation_log::Entity")] + ModerationLog, + #[sea_orm(has_many = "super::muted_note::Entity")] + MutedNote, + #[sea_orm(has_many = "super::note::Entity")] + Note, + #[sea_orm(has_many = "super::note_favorite::Entity")] + NoteFavorite, + #[sea_orm(has_many = "super::note_reaction::Entity")] + NoteReaction, + #[sea_orm(has_many = "super::note_thread_muting::Entity")] + NoteThreadMuting, + #[sea_orm(has_many = "super::note_unread::Entity")] + NoteUnread, + #[sea_orm(has_many = "super::note_watching::Entity")] + NoteWatching, + #[sea_orm(has_many = "super::page::Entity")] + Page, + #[sea_orm(has_many = "super::page_like::Entity")] + PageLike, + #[sea_orm(has_many = "super::password_reset_request::Entity")] + PasswordResetRequest, + #[sea_orm(has_many = "super::poll_vote::Entity")] + PollVote, + #[sea_orm(has_many = "super::promo_read::Entity")] + PromoRead, + #[sea_orm(has_many = "super::registry_item::Entity")] + RegistryItem, + #[sea_orm(has_many = "super::signin::Entity")] + Signin, + #[sea_orm(has_many = "super::sw_subscription::Entity")] + SwSubscription, + #[sea_orm(has_many = "super::user_group::Entity")] + UserGroup, + #[sea_orm(has_many = "super::user_group_invitation::Entity")] + UserGroupInvitation, + #[sea_orm(has_many = "super::user_group_invite::Entity")] + UserGroupInvite, + #[sea_orm(has_many = "super::user_group_joining::Entity")] + UserGroupJoining, + #[sea_orm(has_one = "super::user_keypair::Entity")] + UserKeypair, + #[sea_orm(has_many = "super::user_list::Entity")] + UserList, + #[sea_orm(has_many = "super::user_list_joining::Entity")] + UserListJoining, + #[sea_orm(has_many = "super::user_note_pining::Entity")] + UserNotePining, + #[sea_orm(has_one = "super::user_profile::Entity")] + UserProfile, + #[sea_orm(has_one = "super::user_publickey::Entity")] + UserPublickey, + #[sea_orm(has_many = "super::user_security_key::Entity")] + UserSecurityKey, + #[sea_orm(has_many = "super::webhook::Entity")] + Webhook, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::AccessToken.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::AnnouncementRead.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Antenna.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::App.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::AttestationChallenge.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::AuthSession.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Channel.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::ChannelFollowing.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Clip.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::DriveFolder.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::GalleryLike.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::GalleryPost.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Meta.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::ModerationLog.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::MutedNote.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Note.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::NoteFavorite.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::NoteReaction.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::NoteThreadMuting.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::NoteUnread.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::NoteWatching.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Page.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::PageLike.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::PasswordResetRequest.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::PollVote.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::PromoRead.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::RegistryItem.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Signin.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::SwSubscription.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::UserGroup.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::UserGroupInvitation.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::UserGroupInvite.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::UserGroupJoining.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::UserKeypair.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::UserList.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::UserListJoining.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::UserNotePining.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::UserProfile.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::UserPublickey.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::UserSecurityKey.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Webhook.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/packages/backend/native-utils/src/model/entity/user_group.rs b/packages/backend/native-utils/src/model/entity/user_group.rs new file mode 100644 index 0000000000..74ee4f22fb --- /dev/null +++ b/packages/backend/native-utils/src/model/entity/user_group.rs @@ -0,0 +1,69 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3 + +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Default)] +#[sea_orm(table_name = "user_group")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub id: String, + #[sea_orm(column_name = "createdAt")] + pub created_at: DateTimeWithTimeZone, + pub name: String, + #[sea_orm(column_name = "userId")] + pub user_id: String, + #[sea_orm(column_name = "isPrivate")] + pub is_private: bool, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm(has_many = "super::messaging_message::Entity")] + MessagingMessage, + #[sea_orm( + belongs_to = "super::user::Entity", + from = "Column::UserId", + to = "super::user::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + User, + #[sea_orm(has_many = "super::user_group_invitation::Entity")] + UserGroupInvitation, + #[sea_orm(has_many = "super::user_group_invite::Entity")] + UserGroupInvite, + #[sea_orm(has_many = "super::user_group_joining::Entity")] + UserGroupJoining, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::MessagingMessage.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::User.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::UserGroupInvitation.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::UserGroupInvite.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::UserGroupJoining.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/packages/backend/native-utils/src/model/entity/user_group_invitation.rs b/packages/backend/native-utils/src/model/entity/user_group_invitation.rs new file mode 100644 index 0000000000..baa6fea83e --- /dev/null +++ b/packages/backend/native-utils/src/model/entity/user_group_invitation.rs @@ -0,0 +1,58 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3 + +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Default)] +#[sea_orm(table_name = "user_group_invitation")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub id: String, + #[sea_orm(column_name = "createdAt")] + pub created_at: DateTimeWithTimeZone, + #[sea_orm(column_name = "userId")] + pub user_id: String, + #[sea_orm(column_name = "userGroupId")] + pub user_group_id: String, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm(has_many = "super::notification::Entity")] + Notification, + #[sea_orm( + belongs_to = "super::user::Entity", + from = "Column::UserId", + to = "super::user::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + User, + #[sea_orm( + belongs_to = "super::user_group::Entity", + from = "Column::UserGroupId", + to = "super::user_group::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + UserGroup, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Notification.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::User.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::UserGroup.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/packages/backend/native-utils/src/model/entity/user_group_invite.rs b/packages/backend/native-utils/src/model/entity/user_group_invite.rs new file mode 100644 index 0000000000..dbbc055f01 --- /dev/null +++ b/packages/backend/native-utils/src/model/entity/user_group_invite.rs @@ -0,0 +1,50 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3 + +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Default)] +#[sea_orm(table_name = "user_group_invite")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub id: String, + #[sea_orm(column_name = "createdAt")] + pub created_at: DateTimeWithTimeZone, + #[sea_orm(column_name = "userId")] + pub user_id: String, + #[sea_orm(column_name = "userGroupId")] + pub user_group_id: String, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::user::Entity", + from = "Column::UserId", + to = "super::user::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + User, + #[sea_orm( + belongs_to = "super::user_group::Entity", + from = "Column::UserGroupId", + to = "super::user_group::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + UserGroup, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::User.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::UserGroup.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/packages/backend/native-utils/src/model/entity/user_group_joining.rs b/packages/backend/native-utils/src/model/entity/user_group_joining.rs new file mode 100644 index 0000000000..e7741520cd --- /dev/null +++ b/packages/backend/native-utils/src/model/entity/user_group_joining.rs @@ -0,0 +1,58 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3 + +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Default)] +#[sea_orm(table_name = "user_group_joining")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub id: String, + #[sea_orm(column_name = "createdAt")] + pub created_at: DateTimeWithTimeZone, + #[sea_orm(column_name = "userId")] + pub user_id: String, + #[sea_orm(column_name = "userGroupId")] + pub user_group_id: String, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm(has_many = "super::antenna::Entity")] + Antenna, + #[sea_orm( + belongs_to = "super::user::Entity", + from = "Column::UserId", + to = "super::user::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + User, + #[sea_orm( + belongs_to = "super::user_group::Entity", + from = "Column::UserGroupId", + to = "super::user_group::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + UserGroup, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Antenna.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::User.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::UserGroup.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/packages/backend/native-utils/src/model/entity/user_ip.rs b/packages/backend/native-utils/src/model/entity/user_ip.rs new file mode 100644 index 0000000000..ce0af264d9 --- /dev/null +++ b/packages/backend/native-utils/src/model/entity/user_ip.rs @@ -0,0 +1,20 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3 + +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Default)] +#[sea_orm(table_name = "user_ip")] +pub struct Model { + #[sea_orm(primary_key)] + pub id: i32, + #[sea_orm(column_name = "createdAt")] + pub created_at: DateTimeWithTimeZone, + #[sea_orm(column_name = "userId")] + pub user_id: String, + pub ip: String, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation {} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/packages/backend/native-utils/src/model/entity/user_keypair.rs b/packages/backend/native-utils/src/model/entity/user_keypair.rs new file mode 100644 index 0000000000..0382d5d768 --- /dev/null +++ b/packages/backend/native-utils/src/model/entity/user_keypair.rs @@ -0,0 +1,34 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3 + +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Default)] +#[sea_orm(table_name = "user_keypair")] +pub struct Model { + #[sea_orm(column_name = "userId", primary_key, auto_increment = false, unique)] + pub user_id: String, + #[sea_orm(column_name = "publicKey")] + pub public_key: String, + #[sea_orm(column_name = "privateKey")] + pub private_key: String, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::user::Entity", + from = "Column::UserId", + to = "super::user::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + User, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::User.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/packages/backend/native-utils/src/model/entity/user_list.rs b/packages/backend/native-utils/src/model/entity/user_list.rs new file mode 100644 index 0000000000..7cc972133a --- /dev/null +++ b/packages/backend/native-utils/src/model/entity/user_list.rs @@ -0,0 +1,51 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3 + +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Default)] +#[sea_orm(table_name = "user_list")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub id: String, + #[sea_orm(column_name = "createdAt")] + pub created_at: DateTimeWithTimeZone, + #[sea_orm(column_name = "userId")] + pub user_id: String, + pub name: String, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm(has_many = "super::antenna::Entity")] + Antenna, + #[sea_orm( + belongs_to = "super::user::Entity", + from = "Column::UserId", + to = "super::user::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + User, + #[sea_orm(has_many = "super::user_list_joining::Entity")] + UserListJoining, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Antenna.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::User.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::UserListJoining.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/packages/backend/native-utils/src/model/entity/user_list_joining.rs b/packages/backend/native-utils/src/model/entity/user_list_joining.rs new file mode 100644 index 0000000000..4f28a21dba --- /dev/null +++ b/packages/backend/native-utils/src/model/entity/user_list_joining.rs @@ -0,0 +1,50 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3 + +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Default)] +#[sea_orm(table_name = "user_list_joining")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub id: String, + #[sea_orm(column_name = "createdAt")] + pub created_at: DateTimeWithTimeZone, + #[sea_orm(column_name = "userId")] + pub user_id: String, + #[sea_orm(column_name = "userListId")] + pub user_list_id: String, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::user::Entity", + from = "Column::UserId", + to = "super::user::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + User, + #[sea_orm( + belongs_to = "super::user_list::Entity", + from = "Column::UserListId", + to = "super::user_list::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + UserList, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::User.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::UserList.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/packages/backend/native-utils/src/model/entity/user_note_pining.rs b/packages/backend/native-utils/src/model/entity/user_note_pining.rs new file mode 100644 index 0000000000..e657fcb532 --- /dev/null +++ b/packages/backend/native-utils/src/model/entity/user_note_pining.rs @@ -0,0 +1,50 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3 + +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Default)] +#[sea_orm(table_name = "user_note_pining")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub id: String, + #[sea_orm(column_name = "createdAt")] + pub created_at: DateTimeWithTimeZone, + #[sea_orm(column_name = "userId")] + pub user_id: String, + #[sea_orm(column_name = "noteId")] + pub note_id: String, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::note::Entity", + from = "Column::NoteId", + to = "super::note::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + Note, + #[sea_orm( + belongs_to = "super::user::Entity", + from = "Column::UserId", + to = "super::user::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + User, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Note.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::User.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/packages/backend/native-utils/src/model/entity/user_pending.rs b/packages/backend/native-utils/src/model/entity/user_pending.rs new file mode 100644 index 0000000000..297fe553cc --- /dev/null +++ b/packages/backend/native-utils/src/model/entity/user_pending.rs @@ -0,0 +1,21 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3 + +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Default)] +#[sea_orm(table_name = "user_pending")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub id: String, + #[sea_orm(column_name = "createdAt")] + pub created_at: DateTimeWithTimeZone, + pub code: String, + pub username: String, + pub email: String, + pub password: String, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation {} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/packages/backend/native-utils/src/model/entity/user_profile.rs b/packages/backend/native-utils/src/model/entity/user_profile.rs new file mode 100644 index 0000000000..4c2f903d4b --- /dev/null +++ b/packages/backend/native-utils/src/model/entity/user_profile.rs @@ -0,0 +1,112 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3 + +use super::sea_orm_active_enums::UserProfileFfvisibilityEnum; +use sea_orm::entity::prelude::*; + +use super::newtype::StringVec; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Default)] +#[sea_orm(table_name = "user_profile")] +pub struct Model { + #[sea_orm(column_name = "userId", primary_key, auto_increment = false, unique)] + pub user_id: String, + pub location: Option, + pub birthday: Option, + pub description: Option, + #[sea_orm(column_type = "JsonBinary")] + pub fields: Json, + pub url: Option, + pub email: Option, + #[sea_orm(column_name = "emailVerifyCode")] + pub email_verify_code: Option, + #[sea_orm(column_name = "emailVerified")] + pub email_verified: bool, + #[sea_orm(column_name = "twoFactorTempSecret")] + pub two_factor_temp_secret: Option, + #[sea_orm(column_name = "twoFactorSecret")] + pub two_factor_secret: Option, + #[sea_orm(column_name = "twoFactorEnabled")] + pub two_factor_enabled: bool, + pub password: Option, + #[sea_orm(column_name = "clientData", column_type = "JsonBinary")] + pub client_data: Json, + #[sea_orm(column_name = "autoAcceptFollowed")] + pub auto_accept_followed: bool, + #[sea_orm(column_name = "alwaysMarkNsfw")] + pub always_mark_nsfw: bool, + #[sea_orm(column_name = "carefulBot")] + pub careful_bot: bool, + #[sea_orm(column_name = "userHost")] + pub user_host: Option, + #[sea_orm(column_name = "securityKeysAvailable")] + pub security_keys_available: bool, + #[sea_orm(column_name = "usePasswordLessLogin")] + pub use_password_less_login: bool, + #[sea_orm(column_name = "pinnedPageId", unique)] + pub pinned_page_id: Option, + #[sea_orm(column_type = "JsonBinary")] + pub room: Json, + #[sea_orm(column_type = "JsonBinary")] + pub integrations: Json, + #[sea_orm(column_name = "injectFeaturedNote")] + pub inject_featured_note: bool, + #[sea_orm(column_name = "enableWordMute")] + pub enable_word_mute: bool, + #[sea_orm(column_name = "mutedWords", column_type = "JsonBinary")] + pub muted_words: Json, + #[sea_orm(column_name = "mutingNotificationTypes")] + pub muting_notification_types: StringVec, + #[sea_orm(column_name = "noCrawle")] + pub no_crawle: bool, + #[sea_orm(column_name = "receiveAnnouncementEmail")] + pub receive_announcement_email: bool, + #[sea_orm(column_name = "emailNotificationTypes", column_type = "JsonBinary")] + pub email_notification_types: Json, + pub lang: Option, + #[sea_orm(column_name = "mutedInstances", column_type = "JsonBinary")] + pub muted_instances: Json, + #[sea_orm(column_name = "publicReactions")] + pub public_reactions: bool, + #[sea_orm(column_name = "ffVisibility")] + pub ff_visibility: UserProfileFfvisibilityEnum, + #[sea_orm(column_name = "autoSensitive")] + pub auto_sensitive: bool, + #[sea_orm(column_name = "moderationNote")] + pub moderation_note: String, + #[sea_orm(column_name = "preventAiLearning")] + pub prevent_ai_learning: bool, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::page::Entity", + from = "Column::PinnedPageId", + to = "super::page::Column::Id", + on_update = "NoAction", + on_delete = "SetNull" + )] + Page, + #[sea_orm( + belongs_to = "super::user::Entity", + from = "Column::UserId", + to = "super::user::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + User, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Page.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::User.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/packages/backend/native-utils/src/model/entity/user_publickey.rs b/packages/backend/native-utils/src/model/entity/user_publickey.rs new file mode 100644 index 0000000000..b1f426c5b0 --- /dev/null +++ b/packages/backend/native-utils/src/model/entity/user_publickey.rs @@ -0,0 +1,34 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3 + +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Default)] +#[sea_orm(table_name = "user_publickey")] +pub struct Model { + #[sea_orm(column_name = "userId", primary_key, auto_increment = false, unique)] + pub user_id: String, + #[sea_orm(column_name = "keyId")] + pub key_id: String, + #[sea_orm(column_name = "keyPem")] + pub key_pem: String, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::user::Entity", + from = "Column::UserId", + to = "super::user::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + User, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::User.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/packages/backend/native-utils/src/model/entity/user_security_key.rs b/packages/backend/native-utils/src/model/entity/user_security_key.rs new file mode 100644 index 0000000000..4bc9763365 --- /dev/null +++ b/packages/backend/native-utils/src/model/entity/user_security_key.rs @@ -0,0 +1,37 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3 + +use sea_orm::entity::prelude::*; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Default)] +#[sea_orm(table_name = "user_security_key")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub id: String, + #[sea_orm(column_name = "userId")] + pub user_id: String, + #[sea_orm(column_name = "publicKey")] + pub public_key: String, + #[sea_orm(column_name = "lastUsed")] + pub last_used: DateTimeWithTimeZone, + pub name: String, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::user::Entity", + from = "Column::UserId", + to = "super::user::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + User, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::User.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/packages/backend/native-utils/src/model/entity/webhook.rs b/packages/backend/native-utils/src/model/entity/webhook.rs new file mode 100644 index 0000000000..06ea1516b3 --- /dev/null +++ b/packages/backend/native-utils/src/model/entity/webhook.rs @@ -0,0 +1,45 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3 + +use sea_orm::entity::prelude::*; + +use super::newtype::StringVec; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Default)] +#[sea_orm(table_name = "webhook")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub id: String, + #[sea_orm(column_name = "createdAt")] + pub created_at: DateTimeWithTimeZone, + #[sea_orm(column_name = "userId")] + pub user_id: String, + pub name: String, + pub on: StringVec, + pub url: String, + pub secret: String, + pub active: bool, + #[sea_orm(column_name = "latestSentAt")] + pub latest_sent_at: Option, + #[sea_orm(column_name = "latestStatus")] + pub latest_status: Option, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::user::Entity", + from = "Column::UserId", + to = "super::user::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + User, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::User.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/packages/backend/native-utils/src/model/error.rs b/packages/backend/native-utils/src/model/error.rs new file mode 100644 index 0000000000..8e9213066b --- /dev/null +++ b/packages/backend/native-utils/src/model/error.rs @@ -0,0 +1,15 @@ +use crate::impl_into_napi_error; + +#[derive(thiserror::Error, Debug, PartialEq, Eq)] +pub enum Error { + #[error("Failed to parse string: {0}")] + ParseError(#[from] parse_display::ParseError), + #[error("Failed to get database connection: {0}")] + DbConnError(#[from] crate::database::error::Error), + #[error("Database operation error: {0}")] + DbOperationError(#[from] sea_orm::DbErr), + #[error("Requested entity not found")] + NotFound, +} + +impl_into_napi_error!(Error); diff --git a/packages/backend/native-utils/src/model/mod.rs b/packages/backend/native-utils/src/model/mod.rs new file mode 100644 index 0000000000..6e86ec0524 --- /dev/null +++ b/packages/backend/native-utils/src/model/mod.rs @@ -0,0 +1,4 @@ +pub mod entity; +pub mod error; +pub mod repository; +pub mod schema; diff --git a/packages/backend/native-utils/src/model/repository.rs b/packages/backend/native-utils/src/model/repository.rs new file mode 100644 index 0000000000..5abf7907fe --- /dev/null +++ b/packages/backend/native-utils/src/model/repository.rs @@ -0,0 +1,33 @@ +pub mod antenna; + +use async_trait::async_trait; +use schemars::JsonSchema; + +use super::error::Error; + +/// Repositories have a packer that converts a database model to its +/// corresponding API schema. +#[async_trait] +pub trait Repository { + async fn pack(self) -> Result; + /// Retrieves one model by its id and pack it. + async fn pack_by_id(id: String) -> Result; +} + +mod macros { + /// Provides the default implementation of + /// [crate::model::repository::Repository::pack_by_id]. + macro_rules! impl_pack_by_id { + ($a:ty, $b:ident) => { + match <$a>::find_by_id($b) + .one(crate::database::get_database()?) + .await? + { + None => Err(Error::NotFound), + Some(m) => m.pack().await, + } + }; + } + + pub(crate) use impl_pack_by_id; +} diff --git a/packages/backend/native-utils/src/model/repository/antenna.rs b/packages/backend/native-utils/src/model/repository/antenna.rs new file mode 100644 index 0000000000..80f34fed10 --- /dev/null +++ b/packages/backend/native-utils/src/model/repository/antenna.rs @@ -0,0 +1,56 @@ +use async_trait::async_trait; +use cfg_if::cfg_if; +use sea_orm::EntityTrait; + +use crate::database; +use crate::model::entity::{antenna, user_group_joining}; +use crate::model::error::Error; +use crate::model::schema::Antenna; + +use super::macros::impl_pack_by_id; +use super::Repository; + +#[async_trait] +impl Repository for antenna::Model { + async fn pack(self) -> Result { + let db = database::get_database()?; + let user_group_joining = match self.user_group_joining_id { + None => None, + Some(id) => user_group_joining::Entity::find_by_id(id).one(db).await?, + }; + let user_group_id = match user_group_joining { + None => None, + Some(m) => Some(m.user_group_id), + }; + + cfg_if! { + if #[cfg(feature = "napi")] { + let created_at: String = self.created_at.to_rfc3339(); + } else { + let created_at: chrono::DateTime = self.created_at.into(); + } + } + + Ok(Antenna { + id: self.id, + created_at, + name: self.name, + keywords: self.keywords.into(), + exclude_keywords: self.exclude_keywords.into(), + src: self.src.try_into()?, + user_list_id: self.user_list_id, + user_group_id, + users: self.users, + instances: self.instances.into(), + case_sensitive: self.case_sensitive, + notify: self.notify, + with_replies: self.with_replies, + with_file: self.with_file, + has_unread_note: false, + }) + } + + async fn pack_by_id(id: String) -> Result { + impl_pack_by_id!(antenna::Entity, id) + } +} diff --git a/packages/backend/native-utils/src/model/schema.rs b/packages/backend/native-utils/src/model/schema.rs new file mode 100644 index 0000000000..4c0ca7941c --- /dev/null +++ b/packages/backend/native-utils/src/model/schema.rs @@ -0,0 +1,35 @@ +pub mod antenna; +pub mod app; + +use cfg_if::cfg_if; +use jsonschema::JSONSchema; +use schemars::{schema_for, JsonSchema}; + +/// Structs of schema defitions implement this trait in order to +/// provide the JSON Schema validator [`jsonschema::JSONSchema`]. +pub trait Schema { + /// Returns the validator of [JSON Schema Draft + /// 7](https://json-schema.org/specification-links.html#draft-7) with the + /// default settings of [`schemars::gen::SchemaSettings`]. + fn validator() -> JSONSchema { + let root = schema_for!(T); + let schema = serde_json::to_value(&root).expect("Schema definition invalid"); + JSONSchema::options() + .with_draft(jsonschema::Draft::Draft7) + .compile(&schema) + .expect("Unable to compile schema") + } +} + +cfg_if! { + if #[cfg(feature = "napi")] { + // Will be disabled once we completely migrate to rust + pub use antenna::NativeAntennaSchema as Antenna; + pub use antenna::NativeAntennaSrc as AntennaSrc; + } else { + pub use antenna::Antenna; + pub use antenna::AntennaSrc; + pub use app::App; + pub use app::AppPermission; + } +} diff --git a/packages/backend/native-utils/src/model/schema/antenna.rs b/packages/backend/native-utils/src/model/schema/antenna.rs new file mode 100644 index 0000000000..da2c3061b4 --- /dev/null +++ b/packages/backend/native-utils/src/model/schema/antenna.rs @@ -0,0 +1,217 @@ +use cfg_if::cfg_if; +use jsonschema::JSONSchema; +use once_cell::sync::Lazy; +use parse_display::FromStr; +use schemars::JsonSchema; +use utoipa::ToSchema; + +use super::Schema; +use crate::model; +use crate::model::entity::sea_orm_active_enums::AntennaSrcEnum; + +#[derive(Clone, Debug, PartialEq, Eq, JsonSchema, ToSchema)] +#[serde(rename_all = "camelCase")] +pub struct Antenna { + pub id: String, + pub created_at: chrono::DateTime, + pub name: String, + pub keywords: Vec>, + pub exclude_keywords: Vec>, + #[schema(inline)] + pub src: AntennaSrc, + pub user_list_id: Option, + pub user_group_id: Option, + pub users: Vec, + pub instances: Vec, + #[serde(default)] + pub case_sensitive: bool, + #[serde(default)] + pub notify: bool, + #[serde(default)] + pub with_replies: bool, + #[serde(default)] + pub with_file: bool, + #[serde(default)] + pub has_unread_note: bool, +} + +#[derive(Clone, Debug, FromStr, PartialEq, Eq, JsonSchema, ToSchema)] +#[serde(rename_all = "camelCase")] +#[display(style = "camelCase")] +#[display("'{}'")] +pub enum AntennaSrc { + Home, + All, + Users, + List, + Group, + Instances, +} + +impl TryFrom for super::AntennaSrc { + type Error = model::error::Error; + + fn try_from(value: AntennaSrcEnum) -> Result { + value.to_string().parse().map_err(model::error::Error::from) + } +} + +// ---- TODO: could be macro +impl Schema for super::Antenna {} +pub static VALIDATOR: Lazy = Lazy::new(super::Antenna::validator); +// ---- + +cfg_if! { + if #[cfg(feature = "napi")] { + use napi::bindgen_prelude::{FromNapiValue, ToNapiValue}; + use napi_derive::napi; + + use crate::model::entity::antenna; + use crate::model::repository::Repository; + + /// For NAPI because [chrono] is not supported. + #[napi(object)] + #[derive(Clone, Debug, PartialEq, Eq, JsonSchema, ToSchema)] + #[serde(rename_all = "camelCase")] + pub struct NativeAntennaSchema { + pub id: String, + pub created_at: String, + pub name: String, + pub keywords: Vec>, + pub exclude_keywords: Vec>, + #[schema(inline)] + pub src: NativeAntennaSrc, + pub user_list_id: Option, + pub user_group_id: Option, + pub users: Vec, + pub instances: Vec, + #[serde(default)] + pub case_sensitive: bool, + #[serde(default)] + pub notify: bool, + #[serde(default)] + pub with_replies: bool, + #[serde(default)] + pub with_file: bool, + #[serde(default)] + pub has_unread_note: bool, + } + + #[napi(string_enum)] + #[derive(Debug, FromStr, PartialEq, Eq, JsonSchema, ToSchema)] + #[display("'{}'")] + #[allow(non_camel_case_types)] + pub enum NativeAntennaSrc { + home, + all, + users, + list, + group, + instances, + } + + #[napi] + pub async fn native_pack_antenna_by_id(id: String) -> napi::Result { + antenna::Model::pack_by_id(id).await.map_err(Into::into) + } + } +} + +#[cfg(test)] +mod unit_test { + use cfg_if::cfg_if; + use pretty_assertions::assert_eq; + use serde_json::json; + + use crate::model::{entity::sea_orm_active_enums::AntennaSrcEnum, schema::AntennaSrc}; + + use super::VALIDATOR; + + #[test] + fn src_from_active_enum() { + let src = AntennaSrc::try_from(AntennaSrcEnum::All).unwrap(); + cfg_if! { + if #[cfg(feature = "napi")] { + assert_eq!(src, AntennaSrc::all); + } else { + assert_eq!(src, AntennaSrc::All); + } + } + } + + #[test] + fn antenna_valid() { + let instance = json!({ + "id": "9fil64s6g7cskdrb", + "createdAt": "2023-05-24T06:56:14.323Z", + "name": "Valid Antenna", + "keywords": [["first", "keyword"], ["second"]], + "excludeKeywords": [["excluding", "keywrods"], ["from", "antenna"]], + "src": "users", + // "userListId" and "userGroupId" can be null or be omitted + "userListId": null, + "users": ["9fil64s6g7cskdrb", "9fil66brl1udxau2"], + "instances": [], + // "caseSensitive", "notify", "withReplies", "withFile", and + // "hasUnreadNote" are false if ommited + "notify": false, + "withReplies": false, + "withFile": false, + "hasUnreadNote": false, + }); + + assert!(VALIDATOR.is_valid(&instance)); + } + + #[test] + fn antenna_invalid() { + let instance = json!({ + // "id" is required + "id": null, + // trailing "Z" is missing + "createdAt": "2023-05-24T07:36:34.389", + // "name" is required + // "keywords" must be an array + "keywords": "invalid keyword", + // "excludeKeywords" is required + "excludeKeywords": null, + // "src" must be one of "home", "all", "users", "list", "group", and + // "instances" + "src": "invalid_src", + // "userListId" is string + "userListId": ["9f4ziiqfxw"], + // "users" must be an array of strings + "users": [1, "9fil64s6g7cskdrb"], + "instances": ["9fil65jzhtjpi3xn"], + // "caseSensitive" is boolean + "caseSensitive": 0, + "notify": true, + "withReplies": true, + "withFile": true, + "hasUnreadNote": true, + }); + + let result = VALIDATOR + .validate(&instance) + .expect_err("validation must fail"); + let mut paths: Vec = result + .map(|e| e.instance_path.to_string()) + .filter(|e| !e.is_empty()) + .collect(); + paths.sort(); + assert_eq!( + paths, + vec![ + "/caseSensitive", + #[cfg(not(feature = "napi"))] + "/createdAt", + "/excludeKeywords", + "/id", + "/keywords", + "/src", + "/userListId", + "/users/0" + ] + ); + } +} diff --git a/packages/backend/native-utils/src/model/schema/app.rs b/packages/backend/native-utils/src/model/schema/app.rs new file mode 100644 index 0000000000..1da7b35201 --- /dev/null +++ b/packages/backend/native-utils/src/model/schema/app.rs @@ -0,0 +1,147 @@ +use jsonschema::JSONSchema; +use once_cell::sync::Lazy; +use schemars::JsonSchema; +use utoipa::ToSchema; + +use super::Schema; + +#[derive(Clone, Debug, PartialEq, Eq, JsonSchema, ToSchema)] +#[serde(rename_all = "camelCase")] +pub struct App { + pub id: String, + pub name: String, + #[schemars(url)] + pub callback_url: Option, + #[schema(inline)] + pub permission: Vec, + pub secret: Option, + pub is_authorized: Option, +} + +/// This represents `permissions` in `packages/firefish-js/src/consts.ts`. +#[derive(Clone, Debug, PartialEq, Eq, JsonSchema, ToSchema)] +pub enum AppPermission { + #[serde(rename = "read:account")] + ReadAccount, + #[serde(rename = "write:account")] + WriteAccount, + #[serde(rename = "read:blocks")] + ReadBlocks, + #[serde(rename = "write:blocks")] + WriteBlocks, + #[serde(rename = "read:drive")] + ReadDrive, + #[serde(rename = "write:drive")] + WriteDrive, + #[serde(rename = "read:favorites")] + ReadFavorites, + #[serde(rename = "write:favorites")] + WriteFavorites, + #[serde(rename = "read:following")] + ReadFollowing, + #[serde(rename = "write:following")] + WriteFollowing, + #[serde(rename = "read:messaging")] + ReadMessaging, + #[serde(rename = "write:messaging")] + WriteMessaging, + #[serde(rename = "read:mutes")] + ReadMutes, + #[serde(rename = "write:mutes")] + WriteMutes, + #[serde(rename = "read:notes")] + ReadNotes, + #[serde(rename = "write:notes")] + WriteNotes, + #[serde(rename = "read:notifications")] + ReadNotifications, + #[serde(rename = "write:notifications")] + WriteNotifications, + #[serde(rename = "read:reactions")] + ReadReactions, + #[serde(rename = "write:reactions")] + WriteReactions, + #[serde(rename = "write:votes")] + WriteVotes, + #[serde(rename = "read:pages")] + ReadPages, + #[serde(rename = "write:pages")] + WritePages, + #[serde(rename = "read:page-likes")] + ReadPageLikes, + #[serde(rename = "write:page-likes")] + WritePageLikes, + #[serde(rename = "read:user-groups")] + ReadUserGroups, + #[serde(rename = "write:user-groups")] + WriteUserGroups, + #[serde(rename = "read:channels")] + ReadChannels, + #[serde(rename = "write:channels")] + WriteChannels, + #[serde(rename = "read:gallery")] + ReadGallery, + #[serde(rename = "write:gallery")] + WriteGallery, + #[serde(rename = "read:gallery-likes")] + ReadGalleryLikes, + #[serde(rename = "write:gallery-likes")] + WriteGalleryLikes, +} + +impl Schema for App {} + +pub static VALIDATOR: Lazy = Lazy::new(App::validator); + +#[cfg(test)] +mod unit_test { + use pretty_assertions::assert_eq; + use serde_json::json; + + use crate::util::id::{create_id, init_id}; + use crate::util::random::gen_string; + + use super::VALIDATOR; + + #[test] + fn app_valid() { + init_id(16, ""); + let instance = json!({ + "id": create_id(0).unwrap(), + "name": "Test App", + "secret": gen_string(24), + "callbackUrl": "urn:ietf:wg:oauth:2.0:oob", + "permission": ["read:account", "write:account", "read:notes"], + }); + + assert!(VALIDATOR.is_valid(&instance)); + } + + #[test] + fn app_invalid() { + init_id(16, ""); + let instance = json!({ + "id": create_id(0).unwrap(), + // "name" is required + "name": null, + // "permission" must be one of the app permissions + "permission": ["write:invalid_perm", "write:notes"], + // "secret" is a nullable string + "secret": 123, + // "is_authorized" is a nullable boolean + "isAuthorized": "true-ish", + }); + let result = VALIDATOR + .validate(&instance) + .expect_err("validation must fail"); + let mut paths: Vec = result + .map(|e| e.instance_path.to_string()) + .filter(|e| !e.is_empty()) + .collect(); + paths.sort(); + assert_eq!( + paths, + vec!["/isAuthorized", "/name", "/permission/0", "/secret"] + ); + } +} diff --git a/packages/backend/native-utils/src/util/id.rs b/packages/backend/native-utils/src/util/id.rs new file mode 100644 index 0000000000..b18637fdbb --- /dev/null +++ b/packages/backend/native-utils/src/util/id.rs @@ -0,0 +1,90 @@ +//! ID generation utility based on [cuid2] + +use cfg_if::cfg_if; +use chrono::Utc; +use once_cell::sync::OnceCell; +use radix_fmt::radix_36; +use std::cmp; + +use crate::impl_into_napi_error; + +#[derive(thiserror::Error, Debug, PartialEq, Eq)] +#[error("ID generator has not been initialized yet")] +pub struct ErrorUninitialized; + +impl_into_napi_error!(ErrorUninitialized); + +static FINGERPRINT: OnceCell = OnceCell::new(); +static GENERATOR: OnceCell = OnceCell::new(); + +const TIME_2000: i64 = 946_684_800_000; +const TIMESTAMP_LENGTH: u16 = 8; + +/// Initializes Cuid2 generator. Must be called before any [create_id]. +pub fn init_id<'a>(length: u16, fingerprint: &'a str) { + FINGERPRINT.get_or_init(move || format!("{}{}", fingerprint, cuid2::create_id())); + GENERATOR.get_or_init(move || { + cuid2::CuidConstructor::new() + // length to pass shoule be greater than or equal to 8. + .with_length(cmp::max(length - TIMESTAMP_LENGTH, 8)) + .with_fingerprinter(|| FINGERPRINT.get().unwrap().clone()) + }); +} + +/// Returns Cuid2 with the length specified by [init_id]. Must be called after +/// [init_id], otherwise returns [ErrorUninitialized]. +/// The current timestamp via [chrono::Utc] is used if `date_num` is `0`. +pub fn create_id(date_num: i64) -> Result { + match GENERATOR.get() { + None => Err(ErrorUninitialized), + Some(gen) => { + let date_num = if date_num > 0 { + date_num + } else { + Utc::now().timestamp_millis() + }; + let time = cmp::max(date_num - TIME_2000, 0); + Ok(format!( + "{:0>8}{}", + radix_36(time).to_string(), + gen.create_id() + )) + } + } +} + +cfg_if! { + if #[cfg(feature = "napi")] { + use napi_derive::napi; + + /// Calls [init_id] inside. Must be called before [native_create_id]. + #[napi] + pub fn native_init_id_generator(length: u16, fingerprint: String) { + init_id(length, &fingerprint); + } + + /// Generates + #[napi] + pub fn native_create_id(date_num: i64) -> String { + create_id(date_num).unwrap() + } + } +} + +#[cfg(test)] +mod unit_test { + use crate::util::id; + use pretty_assertions::{assert_eq, assert_ne}; + use std::thread; + + #[test] + fn can_generate_unique_ids() { + assert_eq!(id::create_id(0), Err(id::ErrorUninitialized)); + id::init_id(16, ""); + assert_eq!(id::create_id(0).unwrap().len(), 16); + assert_ne!(id::create_id(0).unwrap(), id::create_id(0).unwrap()); + let id1 = thread::spawn(|| id::create_id(0).unwrap()); + let id2 = thread::spawn(|| id::create_id(0).unwrap()); + assert_ne!(id1.join().unwrap(), id2.join().unwrap()); + } +} diff --git a/packages/backend/native-utils/src/util/mod.rs b/packages/backend/native-utils/src/util/mod.rs new file mode 100644 index 0000000000..1be5a7fd1f --- /dev/null +++ b/packages/backend/native-utils/src/util/mod.rs @@ -0,0 +1,2 @@ +pub mod id; +pub mod random; diff --git a/packages/backend/native-utils/src/util/random.rs b/packages/backend/native-utils/src/util/random.rs new file mode 100644 index 0000000000..ffcbca980f --- /dev/null +++ b/packages/backend/native-utils/src/util/random.rs @@ -0,0 +1,33 @@ +use rand::{distributions::Alphanumeric, thread_rng, Rng}; + +/// Generate random string based on [thread_rng] and [Alphanumeric]. +pub fn gen_string(length: u16) -> String { + thread_rng() + .sample_iter(Alphanumeric) + .take(length.into()) + .map(char::from) + .collect() +} + +#[cfg(feature = "napi")] +#[napi_derive::napi] +pub fn native_random_str(length: u16) -> String { + gen_string(length) +} + +#[cfg(test)] +mod unit_test { + use pretty_assertions::{assert_eq, assert_ne}; + use std::thread; + + use super::gen_string; + + #[test] + fn can_generate_unique_strings() { + assert_eq!(gen_string(16).len(), 16); + assert_ne!(gen_string(16), gen_string(16)); + let s1 = thread::spawn(|| gen_string(16)); + let s2 = thread::spawn(|| gen_string(16)); + assert_ne!(s1.join().unwrap(), s2.join().unwrap()); + } +} diff --git a/packages/backend/native-utils/tests/common.rs b/packages/backend/native-utils/tests/common.rs new file mode 100644 index 0000000000..674297606f --- /dev/null +++ b/packages/backend/native-utils/tests/common.rs @@ -0,0 +1,216 @@ +#![cfg(not(feature = "napi"))] + +mod model; + +use chrono::Utc; +use native_utils::database; +use native_utils::model::entity; +use native_utils::model::entity::sea_orm_active_enums::AntennaSrcEnum; +use native_utils::util::{ + id::{create_id, init_id}, + random::gen_string, +}; +use sea_orm::{ + sea_query::TableCreateStatement, ActiveModelTrait, ConnectionTrait, DbBackend, DbConn, DbErr, + EntityTrait, IntoActiveModel, TransactionTrait, +}; + +/// Insert predefined entries in the database. +async fn prepare() { + database::init_database("sqlite::memory:") + .await + .expect("Unable to initialize database connection"); + let db = database::get_database().expect("Unable to get database connection from pool"); + setup_schema(db).await; + setup_model(db).await; +} + +/// Setup schemas in the database. +async fn setup_schema(db: &DbConn) { + let schema = sea_orm::Schema::new(DbBackend::Sqlite); + let mut stmts: Vec = Vec::new(); + macro_rules! create_table_statement { + ($a:tt) => { + stmts.push(schema.create_table_from_entity(entity::$a::Entity).if_not_exists().to_owned()); + }; + ($a:tt, $($b:tt),+) => { + create_table_statement!($a); + create_table_statement!($($b),+); + }; + } + create_table_statement!( + abuse_user_report, + access_token, + ad, + announcement_read, + announcement, + antenna_note, + antenna, + app, + attestation_challenge, + auth_session, + blocking, + channel_following, + channel_note_pining, + channel, + clip_note, + clip, + drive_file, + drive_folder, + emoji, + following, + follow_request, + gallery_like, + gallery_post, + hashtag, + instance, + messaging_message, + meta, + migrations, + moderation_log, + muted_note, + muting, + note_edit, + note_favorite, + note_reaction, + note, + note_thread_muting, + note_unread, + note_watching, + notification, + page_like, + page, + password_reset_request, + poll, + poll_vote, + promo_note, + promo_read, + registration_ticket, + registry_item, + relay, + renote_muting, + signin, + sw_subscription, + used_username, + user_group_invitation, + user_group_invite, + user_group_joining, + user_group, + user_ip, + user_keypair, + user_list_joining, + user_list, + user_note_pining, + user_pending, + user_profile, + user_publickey, + user, + user_security_key, + webhook + ); + db.transaction::<_, (), DbErr>(|txn| { + Box::pin(async move { + for stmt in stmts { + txn.execute(txn.get_database_backend().build(&stmt)).await?; + } + Ok(()) + }) + }) + .await + .expect("Unable to setup schemas"); +} + +/// Delete all entries in the database. +async fn cleanup() { + let db = database::get_database().expect("Unable to get database connection from pool"); + db.transaction::<_, (), DbErr>(|txn| { + Box::pin(async move { + entity::user::Entity::delete_many().exec(txn).await.unwrap(); + entity::antenna::Entity::delete_many() + .exec(txn) + .await + .unwrap(); + + Ok(()) + }) + }) + .await + .expect("Unable to delete predefined models"); +} + +async fn setup_model(db: &DbConn) { + init_id(16, ""); + + db.transaction::<_, (), DbErr>(|txn| { + Box::pin(async move { + let user_id = create_id(0).unwrap(); + let name = "Alice"; + let user_model = entity::user::Model { + id: user_id.to_owned(), + created_at: Utc::now().into(), + username: name.to_lowercase(), + username_lower: name.to_lowercase(), + name: Some(name.to_string()), + token: Some(gen_string(16)), + is_admin: true, + ..Default::default() + }; + user_model + .into_active_model() + .reset_all() + .insert(txn) + .await?; + let antenna_model = entity::antenna::Model { + id: create_id(0).unwrap(), + created_at: Utc::now().into(), + user_id: user_id.to_owned(), + name: "Alice Antenna".to_string(), + src: AntennaSrcEnum::All, + 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(), + notify: true, + case_sensitive: true, + ..Default::default() + }; + antenna_model + .into_active_model() + .reset_all() + .insert(txn) + .await?; + let note_model = entity::note::Model { + id: create_id(0).unwrap(), + created_at: Utc::now().into(), + text: Some("Testing 123".to_string()), + user_id: user_id.to_owned(), + ..Default::default() + }; + note_model + .into_active_model() + .reset_all() + .insert(txn) + .await?; + + Ok(()) + }) + }) + .await + .expect("Unable to setup predefined models"); +} + +mod int_test { + use super::{cleanup, prepare}; + + #[tokio::test] + async fn can_prepare_and_cleanup() { + prepare().await; + cleanup().await; + } +} diff --git a/packages/backend/native-utils/tests/model/mod.rs b/packages/backend/native-utils/tests/model/mod.rs new file mode 100644 index 0000000000..a35bac056b --- /dev/null +++ b/packages/backend/native-utils/tests/model/mod.rs @@ -0,0 +1 @@ +mod repository; diff --git a/packages/backend/native-utils/tests/model/repository.rs b/packages/backend/native-utils/tests/model/repository.rs new file mode 100644 index 0000000000..c11ef7687f --- /dev/null +++ b/packages/backend/native-utils/tests/model/repository.rs @@ -0,0 +1 @@ +mod antenna; diff --git a/packages/backend/native-utils/tests/model/repository/antenna.rs b/packages/backend/native-utils/tests/model/repository/antenna.rs new file mode 100644 index 0000000000..9b03d3652d --- /dev/null +++ b/packages/backend/native-utils/tests/model/repository/antenna.rs @@ -0,0 +1,116 @@ +mod int_test { + use native_utils::{database, model, util}; + + use model::{ + entity::{antenna, antenna_note, note, user}, + repository::Repository, + schema, + }; + use pretty_assertions::assert_eq; + use sea_orm::{ActiveModelTrait, ColumnTrait, EntityTrait, IntoActiveModel, QueryFilter}; + + use crate::{cleanup, prepare}; + + #[tokio::test] + async fn can_pack() { + prepare().await; + let db = database::get_database().unwrap(); + + let alice_antenna = user::Entity::find() + .filter(user::Column::Username.eq("alice")) + .find_also_related(antenna::Entity) + .one(db) + .await + .unwrap() + .expect("alice not found") + .1 + .expect("alice's antenna not found"); + + let packed = alice_antenna + .to_owned() + .pack() + .await + .expect("Unable to pack"); + + let packed_by_id = antenna::Model::pack_by_id(alice_antenna.id.to_owned()) + .await + .expect("Unable to pack"); + + let result = schema::Antenna { + id: alice_antenna.id, + created_at: alice_antenna.created_at.into(), + name: "Alice Antenna".to_string(), + keywords: vec![ + vec!["foo".to_string(), "bar".to_string()], + vec!["foobar".to_string()], + ], + exclude_keywords: vec![ + vec!["abc".to_string()], + vec!["def".to_string(), "ghi".to_string()], + ], + src: schema::AntennaSrc::All, + user_list_id: None, + user_group_id: None, + users: vec![], + instances: vec![], + case_sensitive: true, + notify: true, + with_replies: false, + with_file: false, + has_unread_note: false, + }; + + assert_eq!(packed, result); + assert_eq!(packed_by_id, result); + + cleanup().await; + } + + #[tokio::test] + async fn unread_note() { + prepare().await; + let db = database::get_database().unwrap(); + + let (alice, alice_antenna) = user::Entity::find() + .filter(user::Column::Username.eq("alice")) + .find_also_related(antenna::Entity) + .one(db) + .await + .unwrap() + .expect("alice not found"); + let alice_antenna = alice_antenna.expect("alice's antenna not found"); + let packed = alice_antenna + .to_owned() + .pack() + .await + .expect("Unable to pack"); + assert_eq!(packed.has_unread_note, false); + + let note_model = note::Entity::find() + .filter(note::Column::UserId.eq(alice.id)) + .one(db) + .await + .unwrap() + .expect("note not found"); + let antenna_note = antenna_note::Model { + id: util::id::create_id(0).unwrap(), + antenna_id: alice_antenna.id.to_owned(), + note_id: note_model.id.to_owned(), + read: false, + }; + antenna_note + .into_active_model() + .reset_all() + .insert(db) + .await + .unwrap(); + let packed = alice_antenna + .to_owned() + .pack() + .await + .expect("Unable to pack"); + assert_eq!(packed.has_unread_note, true); + + cleanup().await; + } +} diff --git a/packages/backend/ormconfig.js b/packages/backend/ormconfig.js index a4e903abad..5f85cead8a 100644 --- a/packages/backend/ormconfig.js +++ b/packages/backend/ormconfig.js @@ -1,9 +1,9 @@ -import { DataSource } from 'typeorm'; -import config from './built/config/index.js'; -import { entities } from './built/db/postgre.js'; +import { DataSource } from "typeorm"; +import config from "./built/config/index.js"; +import { entities } from "./built/db/postgre.js"; export default new DataSource({ - type: 'postgres', + type: "postgres", host: config.db.host, port: config.db.port, username: config.db.user, @@ -11,5 +11,5 @@ export default new DataSource({ database: config.db.db, extra: config.db.extra, entities: entities, - migrations: ['migration/*.js'], + migrations: ["migration/*.js"], }); diff --git a/packages/backend/package.json b/packages/backend/package.json index 80484f95ce..cc8f4747ec 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -6,139 +6,153 @@ "scripts": { "start": "pnpm node ./built/index.js", "start:test": "NODE_ENV=test pnpm node ./built/index.js", - "migrate": "typeorm migration:run -d ormconfig.js", - "revertmigration": "typeorm migration:revert -d ormconfig.js", + "migrate": "pnpm run migrate:typeorm && pnpm run migrate:cargo", + "migrate:typeorm": "typeorm migration:run -d ormconfig.js", + "migrate:cargo": "./native-utils/built/migration up", + "revertmigration": "pnpm run revertmigration:cargo && pnpm run revertmigration:typeorm", + "revertmigration:typeorm": "typeorm migration:revert -d ormconfig.js", + "revertmigration:cargo": "./native-utils/built/migration down", + "check:connect": "node ./check_connect.js", "build": "pnpm swc src -d built -D", "watch": "pnpm swc src -d built -D -w", - "lint": "pnpm rome check \"src/**/*.ts\"", + "lint": "pnpm rome check --apply *", "mocha": "cross-env NODE_ENV=test TS_NODE_FILES=true TS_NODE_TRANSPILE_ONLY=true TS_NODE_PROJECT=\"./test/tsconfig.json\" mocha", - "test": "pnpm run mocha" - }, - "resolutions": { - "chokidar": "^3.3.1" + "test": "pnpm run mocha", + "format": "pnpm rome format * --write" }, "optionalDependencies": { "@swc/core-android-arm64": "1.3.11", "@tensorflow/tfjs-node": "3.21.1" }, "dependencies": { - "@bull-board/api": "^4.6.4", - "@bull-board/koa": "^4.6.4", - "@bull-board/ui": "^4.6.4", - "@discordapp/twemoji": "14.0.2", + "@bull-board/api": "5.6.0", + "@bull-board/koa": "5.6.0", + "@bull-board/ui": "5.6.0", + "@discordapp/twemoji": "14.1.2", "@elastic/elasticsearch": "7.17.0", "@koa/cors": "3.4.3", - "@koa/multer": "3.0.0", + "@koa/multer": "3.0.2", "@koa/router": "9.0.1", "@peertube/http-signature": "1.7.0", - "@redocly/openapi-core": "1.0.0-beta.120", + "@redocly/openapi-core": "1.0.0-beta.131", "@sinonjs/fake-timers": "9.1.2", "@syuilo/aiscript": "0.11.1", "@tensorflow/tfjs": "^4.2.0", - "ajv": "8.11.2", + "adm-zip": "^0.5.10", + "ajv": "8.12.0", "archiver": "5.3.1", - "koa-body": "^6.0.1", - "autobind-decorator": "2.4.0", + "argon2": "^0.30.3", "autolinker": "4.0.0", - "axios": "^1.3.2", "autwh": "0.1.0", - "aws-sdk": "2.1277.0", + "aws-sdk": "2.1413.0", + "axios": "^1.4.0", "bcryptjs": "2.4.3", - "blurhash": "1.1.5", - "bull": "4.10.2", + "blurhash": "2.0.5", + "bull": "4.10.4", "cacheable-lookup": "7.0.0", - "calckey-js": "^0.0.20", + "firefish-js": "workspace:*", "cbor": "8.1.0", - "chalk": "5.2.0", + "chalk": "5.3.0", "chalk-template": "0.4.0", - "chokidar": "3.5.3", + "chokidar": "^3.5.3", "cli-highlight": "2.1.11", "color-convert": "2.0.1", "content-disposition": "0.5.4", - "date-fns": "2.29.3", + "date-fns": "2.30.0", + "decompress": "^4.2.1", "deep-email-validator": "0.1.21", "escape-regexp": "0.0.1", "feed": "4.2.2", "file-type": "17.1.6", "fluent-ffmpeg": "2.1.2", "got": "12.5.3", + "gunzip-maybe": "^1.4.2", "hpagent": "0.1.2", - "ioredis": "5.2.4", - "ip-cidr": "3.0.11", + "ioredis": "5.3.2", + "ip-cidr": "3.1.0", "is-svg": "4.3.2", "js-yaml": "4.1.0", "jsdom": "20.0.3", - "jsonld": "6.0.0", - "jsrsasign": "10.6.1", - "koa": "2.13.4", - "koa-bodyparser": "4.3.0", + "json5": "2.2.3", + "jsonld": "8.2.0", + "jsrsasign": "10.8.6", + "koa": "2.14.2", + "koa-body": "^6.0.1", + "koa-bodyparser": "4.4.1", "koa-favicon": "2.1.0", "koa-json-body": "5.3.0", "koa-logger": "3.2.1", "koa-mount": "4.0.0", + "koa-remove-trailing-slashes": "2.0.3", "koa-send": "5.0.1", "koa-slow": "2.1.0", "koa-views": "7.0.2", - "@cutls/megalodon": "5.1.15", - "mfm-js": "0.23.2", + "megalodon": "workspace:*", + "meilisearch": "0.33.0", + "mfm-js": "0.23.3", "mime-types": "2.1.35", + "msgpackr": "1.9.5", "multer": "1.4.4-lts.1", + "native-utils": "link:native-utils", "nested-property": "4.0.0", - "node-fetch": "3.3.0", - "nodemailer": "6.8.0", + "node-fetch": "3.3.1", + "nodemailer": "6.9.3", "nsfwjs": "2.4.2", "oauth": "^0.10.0", "os-utils": "0.0.14", + "otpauth": "^9.1.3", "parse5": "7.1.2", - "pg": "8.8.0", + "pg": "8.11.1", "private-ip": "2.3.4", "probe-image-size": "7.2.3", "promise-limit": "2.7.0", - "punycode": "2.1.1", + "punycode": "2.3.0", "pureimage": "0.3.15", - "qrcode": "1.5.1", + "qrcode": "1.5.3", + "qs": "6.11.2", "random-seed": "0.3.0", "ratelimiter": "3.4.1", - "re2": "1.18.0", + "re2": "1.19.1", "redis-lock": "0.1.4", + "redis-semaphore": "5.3.1", "reflect-metadata": "0.1.13", "rename": "1.0.4", "rndstr": "1.0.0", - "rss-parser": "3.12.0", - "sanitize-html": "2.8.1", + "rss-parser": "3.13.0", + "sanitize-html": "2.10.0", "seedrandom": "^3.0.5", - "semver": "7.3.8", - "sharp": "0.31.3", - "speakeasy": "2.0.0", + "semver": "7.5.4", + "sharp": "0.32.1", + "sonic-channel": "^1.3.1", "stringz": "2.1.0", "summaly": "2.7.0", "syslog-pro": "1.0.0", - "systeminformation": "5.16.9", + "systeminformation": "5.17.17", + "tar-stream": "^3.1.6", "tesseract.js": "^3.0.3", "tinycolor2": "1.5.2", "tmp": "0.2.1", "twemoji-parser": "14.0.0", - "typeorm": "0.3.11", + "typeorm": "0.3.17", "ulid": "2.3.0", - "unzipper": "0.10.11", "uuid": "9.0.0", - "web-push": "3.5.0", + "web-push": "3.6.3", "websocket": "1.0.34", "xev": "3.0.2" }, "devDependencies": { - "@swc/cli": "^0.1.59", - "@swc/core": "^1.3.26", + "@swc/cli": "^0.1.62", + "@swc/core": "^1.3.68", + "@types/adm-zip": "^0.5.0", "@types/bcryptjs": "2.4.2", - "@types/bull": "3.15.9", "@types/cbor": "6.0.0", "@types/escape-regexp": "0.0.1", - "@types/fluent-ffmpeg": "2.1.20", + "@types/fluent-ffmpeg": "2.1.21", "@types/js-yaml": "4.0.5", - "@types/jsdom": "20.0.1", - "@types/jsonld": "1.5.8", - "@types/jsrsasign": "10.5.4", - "@types/koa": "2.13.5", + "@types/jsdom": "21.1.1", + "@types/jsonld": "1.5.9", + "@types/jsrsasign": "10.5.8", + "@types/koa": "2.13.6", "@types/koa-bodyparser": "4.3.10", "@types/koa-cors": "0.0.2", "@types/koa-favicon": "2.0.21", @@ -152,41 +166,39 @@ "@types/mocha": "9.1.1", "@types/node": "18.11.18", "@types/node-fetch": "3.0.3", - "@types/nodemailer": "6.4.7", + "@types/nodemailer": "6.4.8", "@types/oauth": "0.9.1", + "@types/probe-image-size": "^7.2.0", "@types/pug": "2.0.6", "@types/punycode": "2.1.0", - "@types/qrcode": "1.5.0", + "@types/qrcode": "1.5.1", + "@types/qs": "6.9.7", "@types/random-seed": "0.3.3", "@types/ratelimiter": "3.4.4", "@types/redis": "4.0.11", "@types/rename": "1.0.4", - "@types/sanitize-html": "2.8.0", - "@types/semver": "7.3.13", - "@types/sharp": "0.31.1", + "@types/sanitize-html": "2.9.0", + "@types/semver": "7.5.0", "@types/sinonjs__fake-timers": "8.1.2", - "@types/speakeasy": "2.0.7", "@types/tinycolor2": "1.4.3", "@types/tmp": "0.2.3", - "@types/uuid": "8.3.4", + "@types/uuid": "9.0.2", "@types/web-push": "3.3.2", "@types/websocket": "1.0.5", - "@types/ws": "8.5.3", - "autobind-decorator": "2.4.0", + "@types/ws": "8.5.5", "cross-env": "7.0.3", - "eslint": "^8.31.0", + "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", "strict-event-emitter-types": "2.0.0", "swc-loader": "^0.2.3", - "ts-loader": "9.4.2", + "ts-loader": "9.4.4", "ts-node": "10.9.1", - "tsconfig-paths": "4.1.2", - "typescript": "4.9.4", - "webpack": "^5.75.0", - "ws": "8.11.0" + "tsconfig-paths": "4.2.0", + "typescript": "5.1.6", + "webpack": "^5.88.1", + "ws": "8.13.0" } } diff --git a/packages/backend/src/@types/koa-remove-trailing-slashes/index.d.ts b/packages/backend/src/@types/koa-remove-trailing-slashes/index.d.ts new file mode 100644 index 0000000000..429d1d53e0 --- /dev/null +++ b/packages/backend/src/@types/koa-remove-trailing-slashes/index.d.ts @@ -0,0 +1 @@ +declare module "koa-remove-trailing-slashes"; diff --git a/packages/backend/src/@types/probe-image-size.d.ts b/packages/backend/src/@types/probe-image-size.d.ts deleted file mode 100644 index 4ed13df7fa..0000000000 --- a/packages/backend/src/@types/probe-image-size.d.ts +++ /dev/null @@ -1,37 +0,0 @@ -declare module "probe-image-size" { - import type { ReadStream } from "node:fs"; - - type ProbeOptions = { - retries: 1; - timeout: 30000; - }; - - type ProbeResult = { - width: number; - height: number; - length?: number; - type: string; - mime: string; - wUnits: "in" | "mm" | "cm" | "pt" | "pc" | "px" | "em" | "ex"; - hUnits: "in" | "mm" | "cm" | "pt" | "pc" | "px" | "em" | "ex"; - url?: string; - }; - - function probeImageSize( - src: string | ReadStream, - options?: ProbeOptions, - ): Promise; - function probeImageSize( - src: string | ReadStream, - callback: (err: Error | null, result?: ProbeResult) => void, - ): void; - function probeImageSize( - src: string | ReadStream, - options: ProbeOptions, - callback: (err: Error | null, result?: ProbeResult) => void, - ): void; - - namespace probeImageSize {} // Hack - - export = probeImageSize; -} diff --git a/packages/backend/src/boot/index.ts b/packages/backend/src/boot/index.ts index 4e1d947656..c78d888383 100644 --- a/packages/backend/src/boot/index.ts +++ b/packages/backend/src/boot/index.ts @@ -9,6 +9,7 @@ import { envOption } from "../env.js"; import "reflect-metadata"; import { masterMain } from "./master.js"; import { workerMain } from "./worker.js"; +import os from "node:os"; const logger = new Logger("core", "cyan"); const clusterLogger = logger.createSubLogger("cluster", "orange", false); @@ -18,7 +19,7 @@ const ev = new Xev(); * Init process */ export default async function () { - process.title = `Calckey (${cluster.isPrimary ? "master" : "worker"})`; + process.title = `Firefish (${cluster.isPrimary ? "master" : "worker"})`; if (cluster.isPrimary || envOption.disableClustering) { await masterMain(); @@ -31,7 +32,17 @@ export default async function () { await workerMain(); } - // For when Calckey is started in a child process during unit testing. + if (cluster.isPrimary) { + // Leave the master process with a marginally lower priority but not too low. + os.setPriority(2); + } + if (cluster.isWorker) { + // Set workers to a much lower priority so that the master process will be + // able to respond to api calls even if the workers gank everything. + os.setPriority(10); + } + + // For when Firefish is started in a child process during unit testing. // Otherwise, process.send cannot be used, so start it. if (process.send) { process.send("ok"); diff --git a/packages/backend/src/boot/master.ts b/packages/backend/src/boot/master.ts index 193f02429d..3f779b3b98 100644 --- a/packages/backend/src/boot/master.ts +++ b/packages/backend/src/boot/master.ts @@ -29,18 +29,19 @@ const themeColor = chalk.hex("#31748f"); function greet() { if (!envOption.quiet) { - //#region Calckey logo + //#region Firefish logo const v = `v${meta.version}`; - console.log(themeColor(" ___ _ _ ")); - console.log(themeColor(" / __\\__ _| | ___| | _____ _ _ ")); - console.log(themeColor(" / / / _` | |/ __| |/ / _ | | |")); - console.log(themeColor("/ /__| (_| | | (__| < __/ |_| |")); - console.log(themeColor("\\____/\\__,_|_|\\___|_|\\_\\___|\\__, |")); - console.log(themeColor(" (___/ ")); + console.log(themeColor(" ▄▄▄▄▄▄▄ ▄▄▄ ▄▄▄▄▄▄ ▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄ ▄▄▄ ▄▄▄▄▄▄▄ ▄▄ ▄▄ ◯ ")); + console.log(themeColor("█ █ █ ▄ █ █ █ █ █ █ █ █ █ ○ ▄ ▄")); + console.log(themeColor("█ ▄▄▄█ █ █ █ █ █ ▄▄▄█ ▄▄▄█ █ ▄▄▄▄▄█ █▄█ █ ⚬ █▄▄ █▄▄ ")); + console.log(themeColor("█ █▄▄▄█ █ █▄▄█▄█ █▄▄▄█ █▄▄▄█ █ █▄▄▄▄▄█ █ ▄▄▄▄▄▄ ▄")); + console.log(themeColor("█ ▄▄▄█ █ ▄▄ █ ▄▄▄█ ▄▄▄█ █▄▄▄▄▄ █ ▄ █ █ █ █▄▄")); + console.log(themeColor("█ █ █ █ █ █ █ █▄▄▄█ █ █ █▄▄▄▄▄█ █ █ █ █ █ ● ● █")); + console.log(themeColor("█▄▄▄█ █▄▄▄█▄▄▄█ █▄█▄▄▄▄▄▄▄█▄▄▄█ █▄▄▄█▄▄▄▄▄▄▄█▄▄█ █▄▄█ ▀▄▄▄▄▄▄▀")); //#endregion console.log( - " Calckey is an open-source decentralized microblogging platform.", + " Firefish is an open-source decentralized microblogging platform.", ); console.log( chalk.rgb( @@ -48,7 +49,7 @@ function greet() { 136, 0, )( - " If you like Calckey, please consider starring or contributing to the repo. https://codeberg.org/calckey/calckey", + " If you like Firefish, please consider starring or contributing to the repo. https://gitlab.prometheus.systems/firefish/firefish", ), ); @@ -58,8 +59,8 @@ function greet() { ); } - bootLogger.info("Welcome to Calckey!"); - bootLogger.info(`Calckey v${meta.version}`, null, true); + bootLogger.info("Welcome to Firefish!"); + bootLogger.info(`Firefish v${meta.version}`, null, true); } /** @@ -81,7 +82,7 @@ export async function masterMain() { process.exit(1); } - bootLogger.succ("Calckey initialized"); + bootLogger.succ("Firefish initialized"); if (!envOption.disableClustering) { await spawnWorkers(config.clusterLimit); @@ -93,7 +94,7 @@ export async function masterMain() { true, ); - if (!envOption.noDaemons) { + if (!envOption.noDaemons && !config.onlyQueueProcessor) { import("../daemons/server-stats.js").then((x) => x.default()); import("../daemons/queue-stats.js").then((x) => x.default()); import("../daemons/janitor.js").then((x) => x.default()); @@ -167,7 +168,7 @@ async function connectDb(): Promise { } } -async function spawnWorkers(limit: number = 1) { +async function spawnWorkers(limit = 1) { const workers = Math.min(limit, os.cpus().length); bootLogger.info(`Starting ${workers} worker${workers === 1 ? "" : "s"}...`); await Promise.all([...Array(workers)].map(spawnWorker)); diff --git a/packages/backend/src/boot/worker.ts b/packages/backend/src/boot/worker.ts index 70442b096e..052c7397f3 100644 --- a/packages/backend/src/boot/worker.ts +++ b/packages/backend/src/boot/worker.ts @@ -1,5 +1,6 @@ import cluster from "node:cluster"; import { initDb } from "../db/postgre.js"; +import config from "@/config/index.js"; /** * Init worker process @@ -7,8 +8,10 @@ import { initDb } from "../db/postgre.js"; export async function workerMain() { await initDb(); - // start server - await import("../server/index.js").then((x) => x.default()); + if (!config.onlyQueueProcessor) { + // start server + await import("../server/index.js").then((x) => x.default()); + } // start job queue import("../queue/index.js").then((x) => x.default()); diff --git a/packages/backend/src/config/load.ts b/packages/backend/src/config/load.ts index 9b8ee5edbb..7a67c4bd32 100644 --- a/packages/backend/src/config/load.ts +++ b/packages/backend/src/config/load.ts @@ -51,10 +51,12 @@ export default function load() { mixin.apiUrl = `${mixin.scheme}://${mixin.host}/api`; mixin.authUrl = `${mixin.scheme}://${mixin.host}/auth`; mixin.driveUrl = `${mixin.scheme}://${mixin.host}/files`; - mixin.userAgent = `Calckey/${meta.version} (${config.url})`; + mixin.userAgent = `Firefish/${meta.version} (${config.url})`; mixin.clientEntry = clientManifest["src/init.ts"]; - if (!config.redis.prefix) config.redis.prefix = mixin.host; + if (!config.redis.prefix) config.redis.prefix = mixin.hostname; + if (config.cacheServer && !config.cacheServer.prefix) + config.cacheServer.prefix = mixin.hostname; return Object.assign(config, mixin); } diff --git a/packages/backend/src/config/types.ts b/packages/backend/src/config/types.ts index e9c42c08f6..7789c26e07 100644 --- a/packages/backend/src/config/types.ts +++ b/packages/backend/src/config/types.ts @@ -20,9 +20,21 @@ export type Source = { host: string; port: number; family?: number; - pass: string; + pass?: string; db?: number; prefix?: string; + user?: string; + tls?: { [y: string]: string }; + }; + cacheServer?: { + host: string; + port: number; + family?: number; + pass?: string; + db?: number; + prefix?: string; + user?: string; + tls?: { [z: string]: string }; }; elasticsearch: { host: string; @@ -32,6 +44,19 @@ export type Source = { pass?: string; index?: string; }; + sonic: { + host: string; + port: number; + auth?: string; + collection?: string; + bucket?: string; + }; + meilisearch: { + host: string; + port: number; + apiKey?: string; + ssl: boolean; + }; proxy?: string; proxySmtp?: string; @@ -45,7 +70,12 @@ export type Source = { clusterLimit?: number; - id: string; + onlyQueueProcessor?: boolean; + + cuid?: { + length?: number; + fingerprint?: string; + }; outgoingAddressFamily?: "ipv4" | "ipv6" | "dual"; @@ -70,15 +100,23 @@ export type Source = { sha256CertFingerprints?: string[]; }; + reservedUsernames?: string[]; + // Managed hosting stuff maxUserSignups?: number; isManagedHosting?: boolean; maxNoteLength?: number; + maxCaptionLength?: number; deepl: { managed?: boolean; authKey?: string; isPro?: boolean; }; + libreTranslate: { + managed?: boolean; + apiUrl?: string; + apiKey?: string; + }; email: { managed?: boolean; address?: string; diff --git a/packages/backend/src/const.ts b/packages/backend/src/const.ts index 82a093100b..2a955ee521 100644 --- a/packages/backend/src/const.ts +++ b/packages/backend/src/const.ts @@ -1,7 +1,17 @@ import config from "@/config/index.js"; +import { + DB_MAX_NOTE_TEXT_LENGTH, + DB_MAX_IMAGE_COMMENT_LENGTH, +} from "@/misc/hard-limits.js"; -export const MAX_NOTE_TEXT_LENGTH = - config.maxNoteLength != null ? config.maxNoteLength : 3000; // <- should we increase this? +export const MAX_NOTE_TEXT_LENGTH = Math.min( + config.maxNoteLength ?? 3000, + DB_MAX_NOTE_TEXT_LENGTH, +); +export const MAX_CAPTION_TEXT_LENGTH = Math.min( + config.maxCaptionLength ?? 1500, + DB_MAX_IMAGE_COMMENT_LENGTH, +); export const SECOND = 1000; export const SEC = 1000; // why do we need this duplicate here? diff --git a/packages/backend/src/daemons/server-stats.ts b/packages/backend/src/daemons/server-stats.ts index b0bf1288fd..7d4fcd25e1 100644 --- a/packages/backend/src/daemons/server-stats.ts +++ b/packages/backend/src/daemons/server-stats.ts @@ -1,6 +1,8 @@ import si from "systeminformation"; import Xev from "xev"; import * as osUtils from "os-utils"; +import { fetchMeta } from "@/misc/fetch-meta.js"; +import meilisearch from "../db/meilisearch.js"; const ev = new Xev(); @@ -19,11 +21,16 @@ export default function () { ev.emit(`serverStatsLog:${x.id}`, log.slice(0, x.length || 50)); }); + fetchMeta().then((meta) => { + if (!meta.enableServerMachineStats) return; + }); + async function tick() { const cpu = await cpuUsage(); const memStats = await mem(); const netStats = await net(); const fsStats = await fs(); + const meilisearchStats = await meilisearchStatus(); const stats = { cpu: roundCpu(cpu), @@ -39,6 +46,7 @@ export default function () { r: round(Math.max(0, fsStats.rIO_sec ?? 0)), w: round(Math.max(0, fsStats.wIO_sec ?? 0)), }, + meilisearch: meilisearchStats, }; ev.emit("serverStats", stats); log.unshift(stats); @@ -77,3 +85,16 @@ async function fs() { const data = await si.disksIO().catch(() => ({ rIO_sec: 0, wIO_sec: 0 })); return data || { rIO_sec: 0, wIO_sec: 0 }; } + +// MEILI STAT +async function meilisearchStatus() { + if (meilisearch) { + return meilisearch.serverStats(); + } else { + return { + health: "unconfigured", + size: 0, + indexed_count: 0, + }; + } +} diff --git a/packages/backend/src/db/meilisearch.ts b/packages/backend/src/db/meilisearch.ts new file mode 100644 index 0000000000..4a8985d080 --- /dev/null +++ b/packages/backend/src/db/meilisearch.ts @@ -0,0 +1,411 @@ +import { Health, Index, MeiliSearch, Stats } from "meilisearch"; +import { dbLogger } from "./logger.js"; + +import config from "@/config/index.js"; +import { Note } from "@/models/entities/note.js"; +import * as url from "url"; +import { ILocalUser } from "@/models/entities/user.js"; +import { Followings, Users } from "@/models/index.js"; + +const logger = dbLogger.createSubLogger("meilisearch", "gray", false); + +let posts: Index; +let client: MeiliSearch; + +const hasConfig = + config.meilisearch && + (config.meilisearch.host || + config.meilisearch.port || + config.meilisearch.apiKey); + +if (hasConfig) { + const host = hasConfig ? config.meilisearch.host ?? "localhost" : ""; + const port = hasConfig ? config.meilisearch.port ?? 7700 : 0; + const auth = hasConfig ? config.meilisearch.apiKey ?? "" : ""; + const ssl = hasConfig ? config.meilisearch.ssl ?? false : false; + + logger.info("Connecting to MeiliSearch"); + + client = new MeiliSearch({ + host: `${ssl ? "https" : "http"}://${host}:${port}`, + apiKey: auth, + }); + + posts = client.index("posts"); + + posts + .updateSearchableAttributes(["text"]) + .catch((e) => + logger.error(`Setting searchable attr failed, searches won't work: ${e}`), + ); + + posts + .updateFilterableAttributes([ + "userName", + "userHost", + "mediaAttachment", + "createdAt", + "userId", + ]) + .catch((e) => + logger.error( + `Setting filterable attr failed, advanced searches won't work: ${e}`, + ), + ); + + posts + .updateSortableAttributes(["createdAt"]) + .catch((e) => + logger.error( + `Setting sortable attr failed, placeholder searches won't sort properly: ${e}`, + ), + ); + + posts + .updateStopWords([ + "the", + "a", + "as", + "be", + "of", + "they", + "these", + "これ", + "それ", + "あれ", + "この", + "その", + "あの", + "ここ", + "そこ", + "あそこ", + "こちら", + "どこ", + "だれ", + "なに", + "なん", + "何", + "私", + "貴方", + "貴方方", + "我々", + "私達", + "あの人", + "あのか", + "彼女", + "彼", + "です", + "ありま", + "おりま", + "います", + "は", + "が", + "の", + "に", + "を", + "で", + "え", + "から", + "まで", + "より", + "も", + "どの", + "と", + "し", + "それで", + "しかし", + ]) + .catch((e) => + logger.error( + `Failed to set Meilisearch stop words, database size will be larger: ${e}`, + ), + ); + + logger.info("Connected to MeiliSearch"); +} + +export type MeilisearchNote = { + id: string; + text: string; + userId: string; + userHost: string; + userName: string; + channelId: string; + mediaAttachment: string; + createdAt: number; +}; + +function timestampToUnix(timestamp: string) { + let unix = 0; + + // Only contains numbers => UNIX timestamp + if (/^\d+$/.test(timestamp)) { + unix = Number.parseInt(timestamp); + } + + if (unix === 0) { + // Try to parse the timestamp as JavaScript Date + const date = Date.parse(timestamp); + if (isNaN(date)) return 0; + unix = date / 1000; + } + + return unix; +} + +export default hasConfig + ? { + search: async ( + query: string, + limit: number, + offset: number, + userCtx: ILocalUser | null, + ) => { + /// Advanced search syntax + /// from:user => filter by user + optional domain + /// has:image/video/audio/text/file => filter by attachment types + /// domain:domain.com => filter by domain + /// before:Date => show posts made before Date + /// after: Date => show posts made after Date + /// "text" => get posts with exact text between quotes + /// filter:following => show results only from users you follow + /// filter:followers => show results only from followers + + const constructedFilters: string[] = []; + + const splitSearch = query.split(" "); + + // Detect search operators and remove them from the actual query + const filteredSearchTerms = ( + await Promise.all( + splitSearch.map(async (term) => { + if (term.startsWith("has:")) { + const fileType = term.slice(4); + constructedFilters.push(`mediaAttachment = "${fileType}"`); + return null; + } else if (term.startsWith("from:")) { + let user = term.slice(5); + + if (user.length === 0) return null; + + // Cut off leading @, those aren't saved in the DB + if (user.charAt(0) === "@") { + user = user.slice(1); + } + + // Determine if we got a webfinger address or a single username + if (user.split("@").length > 1) { + let splitUser = user.split("@"); + + let domain = splitUser.pop(); + user = splitUser.join("@"); + + constructedFilters.push( + `userName = ${user} AND userHost = ${domain}`, + ); + } else { + constructedFilters.push(`userName = ${user}`); + } + + return null; + } else if (term.startsWith("domain:")) { + const domain = term.slice(7); + constructedFilters.push(`userHost = ${domain}`); + return null; + } else if (term.startsWith("after:")) { + const timestamp = term.slice(6); + + let unix = timestampToUnix(timestamp); + + if (unix !== 0) constructedFilters.push(`createdAt > ${unix}`); + + return null; + } else if (term.startsWith("before:")) { + const timestamp = term.slice(7); + + let unix = timestampToUnix(timestamp); + if (unix !== 0) constructedFilters.push(`createdAt < ${unix}`); + + return null; + } else if (term.startsWith("filter:following")) { + // Check if we got a context user + if (userCtx) { + // Fetch user follows from DB + const followedUsers = await Followings.find({ + where: { + followerId: userCtx.id, + }, + select: { + followeeId: true, + }, + }); + const followIDs = followedUsers.map( + (user) => user.followeeId, + ); + + if (followIDs.length === 0) return null; + + constructedFilters.push(`userId IN [${followIDs.join(",")}]`); + } else { + logger.warn( + "search filtered to follows called without user context", + ); + } + + return null; + } else if (term.startsWith("filter:followers")) { + // Check if we got a context user + if (userCtx) { + // Fetch users follows from DB + const followedUsers = await Followings.find({ + where: { + followeeId: userCtx.id, + }, + select: { + followerId: true, + }, + }); + const followIDs = followedUsers.map( + (user) => user.followerId, + ); + + if (followIDs.length === 0) return null; + + constructedFilters.push(`userId IN [${followIDs.join(",")}]`); + } else { + logger.warn( + "search filtered to followers called without user context", + ); + } + + return null; + } + + return term; + }), + ) + ).filter((term) => term !== null); + + const sortRules = []; + + // An empty search term with defined filters means we have a placeholder search => https://www.meilisearch.com/docs/reference/api/search#placeholder-search + // These have to be ordered manually, otherwise the *oldest* posts are returned first, which we don't want + if (filteredSearchTerms.length === 0 && constructedFilters.length > 0) { + sortRules.push("createdAt:desc"); + } + + logger.info(`Searching for ${filteredSearchTerms.join(" ")}`); + logger.info(`Limit: ${limit}`); + logger.info(`Offset: ${offset}`); + logger.info(`Filters: ${constructedFilters}`); + logger.info(`Ordering: ${sortRules}`); + + return posts.search(filteredSearchTerms.join(" "), { + limit: limit, + offset: offset, + filter: constructedFilters, + sort: sortRules, + }); + }, + ingestNote: async (ingestNotes: Note | Note[]) => { + if (ingestNotes instanceof Note) { + ingestNotes = [ingestNotes]; + } + + const indexingBatch: MeilisearchNote[] = []; + + for (const note of ingestNotes) { + if (note.user === undefined) { + note.user = await Users.findOne({ + where: { + id: note.userId, + }, + }); + } + + let attachmentType = ""; + if (note.attachedFileTypes.length > 0) { + attachmentType = note.attachedFileTypes[0].split("/")[0]; + switch (attachmentType) { + case "image": + case "video": + case "audio": + case "text": + break; + default: + attachmentType = "file"; + break; + } + } + + indexingBatch.push({ + id: note.id.toString(), + text: note.text ? note.text : "", + userId: note.userId, + userHost: + note.userHost !== "" + ? note.userHost + : url.parse(config.host).host, + channelId: note.channelId ? note.channelId : "", + mediaAttachment: attachmentType, + userName: note.user?.username ?? "UNKNOWN", + createdAt: note.createdAt.getTime() / 1000, // division by 1000 is necessary because Node returns in ms-accuracy + }); + } + + return posts + .addDocuments(indexingBatch, { + primaryKey: "id", + }) + .then(() => + logger.info(`sent ${indexingBatch.length} posts for indexing`), + ); + }, + serverStats: async () => { + const health: Health = await client.health(); + const stats: Stats = await client.getStats(); + + return { + health: health.status, + size: stats.databaseSize, + indexed_count: stats.indexes["posts"].numberOfDocuments, + }; + }, + deleteNotes: async (note: Note | Note[] | string | string[]) => { + if (note instanceof Note) { + note = [note]; + } + if (typeof note === "string") { + note = [note]; + } + + const deletionBatch = note + .map((n) => { + if (n instanceof Note) { + return n.id; + } + + if (n.length > 0) return n; + + logger.error( + `Failed to delete note from Meilisearch, invalid post ID: ${JSON.stringify( + n, + )}`, + ); + + throw new Error( + `Invalid note ID passed to meilisearch deleteNote: ${JSON.stringify( + n, + )}`, + ); + }) + .filter((el) => el !== null); + + await posts.deleteDocuments(deletionBatch as string[]).then(() => { + logger.info( + `submitted ${deletionBatch.length} large batch for deletion`, + ); + }); + }, + } + : null; diff --git a/packages/backend/src/db/postgre.ts b/packages/backend/src/db/postgre.ts index f95bd2594d..10ea5b15f6 100644 --- a/packages/backend/src/db/postgre.ts +++ b/packages/backend/src/db/postgre.ts @@ -23,6 +23,7 @@ import { Meta } from "@/models/entities/meta.js"; import { Following } from "@/models/entities/following.js"; import { Instance } from "@/models/entities/instance.js"; import { Muting } from "@/models/entities/muting.js"; +import { RenoteMuting } from "@/models/entities/renote-muting.js"; import { SwSubscription } from "@/models/entities/sw-subscription.js"; import { Blocking } from "@/models/entities/blocking.js"; import { UserList } from "@/models/entities/user-list.js"; @@ -57,7 +58,6 @@ import { AnnouncementRead } from "@/models/entities/announcement-read.js"; import { Clip } from "@/models/entities/clip.js"; import { ClipNote } from "@/models/entities/clip-note.js"; import { Antenna } from "@/models/entities/antenna.js"; -import { AntennaNote } from "@/models/entities/antenna-note.js"; import { PromoNote } from "@/models/entities/promo-note.js"; import { PromoRead } from "@/models/entities/promo-read.js"; import { Relay } from "@/models/entities/relay.js"; @@ -71,11 +71,13 @@ import { PasswordResetRequest } from "@/models/entities/password-reset-request.j import { UserPending } from "@/models/entities/user-pending.js"; import { Webhook } from "@/models/entities/webhook.js"; import { UserIp } from "@/models/entities/user-ip.js"; +import { NoteEdit } from "@/models/entities/note-edit.js"; import { entities as charts } from "@/services/chart/entities.js"; import { envOption } from "../env.js"; import { dbLogger } from "./logger.js"; import { redisClient } from "./redis.js"; +import { nativeInitDatabase } from "native-utils/built/index.js"; const sqlLogger = dbLogger.createSubLogger("sql", "gray", false); @@ -136,8 +138,10 @@ export const entities = [ Following, FollowRequest, Muting, + RenoteMuting, Blocking, Note, + NoteEdit, NoteFavorite, NoteReaction, NoteWatching, @@ -163,7 +167,6 @@ export const entities = [ Clip, ClipNote, Antenna, - AntennaNote, PromoNote, PromoRead, Relay, @@ -202,9 +205,11 @@ export const db = new DataSource({ host: config.redis.host, port: config.redis.port, family: config.redis.family == null ? 0 : config.redis.family, + username: config.redis.user ?? "default", password: config.redis.pass, keyPrefix: `${config.redis.prefix}:query:`, db: config.redis.db || 0, + tls: config.redis.tls, }, } : false, @@ -216,6 +221,11 @@ export const db = new DataSource({ }); export async function initDb(force = false) { + await nativeInitDatabase( + `postgres://${config.db.user}:${encodeURIComponent(config.db.pass)}@${ + config.db.host + }:${config.db.port}/${config.db.db}`, + ); if (force) { if (db.isInitialized) { await db.destroy(); diff --git a/packages/backend/src/db/redis.ts b/packages/backend/src/db/redis.ts index 6ad3de386f..215effd8ea 100644 --- a/packages/backend/src/db/redis.ts +++ b/packages/backend/src/db/redis.ts @@ -2,13 +2,19 @@ import Redis from "ioredis"; import config from "@/config/index.js"; export function createConnection() { + let source = config.redis; + if (config.cacheServer) { + source = config.cacheServer; + } return new Redis({ - port: config.redis.port, - host: config.redis.host, - family: config.redis.family == null ? 0 : config.redis.family, - password: config.redis.pass, - keyPrefix: `${config.redis.prefix}:`, - db: config.redis.db || 0, + port: source.port, + host: source.host, + family: source.family ?? 0, + password: source.pass, + username: source.user ?? "default", + keyPrefix: `${source.prefix}:`, + db: source.db || 0, + tls: source.tls, }); } diff --git a/packages/backend/src/db/sonic.ts b/packages/backend/src/db/sonic.ts new file mode 100644 index 0000000000..032982f083 --- /dev/null +++ b/packages/backend/src/db/sonic.ts @@ -0,0 +1,51 @@ +import * as SonicChannel from "sonic-channel"; +import { dbLogger } from "./logger.js"; + +import config from "@/config/index.js"; + +const logger = dbLogger.createSubLogger("sonic", "gray", false); + +const handlers = (type: string): SonicChannel.Handlers => ({ + connected: () => { + logger.succ(`Connected to Sonic ${type}`); + }, + disconnected: (error) => { + logger.warn(`Disconnected from Sonic ${type}, error: ${error}`); + }, + error: (error) => { + logger.warn(`Sonic ${type} error: ${error}`); + }, + retrying: () => { + logger.info(`Sonic ${type} retrying`); + }, + timeout: () => { + logger.warn(`Sonic ${type} timeout`); + }, +}); + +const hasConfig = + config.sonic && (config.sonic.host || config.sonic.port || config.sonic.auth); + +if (hasConfig) { + logger.info("Connecting to Sonic"); +} + +const host = hasConfig ? config.sonic.host ?? "localhost" : ""; +const port = hasConfig ? config.sonic.port ?? 1491 : 0; +const auth = hasConfig ? config.sonic.auth ?? "SecretPassword" : ""; +const collection = hasConfig ? config.sonic.collection ?? "main" : ""; +const bucket = hasConfig ? config.sonic.bucket ?? "default" : ""; + +export default hasConfig + ? { + search: new SonicChannel.Search({ host, port, auth }).connect( + handlers("search"), + ), + ingest: new SonicChannel.Ingest({ host, port, auth }).connect( + handlers("ingest"), + ), + + collection, + bucket, + } + : null; diff --git a/packages/backend/src/misc/acct.ts b/packages/backend/src/misc/acct.ts index 5b7767a106..cb6808b4b4 100644 --- a/packages/backend/src/misc/acct.ts +++ b/packages/backend/src/misc/acct.ts @@ -4,7 +4,7 @@ export type Acct = { }; export function parse(acct: string): Acct { - if (acct.startsWith("@")) acct = acct.substr(1); + if (acct.startsWith("@")) acct = acct.slice(1); const split = acct.split("@", 2); return { username: split[0], host: split[1] || null }; } diff --git a/packages/backend/src/misc/cache.ts b/packages/backend/src/misc/cache.ts index 9abebc91cb..913258f05a 100644 --- a/packages/backend/src/misc/cache.ts +++ b/packages/backend/src/misc/cache.ts @@ -1,43 +1,85 @@ +import { redisClient } from "@/db/redis.js"; +import { encode, decode } from "msgpackr"; +import { ChainableCommander } from "ioredis"; + export class Cache { - public cache: Map; - private lifetime: number; + private ttl: number; + private prefix: string; - constructor(lifetime: Cache["lifetime"]) { - this.cache = new Map(); - this.lifetime = lifetime; + constructor(name: string, ttlSeconds: number) { + this.ttl = ttlSeconds; + this.prefix = `cache:${name}`; } - public set(key: string | null, value: T): void { - this.cache.set(key, { - date: Date.now(), - value, - }); + private prefixedKey(key: string | null): string { + return key ? `${this.prefix}:${key}` : this.prefix; } - public get(key: string | null): T | undefined { - const cached = this.cache.get(key); - if (cached == null) return undefined; - if (Date.now() - cached.date > this.lifetime) { - this.cache.delete(key); - return undefined; + public async set( + key: string | null, + value: T, + transaction?: ChainableCommander, + ): Promise { + const _key = this.prefixedKey(key); + const _value = Buffer.from(encode(value)); + const commander = transaction ?? redisClient; + await commander.set(_key, _value, "EX", this.ttl); + } + + public async get(key: string | null, renew = false): Promise { + const _key = this.prefixedKey(key); + const cached = await redisClient.getBuffer(_key); + if (cached === null) return undefined; + + if (renew) await redisClient.expire(_key, this.ttl); + + return decode(cached) as T; + } + + public async getAll(renew = false): Promise> { + const keys = await redisClient.keys(`${this.prefix}*`); + const map = new Map(); + if (keys.length === 0) { + return map; } - return cached.value; + const values = await redisClient.mgetBuffer(keys); + + for (const [i, key] of keys.entries()) { + const val = values[i]; + if (val !== null) { + map.set(key, decode(val) as T); + } + } + + if (renew) { + const trans = redisClient.multi(); + for (const key of map.keys()) { + trans.expire(key, this.ttl); + } + await trans.exec(); + } + + return map; } - public delete(key: string | null) { - this.cache.delete(key); + public async delete(...keys: (string | null)[]): Promise { + if (keys.length > 0) { + const _keys = keys.map(this.prefixedKey); + await redisClient.del(_keys); + } } /** - * キャッシュがあればそれを返し、無ければfetcherを呼び出して結果をキャッシュ&返します - * optional: キャッシュが存在してもvalidatorでfalseを返すとキャッシュ無効扱いにします + * Returns if cached value exists. Otherwise, calls fetcher and caches. + * Overwrites cached value if invalidated by the optional validator. */ public async fetch( key: string | null, fetcher: () => Promise, + renew = false, validator?: (cachedValue: T) => boolean, ): Promise { - const cachedValue = this.get(key); + const cachedValue = await this.get(key, renew); if (cachedValue !== undefined) { if (validator) { if (validator(cachedValue)) { @@ -52,20 +94,21 @@ export class Cache { // Cache MISS const value = await fetcher(); - this.set(key, value); + await this.set(key, value); return value; } /** - * キャッシュがあればそれを返し、無ければfetcherを呼び出して結果をキャッシュ&返します - * optional: キャッシュが存在してもvalidatorでfalseを返すとキャッシュ無効扱いにします + * Returns if cached value exists. Otherwise, calls fetcher and caches if the fetcher returns a value. + * Overwrites cached value if invalidated by the optional validator. */ public async fetchMaybe( key: string | null, fetcher: () => Promise, + renew = false, validator?: (cachedValue: T) => boolean, ): Promise { - const cachedValue = this.get(key); + const cachedValue = await this.get(key, renew); if (cachedValue !== undefined) { if (validator) { if (validator(cachedValue)) { @@ -81,7 +124,7 @@ export class Cache { // Cache MISS const value = await fetcher(); if (value !== undefined) { - this.set(key, value); + await this.set(key, value); } return value; } diff --git a/packages/backend/src/misc/check-hit-antenna.ts b/packages/backend/src/misc/check-hit-antenna.ts index aa38d9e275..1ff09d6299 100644 --- a/packages/backend/src/misc/check-hit-antenna.ts +++ b/packages/backend/src/misc/check-hit-antenna.ts @@ -11,7 +11,7 @@ import * as Acct from "@/misc/acct.js"; import type { Packed } from "./schema.js"; import { Cache } from "./cache.js"; -const blockingCache = new Cache(1000 * 60 * 5); +const blockingCache = new Cache("blocking", 60 * 5); // NOTE: フォローしているユーザーのノート、リストのユーザーのノート、グループのユーザーのノート指定はパフォーマンス上の理由で無効になっている @@ -26,6 +26,7 @@ export async function checkHitAntenna( antennaUserFollowing?: User["id"][], ): Promise { if (note.visibility === "specified") return false; + if (note.visibility === "home") return false; // アンテナ作成者がノート作成者にブロックされていたらスキップ const blockings = await blockingCache.fetch(noteUser.id, () => @@ -80,6 +81,13 @@ export async function checkHitAntenna( ) ) return false; + } else if (antenna.src === "instances") { + const instances = antenna.instances + .filter((x) => x !== "") + .map((host) => { + return host.toLowerCase(); + }); + if (!instances.includes(noteUser.host?.toLowerCase() ?? "")) return false; } const keywords = antenna.keywords diff --git a/packages/backend/src/misc/check-word-mute.ts b/packages/backend/src/misc/check-word-mute.ts index ffdf3caf84..8c7d950861 100644 --- a/packages/backend/src/misc/check-word-mute.ts +++ b/packages/backend/src/misc/check-word-mute.ts @@ -5,45 +5,74 @@ import type { User } from "@/models/entities/user.js"; type NoteLike = { userId: Note["userId"]; text: Note["text"]; + files?: Note["files"]; + cw?: Note["cw"]; }; type UserLike = { id: User["id"]; }; -export async function checkWordMute( +function checkWordMute( + note: NoteLike, + mutedWords: Array, +): boolean { + if (note == null) return false; + + let text = `${note.cw ?? ""} ${note.text ?? ""}`; + if (note.files != null) + text += ` ${note.files.map((f) => f.comment ?? "").join(" ")}`; + text = text.trim(); + + if (text === "") return false; + + for (const mutePattern of mutedWords) { + if (Array.isArray(mutePattern)) { + // Clean up + const keywords = mutePattern.filter((keyword) => keyword !== ""); + + if ( + keywords.length > 0 && + keywords.every((keyword) => text.includes(keyword)) + ) + return true; + } else { + // represents RegExp + const regexp = mutePattern.match(/^\/(.+)\/(.*)$/); + + // This should never happen due to input sanitisation. + if (!regexp) { + console.warn(`Found invalid regex in word mutes: ${mutePattern}`); + continue; + } + + try { + if (new RE2(regexp[1], regexp[2]).test(text)) return true; + } catch (err) { + // This should never happen due to input sanitisation. + } + } + } + + return false; +} + +export async function getWordHardMute( note: NoteLike, me: UserLike | null | undefined, mutedWords: Array, ): Promise { // 自分自身 - if (me && note.userId === me.id) return false; + if (me && note.userId === me.id) { + return false; + } if (mutedWords.length > 0) { - const text = ((note.cw ?? "") + "\n" + (note.text ?? "")).trim(); - - if (text === "") return false; - - const matched = mutedWords.some((filter) => { - if (Array.isArray(filter)) { - return filter.every((keyword) => text.includes(keyword)); - } else { - // represents RegExp - const regexp = filter.match(/^\/(.+)\/(.*)$/); - - // This should never happen due to input sanitisation. - if (!regexp) return false; - - try { - return new RE2(regexp[1], regexp[2]).test(text); - } catch (err) { - // This should never happen due to input sanitisation. - return false; - } - } - }); - - if (matched) return true; + return ( + checkWordMute(note, mutedWords) || + checkWordMute(note.reply, mutedWords) || + checkWordMute(note.renote, mutedWords) + ); } return false; diff --git a/packages/backend/src/misc/convert-milliseconds.ts b/packages/backend/src/misc/convert-milliseconds.ts new file mode 100644 index 0000000000..d8c163ffda --- /dev/null +++ b/packages/backend/src/misc/convert-milliseconds.ts @@ -0,0 +1,17 @@ +export function convertMilliseconds(ms: number) { + let seconds = Math.round(ms / 1000); + let minutes = Math.round(seconds / 60); + let hours = Math.round(minutes / 60); + const days = Math.round(hours / 24); + seconds %= 60; + minutes %= 60; + hours %= 24; + + const result = []; + if (days > 0) result.push(`${days} day(s)`); + if (hours > 0) result.push(`${hours} hour(s)`); + if (minutes > 0) result.push(`${minutes} minute(s)`); + if (seconds > 0) result.push(`${seconds} second(s)`); + + return result.join(", "); +} diff --git a/packages/backend/src/misc/download-url.ts b/packages/backend/src/misc/download-url.ts index 7fafb635ba..e9975f3486 100644 --- a/packages/backend/src/misc/download-url.ts +++ b/packages/backend/src/misc/download-url.ts @@ -24,6 +24,7 @@ export async function downloadUrl(url: string, path: string): Promise { .stream(url, { headers: { "User-Agent": config.userAgent, + Host: new URL(url).hostname, }, timeout: { lookup: timeout, diff --git a/packages/backend/src/misc/emoji-meta.ts b/packages/backend/src/misc/emoji-meta.ts new file mode 100644 index 0000000000..2b9365b826 --- /dev/null +++ b/packages/backend/src/misc/emoji-meta.ts @@ -0,0 +1,58 @@ +import probeImageSize from "probe-image-size"; +import { Mutex } from "redis-semaphore"; + +import { FILE_TYPE_BROWSERSAFE } from "@/const.js"; +import Logger from "@/services/logger.js"; +import { Cache } from "./cache.js"; +import { redisClient } from "@/db/redis.js"; + +export type Size = { + width: number; + height: number; +}; + +const cache = new Cache("emojiMeta", 60 * 10); // once every 10 minutes for the same url +const logger = new Logger("emoji"); + +export async function getEmojiSize(url: string): Promise { + let attempted = true; + + const lock = new Mutex(redisClient, "getEmojiSize"); + await lock.acquire(); + + try { + attempted = (await cache.get(url)) === true; + if (!attempted) { + await cache.set(url, true); + } + } finally { + await lock.release(); + } + + if (attempted) { + logger.warn(`Attempt limit exceeded: ${url}`); + throw new Error("Too many attempts"); + } + + try { + logger.debug(`Retrieving emoji size from ${url}`); + const { width, height, mime } = await probeImageSize(url, { + timeout: 5000, + }); + if (!(mime.startsWith("image/") && FILE_TYPE_BROWSERSAFE.includes(mime))) { + throw new Error("Unsupported image type"); + } + return { width, height }; + } catch (e) { + throw new Error(`Unable to retrieve metadata: ${e}`); + } +} + +export function getNormalSize( + { width, height }: Size, + orientation?: number, +): Size { + return (orientation || 0) >= 5 + ? { width: height, height: width } + : { width, height }; +} diff --git a/packages/backend/src/misc/fetch-meta.ts b/packages/backend/src/misc/fetch-meta.ts index 32c45813ca..b3a5e30ae4 100644 --- a/packages/backend/src/misc/fetch-meta.ts +++ b/packages/backend/src/misc/fetch-meta.ts @@ -3,6 +3,32 @@ import { Meta } from "@/models/entities/meta.js"; let cache: Meta; +export function metaToPugArgs(meta: Meta): object { + let motd = ["Loading..."]; + if (meta.customMOTD.length > 0) { + motd = meta.customMOTD; + } + let splashIconUrl = meta.iconUrl; + if (meta.customSplashIcons.length > 0) { + splashIconUrl = + meta.customSplashIcons[ + Math.floor(Math.random() * meta.customSplashIcons.length) + ]; + } + + return { + img: meta.bannerUrl, + title: meta.name || "Firefish", + instanceName: meta.name || "Firefish", + desc: meta.description, + icon: meta.iconUrl, + splashIcon: splashIconUrl, + themeColor: meta.themeColor, + randomMOTD: motd[Math.floor(Math.random() * motd.length)], + privateMode: meta.privateMode, + }; +} + export async function fetchMeta(noCache = false): Promise { if (!noCache && cache) return cache; diff --git a/packages/backend/src/misc/gen-id.ts b/packages/backend/src/misc/gen-id.ts index b7cc0965a1..580c39c3c1 100644 --- a/packages/backend/src/misc/gen-id.ts +++ b/packages/backend/src/misc/gen-id.ts @@ -1,27 +1,21 @@ -import { ulid } from "ulid"; -import { genAid } from "./id/aid.js"; -import { genMeid } from "./id/meid.js"; -import { genMeidg } from "./id/meidg.js"; -import { genObjectId } from "./id/object-id.js"; import config from "@/config/index.js"; +import { + nativeCreateId, + nativeInitIdGenerator, +} from "native-utils/built/index.js"; -const metohd = config.id.toLowerCase(); +const length = Math.min(Math.max(config.cuid?.length ?? 16, 16), 24); +const fingerprint = config.cuid?.fingerprint ?? ""; +nativeInitIdGenerator(length, fingerprint); +/** + * The generated ID results in the form of `[8 chars timestamp] + [cuid2]`. + * The minimum and maximum lengths are 16 and 24, respectively. + * With the length of 16, namely 8 for cuid2, roughly 1427399 IDs are needed + * in the same millisecond to reach 50% chance of collision. + * + * Ref: https://github.com/paralleldrive/cuid2#parameterized-length + */ export function genId(date?: Date): string { - if (!date || date > new Date()) date = new Date(); - - switch (metohd) { - case "aid": - return genAid(date); - case "meid": - return genMeid(date); - case "meidg": - return genMeidg(date); - case "ulid": - return ulid(date.getTime()); - case "objectid": - return genObjectId(date); - default: - throw new Error("unrecognized id generation method"); - } + return nativeCreateId((date ?? new Date()).getTime()); } diff --git a/packages/backend/src/misc/gen-identicon.ts b/packages/backend/src/misc/gen-identicon.ts index 79297f8f2c..1e51dfe2ac 100644 --- a/packages/backend/src/misc/gen-identicon.ts +++ b/packages/backend/src/misc/gen-identicon.ts @@ -11,26 +11,41 @@ const size = 128; // px const n = 5; // resolution const margin = size / 4; const colors = [ - ["#FF512F", "#DD2476"], - ["#FF61D2", "#FE9090"], - ["#72FFB6", "#10D164"], - ["#FD8451", "#FFBD6F"], - ["#305170", "#6DFC6B"], - ["#00C0FF", "#4218B8"], - ["#009245", "#FCEE21"], - ["#0100EC", "#FB36F4"], - ["#FDABDD", "#374A5A"], - ["#38A2D7", "#561139"], - ["#121C84", "#8278DA"], - ["#5761B2", "#1FC5A8"], - ["#FFDB01", "#0E197D"], - ["#FF3E9D", "#0E1F40"], - ["#766eff", "#00d4ff"], - ["#9bff6e", "#00d4ff"], - ["#ff6e94", "#00d4ff"], - ["#ffa96e", "#00d4ff"], - ["#ffa96e", "#ff009d"], - ["#ffdd6e", "#ff009d"], + ["#eb6f92", "#b4637a"], + ["#f6c177", "#ea9d34"], + ["#ebbcba", "#d7827e"], + ["#9ccfd8", "#56949f"], + ["#c4a7e7", "#907aa9"], + ["#eb6f92", "#f6c177"], + ["#eb6f92", "#ebbcba"], + ["#eb6f92", "#31748f"], + ["#eb6f92", "#9ccfd8"], + ["#eb6f92", "#c4a7e7"], + ["#f6c177", "#eb6f92"], + ["#f6c177", "#ebbcba"], + ["#f6c177", "#31748f"], + ["#f6c177", "#9ccfd8"], + ["#f6c177", "#c4a7e7"], + ["#ebbcba", "#eb6f92"], + ["#ebbcba", "#f6c177"], + ["#ebbcba", "#31748f"], + ["#ebbcba", "#9ccfd8"], + ["#ebbcba", "#c4a7e7"], + ["#31748f", "#eb6f92"], + ["#31748f", "#f6c177"], + ["#31748f", "#ebbcba"], + ["#31748f", "#9ccfd8"], + ["#31748f", "#c4a7e7"], + ["#9ccfd8", "#eb6f92"], + ["#9ccfd8", "#f6c177"], + ["#9ccfd8", "#ebbcba"], + ["#9ccfd8", "#31748f"], + ["#9ccfd8", "#c4a7e7"], + ["#c4a7e7", "#eb6f92"], + ["#c4a7e7", "#f6c177"], + ["#c4a7e7", "#ebbcba"], + ["#c4a7e7", "#31748f"], + ["#c4a7e7", "#9ccfd8"], ]; const actualSize = size - margin * 2; diff --git a/packages/backend/src/misc/get-file-info.ts b/packages/backend/src/misc/get-file-info.ts index a63de286ea..76964890e7 100644 --- a/packages/backend/src/misc/get-file-info.ts +++ b/packages/backend/src/misc/get-file-info.ts @@ -5,9 +5,9 @@ import * as stream from "node:stream"; import * as util from "node:util"; import { FSWatcher } from "chokidar"; import { fileTypeFromFile } from "file-type"; +import probeImageSize from "probe-image-size"; import FFmpeg from "fluent-ffmpeg"; import isSvg from "is-svg"; -import probeImageSize from "probe-image-size"; import { type predictionType } from "nsfwjs"; import sharp from "sharp"; import { encode } from "blurhash"; diff --git a/packages/backend/src/misc/get-note-summary.ts b/packages/backend/src/misc/get-note-summary.ts index 446e3fc140..0a662e434e 100644 --- a/packages/backend/src/misc/get-note-summary.ts +++ b/packages/backend/src/misc/get-note-summary.ts @@ -20,12 +20,13 @@ export const getNoteSummary = (note: Packed<"Note">): string => { // ファイルが添付されているとき if ((note.files || []).length !== 0) { - summary += ` (📎${note.files!.length})`; + const len = note.files?.length; + summary += ` 📎${len !== 1 ? ` (${len})` : ""}`; } // 投票が添付されているとき if (note.poll) { - summary += " (📊)"; + summary += " 📊"; } /* diff --git a/packages/backend/src/misc/hard-limits.ts b/packages/backend/src/misc/hard-limits.ts index 4ba90293cf..5ce3e0ac9a 100644 --- a/packages/backend/src/misc/hard-limits.ts +++ b/packages/backend/src/misc/hard-limits.ts @@ -3,11 +3,16 @@ /** * Maximum note text length that can be stored in DB. * Surrogate pairs count as one + * + * NOTE: this can hypothetically be pushed further + * (up to 250000000), but will likely cause truncations + * and incompatibilities with other servers, + * as well as potential performance issues. */ -export const DB_MAX_NOTE_TEXT_LENGTH = 8192; +export const DB_MAX_NOTE_TEXT_LENGTH = 100000; /** * Maximum image description length that can be stored in DB. * Surrogate pairs count as one */ -export const DB_MAX_IMAGE_COMMENT_LENGTH = 512; +export const DB_MAX_IMAGE_COMMENT_LENGTH = 8192; diff --git a/packages/backend/src/misc/is-duplicate-key-value-error.ts b/packages/backend/src/misc/is-duplicate-key-value-error.ts index 18d22bb77c..670277fe1a 100644 --- a/packages/backend/src/misc/is-duplicate-key-value-error.ts +++ b/packages/backend/src/misc/is-duplicate-key-value-error.ts @@ -1,3 +1,4 @@ export function isDuplicateKeyValueError(e: unknown | Error): boolean { - return (e as Error).message?.startsWith("duplicate key value"); + const nodeError = e as NodeJS.ErrnoException; + return nodeError.code === "23505"; } diff --git a/packages/backend/src/misc/keypair-store.ts b/packages/backend/src/misc/keypair-store.ts index 4551bfd988..6255773599 100644 --- a/packages/backend/src/misc/keypair-store.ts +++ b/packages/backend/src/misc/keypair-store.ts @@ -3,10 +3,12 @@ import type { User } from "@/models/entities/user.js"; import type { UserKeypair } from "@/models/entities/user-keypair.js"; import { Cache } from "./cache.js"; -const cache = new Cache(Infinity); +const cache = new Cache("keypairStore", 60 * 30); export async function getUserKeypair(userId: User["id"]): Promise { - return await cache.fetch(userId, () => - UserKeypairs.findOneByOrFail({ userId: userId }), + return await cache.fetch( + userId, + () => UserKeypairs.findOneByOrFail({ userId: userId }), + true, ); } diff --git a/packages/backend/src/misc/nyaize.ts b/packages/backend/src/misc/nyaize.ts index b85f1d918e..13a112ce57 100644 --- a/packages/backend/src/misc/nyaize.ts +++ b/packages/backend/src/misc/nyaize.ts @@ -9,6 +9,9 @@ export function nyaize(text: string): string { .replace(/(?<=n)a/gi, (x) => (x === "A" ? "YA" : "ya")) .replace(/(?<=morn)ing/gi, (x) => (x === "ING" ? "YAN" : "yan")) .replace(/(?<=every)one/gi, (x) => (x === "ONE" ? "NYAN" : "nyan")) + .replace(/non(?=[bcdfghjklmnpqrstvwxyz])/gi, (x) => + x === "NON" ? "NYAN" : "nyan", + ) // ko-KR .replace(/[나-낳]/g, (match) => String.fromCharCode( @@ -17,5 +20,9 @@ export function nyaize(text: string): string { ) .replace(/(다$)|(다(?=\.))|(다(?= ))|(다(?=!))|(다(?=\?))/gm, "다냥") .replace(/(야(?=\?))|(야$)|(야(?= ))/gm, "냥") + // el-GR + .replaceAll("να", "νια") + .replaceAll("ΝΑ", "ΝΙΑ") + .replaceAll("Να", "Νια") ); } diff --git a/packages/backend/src/misc/password.ts b/packages/backend/src/misc/password.ts new file mode 100644 index 0000000000..c63f89f5c9 --- /dev/null +++ b/packages/backend/src/misc/password.ts @@ -0,0 +1,20 @@ +import bcrypt from "bcryptjs"; +import * as argon2 from "argon2"; + +export async function hashPassword(password: string): Promise { + return argon2.hash(password); +} + +export async function comparePassword( + password: string, + hash: string, +): Promise { + if (isOldAlgorithm(hash)) return bcrypt.compare(password, hash); + + return argon2.verify(hash, password); +} + +export function isOldAlgorithm(hash: string): boolean { + // bcrypt hashes start with $2[ab]$ + return hash.startsWith("$2"); +} diff --git a/packages/backend/src/misc/populate-emojis.ts b/packages/backend/src/misc/populate-emojis.ts index 3f20f9f10d..795a267f91 100644 --- a/packages/backend/src/misc/populate-emojis.ts +++ b/packages/backend/src/misc/populate-emojis.ts @@ -7,8 +7,9 @@ import { isSelfHost, toPunyNullable } from "./convert-host.js"; import { decodeReaction } from "./reaction-lib.js"; import config from "@/config/index.js"; import { query } from "@/prelude/url.js"; +import { redisClient } from "@/db/redis.js"; -const cache = new Cache(1000 * 60 * 60 * 12); +const cache = new Cache("populateEmojis", 60 * 60 * 12); /** * 添付用絵文字情報 @@ -16,6 +17,8 @@ const cache = new Cache(1000 * 60 * 60 * 12); type PopulatedEmoji = { name: string; url: string; + width: number | null; + height: number | null; }; function normalizeHost( @@ -68,7 +71,13 @@ export async function populateEmoji( host: host ?? IsNull(), })) || null; - const emoji = await cache.fetch(`${name} ${host}`, queryOrNull); + const cacheKey = `${name} ${host}`; + let emoji = await cache.fetch(cacheKey, queryOrNull); + + if (emoji && !(emoji.width && emoji.height)) { + emoji = await queryOrNull(); + await cache.set(cacheKey, emoji); + } if (emoji == null) return null; @@ -83,6 +92,8 @@ export async function populateEmoji( return { name: emojiName, url, + width: emoji.width, + height: emoji.height, }; } @@ -140,7 +151,7 @@ export async function prefetchEmojis( emojis: { name: string; host: string | null }[], ): Promise { const notCachedEmojis = emojis.filter( - (emoji) => cache.get(`${emoji.name} ${emoji.host}`) == null, + async (emoji) => !(await cache.get(`${emoji.name} ${emoji.host}`)), ); const emojisQuery: any[] = []; const hosts = new Set(notCachedEmojis.map((e) => e.host)); @@ -159,7 +170,9 @@ export async function prefetchEmojis( select: ["name", "host", "originalUrl", "publicUrl"], }) : []; + const trans = redisClient.multi(); for (const emoji of _emojis) { - cache.set(`${emoji.name} ${emoji.host}`, emoji); + cache.set(`${emoji.name} ${emoji.host}`, emoji, trans); } + await trans.exec(); } diff --git a/packages/backend/src/misc/post.ts b/packages/backend/src/misc/post.ts new file mode 100644 index 0000000000..90f4f75283 --- /dev/null +++ b/packages/backend/src/misc/post.ts @@ -0,0 +1,19 @@ +export type Post = { + text: string | null; + cw: string | null; + localOnly: boolean; + createdAt: Date; +}; + +export function parse(acct: any): Post { + return { + text: acct.text, + cw: acct.cw, + localOnly: acct.localOnly, + createdAt: new Date(acct.createdAt), + }; +} + +export function toJson(acct: Post): string { + return { text: acct.text, cw: acct.cw, localOnly: acct.localOnly }.toString(); +} diff --git a/packages/backend/src/misc/process-masto-notes.ts b/packages/backend/src/misc/process-masto-notes.ts new file mode 100644 index 0000000000..1327b4cff0 --- /dev/null +++ b/packages/backend/src/misc/process-masto-notes.ts @@ -0,0 +1,143 @@ +import * as fs from "node:fs"; +import Logger from "@/services/logger.js"; +import { createTemp, createTempDir } from "./create-temp.js"; +import { downloadUrl } from "./download-url.js"; +import { addFile } from "@/services/drive/add-file.js"; +import { Users } from "@/models/index.js"; +import * as tar from "tar-stream"; +import gunzip from "gunzip-maybe"; +import decompress from "decompress"; +import * as Path from "node:path"; + +const logger = new Logger("process-masto-notes"); + +export async function processMastoNotes( + fn: string, + url: string, + uid: string, +): Promise { + // Create temp file + const [path, cleanup] = await createTemp(); + + const [unzipPath, unzipCleanup] = await createTempDir(); + + logger.info(`Temp file is ${path}`); + + try { + // write content at URL to temp file + await downloadUrl(url, path); + return await processMastoFile(fn, path, unzipPath, uid); + } finally { + cleanup(); + //unzipCleanup(); + } +} + +function processMastoFile(fn: string, path: string, dir: string, uid: string) { + return new Promise(async (resolve, reject) => { + const user = await Users.findOneBy({ id: uid }); + try { + logger.info(`Start unzip ${path}`); + fn.endsWith("tar.gz") + ? await unzipTarGz(path, dir) + : await unzipZip(path, dir); + logger.info(`Unzip to ${dir}`); + const outbox = JSON.parse(fs.readFileSync(`${dir}/outbox.json`)); + for (const note of outbox.orderedItems) { + // Skip if attachment is undefined or not iterable + if ( + note.object.attachment == null || + !note.object.attachment[Symbol.iterator] + ) { + continue; + } + for (const attachment of note.object.attachment) { + const url = attachment.url.replaceAll("..", ""); + if (url.indexOf("\0") !== -1) { + logger.error(`Found Poison Null Bytes Attack: ${url}`); + reject(); + return; + } + try { + const fpath = Path.resolve(`${dir}${url}`); + if (!fpath.startsWith(dir)) { + logger.error(`Found Path Attack: ${url}`); + reject(); + return; + } + logger.info(fpath); + const driveFile = await addFile({ user: user, path: fpath }); + attachment.driveFile = driveFile; + } catch (e) { + logger.error(`Skipped adding file to drive: ${url}`); + } + } + } + resolve(outbox); + } catch (e) { + logger.error(`Error on extract masto note package: ${fn}`); + reject(e); + } + }); +} + +function createFileDir(fn: string) { + if (!fs.existsSync(fn)) { + fs.mkdirSync(fn, { recursive: true }); + fs.rmdirSync(fn); + } +} + +function unzipZip(fn: string, dir: string) { + return new Promise(async (resolve, reject) => { + try { + decompress(fn, dir).then((files: any) => { + resolve(files); + }); + } catch (e) { + reject(); + } + }); +} + +function unzipTarGz(fn: string, dir: string) { + return new Promise(async (resolve, reject) => { + const onErr = (err: any) => { + logger.error(`pipe broken: ${err}`); + reject(); + }; + try { + const extract = tar.extract().on("error", onErr); + dir = dir.endsWith("/") ? dir : dir + "/"; + const ls: string[] = []; + extract.on("entry", function (header: any, stream: any, next: any) { + try { + ls.push(dir + header.name); + createFileDir(dir + header.name); + stream + .on("error", onErr) + .pipe(fs.createWriteStream(dir + header.name)) + .on("error", onErr); + next(); + } catch (e) { + logger.error(`create dir error:${e}`); + reject(); + } + }); + + extract.on("finish", function () { + resolve(ls); + }); + + fs.createReadStream(fn) + .on("error", onErr) + .pipe(gunzip()) + .on("error", onErr) + .pipe(extract) + .on("error", onErr); + } catch (e) { + logger.error(`unzipTarGz error: ${e}`); + reject(); + } + }); +} diff --git a/packages/backend/src/misc/schema.ts b/packages/backend/src/misc/schema.ts index 35637e6ed5..6e03d30d91 100644 --- a/packages/backend/src/misc/schema.ts +++ b/packages/backend/src/misc/schema.ts @@ -16,6 +16,7 @@ import { packedDriveFileSchema } from "@/models/schema/drive-file.js"; import { packedDriveFolderSchema } from "@/models/schema/drive-folder.js"; import { packedFollowingSchema } from "@/models/schema/following.js"; import { packedMutingSchema } from "@/models/schema/muting.js"; +import { packedRenoteMutingSchema } from "@/models/schema/renote-muting.js"; import { packedBlockingSchema } from "@/models/schema/blocking.js"; import { packedNoteReactionSchema } from "@/models/schema/note-reaction.js"; import { packedHashtagSchema } from "@/models/schema/hashtag.js"; @@ -29,6 +30,7 @@ import { packedFederationInstanceSchema } from "@/models/schema/federation-insta import { packedQueueCountSchema } from "@/models/schema/queue.js"; import { packedGalleryPostSchema } from "@/models/schema/gallery-post.js"; import { packedEmojiSchema } from "@/models/schema/emoji.js"; +import { packedNoteEdit } from "@/models/schema/note-edit.js"; export const refs = { UserLite: packedUserLiteSchema, @@ -44,6 +46,7 @@ export const refs = { App: packedAppSchema, MessagingMessage: packedMessagingMessageSchema, Note: packedNoteSchema, + NoteEdit: packedNoteEdit, NoteReaction: packedNoteReactionSchema, NoteFavorite: packedNoteFavoriteSchema, Notification: packedNotificationSchema, @@ -51,6 +54,7 @@ export const refs = { DriveFolder: packedDriveFolderSchema, Following: packedFollowingSchema, Muting: packedMutingSchema, + RenoteMuting: packedRenoteMutingSchema, Blocking: packedBlockingSchema, Hashtag: packedHashtagSchema, Page: packedPageSchema, diff --git a/packages/backend/src/misc/secure-rndstr.ts b/packages/backend/src/misc/secure-rndstr.ts index 7f5754e1c5..3d69a4d4a5 100644 --- a/packages/backend/src/misc/secure-rndstr.ts +++ b/packages/backend/src/misc/secure-rndstr.ts @@ -1,24 +1,5 @@ -import * as crypto from "node:crypto"; +import { nativeRandomStr } from "native-utils/built/index.js"; -const L_CHARS = "0123456789abcdefghijklmnopqrstuvwxyz"; -const LU_CHARS = - "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; - -export function secureRndstr(length = 32, useLU = true): string { - const chars = useLU ? LU_CHARS : L_CHARS; - const chars_len = chars.length; - - let str = ""; - - for (let i = 0; i < length; i++) { - let rand = Math.floor( - (crypto.randomBytes(1).readUInt8(0) / 0xff) * chars_len, - ); - if (rand === chars_len) { - rand = chars_len - 1; - } - str += chars.charAt(rand); - } - - return str; +export function secureRndstr(length = 32, _ = true): string { + return nativeRandomStr(length); } diff --git a/packages/backend/src/misc/should-block-instance.ts b/packages/backend/src/misc/should-block-instance.ts index 6e46232428..35ed307931 100644 --- a/packages/backend/src/misc/should-block-instance.ts +++ b/packages/backend/src/misc/should-block-instance.ts @@ -18,3 +18,21 @@ export async function shouldBlockInstance( (blockedHost) => host === blockedHost || host.endsWith(`.${blockedHost}`), ); } + +/** + * Returns whether a specific host (punycoded) should be limited. + * + * @param host punycoded instance host + * @param meta a resolved Meta table + * @returns whether the given host should be limited + */ +export async function shouldSilenceInstance( + host: Instance["host"], + meta?: Meta, +): Promise { + const { silencedHosts } = meta ?? (await fetchMeta()); + return silencedHosts.some( + (silencedHost) => + host === silencedHost || host.endsWith(`.${silencedHost}`), + ); +} diff --git a/packages/backend/src/misc/sql-like-escape.ts b/packages/backend/src/misc/sql-like-escape.ts new file mode 100644 index 0000000000..453947d6ec --- /dev/null +++ b/packages/backend/src/misc/sql-like-escape.ts @@ -0,0 +1,3 @@ +export function sqlLikeEscape(s: string) { + return s.replace(/([%_])/g, "\\$1"); +} diff --git a/packages/backend/src/models/entities/abuse-user-report.ts b/packages/backend/src/models/entities/abuse-user-report.ts index 655fdd3ca6..be183548d8 100644 --- a/packages/backend/src/models/entities/abuse-user-report.ts +++ b/packages/backend/src/models/entities/abuse-user-report.ts @@ -15,8 +15,8 @@ export class AbuseUserReport { public id: string; @Index() - @Column('timestamp with time zone', { - comment: 'The created date of the AbuseUserReport.', + @Column("timestamp with time zone", { + comment: "The created date of the AbuseUserReport.", }) public createdAt: Date; @@ -24,8 +24,8 @@ export class AbuseUserReport { @Column(id()) public targetUserId: User["id"]; - @ManyToOne(type => User, { - onDelete: 'CASCADE', + @ManyToOne((type) => User, { + onDelete: "CASCADE", }) @JoinColumn() public targetUser: User | null; @@ -34,8 +34,8 @@ export class AbuseUserReport { @Column(id()) public reporterId: User["id"]; - @ManyToOne(type => User, { - onDelete: 'CASCADE', + @ManyToOne((type) => User, { + onDelete: "CASCADE", }) @JoinColumn() public reporter: User | null; @@ -46,40 +46,42 @@ export class AbuseUserReport { }) public assigneeId: User["id"] | null; - @ManyToOne(type => User, { - onDelete: 'SET NULL', + @ManyToOne((type) => User, { + onDelete: "SET NULL", }) @JoinColumn() public assignee: User | null; @Index() - @Column('boolean', { + @Column("boolean", { default: false, }) public resolved: boolean; - @Column('boolean', { - default: false + @Column("boolean", { + default: false, }) public forwarded: boolean; - @Column('varchar', { + @Column("varchar", { length: 2048, }) public comment: string; //#region Denormalized fields @Index() - @Column('varchar', { - length: 128, nullable: true, - comment: '[Denormalized]', + @Column("varchar", { + length: 128, + nullable: true, + comment: "[Denormalized]", }) public targetUserHost: string | null; @Index() - @Column('varchar', { - length: 128, nullable: true, - comment: '[Denormalized]', + @Column("varchar", { + length: 128, + nullable: true, + comment: "[Denormalized]", }) public reporterHost: string | null; //#endregion diff --git a/packages/backend/src/models/entities/access-token.ts b/packages/backend/src/models/entities/access-token.ts index 83d7bbda86..8b950b171b 100644 --- a/packages/backend/src/models/entities/access-token.ts +++ b/packages/backend/src/models/entities/access-token.ts @@ -15,31 +15,31 @@ export class AccessToken { @PrimaryColumn(id()) public id: string; - @Column('timestamp with time zone', { - comment: 'The created date of the AccessToken.', + @Column("timestamp with time zone", { + comment: "The created date of the AccessToken.", }) public createdAt: Date; - @Column('timestamp with time zone', { + @Column("timestamp with time zone", { nullable: true, }) public lastUsedAt: Date | null; @Index() - @Column('varchar', { + @Column("varchar", { length: 128, }) public token: string; @Index() - @Column('varchar', { + @Column("varchar", { length: 128, nullable: true, }) public session: string | null; @Index() - @Column('varchar', { + @Column("varchar", { length: 128, }) public hash: string; @@ -48,8 +48,8 @@ export class AccessToken { @Column(id()) public userId: User["id"]; - @ManyToOne(type => User, { - onDelete: 'CASCADE', + @ManyToOne((type) => User, { + onDelete: "CASCADE", }) @JoinColumn() public user: User | null; @@ -60,37 +60,38 @@ export class AccessToken { }) public appId: App["id"] | null; - @ManyToOne(type => App, { - onDelete: 'CASCADE', + @ManyToOne((type) => App, { + onDelete: "CASCADE", }) @JoinColumn() public app: App | null; - @Column('varchar', { + @Column("varchar", { length: 128, nullable: true, }) public name: string | null; - @Column('varchar', { + @Column("varchar", { length: 512, nullable: true, }) public description: string | null; - @Column('varchar', { + @Column("varchar", { length: 512, nullable: true, }) public iconUrl: string | null; - @Column('varchar', { - length: 64, array: true, - default: '{}', + @Column("varchar", { + length: 64, + array: true, + default: "{}", }) public permission: string[]; - @Column('boolean', { + @Column("boolean", { default: false, }) public fetched: boolean; diff --git a/packages/backend/src/models/entities/ad.ts b/packages/backend/src/models/entities/ad.ts index fa42973652..80d54ddd52 100644 --- a/packages/backend/src/models/entities/ad.ts +++ b/packages/backend/src/models/entities/ad.ts @@ -7,45 +7,51 @@ export class Ad { public id: string; @Index() - @Column('timestamp with time zone', { - comment: 'The created date of the Ad.', + @Column("timestamp with time zone", { + comment: "The created date of the Ad.", }) public createdAt: Date; @Index() - @Column('timestamp with time zone', { - comment: 'The expired date of the Ad.', + @Column("timestamp with time zone", { + comment: "The expired date of the Ad.", }) public expiresAt: Date; - @Column('varchar', { - length: 32, nullable: false, + @Column("varchar", { + length: 32, + nullable: false, }) public place: string; // 今は使われていないが将来的に活用される可能性はある - @Column('varchar', { - length: 32, nullable: false, + @Column("varchar", { + length: 32, + nullable: false, }) public priority: string; - @Column('integer', { - default: 1, nullable: false, + @Column("integer", { + default: 1, + nullable: false, }) public ratio: number; - @Column('varchar', { - length: 1024, nullable: false, + @Column("varchar", { + length: 1024, + nullable: false, }) public url: string; - @Column('varchar', { - length: 1024, nullable: false, + @Column("varchar", { + length: 1024, + nullable: false, }) public imageUrl: string; - @Column('varchar', { - length: 8192, nullable: false, + @Column("varchar", { + length: 8192, + nullable: false, }) public memo: string; diff --git a/packages/backend/src/models/entities/announcement-read.ts b/packages/backend/src/models/entities/announcement-read.ts index 87d0f0e9ed..79af9e48e3 100644 --- a/packages/backend/src/models/entities/announcement-read.ts +++ b/packages/backend/src/models/entities/announcement-read.ts @@ -11,13 +11,13 @@ import { Announcement } from "./announcement.js"; import { id } from "../id.js"; @Entity() -@Index(['userId', 'announcementId'], { unique: true }) +@Index(["userId", "announcementId"], { unique: true }) export class AnnouncementRead { @PrimaryColumn(id()) public id: string; - @Column('timestamp with time zone', { - comment: 'The created date of the AnnouncementRead.', + @Column("timestamp with time zone", { + comment: "The created date of the AnnouncementRead.", }) public createdAt: Date; @@ -25,8 +25,8 @@ export class AnnouncementRead { @Column(id()) public userId: User["id"]; - @ManyToOne(type => User, { - onDelete: 'CASCADE', + @ManyToOne((type) => User, { + onDelete: "CASCADE", }) @JoinColumn() public user: User | null; @@ -35,8 +35,8 @@ export class AnnouncementRead { @Column(id()) public announcementId: Announcement["id"]; - @ManyToOne(type => Announcement, { - onDelete: 'CASCADE', + @ManyToOne((type) => Announcement, { + onDelete: "CASCADE", }) @JoinColumn() public announcement: Announcement | null; diff --git a/packages/backend/src/models/entities/announcement.ts b/packages/backend/src/models/entities/announcement.ts index 9d45af0149..7872c0fe1c 100644 --- a/packages/backend/src/models/entities/announcement.ts +++ b/packages/backend/src/models/entities/announcement.ts @@ -7,32 +7,45 @@ export class Announcement { public id: string; @Index() - @Column('timestamp with time zone', { - comment: 'The created date of the Announcement.', + @Column("timestamp with time zone", { + comment: "The created date of the Announcement.", }) public createdAt: Date; - @Column('timestamp with time zone', { - comment: 'The updated date of the Announcement.', + @Column("timestamp with time zone", { + comment: "The updated date of the Announcement.", nullable: true, }) public updatedAt: Date | null; - @Column('varchar', { - length: 8192, nullable: false, + @Column("varchar", { + length: 8192, + nullable: false, }) public text: string; - @Column('varchar', { - length: 256, nullable: false, + @Column("varchar", { + length: 256, + nullable: false, }) public title: string; - @Column('varchar', { - length: 1024, nullable: true, + @Column("varchar", { + length: 1024, + nullable: true, }) public imageUrl: string | null; + @Column("boolean", { + default: false, + }) + public showPopup: boolean; + + @Column("boolean", { + default: false, + }) + public isGoodNews: boolean; + constructor(data: Partial) { if (data == null) return; diff --git a/packages/backend/src/models/entities/antenna-note.ts b/packages/backend/src/models/entities/antenna-note.ts deleted file mode 100644 index c47c796bbf..0000000000 --- a/packages/backend/src/models/entities/antenna-note.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { - Entity, - Index, - JoinColumn, - Column, - ManyToOne, - PrimaryColumn, -} from "typeorm"; -import { Note } from "./note.js"; -import { Antenna } from "./antenna.js"; -import { id } from "../id.js"; - -@Entity() -@Index(['noteId', 'antennaId'], { unique: true }) -export class AntennaNote { - @PrimaryColumn(id()) - public id: string; - - @Index() - @Column({ - ...id(), - comment: 'The note ID.', - }) - public noteId: Note["id"]; - - @ManyToOne(type => Note, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public note: Note | null; - - @Index() - @Column({ - ...id(), - comment: 'The antenna ID.', - }) - public antennaId: Antenna["id"]; - - @ManyToOne(type => Antenna, { - onDelete: 'CASCADE', - }) - @JoinColumn() - public antenna: Antenna | null; - - @Index() - @Column('boolean', { - default: false, - }) - public read: boolean; -} diff --git a/packages/backend/src/models/entities/antenna.ts b/packages/backend/src/models/entities/antenna.ts index 45d9553e4e..633dcc1d27 100644 --- a/packages/backend/src/models/entities/antenna.ts +++ b/packages/backend/src/models/entities/antenna.ts @@ -16,32 +16,34 @@ export class Antenna { @PrimaryColumn(id()) public id: string; - @Column('timestamp with time zone', { - comment: 'The created date of the Antenna.', + @Column("timestamp with time zone", { + comment: "The created date of the Antenna.", }) public createdAt: Date; @Index() @Column({ ...id(), - comment: 'The owner ID.', + comment: "The owner ID.", }) public userId: User["id"]; - @ManyToOne(type => User, { - onDelete: 'CASCADE', + @ManyToOne((type) => User, { + onDelete: "CASCADE", }) @JoinColumn() public user: User | null; - @Column('varchar', { + @Column("varchar", { length: 128, - comment: 'The name of the Antenna.', + comment: "The name of the Antenna.", }) public name: string; - @Column('enum', { enum: ['home', 'all', 'users', 'list', 'group'] }) - public src: "home" | "all" | "users" | "list" | "group"; + @Column("enum", { + enum: ["home", "all", "users", "list", "group", "instances"], + }) + public src: "home" | "all" | "users" | "list" | "group" | "instances"; @Column({ ...id(), @@ -49,8 +51,8 @@ export class Antenna { }) public userListId: UserList["id"] | null; - @ManyToOne(type => UserList, { - onDelete: 'CASCADE', + @ManyToOne((type) => UserList, { + onDelete: "CASCADE", }) @JoinColumn() public userList: UserList | null; @@ -61,46 +63,53 @@ export class Antenna { }) public userGroupJoiningId: UserGroupJoining["id"] | null; - @ManyToOne(type => UserGroupJoining, { - onDelete: 'CASCADE', + @ManyToOne((type) => UserGroupJoining, { + onDelete: "CASCADE", }) @JoinColumn() public userGroupJoining: UserGroupJoining | null; - @Column('varchar', { - length: 1024, array: true, - default: '{}', + @Column("varchar", { + length: 1024, + array: true, + default: "{}", }) public users: string[]; - @Column('jsonb', { + @Column("jsonb", { + default: [], + }) + public instances: string[]; + + @Column("jsonb", { default: [], }) public keywords: string[][]; - @Column('jsonb', { + @Column("jsonb", { default: [], }) public excludeKeywords: string[][]; - @Column('boolean', { + @Column("boolean", { default: false, }) public caseSensitive: boolean; - @Column('boolean', { + @Column("boolean", { default: false, }) public withReplies: boolean; - @Column('boolean') + @Column("boolean") public withFile: boolean; - @Column('varchar', { - length: 2048, nullable: true, + @Column("varchar", { + length: 2048, + nullable: true, }) public expression: string | null; - @Column('boolean') + @Column("boolean") public notify: boolean; } diff --git a/packages/backend/src/models/entities/app.ts b/packages/backend/src/models/entities/app.ts index bb33eede4f..a41e35aa91 100644 --- a/packages/backend/src/models/entities/app.ts +++ b/packages/backend/src/models/entities/app.ts @@ -8,8 +8,8 @@ export class App { public id: string; @Index() - @Column('timestamp with time zone', { - comment: 'The created date of the App.', + @Column("timestamp with time zone", { + comment: "The created date of the App.", }) public createdAt: Date; @@ -17,44 +17,46 @@ export class App { @Column({ ...id(), nullable: true, - comment: 'The owner ID.', + comment: "The owner ID.", }) public userId: User["id"] | null; - @ManyToOne(type => User, { - onDelete: 'SET NULL', + @ManyToOne((type) => User, { + onDelete: "SET NULL", nullable: true, }) public user: User | null; @Index() - @Column('varchar', { + @Column("varchar", { length: 64, - comment: 'The secret key of the App.', + comment: "The secret key of the App.", }) public secret: string; - @Column('varchar', { + @Column("varchar", { length: 128, - comment: 'The name of the App.', + comment: "The name of the App.", }) public name: string; - @Column('varchar', { + @Column("varchar", { length: 512, - comment: 'The description of the App.', + comment: "The description of the App.", }) public description: string; - @Column('varchar', { - length: 64, array: true, - comment: 'The permission of the App.', + @Column("varchar", { + length: 64, + array: true, + comment: "The permission of the App.", }) public permission: string[]; - @Column('varchar', { - length: 512, nullable: true, - comment: 'The callbackUrl of the App.', + @Column("varchar", { + length: 512, + nullable: true, + comment: "The callbackUrl of the App.", }) public callbackUrl: string | null; } diff --git a/packages/backend/src/models/entities/attestation-challenge.ts b/packages/backend/src/models/entities/attestation-challenge.ts index 7a87d42be0..6a3a9c8ed7 100644 --- a/packages/backend/src/models/entities/attestation-challenge.ts +++ b/packages/backend/src/models/entities/attestation-challenge.ts @@ -18,27 +18,27 @@ export class AttestationChallenge { @PrimaryColumn(id()) public userId: User["id"]; - @ManyToOne(type => User, { - onDelete: 'CASCADE', + @ManyToOne((type) => User, { + onDelete: "CASCADE", }) @JoinColumn() public user: User | null; @Index() - @Column('varchar', { + @Column("varchar", { length: 64, - comment: 'Hex-encoded sha256 hash of the challenge.', + comment: "Hex-encoded sha256 hash of the challenge.", }) public challenge: string; - @Column('timestamp with time zone', { - comment: 'The date challenge was created for expiry purposes.', + @Column("timestamp with time zone", { + comment: "The date challenge was created for expiry purposes.", }) public createdAt: Date; - @Column('boolean', { + @Column("boolean", { comment: - 'Indicates that the challenge is only for registration purposes if true to prevent the challenge for being used as authentication.', + "Indicates that the challenge is only for registration purposes if true to prevent the challenge for being used as authentication.", default: false, }) public registrationChallenge: boolean; diff --git a/packages/backend/src/models/entities/auth-session.ts b/packages/backend/src/models/entities/auth-session.ts index b762f84625..b31dca56cf 100644 --- a/packages/backend/src/models/entities/auth-session.ts +++ b/packages/backend/src/models/entities/auth-session.ts @@ -15,13 +15,13 @@ export class AuthSession { @PrimaryColumn(id()) public id: string; - @Column('timestamp with time zone', { - comment: 'The created date of the AuthSession.', + @Column("timestamp with time zone", { + comment: "The created date of the AuthSession.", }) public createdAt: Date; @Index() - @Column('varchar', { + @Column("varchar", { length: 128, }) public token: string; @@ -32,8 +32,8 @@ export class AuthSession { }) public userId: User["id"] | null; - @ManyToOne(type => User, { - onDelete: 'CASCADE', + @ManyToOne((type) => User, { + onDelete: "CASCADE", nullable: true, }) @JoinColumn() @@ -42,8 +42,8 @@ export class AuthSession { @Column(id()) public appId: App["id"]; - @ManyToOne(type => App, { - onDelete: 'CASCADE', + @ManyToOne((type) => App, { + onDelete: "CASCADE", }) @JoinColumn() public app: App | null; diff --git a/packages/backend/src/models/entities/blocking.ts b/packages/backend/src/models/entities/blocking.ts index 3a44a4d656..55f677a981 100644 --- a/packages/backend/src/models/entities/blocking.ts +++ b/packages/backend/src/models/entities/blocking.ts @@ -10,26 +10,26 @@ import { User } from "./user.js"; import { id } from "../id.js"; @Entity() -@Index(['blockerId', 'blockeeId'], { unique: true }) +@Index(["blockerId", "blockeeId"], { unique: true }) export class Blocking { @PrimaryColumn(id()) public id: string; @Index() - @Column('timestamp with time zone', { - comment: 'The created date of the Blocking.', + @Column("timestamp with time zone", { + comment: "The created date of the Blocking.", }) public createdAt: Date; @Index() @Column({ ...id(), - comment: 'The blockee user ID.', + comment: "The blockee user ID.", }) public blockeeId: User["id"]; - @ManyToOne(type => User, { - onDelete: 'CASCADE', + @ManyToOne((type) => User, { + onDelete: "CASCADE", }) @JoinColumn() public blockee: User | null; @@ -37,12 +37,12 @@ export class Blocking { @Index() @Column({ ...id(), - comment: 'The blocker user ID.', + comment: "The blocker user ID.", }) public blockerId: User["id"]; - @ManyToOne(type => User, { - onDelete: 'CASCADE', + @ManyToOne((type) => User, { + onDelete: "CASCADE", }) @JoinColumn() public blocker: User | null; diff --git a/packages/backend/src/models/entities/channel-following.ts b/packages/backend/src/models/entities/channel-following.ts index 04ec193e19..ee329fa50f 100644 --- a/packages/backend/src/models/entities/channel-following.ts +++ b/packages/backend/src/models/entities/channel-following.ts @@ -11,26 +11,26 @@ import { id } from "../id.js"; import { Channel } from "./channel.js"; @Entity() -@Index(['followerId', 'followeeId'], { unique: true }) +@Index(["followerId", "followeeId"], { unique: true }) export class ChannelFollowing { @PrimaryColumn(id()) public id: string; @Index() - @Column('timestamp with time zone', { - comment: 'The created date of the ChannelFollowing.', + @Column("timestamp with time zone", { + comment: "The created date of the ChannelFollowing.", }) public createdAt: Date; @Index() @Column({ ...id(), - comment: 'The followee channel ID.', + comment: "The followee channel ID.", }) public followeeId: Channel["id"]; - @ManyToOne(type => Channel, { - onDelete: 'CASCADE', + @ManyToOne((type) => Channel, { + onDelete: "CASCADE", }) @JoinColumn() public followee: Channel | null; @@ -38,12 +38,12 @@ export class ChannelFollowing { @Index() @Column({ ...id(), - comment: 'The follower user ID.', + comment: "The follower user ID.", }) public followerId: User["id"]; - @ManyToOne(type => User, { - onDelete: 'CASCADE', + @ManyToOne((type) => User, { + onDelete: "CASCADE", }) @JoinColumn() public follower: User | null; diff --git a/packages/backend/src/models/entities/channel-note-pining.ts b/packages/backend/src/models/entities/channel-note-pining.ts index bd13f4ca39..67d1d48ccd 100644 --- a/packages/backend/src/models/entities/channel-note-pining.ts +++ b/packages/backend/src/models/entities/channel-note-pining.ts @@ -11,13 +11,13 @@ import { Channel } from "./channel.js"; import { id } from "../id.js"; @Entity() -@Index(['channelId', 'noteId'], { unique: true }) +@Index(["channelId", "noteId"], { unique: true }) export class ChannelNotePining { @PrimaryColumn(id()) public id: string; - @Column('timestamp with time zone', { - comment: 'The created date of the ChannelNotePining.', + @Column("timestamp with time zone", { + comment: "The created date of the ChannelNotePining.", }) public createdAt: Date; @@ -25,8 +25,8 @@ export class ChannelNotePining { @Column(id()) public channelId: Channel["id"]; - @ManyToOne(type => Channel, { - onDelete: 'CASCADE', + @ManyToOne((type) => Channel, { + onDelete: "CASCADE", }) @JoinColumn() public channel: Channel | null; @@ -34,8 +34,8 @@ export class ChannelNotePining { @Column(id()) public noteId: Note["id"]; - @ManyToOne(type => Note, { - onDelete: 'CASCADE', + @ManyToOne((type) => Note, { + onDelete: "CASCADE", }) @JoinColumn() public note: Note | null; diff --git a/packages/backend/src/models/entities/channel.ts b/packages/backend/src/models/entities/channel.ts index 7f9851dbf9..ea22fed50b 100644 --- a/packages/backend/src/models/entities/channel.ts +++ b/packages/backend/src/models/entities/channel.ts @@ -16,13 +16,13 @@ export class Channel { public id: string; @Index() - @Column('timestamp with time zone', { - comment: 'The created date of the Channel.', + @Column("timestamp with time zone", { + comment: "The created date of the Channel.", }) public createdAt: Date; @Index() - @Column('timestamp with time zone', { + @Column("timestamp with time zone", { nullable: true, }) public lastNotedAt: Date | null; @@ -31,52 +31,53 @@ export class Channel { @Column({ ...id(), nullable: true, - comment: 'The owner ID.', + comment: "The owner ID.", }) public userId: User["id"] | null; - @ManyToOne(type => User, { - onDelete: 'SET NULL', + @ManyToOne((type) => User, { + onDelete: "SET NULL", }) @JoinColumn() public user: User | null; - @Column('varchar', { + @Column("varchar", { length: 128, - comment: 'The name of the Channel.', + comment: "The name of the Channel.", }) public name: string; - @Column('varchar', { - length: 2048, nullable: true, - comment: 'The description of the Channel.', + @Column("varchar", { + length: 2048, + nullable: true, + comment: "The description of the Channel.", }) public description: string | null; @Column({ ...id(), nullable: true, - comment: 'The ID of banner Channel.', + comment: "The ID of banner Channel.", }) public bannerId: DriveFile["id"] | null; - @ManyToOne(type => DriveFile, { - onDelete: 'SET NULL', + @ManyToOne((type) => DriveFile, { + onDelete: "SET NULL", }) @JoinColumn() public banner: DriveFile | null; @Index() - @Column('integer', { + @Column("integer", { default: 0, - comment: 'The count of notes.', + comment: "The count of notes.", }) public notesCount: number; @Index() - @Column('integer', { + @Column("integer", { default: 0, - comment: 'The count of users.', + comment: "The count of users.", }) public usersCount: number; } diff --git a/packages/backend/src/models/entities/clip-note.ts b/packages/backend/src/models/entities/clip-note.ts index bc51daaf4d..1697474a84 100644 --- a/packages/backend/src/models/entities/clip-note.ts +++ b/packages/backend/src/models/entities/clip-note.ts @@ -11,7 +11,7 @@ import { Clip } from "./clip.js"; import { id } from "../id.js"; @Entity() -@Index(['noteId', 'clipId'], { unique: true }) +@Index(["noteId", "clipId"], { unique: true }) export class ClipNote { @PrimaryColumn(id()) public id: string; @@ -19,12 +19,12 @@ export class ClipNote { @Index() @Column({ ...id(), - comment: 'The note ID.', + comment: "The note ID.", }) public noteId: Note["id"]; - @ManyToOne(type => Note, { - onDelete: 'CASCADE', + @ManyToOne((type) => Note, { + onDelete: "CASCADE", }) @JoinColumn() public note: Note | null; @@ -32,12 +32,12 @@ export class ClipNote { @Index() @Column({ ...id(), - comment: 'The clip ID.', + comment: "The clip ID.", }) public clipId: Clip["id"]; - @ManyToOne(type => Clip, { - onDelete: 'CASCADE', + @ManyToOne((type) => Clip, { + onDelete: "CASCADE", }) @JoinColumn() public clip: Clip | null; diff --git a/packages/backend/src/models/entities/clip.ts b/packages/backend/src/models/entities/clip.ts index 10591cbeef..9554703a4c 100644 --- a/packages/backend/src/models/entities/clip.ts +++ b/packages/backend/src/models/entities/clip.ts @@ -14,38 +14,39 @@ export class Clip { @PrimaryColumn(id()) public id: string; - @Column('timestamp with time zone', { - comment: 'The created date of the Clip.', + @Column("timestamp with time zone", { + comment: "The created date of the Clip.", }) public createdAt: Date; @Index() @Column({ ...id(), - comment: 'The owner ID.', + comment: "The owner ID.", }) public userId: User["id"]; - @ManyToOne(type => User, { - onDelete: 'CASCADE', + @ManyToOne((type) => User, { + onDelete: "CASCADE", }) @JoinColumn() public user: User | null; - @Column('varchar', { + @Column("varchar", { length: 128, - comment: 'The name of the Clip.', + comment: "The name of the Clip.", }) public name: string; - @Column('boolean', { + @Column("boolean", { default: false, }) public isPublic: boolean; - @Column('varchar', { - length: 2048, nullable: true, - comment: 'The description of the Clip.', + @Column("varchar", { + length: 2048, + nullable: true, + comment: "The description of the Clip.", }) public description: string | null; } diff --git a/packages/backend/src/models/entities/drive-file.ts b/packages/backend/src/models/entities/drive-file.ts index 1fa91b1a97..d8b54fa194 100644 --- a/packages/backend/src/models/entities/drive-file.ts +++ b/packages/backend/src/models/entities/drive-file.ts @@ -9,16 +9,17 @@ import { import { id } from "../id.js"; import { User } from "./user.js"; import { DriveFolder } from "./drive-folder.js"; +import { DB_MAX_IMAGE_COMMENT_LENGTH } from "@/misc/hard-limits.js"; @Entity() -@Index(['userId', 'folderId', 'id']) +@Index(["userId", "folderId", "id"]) export class DriveFile { @PrimaryColumn(id()) public id: string; @Index() - @Column('timestamp with time zone', { - comment: 'The created date of the DriveFile.', + @Column("timestamp with time zone", { + comment: "The created date of the DriveFile.", }) public createdAt: Date; @@ -26,63 +27,67 @@ export class DriveFile { @Column({ ...id(), nullable: true, - comment: 'The owner ID.', + comment: "The owner ID.", }) public userId: User["id"] | null; - @ManyToOne(type => User, { - onDelete: 'SET NULL', + @ManyToOne((type) => User, { + onDelete: "SET NULL", }) @JoinColumn() public user: User | null; @Index() - @Column('varchar', { - length: 128, nullable: true, - comment: 'The host of owner. It will be null if the user in local.', + @Column("varchar", { + length: 128, + nullable: true, + comment: "The host of owner. It will be null if the user in local.", }) public userHost: string | null; @Index() - @Column('varchar', { + @Column("varchar", { length: 32, - comment: 'The MD5 hash of the DriveFile.', + comment: "The MD5 hash of the DriveFile.", }) public md5: string; - @Column('varchar', { + @Column("varchar", { length: 256, - comment: 'The file name of the DriveFile.', + comment: "The file name of the DriveFile.", }) public name: string; @Index() - @Column('varchar', { + @Column("varchar", { length: 128, - comment: 'The content type (MIME) of the DriveFile.', + comment: "The content type (MIME) of the DriveFile.", }) public type: string; - @Column('integer', { - comment: 'The file size (bytes) of the DriveFile.', + @Column("integer", { + comment: "The file size (bytes) of the DriveFile.", }) public size: number; - @Column('varchar', { - length: 512, nullable: true, - comment: 'The comment of the DriveFile.', + @Column("varchar", { + length: DB_MAX_IMAGE_COMMENT_LENGTH, + nullable: true, + comment: "The comment of the DriveFile.", }) public comment: string | null; - @Column('varchar', { - length: 128, nullable: true, - comment: 'The BlurHash string.', + @Column("varchar", { + length: 128, + nullable: true, + comment: "The BlurHash string.", }) public blurhash: string | null; - @Column('jsonb', { + @Column("jsonb", { default: {}, - comment: 'The any properties of the DriveFile. For example, it includes image width/height.', + comment: + "The any properties of the DriveFile. For example, it includes image width/height.", }) public properties: { width?: number; @@ -91,59 +96,68 @@ export class DriveFile { avgColor?: string; }; - @Column('boolean') + @Column("boolean") public storedInternal: boolean; - @Column('varchar', { + @Column("varchar", { length: 512, - comment: 'The URL of the DriveFile.', + comment: "The URL of the DriveFile.", }) public url: string; - @Column('varchar', { - length: 512, nullable: true, - comment: 'The URL of the thumbnail of the DriveFile.', + @Column("varchar", { + length: 512, + nullable: true, + comment: "The URL of the thumbnail of the DriveFile.", }) public thumbnailUrl: string | null; - @Column('varchar', { - length: 512, nullable: true, - comment: 'The URL of the webpublic of the DriveFile.', + @Column("varchar", { + length: 512, + nullable: true, + comment: "The URL of the webpublic of the DriveFile.", }) public webpublicUrl: string | null; - @Column('varchar', { - length: 128, nullable: true, + @Column("varchar", { + length: 128, + nullable: true, }) public webpublicType: string | null; @Index({ unique: true }) - @Column('varchar', { - length: 256, nullable: true, + @Column("varchar", { + length: 256, + nullable: true, }) public accessKey: string | null; @Index({ unique: true }) - @Column('varchar', { - length: 256, nullable: true, + @Column("varchar", { + length: 256, + nullable: true, }) public thumbnailAccessKey: string | null; @Index({ unique: true }) - @Column('varchar', { - length: 256, nullable: true, + @Column("varchar", { + length: 256, + nullable: true, }) public webpublicAccessKey: string | null; @Index() - @Column('varchar', { - length: 512, nullable: true, - comment: 'The URI of the DriveFile. it will be null when the DriveFile is local.', + @Column("varchar", { + length: 512, + nullable: true, + comment: + "The URI of the DriveFile. it will be null when the DriveFile is local.", }) public uri: string | null; - @Column('varchar', { - length: 512, nullable: true, + @Column("varchar", { + length: 512, + nullable: true, }) public src: string | null; @@ -151,32 +165,33 @@ export class DriveFile { @Column({ ...id(), nullable: true, - comment: 'The parent folder ID. If null, it means the DriveFile is located in root.', + comment: + "The parent folder ID. If null, it means the DriveFile is located in root.", }) public folderId: DriveFolder["id"] | null; - @ManyToOne(type => DriveFolder, { - onDelete: 'SET NULL', + @ManyToOne((type) => DriveFolder, { + onDelete: "SET NULL", }) @JoinColumn() public folder: DriveFolder | null; @Index() - @Column('boolean', { + @Column("boolean", { default: false, - comment: 'Whether the DriveFile is NSFW.', + comment: "Whether the DriveFile is NSFW.", }) public isSensitive: boolean; @Index() - @Column('boolean', { + @Column("boolean", { default: false, - comment: 'Whether the DriveFile is NSFW. (predict)', + comment: "Whether the DriveFile is NSFW. (predict)", }) public maybeSensitive: boolean; @Index() - @Column('boolean', { + @Column("boolean", { default: false, }) public maybePorn: boolean; @@ -185,20 +200,21 @@ export class DriveFile { * 外部の(信頼されていない)URLへの直リンクか否か */ @Index() - @Column('boolean', { + @Column("boolean", { default: false, - comment: 'Whether the DriveFile is direct link to remote server.', + comment: "Whether the DriveFile is direct link to remote server.", }) public isLink: boolean; - @Column('jsonb', { + @Column("jsonb", { default: {}, nullable: true, }) public requestHeaders: Record | null; - @Column('varchar', { - length: 128, nullable: true, + @Column("varchar", { + length: 128, + nullable: true, }) public requestIp: string | null; } diff --git a/packages/backend/src/models/entities/drive-folder.ts b/packages/backend/src/models/entities/drive-folder.ts index 77031ce4ea..0bb2c7a3d2 100644 --- a/packages/backend/src/models/entities/drive-folder.ts +++ b/packages/backend/src/models/entities/drive-folder.ts @@ -15,14 +15,14 @@ export class DriveFolder { public id: string; @Index() - @Column('timestamp with time zone', { - comment: 'The created date of the DriveFolder.', + @Column("timestamp with time zone", { + comment: "The created date of the DriveFolder.", }) public createdAt: Date; - @Column('varchar', { + @Column("varchar", { length: 128, - comment: 'The name of the DriveFolder.', + comment: "The name of the DriveFolder.", }) public name: string; @@ -30,12 +30,12 @@ export class DriveFolder { @Column({ ...id(), nullable: true, - comment: 'The owner ID.', + comment: "The owner ID.", }) public userId: User["id"] | null; - @ManyToOne(type => User, { - onDelete: 'CASCADE', + @ManyToOne((type) => User, { + onDelete: "CASCADE", }) @JoinColumn() public user: User | null; @@ -44,12 +44,13 @@ export class DriveFolder { @Column({ ...id(), nullable: true, - comment: 'The parent folder ID. If null, it means the DriveFolder is located in root.', + comment: + "The parent folder ID. If null, it means the DriveFolder is located in root.", }) public parentId: DriveFolder["id"] | null; - @ManyToOne(type => DriveFolder, { - onDelete: 'SET NULL', + @ManyToOne((type) => DriveFolder, { + onDelete: "SET NULL", }) @JoinColumn() public parent: DriveFolder | null; diff --git a/packages/backend/src/models/entities/emoji.ts b/packages/backend/src/models/entities/emoji.ts index f251de8973..727ba2f10a 100644 --- a/packages/backend/src/models/entities/emoji.ts +++ b/packages/backend/src/models/entities/emoji.ts @@ -2,57 +2,82 @@ import { PrimaryColumn, Entity, Index, Column } from "typeorm"; import { id } from "../id.js"; @Entity() -@Index(['name', 'host'], { unique: true }) +@Index(["name", "host"], { unique: true }) export class Emoji { @PrimaryColumn(id()) public id: string; - @Column('timestamp with time zone', { + @Column("timestamp with time zone", { nullable: true, }) public updatedAt: Date | null; @Index() - @Column('varchar', { + @Column("varchar", { length: 128, }) public name: string; @Index() - @Column('varchar', { - length: 128, nullable: true, + @Column("varchar", { + length: 128, + nullable: true, }) public host: string | null; - @Column('varchar', { - length: 128, nullable: true, + @Column("varchar", { + length: 128, + nullable: true, }) public category: string | null; - @Column('varchar', { + @Column("varchar", { length: 512, }) public originalUrl: string; - @Column('varchar', { + @Column("varchar", { length: 512, - default: '', + default: "", }) public publicUrl: string; - @Column('varchar', { - length: 512, nullable: true, + @Column("varchar", { + length: 512, + nullable: true, }) public uri: string | null; // publicUrlの方のtypeが入る - @Column('varchar', { - length: 64, nullable: true, + // (mime) + @Column("varchar", { + length: 64, + nullable: true, }) public type: string | null; - @Column('varchar', { - array: true, length: 128, default: '{}', + @Column("varchar", { + array: true, + length: 128, + default: "{}", }) public aliases: string[]; + + @Column("varchar", { + length: 1024, + nullable: true, + }) + public license: string | null; + + @Column("integer", { + nullable: true, + comment: "Image width", + }) + public width: number | null; + + @Column("integer", { + nullable: true, + comment: "Image height", + }) + public height: number | null; } diff --git a/packages/backend/src/models/entities/follow-request.ts b/packages/backend/src/models/entities/follow-request.ts index 658fed5a5e..281eab9174 100644 --- a/packages/backend/src/models/entities/follow-request.ts +++ b/packages/backend/src/models/entities/follow-request.ts @@ -10,25 +10,25 @@ import { User } from "./user.js"; import { id } from "../id.js"; @Entity() -@Index(['followerId', 'followeeId'], { unique: true }) +@Index(["followerId", "followeeId"], { unique: true }) export class FollowRequest { @PrimaryColumn(id()) public id: string; - @Column('timestamp with time zone', { - comment: 'The created date of the FollowRequest.', + @Column("timestamp with time zone", { + comment: "The created date of the FollowRequest.", }) public createdAt: Date; @Index() @Column({ ...id(), - comment: 'The followee user ID.', + comment: "The followee user ID.", }) public followeeId: User["id"]; - @ManyToOne(type => User, { - onDelete: 'CASCADE', + @ManyToOne((type) => User, { + onDelete: "CASCADE", }) @JoinColumn() public followee: User | null; @@ -36,56 +36,63 @@ export class FollowRequest { @Index() @Column({ ...id(), - comment: 'The follower user ID.', + comment: "The follower user ID.", }) public followerId: User["id"]; - @ManyToOne(type => User, { - onDelete: 'CASCADE', + @ManyToOne((type) => User, { + onDelete: "CASCADE", }) @JoinColumn() public follower: User | null; - @Column('varchar', { - length: 128, nullable: true, - comment: 'id of Follow Activity.', + @Column("varchar", { + length: 128, + nullable: true, + comment: "id of Follow Activity.", }) public requestId: string | null; //#region Denormalized fields - @Column('varchar', { - length: 128, nullable: true, - comment: '[Denormalized]', + @Column("varchar", { + length: 128, + nullable: true, + comment: "[Denormalized]", }) public followerHost: string | null; - @Column('varchar', { - length: 512, nullable: true, - comment: '[Denormalized]', + @Column("varchar", { + length: 512, + nullable: true, + comment: "[Denormalized]", }) public followerInbox: string | null; - @Column('varchar', { - length: 512, nullable: true, - comment: '[Denormalized]', + @Column("varchar", { + length: 512, + nullable: true, + comment: "[Denormalized]", }) public followerSharedInbox: string | null; - @Column('varchar', { - length: 128, nullable: true, - comment: '[Denormalized]', + @Column("varchar", { + length: 128, + nullable: true, + comment: "[Denormalized]", }) public followeeHost: string | null; - @Column('varchar', { - length: 512, nullable: true, - comment: '[Denormalized]', + @Column("varchar", { + length: 512, + nullable: true, + comment: "[Denormalized]", }) public followeeInbox: string | null; - @Column('varchar', { - length: 512, nullable: true, - comment: '[Denormalized]', + @Column("varchar", { + length: 512, + nullable: true, + comment: "[Denormalized]", }) public followeeSharedInbox: string | null; //#endregion diff --git a/packages/backend/src/models/entities/following.ts b/packages/backend/src/models/entities/following.ts index 11f633fcd8..fafcf88851 100644 --- a/packages/backend/src/models/entities/following.ts +++ b/packages/backend/src/models/entities/following.ts @@ -10,26 +10,26 @@ import { User } from "./user.js"; import { id } from "../id.js"; @Entity() -@Index(['followerId', 'followeeId'], { unique: true }) +@Index(["followerId", "followeeId"], { unique: true }) export class Following { @PrimaryColumn(id()) public id: string; @Index() - @Column('timestamp with time zone', { - comment: 'The created date of the Following.', + @Column("timestamp with time zone", { + comment: "The created date of the Following.", }) public createdAt: Date; @Index() @Column({ ...id(), - comment: 'The followee user ID.', + comment: "The followee user ID.", }) public followeeId: User["id"]; - @ManyToOne(type => User, { - onDelete: 'CASCADE', + @ManyToOne((type) => User, { + onDelete: "CASCADE", }) @JoinColumn() public followee: User | null; @@ -37,52 +37,58 @@ export class Following { @Index() @Column({ ...id(), - comment: 'The follower user ID.', + comment: "The follower user ID.", }) public followerId: User["id"]; - @ManyToOne(type => User, { - onDelete: 'CASCADE', + @ManyToOne((type) => User, { + onDelete: "CASCADE", }) @JoinColumn() public follower: User | null; //#region Denormalized fields @Index() - @Column('varchar', { - length: 128, nullable: true, - comment: '[Denormalized]', + @Column("varchar", { + length: 128, + nullable: true, + comment: "[Denormalized]", }) public followerHost: string | null; - @Column('varchar', { - length: 512, nullable: true, - comment: '[Denormalized]', + @Column("varchar", { + length: 512, + nullable: true, + comment: "[Denormalized]", }) public followerInbox: string | null; - @Column('varchar', { - length: 512, nullable: true, - comment: '[Denormalized]', + @Column("varchar", { + length: 512, + nullable: true, + comment: "[Denormalized]", }) public followerSharedInbox: string | null; @Index() - @Column('varchar', { - length: 128, nullable: true, - comment: '[Denormalized]', + @Column("varchar", { + length: 128, + nullable: true, + comment: "[Denormalized]", }) public followeeHost: string | null; - @Column('varchar', { - length: 512, nullable: true, - comment: '[Denormalized]', + @Column("varchar", { + length: 512, + nullable: true, + comment: "[Denormalized]", }) public followeeInbox: string | null; - @Column('varchar', { - length: 512, nullable: true, - comment: '[Denormalized]', + @Column("varchar", { + length: 512, + nullable: true, + comment: "[Denormalized]", }) public followeeSharedInbox: string | null; //#endregion diff --git a/packages/backend/src/models/entities/gallery-like.ts b/packages/backend/src/models/entities/gallery-like.ts index e74e3c3ce5..259feb8bbb 100644 --- a/packages/backend/src/models/entities/gallery-like.ts +++ b/packages/backend/src/models/entities/gallery-like.ts @@ -11,20 +11,20 @@ import { id } from "../id.js"; import { GalleryPost } from "./gallery-post.js"; @Entity() -@Index(['userId', 'postId'], { unique: true }) +@Index(["userId", "postId"], { unique: true }) export class GalleryLike { @PrimaryColumn(id()) public id: string; - @Column('timestamp with time zone') + @Column("timestamp with time zone") public createdAt: Date; @Index() @Column(id()) public userId: User["id"]; - @ManyToOne(type => User, { - onDelete: 'CASCADE', + @ManyToOne((type) => User, { + onDelete: "CASCADE", }) @JoinColumn() public user: User | null; @@ -32,8 +32,8 @@ export class GalleryLike { @Column(id()) public postId: GalleryPost["id"]; - @ManyToOne(type => GalleryPost, { - onDelete: 'CASCADE', + @ManyToOne((type) => GalleryPost, { + onDelete: "CASCADE", }) @JoinColumn() public post: GalleryPost | null; diff --git a/packages/backend/src/models/entities/gallery-post.ts b/packages/backend/src/models/entities/gallery-post.ts index a79bb88353..938348659d 100644 --- a/packages/backend/src/models/entities/gallery-post.ts +++ b/packages/backend/src/models/entities/gallery-post.ts @@ -16,36 +16,37 @@ export class GalleryPost { public id: string; @Index() - @Column('timestamp with time zone', { - comment: 'The created date of the GalleryPost.', + @Column("timestamp with time zone", { + comment: "The created date of the GalleryPost.", }) public createdAt: Date; @Index() - @Column('timestamp with time zone', { - comment: 'The updated date of the GalleryPost.', + @Column("timestamp with time zone", { + comment: "The updated date of the GalleryPost.", }) public updatedAt: Date; - @Column('varchar', { + @Column("varchar", { length: 256, }) public title: string; - @Column('varchar', { - length: 2048, nullable: true, + @Column("varchar", { + length: 2048, + nullable: true, }) public description: string | null; @Index() @Column({ ...id(), - comment: 'The ID of author.', + comment: "The ID of author.", }) public userId: User["id"]; - @ManyToOne(type => User, { - onDelete: 'CASCADE', + @ManyToOne((type) => User, { + onDelete: "CASCADE", }) @JoinColumn() public user: User | null; @@ -53,26 +54,29 @@ export class GalleryPost { @Index() @Column({ ...id(), - array: true, default: '{}', + array: true, + default: "{}", }) public fileIds: DriveFile["id"][]; @Index() - @Column('boolean', { + @Column("boolean", { default: false, - comment: 'Whether the post is sensitive.', + comment: "Whether the post is sensitive.", }) public isSensitive: boolean; @Index() - @Column('integer', { + @Column("integer", { default: 0, }) public likedCount: number; @Index() - @Column('varchar', { - length: 128, array: true, default: '{}', + @Column("varchar", { + length: 128, + array: true, + default: "{}", }) public tags: string[]; diff --git a/packages/backend/src/models/entities/hashtag.ts b/packages/backend/src/models/entities/hashtag.ts index 06fa004be4..7b3df1cc22 100644 --- a/packages/backend/src/models/entities/hashtag.ts +++ b/packages/backend/src/models/entities/hashtag.ts @@ -8,7 +8,7 @@ export class Hashtag { public id: string; @Index({ unique: true }) - @Column('varchar', { + @Column("varchar", { length: 128, }) public name: string; @@ -20,7 +20,7 @@ export class Hashtag { public mentionedUserIds: User["id"][]; @Index() - @Column('integer', { + @Column("integer", { default: 0, }) public mentionedUsersCount: number; @@ -32,7 +32,7 @@ export class Hashtag { public mentionedLocalUserIds: User["id"][]; @Index() - @Column('integer', { + @Column("integer", { default: 0, }) public mentionedLocalUsersCount: number; @@ -44,7 +44,7 @@ export class Hashtag { public mentionedRemoteUserIds: User["id"][]; @Index() - @Column('integer', { + @Column("integer", { default: 0, }) public mentionedRemoteUsersCount: number; @@ -56,7 +56,7 @@ export class Hashtag { public attachedUserIds: User["id"][]; @Index() - @Column('integer', { + @Column("integer", { default: 0, }) public attachedUsersCount: number; @@ -68,7 +68,7 @@ export class Hashtag { public attachedLocalUserIds: User["id"][]; @Index() - @Column('integer', { + @Column("integer", { default: 0, }) public attachedLocalUsersCount: number; @@ -80,7 +80,7 @@ export class Hashtag { public attachedRemoteUserIds: User["id"][]; @Index() - @Column('integer', { + @Column("integer", { default: 0, }) public attachedRemoteUsersCount: number; diff --git a/packages/backend/src/models/entities/instance.ts b/packages/backend/src/models/entities/instance.ts index 2b118455db..7e0b085831 100644 --- a/packages/backend/src/models/entities/instance.ts +++ b/packages/backend/src/models/entities/instance.ts @@ -10,8 +10,8 @@ export class Instance { * このインスタンスを捕捉した日時 */ @Index() - @Column('timestamp with time zone', { - comment: 'The caught date of the Instance.', + @Column("timestamp with time zone", { + comment: "The caught date of the Instance.", }) public caughtAt: Date; @@ -19,34 +19,34 @@ export class Instance { * ホスト */ @Index({ unique: true }) - @Column('varchar', { + @Column("varchar", { length: 128, - comment: 'The host of the Instance.', + comment: "The host of the Instance.", }) public host: string; /** * インスタンスのユーザー数 */ - @Column('integer', { + @Column("integer", { default: 0, - comment: 'The count of the users of the Instance.', + comment: "The count of the users of the Instance.", }) public usersCount: number; /** * インスタンスの投稿数 */ - @Column('integer', { + @Column("integer", { default: 0, - comment: 'The count of the notes of the Instance.', + comment: "The count of the notes of the Instance.", }) public notesCount: number; /** * このインスタンスのユーザーからフォローされている、自インスタンスのユーザーの数 */ - @Column('integer', { + @Column("integer", { default: 0, }) public followingCount: number; @@ -54,7 +54,7 @@ export class Instance { /** * このインスタンスのユーザーをフォローしている、自インスタンスのユーザーの数 */ - @Column('integer', { + @Column("integer", { default: 0, }) public followersCount: number; @@ -62,7 +62,7 @@ export class Instance { /** * 直近のリクエスト送信日時 */ - @Column('timestamp with time zone', { + @Column("timestamp with time zone", { nullable: true, }) public latestRequestSentAt: Date | null; @@ -70,7 +70,7 @@ export class Instance { /** * 直近のリクエスト送信時のHTTPステータスコード */ - @Column('integer', { + @Column("integer", { nullable: true, }) public latestStatus: number | null; @@ -78,7 +78,7 @@ export class Instance { /** * 直近のリクエスト受信日時 */ - @Column('timestamp with time zone', { + @Column("timestamp with time zone", { nullable: true, }) public latestRequestReceivedAt: Date | null; @@ -86,13 +86,13 @@ export class Instance { /** * このインスタンスと最後にやり取りした日時 */ - @Column('timestamp with time zone') + @Column("timestamp with time zone") public lastCommunicatedAt: Date; /** * このインスタンスと不通かどうか */ - @Column('boolean', { + @Column("boolean", { default: false, }) public isNotResponding: boolean; @@ -101,63 +101,72 @@ export class Instance { * このインスタンスへの配信を停止するか */ @Index() - @Column('boolean', { + @Column("boolean", { default: false, }) public isSuspended: boolean; - @Column('varchar', { - length: 64, nullable: true, - comment: 'The software of the Instance.', + @Column("varchar", { + length: 64, + nullable: true, + comment: "The software of the Instance.", }) public softwareName: string | null; - @Column('varchar', { - length: 64, nullable: true, + @Column("varchar", { + length: 64, + nullable: true, }) public softwareVersion: string | null; - @Column('boolean', { + @Column("boolean", { nullable: true, }) public openRegistrations: boolean | null; - @Column('varchar', { - length: 256, nullable: true, + @Column("varchar", { + length: 256, + nullable: true, }) public name: string | null; - @Column('varchar', { - length: 4096, nullable: true, + @Column("varchar", { + length: 4096, + nullable: true, }) public description: string | null; - @Column('varchar', { - length: 128, nullable: true, + @Column("varchar", { + length: 128, + nullable: true, }) public maintainerName: string | null; - @Column('varchar', { - length: 256, nullable: true, + @Column("varchar", { + length: 256, + nullable: true, }) public maintainerEmail: string | null; - @Column('varchar', { - length: 256, nullable: true, + @Column("varchar", { + length: 256, + nullable: true, }) public iconUrl: string | null; - @Column('varchar', { - length: 256, nullable: true, + @Column("varchar", { + length: 256, + nullable: true, }) public faviconUrl: string | null; - @Column('varchar', { - length: 64, nullable: true, + @Column("varchar", { + length: 64, + nullable: true, }) public themeColor: string | null; - @Column('timestamp with time zone', { + @Column("timestamp with time zone", { nullable: true, }) public infoUpdatedAt: Date | null; diff --git a/packages/backend/src/models/entities/messaging-message.ts b/packages/backend/src/models/entities/messaging-message.ts index 9cf197fa3b..d1da00eaef 100644 --- a/packages/backend/src/models/entities/messaging-message.ts +++ b/packages/backend/src/models/entities/messaging-message.ts @@ -17,68 +17,73 @@ export class MessagingMessage { public id: string; @Index() - @Column('timestamp with time zone', { - comment: 'The created date of the MessagingMessage.', + @Column("timestamp with time zone", { + comment: "The created date of the MessagingMessage.", }) public createdAt: Date; @Index() @Column({ ...id(), - comment: 'The sender user ID.', + comment: "The sender user ID.", }) public userId: User["id"]; - @ManyToOne(type => User, { - onDelete: 'CASCADE', + @ManyToOne((type) => User, { + onDelete: "CASCADE", }) @JoinColumn() public user: User | null; @Index() @Column({ - ...id(), nullable: true, - comment: 'The recipient user ID.', + ...id(), + nullable: true, + comment: "The recipient user ID.", }) public recipientId: User["id"] | null; - @ManyToOne(type => User, { - onDelete: 'CASCADE', + @ManyToOne((type) => User, { + onDelete: "CASCADE", }) @JoinColumn() public recipient: User | null; @Index() @Column({ - ...id(), nullable: true, - comment: 'The recipient group ID.', + ...id(), + nullable: true, + comment: "The recipient group ID.", }) public groupId: UserGroup["id"] | null; - @ManyToOne(type => UserGroup, { - onDelete: 'CASCADE', + @ManyToOne((type) => UserGroup, { + onDelete: "CASCADE", }) @JoinColumn() public group: UserGroup | null; - @Column('varchar', { - length: 4096, nullable: true, + @Column("varchar", { + length: 4096, + nullable: true, }) public text: string | null; - @Column('boolean', { + @Column("boolean", { default: false, }) public isRead: boolean; - @Column('varchar', { - length: 512, nullable: true, + @Column("varchar", { + length: 512, + nullable: true, }) public uri: string | null; @Column({ ...id(), - array: true, default: '{}', + array: true, + default: "{}", }) public reads: User["id"][]; @@ -88,8 +93,8 @@ export class MessagingMessage { }) public fileId: DriveFile["id"] | null; - @ManyToOne(type => DriveFile, { - onDelete: 'CASCADE', + @ManyToOne((type) => DriveFile, { + onDelete: "CASCADE", }) @JoinColumn() public file: DriveFile | null; diff --git a/packages/backend/src/models/entities/meta.ts b/packages/backend/src/models/entities/meta.ts index 26a7c9c193..1a1111e6e3 100644 --- a/packages/backend/src/models/entities/meta.ts +++ b/packages/backend/src/models/entities/meta.ts @@ -6,114 +6,144 @@ import type { Clip } from "./clip.js"; @Entity() export class Meta { @PrimaryColumn({ - type: 'varchar', + type: "varchar", length: 32, }) public id: string; - @Column('varchar', { - length: 128, nullable: true, + @Column("varchar", { + length: 128, + nullable: true, }) public name: string | null; - @Column('varchar', { - length: 1024, nullable: true, + @Column("varchar", { + length: 1024, + nullable: true, }) public description: string | null; /** * メンテナの名前 */ - @Column('varchar', { - length: 128, nullable: true, + @Column("varchar", { + length: 128, + nullable: true, }) public maintainerName: string | null; /** * メンテナの連絡先 */ - @Column('varchar', { - length: 128, nullable: true, + @Column("varchar", { + length: 128, + nullable: true, }) public maintainerEmail: string | null; - @Column('boolean', { + @Column("boolean", { default: false, }) public disableRegistration: boolean; - @Column('boolean', { + @Column("boolean", { default: false, }) public disableLocalTimeline: boolean; - @Column('boolean', { + @Column("boolean", { default: true, }) public disableRecommendedTimeline: boolean; - @Column('boolean', { + @Column("boolean", { default: false, }) public disableGlobalTimeline: boolean; - @Column('varchar', { - length: 256, default: '⭐', + @Column("varchar", { + length: 256, + default: "⭐", }) public defaultReaction: string; - @Column('varchar', { - length: 64, array: true, default: '{}', + @Column("varchar", { + length: 64, + array: true, + default: "{}", }) public langs: string[]; - @Column('varchar', { - length: 256, array: true, default: '{}', + @Column("varchar", { + length: 256, + array: true, + default: "{}", }) public pinnedUsers: string[]; - @Column('varchar', { - length: 256, array: true, default: '{}', + @Column("varchar", { + length: 256, + array: true, + default: "{}", }) public recommendedInstances: string[]; - @Column('varchar', { - length: 256, array: true, default: '{}', + @Column("varchar", { + length: 256, + array: true, + default: "{}", }) public customMOTD: string[]; - @Column('varchar', { - length: 256, array: true, default: '{}', + @Column("varchar", { + length: 256, + array: true, + default: "{}", }) public customSplashIcons: string[]; - @Column('varchar', { - length: 256, array: true, default: '{}', + @Column("varchar", { + length: 256, + array: true, + default: "{}", }) public hiddenTags: string[]; - @Column('varchar', { - length: 256, array: true, default: '{}', + @Column("varchar", { + length: 256, + array: true, + default: "{}", }) public blockedHosts: string[]; - @Column('boolean', { + @Column("varchar", { + length: 256, + array: true, + default: "{}", + }) + public silencedHosts: string[]; + + @Column("boolean", { default: false, }) public secureMode: boolean; - @Column('boolean', { + @Column("boolean", { default: false, }) public privateMode: boolean; - @Column('varchar', { - length: 256, array: true, default: '{}', + @Column("varchar", { + length: 256, + array: true, + default: "{}", }) public allowedHosts: string[]; - @Column('varchar', { - length: 512, array: true, default: '{/featured,/channels,/explore,/pages,/about-calckey}', + @Column("varchar", { + length: 512, + array: true, + default: "{/featured,/channels,/explore,/pages,/about-firefish}", }) public pinnedPages: string[]; @@ -123,52 +153,52 @@ export class Meta { }) public pinnedClipId: Clip["id"] | null; - @Column('varchar', { + @Column("varchar", { length: 512, nullable: true, }) public themeColor: string | null; - @Column('varchar', { + @Column("varchar", { length: 512, nullable: true, - default: '/assets/ai.png', + default: "/static-assets/badges/info.png", }) public mascotImageUrl: string | null; - @Column('varchar', { + @Column("varchar", { length: 512, nullable: true, }) public bannerUrl: string | null; - @Column('varchar', { + @Column("varchar", { length: 512, nullable: true, }) public backgroundImageUrl: string | null; - @Column('varchar', { + @Column("varchar", { length: 512, nullable: true, }) public logoImageUrl: string | null; - @Column('varchar', { + @Column("varchar", { length: 512, nullable: true, - default: 'https://xn--931a.moe/aiart/yubitun.png', + default: "/static-assets/badges/error.png", }) public errorImageUrl: string | null; - @Column('varchar', { + @Column("varchar", { length: 512, nullable: true, }) public iconUrl: string | null; - @Column('boolean', { - default: true, + @Column("boolean", { + default: false, }) public cacheRemoteFiles: boolean; @@ -178,60 +208,60 @@ export class Meta { }) public proxyAccountId: User["id"] | null; - @ManyToOne(type => User, { - onDelete: 'SET NULL', + @ManyToOne((type) => User, { + onDelete: "SET NULL", }) @JoinColumn() public proxyAccount: User | null; - @Column('boolean', { + @Column("boolean", { default: false, }) public emailRequiredForSignup: boolean; - @Column('boolean', { + @Column("boolean", { default: false, }) public enableHcaptcha: boolean; - @Column('varchar', { + @Column("varchar", { length: 64, nullable: true, }) public hcaptchaSiteKey: string | null; - @Column('varchar', { + @Column("varchar", { length: 64, nullable: true, }) public hcaptchaSecretKey: string | null; - @Column('boolean', { + @Column("boolean", { default: false, }) public enableRecaptcha: boolean; - @Column('varchar', { + @Column("varchar", { length: 64, nullable: true, }) public recaptchaSiteKey: string | null; - @Column('varchar', { + @Column("varchar", { length: 64, nullable: true, }) public recaptchaSecretKey: string | null; - @Column('enum', { - enum: ['none', 'all', 'local', 'remote'], - default: 'none', + @Column("enum", { + enum: ["none", "all", "local", "remote"], + default: "none", }) public sensitiveMediaDetection: "none" | "all" | "local" | "remote"; - @Column('enum', { - enum: ['medium', 'low', 'high', 'veryLow', 'veryHigh'], - default: 'medium', + @Column("enum", { + enum: ["medium", "low", "high", "veryLow", "veryHigh"], + default: "medium", }) public sensitiveMediaDetectionSensitivity: | "medium" @@ -240,263 +270,296 @@ export class Meta { | "veryLow" | "veryHigh"; - @Column('boolean', { + @Column("boolean", { default: false, }) public setSensitiveFlagAutomatically: boolean; - @Column('boolean', { + @Column("boolean", { default: false, }) public enableSensitiveMediaDetectionForVideos: boolean; - @Column('integer', { + @Column("integer", { default: 1024, - comment: 'Drive capacity of a local user (MB)', + comment: "Drive capacity of a local user (MB)", }) public localDriveCapacityMb: number; - @Column('integer', { + @Column("integer", { default: 32, - comment: 'Drive capacity of a remote user (MB)', + comment: "Drive capacity of a remote user (MB)", }) public remoteDriveCapacityMb: number; - @Column('varchar', { + @Column("varchar", { length: 128, nullable: true, }) public summalyProxy: string | null; - @Column('boolean', { + @Column("boolean", { default: false, }) public enableEmail: boolean; - @Column('varchar', { + @Column("varchar", { length: 128, nullable: true, }) public email: string | null; - @Column('boolean', { + @Column("boolean", { default: false, }) public smtpSecure: boolean; - @Column('varchar', { + @Column("varchar", { length: 128, nullable: true, }) public smtpHost: string | null; - @Column('integer', { + @Column("integer", { nullable: true, }) public smtpPort: number | null; - @Column('varchar', { - length: 128, + @Column("varchar", { + length: 1024, nullable: true, }) public smtpUser: string | null; - @Column('varchar', { - length: 128, + @Column("varchar", { + length: 1024, nullable: true, }) public smtpPass: string | null; - @Column('boolean', { + @Column("boolean", { default: false, }) public enableServiceWorker: boolean; - @Column('varchar', { + @Column("varchar", { length: 128, nullable: true, }) public swPublicKey: string | null; - @Column('varchar', { + @Column("varchar", { length: 128, nullable: true, }) public swPrivateKey: string | null; - @Column('boolean', { + @Column("boolean", { default: false, }) public enableTwitterIntegration: boolean; - @Column('varchar', { + @Column("varchar", { length: 128, nullable: true, }) public twitterConsumerKey: string | null; - @Column('varchar', { + @Column("varchar", { length: 128, nullable: true, }) public twitterConsumerSecret: string | null; - @Column('boolean', { + @Column("boolean", { default: false, }) public enableGithubIntegration: boolean; - @Column('varchar', { + @Column("varchar", { length: 128, nullable: true, }) public githubClientId: string | null; - @Column('varchar', { + @Column("varchar", { length: 128, nullable: true, }) public githubClientSecret: string | null; - @Column('boolean', { + @Column("boolean", { default: false, }) public enableDiscordIntegration: boolean; - @Column('varchar', { + @Column("varchar", { length: 128, nullable: true, }) public discordClientId: string | null; - @Column('varchar', { + @Column("varchar", { length: 128, nullable: true, }) public discordClientSecret: string | null; - @Column('varchar', { + @Column("varchar", { length: 128, nullable: true, }) public deeplAuthKey: string | null; - @Column('boolean', { + @Column("boolean", { default: false, }) public deeplIsPro: boolean; - @Column('varchar', { + @Column("varchar", { + length: 512, + nullable: true, + }) + public libreTranslateApiUrl: string | null; + + @Column("varchar", { + length: 128, + nullable: true, + }) + public libreTranslateApiKey: string | null; + + @Column("varchar", { length: 512, nullable: true, }) public ToSUrl: string | null; - @Column('varchar', { + @Column("varchar", { length: 512, - default: 'https://codeberg.org/calckey/calckey', + default: "https://gitlab.prometheus.systems/firefish/firefish", nullable: false, }) public repositoryUrl: string; - @Column('varchar', { + @Column("varchar", { length: 512, - default: 'https://codeberg.org/calckey/calckey/issues/new', + default: "https://gitlab.prometheus.systems/firefish/firefish/issues/new", nullable: true, }) public feedbackUrl: string | null; - @Column('varchar', { + @Column("varchar", { length: 8192, nullable: true, }) public defaultLightTheme: string | null; - @Column('varchar', { + @Column("varchar", { length: 8192, nullable: true, }) public defaultDarkTheme: string | null; - @Column('boolean', { + @Column("boolean", { default: false, }) public useObjectStorage: boolean; - @Column('varchar', { + @Column("varchar", { length: 512, nullable: true, }) public objectStorageBucket: string | null; - @Column('varchar', { + @Column("varchar", { length: 512, nullable: true, }) public objectStoragePrefix: string | null; - @Column('varchar', { + @Column("varchar", { length: 512, nullable: true, }) public objectStorageBaseUrl: string | null; - @Column('varchar', { + @Column("varchar", { length: 512, nullable: true, }) public objectStorageEndpoint: string | null; - @Column('varchar', { + @Column("varchar", { length: 512, nullable: true, }) public objectStorageRegion: string | null; - @Column('varchar', { + @Column("varchar", { length: 512, nullable: true, }) public objectStorageAccessKey: string | null; - @Column('varchar', { + @Column("varchar", { length: 512, nullable: true, }) public objectStorageSecretKey: string | null; - @Column('integer', { + @Column("integer", { nullable: true, }) public objectStoragePort: number | null; - @Column('boolean', { + @Column("boolean", { default: true, }) public objectStorageUseSSL: boolean; - @Column('boolean', { + @Column("boolean", { default: true, }) public objectStorageUseProxy: boolean; - @Column('boolean', { + @Column("boolean", { default: false, }) public objectStorageSetPublicRead: boolean; - @Column('boolean', { + @Column("boolean", { default: true, }) public objectStorageS3ForcePathStyle: boolean; - @Column('boolean', { + @Column("boolean", { default: false, }) public enableIpLogging: boolean; - @Column('boolean', { + @Column("boolean", { default: true, }) public enableActiveEmailValidation: boolean; + + @Column("jsonb", { + default: {}, + }) + public experimentalFeatures: Record; + + @Column("boolean", { + default: false, + }) + public enableServerMachineStats: boolean; + + @Column("boolean", { + default: true, + }) + public enableIdenticonGeneration: boolean; + + @Column("varchar", { + length: 256, + nullable: true, + }) + public donationLink: string | null; } diff --git a/packages/backend/src/models/entities/moderation-log.ts b/packages/backend/src/models/entities/moderation-log.ts index cc745e0d20..26bf1cdfa4 100644 --- a/packages/backend/src/models/entities/moderation-log.ts +++ b/packages/backend/src/models/entities/moderation-log.ts @@ -14,8 +14,8 @@ export class ModerationLog { @PrimaryColumn(id()) public id: string; - @Column('timestamp with time zone', { - comment: 'The created date of the ModerationLog.', + @Column("timestamp with time zone", { + comment: "The created date of the ModerationLog.", }) public createdAt: Date; @@ -23,17 +23,17 @@ export class ModerationLog { @Column(id()) public userId: User["id"]; - @ManyToOne(type => User, { - onDelete: 'CASCADE', + @ManyToOne((type) => User, { + onDelete: "CASCADE", }) @JoinColumn() public user: User | null; - @Column('varchar', { + @Column("varchar", { length: 128, }) public type: string; - @Column('jsonb') + @Column("jsonb") public info: Record; } diff --git a/packages/backend/src/models/entities/muted-note.ts b/packages/backend/src/models/entities/muted-note.ts index 11a6ae95d0..0ee245aea9 100644 --- a/packages/backend/src/models/entities/muted-note.ts +++ b/packages/backend/src/models/entities/muted-note.ts @@ -12,7 +12,7 @@ import { id } from "../id.js"; import { mutedNoteReasons } from "../../types.js"; @Entity() -@Index(['noteId', 'userId'], { unique: true }) +@Index(["noteId", "userId"], { unique: true }) export class MutedNote { @PrimaryColumn(id()) public id: string; @@ -20,12 +20,12 @@ export class MutedNote { @Index() @Column({ ...id(), - comment: 'The note ID.', + comment: "The note ID.", }) public noteId: Note["id"]; - @ManyToOne(type => Note, { - onDelete: 'CASCADE', + @ManyToOne((type) => Note, { + onDelete: "CASCADE", }) @JoinColumn() public note: Note | null; @@ -33,12 +33,12 @@ export class MutedNote { @Index() @Column({ ...id(), - comment: 'The user ID.', + comment: "The user ID.", }) public userId: User["id"]; - @ManyToOne(type => User, { - onDelete: 'CASCADE', + @ManyToOne((type) => User, { + onDelete: "CASCADE", }) @JoinColumn() public user: User | null; @@ -47,9 +47,9 @@ export class MutedNote { * ミュートされた理由。 */ @Index() - @Column('enum', { + @Column("enum", { enum: mutedNoteReasons, - comment: 'The reason of the MutedNote.', + comment: "The reason of the MutedNote.", }) public reason: typeof mutedNoteReasons[number]; } diff --git a/packages/backend/src/models/entities/muting.ts b/packages/backend/src/models/entities/muting.ts index 561bcfb95f..603619b468 100644 --- a/packages/backend/src/models/entities/muting.ts +++ b/packages/backend/src/models/entities/muting.ts @@ -10,19 +10,19 @@ import { User } from "./user.js"; import { id } from "../id.js"; @Entity() -@Index(['muterId', 'muteeId'], { unique: true }) +@Index(["muterId", "muteeId"], { unique: true }) export class Muting { @PrimaryColumn(id()) public id: string; @Index() - @Column('timestamp with time zone', { - comment: 'The created date of the Muting.', + @Column("timestamp with time zone", { + comment: "The created date of the Muting.", }) public createdAt: Date; @Index() - @Column('timestamp with time zone', { + @Column("timestamp with time zone", { nullable: true, }) public expiresAt: Date | null; @@ -30,12 +30,12 @@ export class Muting { @Index() @Column({ ...id(), - comment: 'The mutee user ID.', + comment: "The mutee user ID.", }) public muteeId: User["id"]; - @ManyToOne(type => User, { - onDelete: 'CASCADE', + @ManyToOne((type) => User, { + onDelete: "CASCADE", }) @JoinColumn() public mutee: User | null; @@ -43,12 +43,12 @@ export class Muting { @Index() @Column({ ...id(), - comment: 'The muter user ID.', + comment: "The muter user ID.", }) public muterId: User["id"]; - @ManyToOne(type => User, { - onDelete: 'CASCADE', + @ManyToOne((type) => User, { + onDelete: "CASCADE", }) @JoinColumn() public muter: User | null; diff --git a/packages/backend/src/models/entities/note-edit.ts b/packages/backend/src/models/entities/note-edit.ts new file mode 100644 index 0000000000..8761e2b153 --- /dev/null +++ b/packages/backend/src/models/entities/note-edit.ts @@ -0,0 +1,53 @@ +import { + Entity, + JoinColumn, + Column, + ManyToOne, + PrimaryColumn, + Index, +} from "typeorm"; +import { Note } from "./note.js"; +import { id } from "../id.js"; +import { DriveFile } from "./drive-file.js"; + +@Entity() +export class NoteEdit { + @PrimaryColumn(id()) + public id: string; + + @Index() + @Column({ + ...id(), + comment: "The ID of note.", + }) + public noteId: Note["id"]; + + @ManyToOne((type) => Note, { + onDelete: "CASCADE", + }) + @JoinColumn() + public note: Note | null; + + @Column("text", { + nullable: true, + }) + public text: string | null; + + @Column("varchar", { + length: 512, + nullable: true, + }) + public cw: string | null; + + @Column({ + ...id(), + array: true, + default: "{}", + }) + public fileIds: DriveFile["id"][]; + + @Column("timestamp with time zone", { + comment: "The updated date of the Note.", + }) + public updatedAt: Date; +} diff --git a/packages/backend/src/models/entities/note-favorite.ts b/packages/backend/src/models/entities/note-favorite.ts index ab12d8b1b3..19641ecf45 100644 --- a/packages/backend/src/models/entities/note-favorite.ts +++ b/packages/backend/src/models/entities/note-favorite.ts @@ -11,13 +11,13 @@ import { User } from "./user.js"; import { id } from "../id.js"; @Entity() -@Index(['userId', 'noteId'], { unique: true }) +@Index(["userId", "noteId"], { unique: true }) export class NoteFavorite { @PrimaryColumn(id()) public id: string; - @Column('timestamp with time zone', { - comment: 'The created date of the NoteFavorite.', + @Column("timestamp with time zone", { + comment: "The created date of the NoteFavorite.", }) public createdAt: Date; @@ -25,8 +25,8 @@ export class NoteFavorite { @Column(id()) public userId: User["id"]; - @ManyToOne(type => User, { - onDelete: 'CASCADE', + @ManyToOne((type) => User, { + onDelete: "CASCADE", }) @JoinColumn() public user: User | null; @@ -34,8 +34,8 @@ export class NoteFavorite { @Column(id()) public noteId: Note["id"]; - @ManyToOne(type => Note, { - onDelete: 'CASCADE', + @ManyToOne((type) => Note, { + onDelete: "CASCADE", }) @JoinColumn() public note: Note | null; diff --git a/packages/backend/src/models/entities/note-reaction.ts b/packages/backend/src/models/entities/note-reaction.ts index 0e51c33b16..5e2a8d3e89 100644 --- a/packages/backend/src/models/entities/note-reaction.ts +++ b/packages/backend/src/models/entities/note-reaction.ts @@ -11,14 +11,14 @@ import { Note } from "./note.js"; import { id } from "../id.js"; @Entity() -@Index(['userId', 'noteId'], { unique: true }) +@Index(["userId", "noteId"], { unique: true }) export class NoteReaction { @PrimaryColumn(id()) public id: string; @Index() - @Column('timestamp with time zone', { - comment: 'The created date of the NoteReaction.', + @Column("timestamp with time zone", { + comment: "The created date of the NoteReaction.", }) public createdAt: Date; @@ -26,8 +26,8 @@ export class NoteReaction { @Column(id()) public userId: User["id"]; - @ManyToOne(type => User, { - onDelete: 'CASCADE', + @ManyToOne((type) => User, { + onDelete: "CASCADE", }) @JoinColumn() public user?: User | null; @@ -36,15 +36,15 @@ export class NoteReaction { @Column(id()) public noteId: Note["id"]; - @ManyToOne(type => Note, { - onDelete: 'CASCADE', + @ManyToOne((type) => Note, { + onDelete: "CASCADE", }) @JoinColumn() public note?: Note | null; // TODO: 対象noteのuserIdを非正規化したい(「受け取ったリアクション一覧」のようなものを(JOIN無しで)実装したいため) - @Column('varchar', { + @Column("varchar", { length: 260, }) public reaction: string; diff --git a/packages/backend/src/models/entities/note-thread-muting.ts b/packages/backend/src/models/entities/note-thread-muting.ts index 2985b195f0..704b328503 100644 --- a/packages/backend/src/models/entities/note-thread-muting.ts +++ b/packages/backend/src/models/entities/note-thread-muting.ts @@ -11,13 +11,12 @@ import { Note } from "./note.js"; import { id } from "../id.js"; @Entity() -@Index(['userId', 'threadId'], { unique: true }) +@Index(["userId", "threadId"], { unique: true }) export class NoteThreadMuting { @PrimaryColumn(id()) public id: string; - @Column('timestamp with time zone', { - }) + @Column("timestamp with time zone", {}) public createdAt: Date; @Index() @@ -26,14 +25,14 @@ export class NoteThreadMuting { }) public userId: User["id"]; - @ManyToOne(type => User, { - onDelete: 'CASCADE', + @ManyToOne((type) => User, { + onDelete: "CASCADE", }) @JoinColumn() public user: User | null; @Index() - @Column('varchar', { + @Column("varchar", { length: 256, }) public threadId: string; diff --git a/packages/backend/src/models/entities/note-unread.ts b/packages/backend/src/models/entities/note-unread.ts index d5bba72212..95695cbc8e 100644 --- a/packages/backend/src/models/entities/note-unread.ts +++ b/packages/backend/src/models/entities/note-unread.ts @@ -12,7 +12,7 @@ import { id } from "../id.js"; import type { Channel } from "./channel.js"; @Entity() -@Index(['userId', 'noteId'], { unique: true }) +@Index(["userId", "noteId"], { unique: true }) export class NoteUnread { @PrimaryColumn(id()) public id: string; @@ -21,8 +21,8 @@ export class NoteUnread { @Column(id()) public userId: User["id"]; - @ManyToOne(type => User, { - onDelete: 'CASCADE', + @ManyToOne((type) => User, { + onDelete: "CASCADE", }) @JoinColumn() public user: User | null; @@ -31,8 +31,8 @@ export class NoteUnread { @Column(id()) public noteId: Note["id"]; - @ManyToOne(type => Note, { - onDelete: 'CASCADE', + @ManyToOne((type) => Note, { + onDelete: "CASCADE", }) @JoinColumn() public note: Note | null; @@ -41,21 +41,21 @@ export class NoteUnread { * メンションか否か */ @Index() - @Column('boolean') + @Column("boolean") public isMentioned: boolean; /** * ダイレクト投稿か否か */ @Index() - @Column('boolean') + @Column("boolean") public isSpecified: boolean; //#region Denormalized fields @Index() @Column({ ...id(), - comment: '[Denormalized]', + comment: "[Denormalized]", }) public noteUserId: User["id"]; @@ -63,7 +63,7 @@ export class NoteUnread { @Column({ ...id(), nullable: true, - comment: '[Denormalized]', + comment: "[Denormalized]", }) public noteChannelId: Channel["id"] | null; //#endregion diff --git a/packages/backend/src/models/entities/note-watching.ts b/packages/backend/src/models/entities/note-watching.ts index 7ac3e8e297..724b084af2 100644 --- a/packages/backend/src/models/entities/note-watching.ts +++ b/packages/backend/src/models/entities/note-watching.ts @@ -11,26 +11,26 @@ import { Note } from "./note.js"; import { id } from "../id.js"; @Entity() -@Index(['userId', 'noteId'], { unique: true }) +@Index(["userId", "noteId"], { unique: true }) export class NoteWatching { @PrimaryColumn(id()) public id: string; @Index() - @Column('timestamp with time zone', { - comment: 'The created date of the NoteWatching.', + @Column("timestamp with time zone", { + comment: "The created date of the NoteWatching.", }) public createdAt: Date; @Index() @Column({ ...id(), - comment: 'The watcher ID.', + comment: "The watcher ID.", }) public userId: User["id"]; - @ManyToOne(type => User, { - onDelete: 'CASCADE', + @ManyToOne((type) => User, { + onDelete: "CASCADE", }) @JoinColumn() public user: User | null; @@ -38,12 +38,12 @@ export class NoteWatching { @Index() @Column({ ...id(), - comment: 'The target Note ID.', + comment: "The target Note ID.", }) public noteId: Note["id"]; - @ManyToOne(type => Note, { - onDelete: 'CASCADE', + @ManyToOne((type) => Note, { + onDelete: "CASCADE", }) @JoinColumn() public note: Note | null; @@ -52,7 +52,7 @@ export class NoteWatching { @Index() @Column({ ...id(), - comment: '[Denormalized]', + comment: "[Denormalized]", }) public noteUserId: Note["userId"]; //#endregion diff --git a/packages/backend/src/models/entities/note.ts b/packages/backend/src/models/entities/note.ts index fd6b170c0e..edcfdb635e 100644 --- a/packages/backend/src/models/entities/note.ts +++ b/packages/backend/src/models/entities/note.ts @@ -13,16 +13,16 @@ import { noteVisibilities } from "../../types.js"; import { Channel } from "./channel.js"; @Entity() -@Index('IDX_NOTE_TAGS', { synchronize: false }) -@Index('IDX_NOTE_MENTIONS', { synchronize: false }) -@Index('IDX_NOTE_VISIBLE_USER_IDS', { synchronize: false }) +@Index("IDX_NOTE_TAGS", { synchronize: false }) +@Index("IDX_NOTE_MENTIONS", { synchronize: false }) +@Index("IDX_NOTE_VISIBLE_USER_IDS", { synchronize: false }) export class Note { @PrimaryColumn(id()) public id: string; @Index() - @Column('timestamp with time zone', { - comment: 'The created date of the Note.', + @Column("timestamp with time zone", { + comment: "The created date of the Note.", }) public createdAt: Date; @@ -30,12 +30,12 @@ export class Note { @Column({ ...id(), nullable: true, - comment: 'The ID of reply target.', + comment: "The ID of reply target.", }) public replyId: Note["id"] | null; - @ManyToOne(type => Note, { - onDelete: 'CASCADE', + @ManyToOne((type) => Note, { + onDelete: "CASCADE", }) @JoinColumn() public reply: Note | null; @@ -44,66 +44,69 @@ export class Note { @Column({ ...id(), nullable: true, - comment: 'The ID of renote target.', + comment: "The ID of renote target.", }) public renoteId: Note["id"] | null; - @ManyToOne(type => Note, { - onDelete: 'CASCADE', + @ManyToOne((type) => Note, { + onDelete: "CASCADE", }) @JoinColumn() public renote: Note | null; @Index() - @Column('varchar', { - length: 256, nullable: true, + @Column("varchar", { + length: 256, + nullable: true, }) public threadId: string | null; - @Column('text', { + @Column("text", { nullable: true, }) public text: string | null; - @Column('varchar', { - length: 256, nullable: true, + @Column("varchar", { + length: 256, + nullable: true, }) public name: string | null; - @Column('varchar', { - length: 512, nullable: true, + @Column("varchar", { + length: 512, + nullable: true, }) public cw: string | null; @Index() @Column({ ...id(), - comment: 'The ID of author.', + comment: "The ID of author.", }) public userId: User["id"]; - @ManyToOne(type => User, { - onDelete: 'CASCADE', + @ManyToOne((type) => User, { + onDelete: "CASCADE", }) @JoinColumn() public user: User | null; - @Column('boolean', { + @Column("boolean", { default: false, }) public localOnly: boolean; - @Column('smallint', { + @Column("smallint", { default: 0, }) public renoteCount: number; - @Column('smallint', { + @Column("smallint", { default: 0, }) public repliesCount: number; - @Column('jsonb', { + @Column("jsonb", { default: {}, }) public reactions: Record; @@ -111,74 +114,88 @@ export class Note { /** * public ... 公開 * home ... ホームタイムライン(ユーザーページのタイムライン含む)のみに流す + * hidden ... only visible on profile (doesnt federate, like local only, but can be fetched via AP like home) <- for now only used for post imports * followers ... フォロワーのみ * specified ... visibleUserIds で指定したユーザーのみ */ - @Column('enum', { enum: noteVisibilities }) + @Column("enum", { enum: noteVisibilities }) public visibility: typeof noteVisibilities[number]; @Index({ unique: true }) - @Column('varchar', { - length: 512, nullable: true, - comment: 'The URI of a note. it will be null when the note is local.', + @Column("varchar", { + length: 512, + nullable: true, + comment: "The URI of a note. it will be null when the note is local.", }) public uri: string | null; - @Column('varchar', { - length: 512, nullable: true, - comment: 'The human readable url of a note. it will be null when the note is local.', + @Column("varchar", { + length: 512, + nullable: true, + comment: + "The human readable url of a note. it will be null when the note is local.", }) public url: string | null; - @Column('integer', { - default: 0, select: false, + @Column("integer", { + default: 0, + select: false, }) public score: number; @Index() @Column({ ...id(), - array: true, default: '{}', + array: true, + default: "{}", }) public fileIds: DriveFile["id"][]; @Index() - @Column('varchar', { - length: 256, array: true, default: '{}', + @Column("varchar", { + length: 256, + array: true, + default: "{}", }) public attachedFileTypes: string[]; @Index() @Column({ ...id(), - array: true, default: '{}', + array: true, + default: "{}", }) public visibleUserIds: User["id"][]; @Index() @Column({ ...id(), - array: true, default: '{}', + array: true, + default: "{}", }) public mentions: User["id"][]; - @Column('text', { - default: '[]', + @Column("text", { + default: "[]", }) public mentionedRemoteUsers: string; - @Column('varchar', { - length: 128, array: true, default: '{}', + @Column("varchar", { + length: 128, + array: true, + default: "{}", }) public emojis: string[]; @Index() - @Column('varchar', { - length: 128, array: true, default: '{}', + @Column("varchar", { + length: 128, + array: true, + default: "{}", }) public tags: string[]; - @Column('boolean', { + @Column("boolean", { default: false, }) public hasPoll: boolean; @@ -187,49 +204,58 @@ export class Note { @Column({ ...id(), nullable: true, - comment: 'The ID of source channel.', + comment: "The ID of source channel.", }) public channelId: Channel["id"] | null; - @ManyToOne(type => Channel, { - onDelete: 'CASCADE', + @ManyToOne((type) => Channel, { + onDelete: "CASCADE", }) @JoinColumn() public channel: Channel | null; //#region Denormalized fields @Index() - @Column('varchar', { - length: 128, nullable: true, - comment: '[Denormalized]', + @Column("varchar", { + length: 128, + nullable: true, + comment: "[Denormalized]", }) public userHost: string | null; @Column({ ...id(), nullable: true, - comment: '[Denormalized]', + comment: "[Denormalized]", }) public replyUserId: User["id"] | null; - @Column('varchar', { - length: 128, nullable: true, - comment: '[Denormalized]', + @Column("varchar", { + length: 128, + nullable: true, + comment: "[Denormalized]", }) public replyUserHost: string | null; @Column({ ...id(), nullable: true, - comment: '[Denormalized]', + comment: "[Denormalized]", }) public renoteUserId: User["id"] | null; - @Column('varchar', { - length: 128, nullable: true, - comment: '[Denormalized]', + @Column("varchar", { + length: 128, + nullable: true, + comment: "[Denormalized]", }) public renoteUserHost: string | null; + + @Column("timestamp with time zone", { + nullable: true, + comment: "The updated date of the Note.", + }) + public updatedAt: Date; //#endregion constructor(data: Partial) { diff --git a/packages/backend/src/models/entities/notification.ts b/packages/backend/src/models/entities/notification.ts index 2c55e988f1..da23f7d3ee 100644 --- a/packages/backend/src/models/entities/notification.ts +++ b/packages/backend/src/models/entities/notification.ts @@ -20,8 +20,8 @@ export class Notification { public id: string; @Index() - @Column('timestamp with time zone', { - comment: 'The created date of the Notification.', + @Column("timestamp with time zone", { + comment: "The created date of the Notification.", }) public createdAt: Date; @@ -31,12 +31,12 @@ export class Notification { @Index() @Column({ ...id(), - comment: 'The ID of recipient user of the Notification.', + comment: "The ID of recipient user of the Notification.", }) public notifieeId: User["id"]; - @ManyToOne(type => User, { - onDelete: 'CASCADE', + @ManyToOne((type) => User, { + onDelete: "CASCADE", }) @JoinColumn() public notifiee: User | null; @@ -48,12 +48,12 @@ export class Notification { @Column({ ...id(), nullable: true, - comment: 'The ID of sender user of the Notification.', + comment: "The ID of sender user of the Notification.", }) public notifierId: User["id"] | null; - @ManyToOne(type => User, { - onDelete: 'CASCADE', + @ManyToOne((type) => User, { + onDelete: "CASCADE", }) @JoinColumn() public notifier: User | null; @@ -74,9 +74,9 @@ export class Notification { * app - App notifications. */ @Index() - @Column('enum', { + @Column("enum", { enum: notificationTypes, - comment: 'The type of the Notification.', + comment: "The type of the Notification.", }) public type: typeof notificationTypes[number]; @@ -84,9 +84,9 @@ export class Notification { * Whether the notification was read. */ @Index() - @Column('boolean', { + @Column("boolean", { default: false, - comment: 'Whether the notification was read.', + comment: "Whether the notification was read.", }) public isRead: boolean; @@ -96,8 +96,8 @@ export class Notification { }) public noteId: Note["id"] | null; - @ManyToOne(type => Note, { - onDelete: 'CASCADE', + @ManyToOne((type) => Note, { + onDelete: "CASCADE", }) @JoinColumn() public note: Note | null; @@ -108,8 +108,8 @@ export class Notification { }) public followRequestId: FollowRequest["id"] | null; - @ManyToOne(type => FollowRequest, { - onDelete: 'CASCADE', + @ManyToOne((type) => FollowRequest, { + onDelete: "CASCADE", }) @JoinColumn() public followRequest: FollowRequest | null; @@ -120,18 +120,19 @@ export class Notification { }) public userGroupInvitationId: UserGroupInvitation["id"] | null; - @ManyToOne(type => UserGroupInvitation, { - onDelete: 'CASCADE', + @ManyToOne((type) => UserGroupInvitation, { + onDelete: "CASCADE", }) @JoinColumn() public userGroupInvitation: UserGroupInvitation | null; - @Column('varchar', { - length: 128, nullable: true, + @Column("varchar", { + length: 128, + nullable: true, }) public reaction: string | null; - @Column('integer', { + @Column("integer", { nullable: true, }) public choice: number | null; @@ -139,8 +140,9 @@ export class Notification { /** * App notification body */ - @Column('varchar', { - length: 2048, nullable: true, + @Column("varchar", { + length: 2048, + nullable: true, }) public customBody: string | null; @@ -148,8 +150,9 @@ export class Notification { * App notification header * (If omitted, it is expected to be displayed with the app name) */ - @Column('varchar', { - length: 256, nullable: true, + @Column("varchar", { + length: 256, + nullable: true, }) public customHeader: string | null; @@ -157,8 +160,9 @@ export class Notification { * App notification icon (URL) * (If omitted, it is expected to be displayed as an app icon) */ - @Column('varchar', { - length: 1024, nullable: true, + @Column("varchar", { + length: 1024, + nullable: true, }) public customIcon: string | null; @@ -172,8 +176,8 @@ export class Notification { }) public appAccessTokenId: AccessToken["id"] | null; - @ManyToOne(type => AccessToken, { - onDelete: 'CASCADE', + @ManyToOne((type) => AccessToken, { + onDelete: "CASCADE", }) @JoinColumn() public appAccessToken: AccessToken | null; diff --git a/packages/backend/src/models/entities/page-like.ts b/packages/backend/src/models/entities/page-like.ts index 75f4dc49b0..6304e0b24c 100644 --- a/packages/backend/src/models/entities/page-like.ts +++ b/packages/backend/src/models/entities/page-like.ts @@ -11,20 +11,20 @@ import { id } from "../id.js"; import { Page } from "./page.js"; @Entity() -@Index(['userId', 'pageId'], { unique: true }) +@Index(["userId", "pageId"], { unique: true }) export class PageLike { @PrimaryColumn(id()) public id: string; - @Column('timestamp with time zone') + @Column("timestamp with time zone") public createdAt: Date; @Index() @Column(id()) public userId: User["id"]; - @ManyToOne(type => User, { - onDelete: 'CASCADE', + @ManyToOne((type) => User, { + onDelete: "CASCADE", }) @JoinColumn() public user: User | null; @@ -32,8 +32,8 @@ export class PageLike { @Column(id()) public pageId: Page["id"]; - @ManyToOne(type => Page, { - onDelete: 'CASCADE', + @ManyToOne((type) => Page, { + onDelete: "CASCADE", }) @JoinColumn() public page: Page | null; diff --git a/packages/backend/src/models/entities/page.ts b/packages/backend/src/models/entities/page.ts index 5fe9f52088..d0733c8ce4 100644 --- a/packages/backend/src/models/entities/page.ts +++ b/packages/backend/src/models/entities/page.ts @@ -11,51 +11,52 @@ import { id } from "../id.js"; import { DriveFile } from "./drive-file.js"; @Entity() -@Index(['userId', 'name'], { unique: true }) +@Index(["userId", "name"], { unique: true }) export class Page { @PrimaryColumn(id()) public id: string; @Index() - @Column('timestamp with time zone', { - comment: 'The created date of the Page.', + @Column("timestamp with time zone", { + comment: "The created date of the Page.", }) public createdAt: Date; @Index() - @Column('timestamp with time zone', { - comment: 'The updated date of the Page.', + @Column("timestamp with time zone", { + comment: "The updated date of the Page.", }) public updatedAt: Date; - @Column('varchar', { + @Column("varchar", { length: 256, }) public title: string; @Index() - @Column('varchar', { + @Column("varchar", { length: 256, }) public name: string; - @Column('varchar', { - length: 256, nullable: true, + @Column("varchar", { + length: 256, + nullable: true, }) public summary: string | null; - @Column('boolean') + @Column("boolean") public alignCenter: boolean; - @Column('boolean') + @Column("boolean") public isPublic: boolean; - @Column('boolean', { + @Column("boolean", { default: false, }) public hideTitleWhenPinned: boolean; - @Column('varchar', { + @Column("varchar", { length: 32, }) public font: string; @@ -63,12 +64,12 @@ export class Page { @Index() @Column({ ...id(), - comment: 'The ID of author.', + comment: "The ID of author.", }) public userId: User["id"]; - @ManyToOne(type => User, { - onDelete: 'CASCADE', + @ManyToOne((type) => User, { + onDelete: "CASCADE", }) @JoinColumn() public user: User | null; @@ -79,25 +80,25 @@ export class Page { }) public eyeCatchingImageId: DriveFile["id"] | null; - @ManyToOne(type => DriveFile, { - onDelete: 'CASCADE', + @ManyToOne((type) => DriveFile, { + onDelete: "CASCADE", }) @JoinColumn() public eyeCatchingImage: DriveFile | null; - @Column('jsonb', { + @Column("jsonb", { default: [], }) public content: Record[]; - @Column('jsonb', { + @Column("jsonb", { default: [], }) public variables: Record[]; - @Column('varchar', { + @Column("varchar", { length: 16384, - default: '', + default: "", }) public script: string; @@ -106,17 +107,18 @@ export class Page { * followers ... フォロワーのみ * specified ... visibleUserIds で指定したユーザーのみ */ - @Column('enum', { enum: ['public', 'followers', 'specified'] }) + @Column("enum", { enum: ["public", "followers", "specified"] }) public visibility: "public" | "followers" | "specified"; @Index() @Column({ ...id(), - array: true, default: '{}', + array: true, + default: "{}", }) public visibleUserIds: User["id"][]; - @Column('integer', { + @Column("integer", { default: 0, }) public likedCount: number; diff --git a/packages/backend/src/models/entities/password-reset-request.ts b/packages/backend/src/models/entities/password-reset-request.ts index 4c681d4f70..ab0bccbbef 100644 --- a/packages/backend/src/models/entities/password-reset-request.ts +++ b/packages/backend/src/models/entities/password-reset-request.ts @@ -14,11 +14,11 @@ export class PasswordResetRequest { @PrimaryColumn(id()) public id: string; - @Column('timestamp with time zone') + @Column("timestamp with time zone") public createdAt: Date; @Index({ unique: true }) - @Column('varchar', { + @Column("varchar", { length: 256, }) public token: string; @@ -29,8 +29,8 @@ export class PasswordResetRequest { }) public userId: User["id"]; - @ManyToOne(type => User, { - onDelete: 'CASCADE', + @ManyToOne((type) => User, { + onDelete: "CASCADE", }) @JoinColumn() public user: User | null; diff --git a/packages/backend/src/models/entities/poll-vote.ts b/packages/backend/src/models/entities/poll-vote.ts index 0649951cf4..d59a720c37 100644 --- a/packages/backend/src/models/entities/poll-vote.ts +++ b/packages/backend/src/models/entities/poll-vote.ts @@ -11,14 +11,14 @@ import { Note } from "./note.js"; import { id } from "../id.js"; @Entity() -@Index(['userId', 'noteId', 'choice'], { unique: true }) +@Index(["userId", "noteId", "choice"], { unique: true }) export class PollVote { @PrimaryColumn(id()) public id: string; @Index() - @Column('timestamp with time zone', { - comment: 'The created date of the PollVote.', + @Column("timestamp with time zone", { + comment: "The created date of the PollVote.", }) public createdAt: Date; @@ -26,8 +26,8 @@ export class PollVote { @Column(id()) public userId: User["id"]; - @ManyToOne(type => User, { - onDelete: 'CASCADE', + @ManyToOne((type) => User, { + onDelete: "CASCADE", }) @JoinColumn() public user: User | null; @@ -36,12 +36,12 @@ export class PollVote { @Column(id()) public noteId: Note["id"]; - @ManyToOne(type => Note, { - onDelete: 'CASCADE', + @ManyToOne((type) => Note, { + onDelete: "CASCADE", }) @JoinColumn() public note: Note | null; - @Column('integer') + @Column("integer") public choice: number; } diff --git a/packages/backend/src/models/entities/poll.ts b/packages/backend/src/models/entities/poll.ts index 28a70b3c7b..405cca2225 100644 --- a/packages/backend/src/models/entities/poll.ts +++ b/packages/backend/src/models/entities/poll.ts @@ -16,48 +16,51 @@ export class Poll { @PrimaryColumn(id()) public noteId: Note["id"]; - @OneToOne(type => Note, { - onDelete: 'CASCADE', + @OneToOne((type) => Note, { + onDelete: "CASCADE", }) @JoinColumn() public note: Note | null; - @Column('timestamp with time zone', { + @Column("timestamp with time zone", { nullable: true, }) public expiresAt: Date | null; - @Column('boolean') + @Column("boolean") public multiple: boolean; - @Column('varchar', { - length: 256, array: true, default: '{}', + @Column("varchar", { + length: 256, + array: true, + default: "{}", }) public choices: string[]; - @Column('integer', { + @Column("integer", { array: true, }) public votes: number[]; //#region Denormalized fields - @Column('enum', { + @Column("enum", { enum: noteVisibilities, - comment: '[Denormalized]', + comment: "[Denormalized]", }) public noteVisibility: typeof noteVisibilities[number]; @Index() @Column({ ...id(), - comment: '[Denormalized]', + comment: "[Denormalized]", }) public userId: User["id"]; @Index() - @Column('varchar', { - length: 128, nullable: true, - comment: '[Denormalized]', + @Column("varchar", { + length: 128, + nullable: true, + comment: "[Denormalized]", }) public userHost: string | null; //#endregion diff --git a/packages/backend/src/models/entities/promo-note.ts b/packages/backend/src/models/entities/promo-note.ts index 4daacd246a..caa64927e9 100644 --- a/packages/backend/src/models/entities/promo-note.ts +++ b/packages/backend/src/models/entities/promo-note.ts @@ -15,20 +15,20 @@ export class PromoNote { @PrimaryColumn(id()) public noteId: Note["id"]; - @OneToOne(type => Note, { - onDelete: 'CASCADE', + @OneToOne((type) => Note, { + onDelete: "CASCADE", }) @JoinColumn() public note: Note | null; - @Column('timestamp with time zone') + @Column("timestamp with time zone") public expiresAt: Date; //#region Denormalized fields @Index() @Column({ ...id(), - comment: '[Denormalized]', + comment: "[Denormalized]", }) public userId: User["id"]; //#endregion diff --git a/packages/backend/src/models/entities/promo-read.ts b/packages/backend/src/models/entities/promo-read.ts index 5938bfde9d..b31877dc34 100644 --- a/packages/backend/src/models/entities/promo-read.ts +++ b/packages/backend/src/models/entities/promo-read.ts @@ -11,13 +11,13 @@ import { User } from "./user.js"; import { id } from "../id.js"; @Entity() -@Index(['userId', 'noteId'], { unique: true }) +@Index(["userId", "noteId"], { unique: true }) export class PromoRead { @PrimaryColumn(id()) public id: string; - @Column('timestamp with time zone', { - comment: 'The created date of the PromoRead.', + @Column("timestamp with time zone", { + comment: "The created date of the PromoRead.", }) public createdAt: Date; @@ -25,8 +25,8 @@ export class PromoRead { @Column(id()) public userId: User["id"]; - @ManyToOne(type => User, { - onDelete: 'CASCADE', + @ManyToOne((type) => User, { + onDelete: "CASCADE", }) @JoinColumn() public user: User | null; @@ -34,8 +34,8 @@ export class PromoRead { @Column(id()) public noteId: Note["id"]; - @ManyToOne(type => Note, { - onDelete: 'CASCADE', + @ManyToOne((type) => Note, { + onDelete: "CASCADE", }) @JoinColumn() public note: Note | null; diff --git a/packages/backend/src/models/entities/registration-tickets.ts b/packages/backend/src/models/entities/registration-tickets.ts index af785fbc0d..549f05d07a 100644 --- a/packages/backend/src/models/entities/registration-tickets.ts +++ b/packages/backend/src/models/entities/registration-tickets.ts @@ -6,11 +6,11 @@ export class RegistrationTicket { @PrimaryColumn(id()) public id: string; - @Column('timestamp with time zone') + @Column("timestamp with time zone") public createdAt: Date; @Index({ unique: true }) - @Column('varchar', { + @Column("varchar", { length: 64, }) public code: string; diff --git a/packages/backend/src/models/entities/registry-item.ts b/packages/backend/src/models/entities/registry-item.ts index 655573883a..d044222e6e 100644 --- a/packages/backend/src/models/entities/registry-item.ts +++ b/packages/backend/src/models/entities/registry-item.ts @@ -15,51 +15,55 @@ export class RegistryItem { @PrimaryColumn(id()) public id: string; - @Column('timestamp with time zone', { - comment: 'The created date of the RegistryItem.', + @Column("timestamp with time zone", { + comment: "The created date of the RegistryItem.", }) public createdAt: Date; - @Column('timestamp with time zone', { - comment: 'The updated date of the RegistryItem.', + @Column("timestamp with time zone", { + comment: "The updated date of the RegistryItem.", }) public updatedAt: Date; @Index() @Column({ ...id(), - comment: 'The owner ID.', + comment: "The owner ID.", }) public userId: User["id"]; - @ManyToOne(type => User, { - onDelete: 'CASCADE', + @ManyToOne((type) => User, { + onDelete: "CASCADE", }) @JoinColumn() public user: User | null; - @Column('varchar', { + @Column("varchar", { length: 1024, - comment: 'The key of the RegistryItem.', + comment: "The key of the RegistryItem.", }) public key: string; - @Column('jsonb', { - default: {}, nullable: true, - comment: 'The value of the RegistryItem.', + @Column("jsonb", { + default: {}, + nullable: true, + comment: "The value of the RegistryItem.", }) public value: any | null; @Index() - @Column('varchar', { - length: 1024, array: true, default: '{}', + @Column("varchar", { + length: 1024, + array: true, + default: "{}", }) public scope: string[]; // サードパーティアプリに開放するときのためのカラム @Index() - @Column('varchar', { - length: 512, nullable: true, + @Column("varchar", { + length: 512, + nullable: true, }) public domain: string | null; } diff --git a/packages/backend/src/models/entities/relay.ts b/packages/backend/src/models/entities/relay.ts index 82c0779ff8..c7509dcf41 100644 --- a/packages/backend/src/models/entities/relay.ts +++ b/packages/backend/src/models/entities/relay.ts @@ -7,13 +7,14 @@ export class Relay { public id: string; @Index({ unique: true }) - @Column('varchar', { - length: 512, nullable: false, + @Column("varchar", { + length: 512, + nullable: false, }) public inbox: string; - @Column('enum', { - enum: ['requesting', 'accepted', 'rejected'], + @Column("enum", { + enum: ["requesting", "accepted", "rejected"], }) public status: "requesting" | "accepted" | "rejected"; } diff --git a/packages/backend/src/models/entities/renote-muting.ts b/packages/backend/src/models/entities/renote-muting.ts new file mode 100644 index 0000000000..e8856492f1 --- /dev/null +++ b/packages/backend/src/models/entities/renote-muting.ts @@ -0,0 +1,49 @@ +import { + PrimaryColumn, + Entity, + Index, + JoinColumn, + Column, + ManyToOne, +} from "typeorm"; +import { id } from "../id.js"; +import { User } from "./user.js"; + +@Entity() +@Index(["muterId", "muteeId"], { unique: true }) +export class RenoteMuting { + @PrimaryColumn(id()) + public id: string; + + @Index() + @Column("timestamp with time zone", { + comment: "The created date of the Muting.", + }) + public createdAt: Date; + + @Index() + @Column({ + ...id(), + comment: "The mutee user ID.", + }) + public muteeId: User["id"]; + + @ManyToOne((type) => User, { + onDelete: "CASCADE", + }) + @JoinColumn() + public mutee: User | null; + + @Index() + @Column({ + ...id(), + comment: "The muter user ID.", + }) + public muterId: User["id"]; + + @ManyToOne((type) => User, { + onDelete: "CASCADE", + }) + @JoinColumn() + public muter: User | null; +} diff --git a/packages/backend/src/models/entities/signin.ts b/packages/backend/src/models/entities/signin.ts index 7859918238..517e71c8fd 100644 --- a/packages/backend/src/models/entities/signin.ts +++ b/packages/backend/src/models/entities/signin.ts @@ -14,8 +14,8 @@ export class Signin { @PrimaryColumn(id()) public id: string; - @Column('timestamp with time zone', { - comment: 'The created date of the Signin.', + @Column("timestamp with time zone", { + comment: "The created date of the Signin.", }) public createdAt: Date; @@ -23,20 +23,20 @@ export class Signin { @Column(id()) public userId: User["id"]; - @ManyToOne(type => User, { - onDelete: 'CASCADE', + @ManyToOne((type) => User, { + onDelete: "CASCADE", }) @JoinColumn() public user: User | null; - @Column('varchar', { + @Column("varchar", { length: 128, }) public ip: string; - @Column('jsonb') + @Column("jsonb") public headers: Record; - @Column('boolean') + @Column("boolean") public success: boolean; } diff --git a/packages/backend/src/models/entities/sw-subscription.ts b/packages/backend/src/models/entities/sw-subscription.ts index 26891c1ce7..f7823fbaaa 100644 --- a/packages/backend/src/models/entities/sw-subscription.ts +++ b/packages/backend/src/models/entities/sw-subscription.ts @@ -14,31 +14,36 @@ export class SwSubscription { @PrimaryColumn(id()) public id: string; - @Column('timestamp with time zone') + @Column("timestamp with time zone") public createdAt: Date; @Index() @Column(id()) public userId: User["id"]; - @ManyToOne(type => User, { - onDelete: 'CASCADE', + @ManyToOne((type) => User, { + onDelete: "CASCADE", }) @JoinColumn() public user: User | null; - @Column('varchar', { + @Column("varchar", { length: 512, }) public endpoint: string; - @Column('varchar', { + @Column("varchar", { length: 256, }) public auth: string; - @Column('varchar', { + @Column("varchar", { length: 128, }) public publickey: string; + + @Column("boolean", { + default: false, + }) + public sendReadMessage: boolean; } diff --git a/packages/backend/src/models/entities/used-username.ts b/packages/backend/src/models/entities/used-username.ts index a069205a5f..d00a25991e 100644 --- a/packages/backend/src/models/entities/used-username.ts +++ b/packages/backend/src/models/entities/used-username.ts @@ -2,12 +2,12 @@ import { PrimaryColumn, Entity, Column } from "typeorm"; @Entity() export class UsedUsername { - @PrimaryColumn('varchar', { + @PrimaryColumn("varchar", { length: 128, }) public username: string; - @Column('timestamp with time zone') + @Column("timestamp with time zone") public createdAt: Date; constructor(data: Partial) { diff --git a/packages/backend/src/models/entities/user-group-invitation.ts b/packages/backend/src/models/entities/user-group-invitation.ts index 8037b30e1b..fa2655ab67 100644 --- a/packages/backend/src/models/entities/user-group-invitation.ts +++ b/packages/backend/src/models/entities/user-group-invitation.ts @@ -11,25 +11,25 @@ import { UserGroup } from "./user-group.js"; import { id } from "../id.js"; @Entity() -@Index(['userId', 'userGroupId'], { unique: true }) +@Index(["userId", "userGroupId"], { unique: true }) export class UserGroupInvitation { @PrimaryColumn(id()) public id: string; - @Column('timestamp with time zone', { - comment: 'The created date of the UserGroupInvitation.', + @Column("timestamp with time zone", { + comment: "The created date of the UserGroupInvitation.", }) public createdAt: Date; @Index() @Column({ ...id(), - comment: 'The user ID.', + comment: "The user ID.", }) public userId: User["id"]; - @ManyToOne(type => User, { - onDelete: 'CASCADE', + @ManyToOne((type) => User, { + onDelete: "CASCADE", }) @JoinColumn() public user: User | null; @@ -37,12 +37,12 @@ export class UserGroupInvitation { @Index() @Column({ ...id(), - comment: 'The group ID.', + comment: "The group ID.", }) public userGroupId: UserGroup["id"]; - @ManyToOne(type => UserGroup, { - onDelete: 'CASCADE', + @ManyToOne((type) => UserGroup, { + onDelete: "CASCADE", }) @JoinColumn() public userGroup: UserGroup | null; diff --git a/packages/backend/src/models/entities/user-group-joining.ts b/packages/backend/src/models/entities/user-group-joining.ts index 6d503b274e..78f820d0e8 100644 --- a/packages/backend/src/models/entities/user-group-joining.ts +++ b/packages/backend/src/models/entities/user-group-joining.ts @@ -11,25 +11,25 @@ import { UserGroup } from "./user-group.js"; import { id } from "../id.js"; @Entity() -@Index(['userId', 'userGroupId'], { unique: true }) +@Index(["userId", "userGroupId"], { unique: true }) export class UserGroupJoining { @PrimaryColumn(id()) public id: string; - @Column('timestamp with time zone', { - comment: 'The created date of the UserGroupJoining.', + @Column("timestamp with time zone", { + comment: "The created date of the UserGroupJoining.", }) public createdAt: Date; @Index() @Column({ ...id(), - comment: 'The user ID.', + comment: "The user ID.", }) public userId: User["id"]; - @ManyToOne(type => User, { - onDelete: 'CASCADE', + @ManyToOne((type) => User, { + onDelete: "CASCADE", }) @JoinColumn() public user: User | null; @@ -37,12 +37,12 @@ export class UserGroupJoining { @Index() @Column({ ...id(), - comment: 'The group ID.', + comment: "The group ID.", }) public userGroupId: UserGroup["id"]; - @ManyToOne(type => UserGroup, { - onDelete: 'CASCADE', + @ManyToOne((type) => UserGroup, { + onDelete: "CASCADE", }) @JoinColumn() public userGroup: UserGroup | null; diff --git a/packages/backend/src/models/entities/user-group.ts b/packages/backend/src/models/entities/user-group.ts index 38e5af3346..23876ec8b8 100644 --- a/packages/backend/src/models/entities/user-group.ts +++ b/packages/backend/src/models/entities/user-group.ts @@ -15,12 +15,12 @@ export class UserGroup { public id: string; @Index() - @Column('timestamp with time zone', { - comment: 'The created date of the UserGroup.', + @Column("timestamp with time zone", { + comment: "The created date of the UserGroup.", }) public createdAt: Date; - @Column('varchar', { + @Column("varchar", { length: 256, }) public name: string; @@ -28,17 +28,17 @@ export class UserGroup { @Index() @Column({ ...id(), - comment: 'The ID of owner.', + comment: "The ID of owner.", }) public userId: User["id"]; - @ManyToOne(type => User, { - onDelete: 'CASCADE', + @ManyToOne((type) => User, { + onDelete: "CASCADE", }) @JoinColumn() public user: User | null; - @Column('boolean', { + @Column("boolean", { default: false, }) public isPrivate: boolean; diff --git a/packages/backend/src/models/entities/user-ip.ts b/packages/backend/src/models/entities/user-ip.ts index 6b88d52216..c30e56b66b 100644 --- a/packages/backend/src/models/entities/user-ip.ts +++ b/packages/backend/src/models/entities/user-ip.ts @@ -12,20 +12,19 @@ import { Note } from "./note.js"; import type { User } from "./user.js"; @Entity() -@Index(['userId', 'ip'], { unique: true }) +@Index(["userId", "ip"], { unique: true }) export class UserIp { @PrimaryGeneratedColumn() public id: string; - @Column('timestamp with time zone', { - }) + @Column("timestamp with time zone", {}) public createdAt: Date; @Index() @Column(id()) public userId: User["id"]; - @Column('varchar', { + @Column("varchar", { length: 128, }) public ip: string; diff --git a/packages/backend/src/models/entities/user-keypair.ts b/packages/backend/src/models/entities/user-keypair.ts index 212e742b9e..f98384f538 100644 --- a/packages/backend/src/models/entities/user-keypair.ts +++ b/packages/backend/src/models/entities/user-keypair.ts @@ -7,18 +7,18 @@ export class UserKeypair { @PrimaryColumn(id()) public userId: User["id"]; - @OneToOne(type => User, { - onDelete: 'CASCADE', + @OneToOne((type) => User, { + onDelete: "CASCADE", }) @JoinColumn() public user: User | null; - @Column('varchar', { + @Column("varchar", { length: 4096, }) public publicKey: string; - @Column('varchar', { + @Column("varchar", { length: 4096, }) public privateKey: string; diff --git a/packages/backend/src/models/entities/user-list-joining.ts b/packages/backend/src/models/entities/user-list-joining.ts index e52fa7b399..4caa71ad32 100644 --- a/packages/backend/src/models/entities/user-list-joining.ts +++ b/packages/backend/src/models/entities/user-list-joining.ts @@ -11,25 +11,25 @@ import { UserList } from "./user-list.js"; import { id } from "../id.js"; @Entity() -@Index(['userId', 'userListId'], { unique: true }) +@Index(["userId", "userListId"], { unique: true }) export class UserListJoining { @PrimaryColumn(id()) public id: string; - @Column('timestamp with time zone', { - comment: 'The created date of the UserListJoining.', + @Column("timestamp with time zone", { + comment: "The created date of the UserListJoining.", }) public createdAt: Date; @Index() @Column({ ...id(), - comment: 'The user ID.', + comment: "The user ID.", }) public userId: User["id"]; - @ManyToOne(type => User, { - onDelete: 'CASCADE', + @ManyToOne((type) => User, { + onDelete: "CASCADE", }) @JoinColumn() public user: User | null; @@ -37,12 +37,12 @@ export class UserListJoining { @Index() @Column({ ...id(), - comment: 'The list ID.', + comment: "The list ID.", }) public userListId: UserList["id"]; - @ManyToOne(type => UserList, { - onDelete: 'CASCADE', + @ManyToOne((type) => UserList, { + onDelete: "CASCADE", }) @JoinColumn() public userList: UserList | null; diff --git a/packages/backend/src/models/entities/user-list.ts b/packages/backend/src/models/entities/user-list.ts index 7c43452308..3c95d44d6b 100644 --- a/packages/backend/src/models/entities/user-list.ts +++ b/packages/backend/src/models/entities/user-list.ts @@ -14,27 +14,27 @@ export class UserList { @PrimaryColumn(id()) public id: string; - @Column('timestamp with time zone', { - comment: 'The created date of the UserList.', + @Column("timestamp with time zone", { + comment: "The created date of the UserList.", }) public createdAt: Date; @Index() @Column({ ...id(), - comment: 'The owner ID.', + comment: "The owner ID.", }) public userId: User["id"]; - @ManyToOne(type => User, { - onDelete: 'CASCADE', + @ManyToOne((type) => User, { + onDelete: "CASCADE", }) @JoinColumn() public user: User | null; - @Column('varchar', { + @Column("varchar", { length: 128, - comment: 'The name of the UserList.', + comment: "The name of the UserList.", }) public name: string; } diff --git a/packages/backend/src/models/entities/user-note-pining.ts b/packages/backend/src/models/entities/user-note-pining.ts index dc6d61f7e5..c30fe1e028 100644 --- a/packages/backend/src/models/entities/user-note-pining.ts +++ b/packages/backend/src/models/entities/user-note-pining.ts @@ -11,13 +11,13 @@ import { User } from "./user.js"; import { id } from "../id.js"; @Entity() -@Index(['userId', 'noteId'], { unique: true }) +@Index(["userId", "noteId"], { unique: true }) export class UserNotePining { @PrimaryColumn(id()) public id: string; - @Column('timestamp with time zone', { - comment: 'The created date of the UserNotePinings.', + @Column("timestamp with time zone", { + comment: "The created date of the UserNotePinings.", }) public createdAt: Date; @@ -25,8 +25,8 @@ export class UserNotePining { @Column(id()) public userId: User["id"]; - @ManyToOne(type => User, { - onDelete: 'CASCADE', + @ManyToOne((type) => User, { + onDelete: "CASCADE", }) @JoinColumn() public user: User | null; @@ -34,8 +34,8 @@ export class UserNotePining { @Column(id()) public noteId: Note["id"]; - @ManyToOne(type => Note, { - onDelete: 'CASCADE', + @ManyToOne((type) => Note, { + onDelete: "CASCADE", }) @JoinColumn() public note: Note | null; diff --git a/packages/backend/src/models/entities/user-pending.ts b/packages/backend/src/models/entities/user-pending.ts index cac85d1c02..18ae5ad993 100644 --- a/packages/backend/src/models/entities/user-pending.ts +++ b/packages/backend/src/models/entities/user-pending.ts @@ -6,26 +6,26 @@ export class UserPending { @PrimaryColumn(id()) public id: string; - @Column('timestamp with time zone') + @Column("timestamp with time zone") public createdAt: Date; @Index({ unique: true }) - @Column('varchar', { + @Column("varchar", { length: 128, }) public code: string; - @Column('varchar', { + @Column("varchar", { length: 128, }) public username: string; - @Column('varchar', { + @Column("varchar", { length: 128, }) public email: string; - @Column('varchar', { + @Column("varchar", { length: 128, }) public password: string; diff --git a/packages/backend/src/models/entities/user-profile.ts b/packages/backend/src/models/entities/user-profile.ts index cc3d238679..002247d3a3 100644 --- a/packages/backend/src/models/entities/user-profile.ts +++ b/packages/backend/src/models/entities/user-profile.ts @@ -18,163 +18,181 @@ export class UserProfile { @PrimaryColumn(id()) public userId: User["id"]; - @OneToOne(type => User, { - onDelete: 'CASCADE', + @OneToOne((type) => User, { + onDelete: "CASCADE", }) @JoinColumn() public user: User | null; - @Column('varchar', { - length: 128, nullable: true, - comment: 'The location of the User.', + @Column("varchar", { + length: 128, + nullable: true, + comment: "The location of the User.", }) public location: string | null; - @Column('char', { - length: 10, nullable: true, - comment: 'The birthday (YYYY-MM-DD) of the User.', + @Column("char", { + length: 10, + nullable: true, + comment: "The birthday (YYYY-MM-DD) of the User.", }) public birthday: string | null; - @Column('varchar', { - length: 2048, nullable: true, - comment: 'The description (bio) of the User.', + @Column("varchar", { + length: 2048, + nullable: true, + comment: "The description (bio) of the User.", }) public description: string | null; - @Column('jsonb', { + @Column("jsonb", { default: [], }) public fields: { name: string; value: string; + verified?: boolean; }[]; - @Column('varchar', { - length: 32, nullable: true, + @Column("varchar", { + length: 32, + nullable: true, }) public lang: string | null; - @Column('varchar', { - length: 512, nullable: true, - comment: 'Remote URL of the user.', + @Column("varchar", { + length: 512, + nullable: true, + comment: "Remote URL of the user.", }) public url: string | null; - @Column('varchar', { - length: 128, nullable: true, - comment: 'The email address of the User.', + @Column("varchar", { + length: 128, + nullable: true, + comment: "The email address of the User.", }) public email: string | null; - @Column('varchar', { - length: 128, nullable: true, + @Column("varchar", { + length: 128, + nullable: true, }) public emailVerifyCode: string | null; - @Column('boolean', { + @Column("boolean", { default: false, }) public emailVerified: boolean; - @Column('jsonb', { - default: ['follow', 'receiveFollowRequest', 'groupInvited'], + @Column("jsonb", { + default: ["follow", "receiveFollowRequest", "groupInvited"], }) public emailNotificationTypes: string[]; - @Column('boolean', { + @Column("boolean", { default: false, }) public publicReactions: boolean; - @Column('enum', { + @Column("enum", { enum: ffVisibility, - default: 'public', + default: "public", }) public ffVisibility: typeof ffVisibility[number]; - @Column('varchar', { - length: 128, nullable: true, + @Column("varchar", { + length: 128, + nullable: true, }) public twoFactorTempSecret: string | null; - @Column('varchar', { - length: 128, nullable: true, + @Column("varchar", { + length: 128, + nullable: true, }) public twoFactorSecret: string | null; - @Column('boolean', { + @Column("boolean", { default: false, }) public twoFactorEnabled: boolean; - @Column('boolean', { + @Column("boolean", { default: false, }) public securityKeysAvailable: boolean; - @Column('boolean', { + @Column("boolean", { default: false, }) public usePasswordLessLogin: boolean; - @Column('varchar', { - length: 128, nullable: true, - comment: 'The password hash of the User. It will be null if the origin of the user is local.', + @Column("varchar", { + length: 128, + nullable: true, + comment: + "The password hash of the User. It will be null if the origin of the user is local.", }) public password: string | null; - @Column('varchar', { - length: 8192, default: '', + @Column("varchar", { + length: 8192, + default: "", }) public moderationNote: string | null; // TODO: そのうち消す - @Column('jsonb', { + @Column("jsonb", { default: {}, - comment: 'The client-specific data of the User.', + comment: "The client-specific data of the User.", }) public clientData: Record; // TODO: そのうち消す - @Column('jsonb', { + @Column("jsonb", { default: {}, - comment: 'The room data of the User.', + comment: "The room data of the User.", }) public room: Record; - @Column('boolean', { + @Column("boolean", { default: false, }) public autoAcceptFollowed: boolean; - @Column('boolean', { + @Column("boolean", { default: false, - comment: 'Whether reject index by crawler.', + comment: "Whether reject index by crawler.", }) public noCrawle: boolean; - @Column('boolean', { + @Column("boolean", { + default: true, + }) + public preventAiLearning: boolean; + + @Column("boolean", { default: false, }) public alwaysMarkNsfw: boolean; - @Column('boolean', { + @Column("boolean", { default: false, }) public autoSensitive: boolean; - @Column('boolean', { + @Column("boolean", { default: false, }) public carefulBot: boolean; - @Column('boolean', { + @Column("boolean", { default: true, }) public injectFeaturedNote: boolean; - @Column('boolean', { + @Column("boolean", { default: true, }) public receiveAnnouncementEmail: boolean; @@ -185,35 +203,36 @@ export class UserProfile { }) public pinnedPageId: Page["id"] | null; - @OneToOne(type => Page, { - onDelete: 'SET NULL', + @OneToOne((type) => Page, { + onDelete: "SET NULL", }) @JoinColumn() public pinnedPage: Page | null; - @Column('jsonb', { + @Column("jsonb", { default: {}, }) public integrations: Record; @Index() - @Column('boolean', { - default: false, select: false, + @Column("boolean", { + default: false, + select: false, }) public enableWordMute: boolean; - @Column('jsonb', { + @Column("jsonb", { default: [], }) public mutedWords: string[][]; - @Column('jsonb', { + @Column("jsonb", { default: [], - comment: 'List of instances muted by the user.', + comment: "List of instances muted by the user.", }) public mutedInstances: string[]; - @Column('enum', { + @Column("enum", { enum: notificationTypes, array: true, default: [], @@ -222,9 +241,10 @@ export class UserProfile { //#region Denormalized fields @Index() - @Column('varchar', { - length: 128, nullable: true, - comment: '[Denormalized]', + @Column("varchar", { + length: 128, + nullable: true, + comment: "[Denormalized]", }) public userHost: string | null; //#endregion diff --git a/packages/backend/src/models/entities/user-publickey.ts b/packages/backend/src/models/entities/user-publickey.ts index d1a9239d11..83a86b8a3a 100644 --- a/packages/backend/src/models/entities/user-publickey.ts +++ b/packages/backend/src/models/entities/user-publickey.ts @@ -14,19 +14,19 @@ export class UserPublickey { @PrimaryColumn(id()) public userId: User["id"]; - @OneToOne(type => User, { - onDelete: 'CASCADE', + @OneToOne((type) => User, { + onDelete: "CASCADE", }) @JoinColumn() public user: User | null; @Index({ unique: true }) - @Column('varchar', { + @Column("varchar", { length: 256, }) public keyId: string; - @Column('varchar', { + @Column("varchar", { length: 4096, }) public keyPem: string; diff --git a/packages/backend/src/models/entities/user-security-key.ts b/packages/backend/src/models/entities/user-security-key.ts index 3b9d925d9e..511cab4ae4 100644 --- a/packages/backend/src/models/entities/user-security-key.ts +++ b/packages/backend/src/models/entities/user-security-key.ts @@ -11,8 +11,8 @@ import { id } from "../id.js"; @Entity() export class UserSecurityKey { - @PrimaryColumn('varchar', { - comment: 'Variable-length id given to navigator.credentials.get()', + @PrimaryColumn("varchar", { + comment: "Variable-length id given to navigator.credentials.get()", }) public id: string; @@ -20,27 +20,27 @@ export class UserSecurityKey { @Column(id()) public userId: User["id"]; - @ManyToOne(type => User, { - onDelete: 'CASCADE', + @ManyToOne((type) => User, { + onDelete: "CASCADE", }) @JoinColumn() public user: User | null; @Index() - @Column('varchar', { + @Column("varchar", { comment: - 'Variable-length public key used to verify attestations (hex-encoded).', + "Variable-length public key used to verify attestations (hex-encoded).", }) public publicKey: string; - @Column('timestamp with time zone', { + @Column("timestamp with time zone", { comment: - 'The date of the last time the UserSecurityKey was successfully validated.', + "The date of the last time the UserSecurityKey was successfully validated.", }) public lastUsed: Date; - @Column('varchar', { - comment: 'User-defined name for this key', + @Column("varchar", { + comment: "User-defined name for this key", length: 30, }) public name: string; diff --git a/packages/backend/src/models/entities/user.ts b/packages/backend/src/models/entities/user.ts index c57ad916c9..ddad9f3b23 100644 --- a/packages/backend/src/models/entities/user.ts +++ b/packages/backend/src/models/entities/user.ts @@ -10,99 +10,101 @@ import { id } from "../id.js"; import { DriveFile } from "./drive-file.js"; @Entity() -@Index(['usernameLower', 'host'], { unique: true }) +@Index(["usernameLower", "host"], { unique: true }) export class User { @PrimaryColumn(id()) public id: string; @Index() - @Column('timestamp with time zone', { - comment: 'The created date of the User.', + @Column("timestamp with time zone", { + comment: "The created date of the User.", }) public createdAt: Date; @Index() - @Column('timestamp with time zone', { + @Column("timestamp with time zone", { nullable: true, - comment: 'The updated date of the User.', + comment: "The updated date of the User.", }) public updatedAt: Date | null; - @Column('timestamp with time zone', { + @Column("timestamp with time zone", { nullable: true, }) public lastFetchedAt: Date | null; @Index() - @Column('timestamp with time zone', { + @Column("timestamp with time zone", { nullable: true, }) public lastActiveDate: Date | null; - @Column('boolean', { + @Column("boolean", { default: false, }) public hideOnlineStatus: boolean; - @Column('varchar', { + @Column("varchar", { length: 128, - comment: 'The username of the User.', + comment: "The username of the User.", }) public username: string; @Index() - @Column('varchar', { - length: 128, select: false, - comment: 'The username (lowercased) of the User.', + @Column("varchar", { + length: 128, + select: false, + comment: "The username (lowercased) of the User.", }) public usernameLower: string; - @Column('varchar', { - length: 128, nullable: true, - comment: 'The name of the User.', + @Column("varchar", { + length: 128, + nullable: true, + comment: "The name of the User.", }) public name: string | null; - @Column('integer', { + @Column("integer", { default: 0, - comment: 'The count of followers.', + comment: "The count of followers.", }) public followersCount: number; - @Column('integer', { + @Column("integer", { default: 0, - comment: 'The count of following.', + comment: "The count of following.", }) public followingCount: number; - @Column('varchar', { + @Column("varchar", { length: 512, nullable: true, - comment: 'The URI of the new account of the User', + comment: "The URI of the new account of the User", }) public movedToUri: string | null; - @Column('simple-array', { + @Column("simple-array", { nullable: true, - comment: 'URIs the user is known as too', + comment: "URIs the user is known as too", }) public alsoKnownAs: string[] | null; - @Column('integer', { + @Column("integer", { default: 0, - comment: 'The count of notes.', + comment: "The count of notes.", }) public notesCount: number; @Column({ ...id(), nullable: true, - comment: 'The ID of avatar DriveFile.', + comment: "The ID of avatar DriveFile.", }) public avatarId: DriveFile["id"] | null; - @OneToOne(type => DriveFile, { - onDelete: 'SET NULL', + @OneToOne((type) => DriveFile, { + onDelete: "SET NULL", }) @JoinColumn() public avatar: DriveFile | null; @@ -110,137 +112,156 @@ export class User { @Column({ ...id(), nullable: true, - comment: 'The ID of banner DriveFile.', + comment: "The ID of banner DriveFile.", }) public bannerId: DriveFile["id"] | null; - @OneToOne(type => DriveFile, { - onDelete: 'SET NULL', + @OneToOne((type) => DriveFile, { + onDelete: "SET NULL", }) @JoinColumn() public banner: DriveFile | null; @Index() - @Column('varchar', { - length: 128, array: true, default: '{}', + @Column("varchar", { + length: 128, + array: true, + default: "{}", }) public tags: string[]; - @Column('boolean', { + @Column("boolean", { default: false, - comment: 'Whether the User is suspended.', + comment: "Whether the User is suspended.", }) public isSuspended: boolean; - @Column('boolean', { + @Column("boolean", { default: false, - comment: 'Whether the User is silenced.', + comment: "Whether the User is silenced.", }) public isSilenced: boolean; - @Column('boolean', { + @Column("boolean", { default: false, - comment: 'Whether the User is locked.', + comment: "Whether the User is locked.", }) public isLocked: boolean; - @Column('boolean', { + @Column("boolean", { default: false, - comment: 'Whether the User is a bot.', + comment: "Whether the User is a bot.", }) public isBot: boolean; - @Column('boolean', { + @Column("boolean", { default: false, - comment: 'Whether the User is a cat.', + comment: "Whether the User is a cat.", }) public isCat: boolean; - @Column('boolean', { + @Column("boolean", { + default: true, + comment: "Whether to speak as a cat if isCat.", + }) + public speakAsCat: boolean; + + @Column("boolean", { default: false, - comment: 'Whether the User is the admin.', + comment: "Whether the User is the admin.", }) public isAdmin: boolean; - @Column('boolean', { + @Column("boolean", { default: false, - comment: 'Whether the User is a moderator.', + comment: "Whether the User is a moderator.", }) public isModerator: boolean; @Index() - @Column('boolean', { + @Column("boolean", { default: true, - comment: 'Whether the User is explorable.', + comment: "Whether the User is explorable.", }) public isExplorable: boolean; // アカウントが削除されたかどうかのフラグだが、完全に削除される際は物理削除なので実質削除されるまでの「削除が進行しているかどうか」のフラグ - @Column('boolean', { + @Column("boolean", { default: false, - comment: 'Whether the User is deleted.', + comment: "Whether the User is deleted.", }) public isDeleted: boolean; - @Column('varchar', { - length: 128, array: true, default: '{}', + @Column("varchar", { + length: 128, + array: true, + default: "{}", }) public emojis: string[]; @Index() - @Column('varchar', { - length: 128, nullable: true, - comment: 'The host of the User. It will be null if the origin of the user is local.', + @Column("varchar", { + length: 128, + nullable: true, + comment: + "The host of the User. It will be null if the origin of the user is local.", }) public host: string | null; - @Column('varchar', { - length: 512, nullable: true, - comment: 'The inbox URL of the User. It will be null if the origin of the user is local.', + @Column("varchar", { + length: 512, + nullable: true, + comment: + "The inbox URL of the User. It will be null if the origin of the user is local.", }) public inbox: string | null; - @Column('varchar', { - length: 512, nullable: true, - comment: 'The sharedInbox URL of the User. It will be null if the origin of the user is local.', + @Column("varchar", { + length: 512, + nullable: true, + comment: + "The sharedInbox URL of the User. It will be null if the origin of the user is local.", }) public sharedInbox: string | null; - @Column('varchar', { - length: 512, nullable: true, - comment: 'The featured URL of the User. It will be null if the origin of the user is local.', + @Column("varchar", { + length: 512, + nullable: true, + comment: + "The featured URL of the User. It will be null if the origin of the user is local.", }) public featured: string | null; @Index() - @Column('varchar', { - length: 512, nullable: true, - comment: 'The URI of the User. It will be null if the origin of the user is local.', + @Column("varchar", { + length: 512, + nullable: true, + comment: + "The URI of the User. It will be null if the origin of the user is local.", }) public uri: string | null; - @Column('varchar', { - length: 512, nullable: true, - comment: 'The URI of the user Follower Collection. It will be null if the origin of the user is local.', + @Column("varchar", { + length: 512, + nullable: true, + comment: + "The URI of the user Follower Collection. It will be null if the origin of the user is local.", }) public followersUri: string | null; - @Column('boolean', { - default: false, - comment: 'Whether to show users replying to other users in the timeline.', - }) - public showTimelineReplies: boolean; - @Index({ unique: true }) - @Column('char', { - length: 16, nullable: true, unique: true, - comment: 'The native access token of the User. It will be null if the origin of the user is local.', + @Column("char", { + length: 16, + nullable: true, + unique: true, + comment: + "The native access token of the User. It will be null if the origin of the user is local.", }) public token: string | null; - @Column('integer', { + @Column("integer", { nullable: true, - comment: 'Overrides user drive capacity limit', + comment: "Overrides user drive capacity limit", }) public driveCapacityOverrideMb: number | null; diff --git a/packages/backend/src/models/entities/webhook.ts b/packages/backend/src/models/entities/webhook.ts index 5db51c3a3c..47fd79966d 100644 --- a/packages/backend/src/models/entities/webhook.ts +++ b/packages/backend/src/models/entities/webhook.ts @@ -25,48 +25,50 @@ export class Webhook { @PrimaryColumn(id()) public id: string; - @Column('timestamp with time zone', { - comment: 'The created date of the Antenna.', + @Column("timestamp with time zone", { + comment: "The created date of the Antenna.", }) public createdAt: Date; @Index() @Column({ ...id(), - comment: 'The owner ID.', + comment: "The owner ID.", }) public userId: User["id"]; - @ManyToOne(type => User, { - onDelete: 'CASCADE', + @ManyToOne((type) => User, { + onDelete: "CASCADE", }) @JoinColumn() public user: User | null; - @Column('varchar', { + @Column("varchar", { length: 128, - comment: 'The name of the Antenna.', + comment: "The name of the Antenna.", }) public name: string; @Index() - @Column('varchar', { - length: 128, array: true, default: '{}', + @Column("varchar", { + length: 128, + array: true, + default: "{}", }) public on: typeof webhookEventTypes[number][]; - @Column('varchar', { + @Column("varchar", { length: 1024, }) public url: string; - @Column('varchar', { + @Column("varchar", { length: 1024, }) public secret: string; @Index() - @Column('boolean', { + @Column("boolean", { default: true, }) public active: boolean; @@ -74,7 +76,7 @@ export class Webhook { /** * 直近のリクエスト送信日時 */ - @Column('timestamp with time zone', { + @Column("timestamp with time zone", { nullable: true, }) public latestSentAt: Date | null; @@ -82,7 +84,7 @@ export class Webhook { /** * 直近のリクエスト送信時のHTTPステータスコード */ - @Column('integer', { + @Column("integer", { nullable: true, }) public latestStatus: number | null; diff --git a/packages/backend/src/models/index.ts b/packages/backend/src/models/index.ts index 98f6705f42..8782d57408 100644 --- a/packages/backend/src/models/index.ts +++ b/packages/backend/src/models/index.ts @@ -27,6 +27,7 @@ import { UserGroupJoining } from "./entities/user-group-joining.js"; import { UserGroupInvitationRepository } from "./repositories/user-group-invitation.js"; import { FollowRequestRepository } from "./repositories/follow-request.js"; import { MutingRepository } from "./repositories/muting.js"; +import { RenoteMutingRepository } from "./repositories/renote-muting.js"; import { BlockingRepository } from "./repositories/blocking.js"; import { NoteReactionRepository } from "./repositories/note-reaction.js"; import { NotificationRepository } from "./repositories/notification.js"; @@ -50,7 +51,6 @@ import { UsedUsername } from "./entities/used-username.js"; import { ClipRepository } from "./repositories/clip.js"; import { ClipNote } from "./entities/clip-note.js"; import { AntennaRepository } from "./repositories/antenna.js"; -import { AntennaNote } from "./entities/antenna-note.js"; import { PromoNote } from "./entities/promo-note.js"; import { PromoRead } from "./entities/promo-read.js"; import { EmojiRepository } from "./repositories/emoji.js"; @@ -66,11 +66,13 @@ import { UserPending } from "./entities/user-pending.js"; import { InstanceRepository } from "./repositories/instance.js"; import { Webhook } from "./entities/webhook.js"; import { UserIp } from "./entities/user-ip.js"; +import { NoteEdit } from "./entities/note-edit.js"; export const Announcements = db.getRepository(Announcement); export const AnnouncementReads = db.getRepository(AnnouncementRead); export const Apps = AppRepository; export const Notes = NoteRepository; +export const NoteEdits = db.getRepository(NoteEdit); export const NoteFavorites = NoteFavoriteRepository; export const NoteWatchings = db.getRepository(NoteWatching); export const NoteThreadMutings = db.getRepository(NoteThreadMuting); @@ -102,6 +104,7 @@ export const DriveFolders = DriveFolderRepository; export const Notifications = NotificationRepository; export const Metas = db.getRepository(Meta); export const Mutings = MutingRepository; +export const RenoteMutings = RenoteMutingRepository; export const Blockings = BlockingRepository; export const SwSubscriptions = db.getRepository(SwSubscription); export const Hashtags = HashtagRepository; @@ -119,7 +122,6 @@ export const ModerationLogs = ModerationLogRepository; export const Clips = ClipRepository; export const ClipNotes = db.getRepository(ClipNote); export const Antennas = AntennaRepository; -export const AntennaNotes = db.getRepository(AntennaNote); export const PromoNotes = db.getRepository(PromoNote); export const PromoReads = db.getRepository(PromoRead); export const Relays = RelayRepository; diff --git a/packages/backend/src/models/repositories/antenna.ts b/packages/backend/src/models/repositories/antenna.ts index 57ce2fc9e8..d66aab4eef 100644 --- a/packages/backend/src/models/repositories/antenna.ts +++ b/packages/backend/src/models/repositories/antenna.ts @@ -1,35 +1,14 @@ import { db } from "@/db/postgre.js"; import { Antenna } from "@/models/entities/antenna.js"; -import type { Packed } from "@/misc/schema.js"; -import { AntennaNotes, UserGroupJoinings } from "../index.js"; +import { + NativeAntennaSchema, + nativePackAntennaById, +} from "native-utils/built/index.js"; export const AntennaRepository = db.getRepository(Antenna).extend({ - async pack(src: Antenna["id"] | Antenna): Promise> { - const antenna = - typeof src === "object" ? src : await this.findOneByOrFail({ id: src }); + async pack(src: Antenna["id"] | Antenna): Promise { + const id = typeof src === "object" ? src.id : src; - const hasUnreadNote = - (await AntennaNotes.findOneBy({ antennaId: antenna.id, read: false })) != - null; - const userGroupJoining = antenna.userGroupJoiningId - ? await UserGroupJoinings.findOneBy({ id: antenna.userGroupJoiningId }) - : null; - - return { - id: antenna.id, - createdAt: antenna.createdAt.toISOString(), - name: antenna.name, - keywords: antenna.keywords, - excludeKeywords: antenna.excludeKeywords, - src: antenna.src, - userListId: antenna.userListId, - userGroupId: userGroupJoining ? userGroupJoining.userGroupId : null, - users: antenna.users, - caseSensitive: antenna.caseSensitive, - notify: antenna.notify, - withReplies: antenna.withReplies, - withFile: antenna.withFile, - hasUnreadNote, - }; + return await nativePackAntennaById(id); }, }); diff --git a/packages/backend/src/models/repositories/emoji.ts b/packages/backend/src/models/repositories/emoji.ts index e868fe94fe..e9a940f958 100644 --- a/packages/backend/src/models/repositories/emoji.ts +++ b/packages/backend/src/models/repositories/emoji.ts @@ -15,6 +15,9 @@ export const EmojiRepository = db.getRepository(Emoji).extend({ host: emoji.host, // || emoji.originalUrl してるのは後方互換性のため url: emoji.publicUrl || emoji.originalUrl, + license: emoji.license, + width: emoji.width, + height: emoji.height, }; }, diff --git a/packages/backend/src/models/repositories/instance.ts b/packages/backend/src/models/repositories/instance.ts index fb4498911a..667ec948de 100644 --- a/packages/backend/src/models/repositories/instance.ts +++ b/packages/backend/src/models/repositories/instance.ts @@ -1,12 +1,13 @@ import { db } from "@/db/postgre.js"; import { Instance } from "@/models/entities/instance.js"; import type { Packed } from "@/misc/schema.js"; -import { fetchMeta } from "@/misc/fetch-meta.js"; -import { shouldBlockInstance } from "@/misc/should-block-instance.js"; +import { + shouldBlockInstance, + shouldSilenceInstance, +} from "@/misc/should-block-instance.js"; export const InstanceRepository = db.getRepository(Instance).extend({ async pack(instance: Instance): Promise> { - const meta = await fetchMeta(); return { id: instance.id, caughtAt: instance.caughtAt.toISOString(), @@ -22,6 +23,7 @@ export const InstanceRepository = db.getRepository(Instance).extend({ isNotResponding: instance.isNotResponding, isSuspended: instance.isSuspended, isBlocked: await shouldBlockInstance(instance.host), + isSilenced: await shouldSilenceInstance(instance.host), softwareName: instance.softwareName, softwareVersion: instance.softwareVersion, openRegistrations: instance.openRegistrations, diff --git a/packages/backend/src/models/repositories/note.ts b/packages/backend/src/models/repositories/note.ts index 37c5031c0f..453179bd6f 100644 --- a/packages/backend/src/models/repositories/note.ts +++ b/packages/backend/src/models/repositories/note.ts @@ -28,7 +28,7 @@ import { import { db } from "@/db/postgre.js"; import { IdentifiableError } from "@/misc/identifiable-error.js"; -async function populatePoll(note: Note, meId: User["id"] | null) { +export async function populatePoll(note: Note, meId: User["id"] | null) { const poll = await Polls.findOneByOrFail({ noteId: note.id }); const choices = poll.choices.map((c) => ({ text: c, @@ -197,7 +197,10 @@ export const NoteRepository = db.getRepository(Note).extend({ .map((x) => decodeReaction(x).reaction) .map((x) => x.replace(/:/g, "")); - const noteEmoji = await populateEmojis(note.emojis.concat(reactionEmojiNames), host); + const noteEmoji = await populateEmojis( + note.emojis.concat(reactionEmojiNames), + host, + ); const reactionEmoji = await populateEmojis(reactionEmojiNames, host); const packed: Packed<"Note"> = await awaitAll({ id: note.id, @@ -232,6 +235,13 @@ export const NoteRepository = db.getRepository(Note).extend({ mentions: note.mentions.length > 0 ? note.mentions : undefined, uri: note.uri || undefined, url: note.url || undefined, + updatedAt: note.updatedAt?.toISOString() || undefined, + poll: note.hasPoll ? populatePoll(note, meId) : undefined, + ...(meId + ? { + myReaction: populateMyReaction(note, meId, options?._hint_), + } + : {}), ...(opts.detail ? { @@ -248,26 +258,25 @@ export const NoteRepository = db.getRepository(Note).extend({ _hint_: options?._hint_, }) : undefined, - - poll: note.hasPoll ? populatePoll(note, meId) : undefined, - - ...(meId - ? { - myReaction: populateMyReaction(note, meId, options?._hint_), - } - : {}), } : {}), }); - if (packed.user.isCat && packed.text) { + if (packed.user.isCat && packed.user.speakAsCat && packed.text) { const tokens = packed.text ? mfm.parse(packed.text) : []; - mfm.inspect(tokens, (node) => { - if (node.type === "text") { - // TODO: quoteなtextはskip - node.props.text = nyaize(node.props.text); + function nyaizeNode(node: mfm.MfmNode) { + if (node.type === "quote") return; + if (node.type === "text") node.props.text = nyaize(node.props.text); + + if (node.children) { + for (const child of node.children) { + nyaizeNode(child); + } } - }); + } + + for (const node of tokens) nyaizeNode(node); + packed.text = mfm.toString(tokens); } diff --git a/packages/backend/src/models/repositories/renote-muting.ts b/packages/backend/src/models/repositories/renote-muting.ts new file mode 100644 index 0000000000..18fd343a05 --- /dev/null +++ b/packages/backend/src/models/repositories/renote-muting.ts @@ -0,0 +1,29 @@ +import { db } from "@/db/postgre.js"; +import { Packed } from "@/misc/schema.js"; +import { RenoteMuting } from "@/models/entities/renote-muting.js"; +import { User } from "@/models/entities/user.js"; +import { awaitAll } from "@/prelude/await-all.js"; +import { Users } from "../index.js"; + +export const RenoteMutingRepository = db.getRepository(RenoteMuting).extend({ + async pack( + src: RenoteMuting["id"] | RenoteMuting, + me?: { id: User["id"] } | null | undefined, + ): Promise> { + const muting = + typeof src === "object" ? src : await this.findOneByOrFail({ id: src }); + + return await awaitAll({ + id: muting.id, + createdAt: muting.createdAt.toISOString(), + muteeId: muting.muteeId, + mutee: Users.pack(muting.muteeId, me, { + detail: true, + }), + }); + }, + + packMany(mutings: any[], me: { id: User["id"] }) { + return Promise.all(mutings.map((x) => this.pack(x, me))); + }, +}); diff --git a/packages/backend/src/models/repositories/user.ts b/packages/backend/src/models/repositories/user.ts index aa224b6674..e7c4b6f008 100644 --- a/packages/backend/src/models/repositories/user.ts +++ b/packages/backend/src/models/repositories/user.ts @@ -1,4 +1,3 @@ -import { URL } from "url"; import { In, Not } from "typeorm"; import Ajv from "ajv"; import type { ILocalUser, IRemoteUser } from "@/models/entities/user.js"; @@ -19,7 +18,6 @@ import { createPerson } from "@/remote/activitypub/models/person.js"; import { AnnouncementReads, Announcements, - AntennaNotes, Blockings, ChannelFollowings, DriveFiles, @@ -28,6 +26,7 @@ import { Instances, MessagingMessages, Mutings, + RenoteMutings, Notes, NoteUnreads, Notifications, @@ -39,7 +38,10 @@ import { } from "../index.js"; import type { Instance } from "../entities/instance.js"; -const userInstanceCache = new Cache(1000 * 60 * 60 * 3); +const userInstanceCache = new Cache( + "userInstance", + 60 * 60 * 3, +); type IsUserDetailed = Detailed extends true ? Packed<"UserDetailed"> @@ -66,7 +68,7 @@ const nameSchema = { type: "string", minLength: 1, maxLength: 50 } as const; const descriptionSchema = { type: "string", minLength: 1, - maxLength: 500, + maxLength: 2048, } as const; const locationSchema = { type: "string", minLength: 1, maxLength: 50 } as const; const birthdaySchema = { @@ -171,6 +173,13 @@ export const UserRepository = db.getRepository(User).extend({ }, take: 1, }).then((n) => n > 0), + isRenoteMuted: RenoteMutings.count({ + where: { + muterId: me, + muteeId: target, + }, + take: 1, + }).then((n) => n > 0), }); }, @@ -248,17 +257,24 @@ export const UserRepository = db.getRepository(User).extend({ }, async getHasUnreadAntenna(userId: User["id"]): Promise { - const myAntennas = (await getAntennas()).filter((a) => a.userId === userId); + // try { + // const myAntennas = (await getAntennas()).filter( + // (a) => a.userId === userId, + // ); - const unread = - myAntennas.length > 0 - ? await AntennaNotes.findOneBy({ - antennaId: In(myAntennas.map((x) => x.id)), - read: false, - }) - : null; + // const unread = + // myAntennas.length > 0 + // ? await AntennaNotes.findOneBy({ + // antennaId: In(myAntennas.map((x) => x.id)), + // read: false, + // }) + // : null; - return unread != null; + // return unread != null; + // } catch (e) { + // return false; + // } + return false; // TODO }, async getHasUnreadChannel(userId: User["id"]): Promise { @@ -437,7 +453,9 @@ export const UserRepository = db.getRepository(User).extend({ isAdmin: user.isAdmin || falsy, isModerator: user.isModerator || falsy, isBot: user.isBot || falsy, + isLocked: user.isLocked, isCat: user.isCat || falsy, + speakAsCat: user.speakAsCat || falsy, instance: user.host ? userInstanceCache .fetch( @@ -480,7 +498,6 @@ export const UserRepository = db.getRepository(User).extend({ : null, bannerBlurhash: user.banner?.blurhash || null, bannerColor: null, // 後方互換性のため - isLocked: user.isLocked, isSilenced: user.isSilenced || falsy, isSuspended: user.isSuspended || falsy, description: profile!.description, @@ -526,6 +543,7 @@ export const UserRepository = db.getRepository(User).extend({ carefulBot: profile!.carefulBot, autoAcceptFollowed: profile!.autoAcceptFollowed, noCrawle: profile!.noCrawle, + preventAiLearning: profile!.preventAiLearning, isExplorable: user.isExplorable, isDeleted: user.isDeleted, hideOnlineStatus: user.hideOnlineStatus, @@ -551,7 +569,6 @@ export const UserRepository = db.getRepository(User).extend({ mutedInstances: profile!.mutedInstances, mutingNotificationTypes: profile!.mutingNotificationTypes, emailNotificationTypes: profile!.emailNotificationTypes, - showTimelineReplies: user.showTimelineReplies || falsy, } : {}), @@ -584,6 +601,7 @@ export const UserRepository = db.getRepository(User).extend({ isBlocking: relation.isBlocking, isBlocked: relation.isBlocked, isMuted: relation.isMuted, + isRenoteMuted: relation.isRenoteMuted, } : {}), } as Promiseable> as Promiseable< diff --git a/packages/backend/src/models/schema/antenna.ts b/packages/backend/src/models/schema/antenna.ts index c7eed092e9..990e2daa2c 100644 --- a/packages/backend/src/models/schema/antenna.ts +++ b/packages/backend/src/models/schema/antenna.ts @@ -52,7 +52,7 @@ export const packedAntennaSchema = { type: "string", optional: false, nullable: false, - enum: ["home", "all", "users", "list", "group"], + enum: ["home", "all", "users", "list", "group", "instances"], }, userListId: { type: "string", @@ -76,6 +76,16 @@ export const packedAntennaSchema = { nullable: false, }, }, + instances: { + type: "array", + optional: false, + nullable: false, + items: { + type: "string", + optional: false, + nullable: false, + }, + }, caseSensitive: { type: "boolean", optional: false, diff --git a/packages/backend/src/models/schema/emoji.ts b/packages/backend/src/models/schema/emoji.ts index 1e1dab8689..8b01f09ab2 100644 --- a/packages/backend/src/models/schema/emoji.ts +++ b/packages/backend/src/models/schema/emoji.ts @@ -40,5 +40,20 @@ export const packedEmojiSchema = { optional: false, nullable: false, }, + license: { + type: "string", + optional: false, + nullable: true, + }, + width: { + type: "number", + optional: false, + nullable: true, + }, + height: { + type: "number", + optional: false, + nullable: true, + }, }, } as const; diff --git a/packages/backend/src/models/schema/federation-instance.ts b/packages/backend/src/models/schema/federation-instance.ts index ed3369bf11..7a8af7f51d 100644 --- a/packages/backend/src/models/schema/federation-instance.ts +++ b/packages/backend/src/models/schema/federation-instance.ts @@ -19,7 +19,7 @@ export const packedFederationInstanceSchema = { type: "string", optional: false, nullable: false, - example: "calckey.example.com", + example: "firefish.example.com", }, usersCount: { type: "number", @@ -68,11 +68,16 @@ export const packedFederationInstanceSchema = { optional: false, nullable: false, }, + isSilenced: { + type: "boolean", + optional: false, + nullable: false, + }, softwareName: { type: "string", optional: false, nullable: true, - example: "calckey", + example: "firefish", }, softwareVersion: { type: "string", diff --git a/packages/backend/src/models/schema/hashtag.ts b/packages/backend/src/models/schema/hashtag.ts index dacc515070..479ab88f92 100644 --- a/packages/backend/src/models/schema/hashtag.ts +++ b/packages/backend/src/models/schema/hashtag.ts @@ -5,7 +5,7 @@ export const packedHashtagSchema = { type: "string", optional: false, nullable: false, - example: "calckey", + example: "firefish", }, mentionedUsersCount: { type: "number", diff --git a/packages/backend/src/models/schema/note-edit.ts b/packages/backend/src/models/schema/note-edit.ts new file mode 100644 index 0000000000..e877f3f946 --- /dev/null +++ b/packages/backend/src/models/schema/note-edit.ts @@ -0,0 +1,49 @@ +export const packedNoteEdit = { + type: "object", + properties: { + id: { + type: "string", + optional: false, + nullable: false, + format: "id", + example: "xxxxxxxxxx", + }, + updatedAt: { + type: "string", + optional: false, + nullable: false, + format: "date-time", + }, + note: { + type: "object", + optional: false, + nullable: false, + ref: "Note", + }, + noteId: { + type: "string", + optional: false, + nullable: false, + format: "id", + }, + text: { + type: "string", + optional: true, + nullable: true, + }, + cw: { + type: "string", + optional: true, + nullable: true, + }, + fileIds: { + type: "array", + optional: true, + nullable: true, + items: { + type: "string", + format: "id", + }, + }, + }, +} as const; diff --git a/packages/backend/src/models/schema/note.ts b/packages/backend/src/models/schema/note.ts index 6bc8443f0f..e17f054e8e 100644 --- a/packages/backend/src/models/schema/note.ts +++ b/packages/backend/src/models/schema/note.ts @@ -161,8 +161,9 @@ export const packedNoteSchema = { nullable: false, }, emojis: { - type: 'object', - optional: true, nullable: true, + type: "object", + optional: true, + nullable: true, }, reactions: { type: "object", diff --git a/packages/backend/src/models/schema/renote-muting.ts b/packages/backend/src/models/schema/renote-muting.ts new file mode 100644 index 0000000000..2a5824e32b --- /dev/null +++ b/packages/backend/src/models/schema/renote-muting.ts @@ -0,0 +1,30 @@ +export const packedRenoteMutingSchema = { + type: "object", + properties: { + id: { + type: "string", + optional: false, + nullable: false, + format: "id", + example: "xxxxxxxxxx", + }, + createdAt: { + type: "string", + optional: false, + nullable: false, + format: "date-time", + }, + muteeId: { + type: "string", + optional: false, + nullable: false, + format: "id", + }, + mutee: { + type: "object", + optional: false, + nullable: false, + ref: "UserDetailed", + }, + }, +} as const; diff --git a/packages/backend/src/models/schema/user.ts b/packages/backend/src/models/schema/user.ts index 9fb5aa8f3b..4c840d0bac 100644 --- a/packages/backend/src/models/schema/user.ts +++ b/packages/backend/src/models/schema/user.ts @@ -66,6 +66,11 @@ export const packedUserLiteSchema = { nullable: false, optional: true, }, + speakAsCat: { + type: "boolean", + nullable: false, + optional: true, + }, emojis: { type: "array", nullable: false, @@ -330,6 +335,11 @@ export const packedUserDetailedNotMeOnlySchema = { nullable: false, optional: true, }, + isRenoteMuted: { + type: "boolean", + nullable: false, + optional: true, + }, //#endregion }, } as const; @@ -384,6 +394,11 @@ export const packedMeDetailedOnlySchema = { nullable: true, optional: false, }, + preventAiLearning: { + type: "boolean", + nullable: true, + optional: false, + }, isExplorable: { type: "boolean", nullable: false, diff --git a/packages/backend/src/queue/index.ts b/packages/backend/src/queue/index.ts index c40b3c6aeb..93aed7cb8b 100644 --- a/packages/backend/src/queue/index.ts +++ b/packages/backend/src/queue/index.ts @@ -13,6 +13,7 @@ import processDb from "./processors/db/index.js"; import processObjectStorage from "./processors/object-storage/index.js"; import processSystemQueue from "./processors/system/index.js"; import processWebhookDeliver from "./processors/webhook-deliver.js"; +import processBackground from "./processors/background/index.js"; import { endedPollNotification } from "./processors/ended-poll-notification.js"; import { queueLogger } from "./logger.js"; import { getJobInfo } from "./get-job-info.js"; @@ -24,6 +25,7 @@ import { objectStorageQueue, endedPollNotificationQueue, webhookDeliverQueue, + backgroundQueue, } from "./queues.js"; import type { ThinUser } from "./types.js"; @@ -312,6 +314,65 @@ export function createImportFollowingJob( ); } +export function createImportPostsJob( + user: ThinUser, + fileId: DriveFile["id"], + signatureCheck: boolean, +) { + return dbQueue.add( + "importPosts", + { + user: user, + fileId: fileId, + signatureCheck: signatureCheck, + }, + { + removeOnComplete: true, + removeOnFail: true, + }, + ); +} + +export function createImportMastoPostJob( + user: ThinUser, + post: any, + signatureCheck: boolean, +) { + return dbQueue.add( + "importMastoPost", + { + user: user, + post: post, + signatureCheck: signatureCheck, + }, + { + removeOnComplete: true, + removeOnFail: true, + attempts: config.inboxJobMaxAttempts || 8, + timeout: 60 * 1000, // 1min + }, + ); +} + +export function createImportCkPostJob( + user: ThinUser, + post: any, + signatureCheck: boolean, +) { + return dbQueue.add( + "importCkPost", + { + user: user, + post: post, + signatureCheck: signatureCheck, + }, + { + removeOnComplete: true, + removeOnFail: true, + }, + ); +} + export function createImportMutingJob(user: ThinUser, fileId: DriveFile["id"]) { return dbQueue.add( "importMuting", @@ -418,6 +479,14 @@ export function createCleanRemoteFilesJob() { ); } +export function createIndexAllNotesJob(data = {}) { + return backgroundQueue.add("indexAllNotes", data, { + removeOnComplete: true, + removeOnFail: false, + timeout: 1000 * 60 * 60 * 24, + }); +} + export function webhookDeliver( webhook: Webhook, type: typeof webhookEventTypes[number], @@ -454,6 +523,7 @@ export default function () { webhookDeliverQueue.process(64, processWebhookDeliver); processDb(dbQueue); processObjectStorage(objectStorageQueue); + processBackground(backgroundQueue); systemQueue.add( "tickCharts", @@ -500,6 +570,22 @@ export default function () { }, ); + systemQueue.add( + "setLocalEmojiSizes", + {}, + { 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/initialize.ts b/packages/backend/src/queue/initialize.ts index d7945d5dad..16e623d137 100644 --- a/packages/backend/src/queue/initialize.ts +++ b/packages/backend/src/queue/initialize.ts @@ -7,8 +7,10 @@ export function initialize(name: string, limitPerSec = -1) { port: config.redis.port, host: config.redis.host, family: config.redis.family == null ? 0 : config.redis.family, + username: config.redis.user ?? "default", password: config.redis.pass, db: config.redis.db || 0, + tls: config.redis.tls, }, prefix: config.redis.prefix ? `${config.redis.prefix}:queue` : "queue", limiter: @@ -19,6 +21,8 @@ export function initialize(name: string, limitPerSec = -1) { } : undefined, settings: { + stalledInterval: 60, + maxStalledCount: 2, backoffStrategies: { apBackoff, }, diff --git a/packages/backend/src/queue/processors/background/index-all-notes.ts b/packages/backend/src/queue/processors/background/index-all-notes.ts new file mode 100644 index 0000000000..c0275b420a --- /dev/null +++ b/packages/backend/src/queue/processors/background/index-all-notes.ts @@ -0,0 +1,88 @@ +import type Bull from "bull"; +import type { DoneCallback } from "bull"; + +import { queueLogger } from "../../logger.js"; +import { Notes } from "@/models/index.js"; +import { MoreThan } from "typeorm"; +import { index } from "@/services/note/create.js"; +import { Note } from "@/models/entities/note.js"; +import meilisearch from "../../../db/meilisearch.js"; + +const logger = queueLogger.createSubLogger("index-all-notes"); + +export default async function indexAllNotes( + job: Bull.Job>, + done: DoneCallback, +): Promise { + logger.info("Indexing all notes..."); + + let cursor: string | null = (job.data.cursor as string) ?? null; + let indexedCount: number = (job.data.indexedCount as number) ?? 0; + let total: number = (job.data.total as number) ?? 0; + + let running = true; + const take = 10000; + const batch = 100; + while (running) { + logger.info( + `Querying for ${take} notes ${indexedCount}/${ + total ? total : "?" + } at ${cursor}`, + ); + + let notes: Note[] = []; + try { + notes = await Notes.find({ + where: { + ...(cursor ? { id: MoreThan(cursor) } : {}), + }, + take: take, + order: { + id: 1, + }, + relations: ["user"], + }); + } catch (e: any) { + logger.error(`Failed to query notes ${e}`); + done(e); + break; + } + + if (notes.length === 0) { + await job.progress(100); + running = false; + break; + } + + try { + const count = await Notes.count(); + total = count; + await job.update({ indexedCount, cursor, total }); + } catch (e) {} + + for (let i = 0; i < notes.length; i += batch) { + const chunk = notes.slice(i, i + batch); + + if (meilisearch) { + await meilisearch.ingestNote(chunk); + } + + await Promise.all(chunk.map((note) => index(note, true))); + + indexedCount += chunk.length; + const pct = (indexedCount / total) * 100; + await job.update({ indexedCount, cursor, total }); + await job.progress(+pct.toFixed(1)); + logger.info(`Indexed notes ${indexedCount}/${total ? total : "?"}`); + } + cursor = notes[notes.length - 1].id; + await job.update({ indexedCount, cursor, total }); + + if (notes.length < take) { + running = false; + } + } + + done(); + logger.info("All notes have been indexed."); +} diff --git a/packages/backend/src/queue/processors/background/index.ts b/packages/backend/src/queue/processors/background/index.ts new file mode 100644 index 0000000000..6674f954b0 --- /dev/null +++ b/packages/backend/src/queue/processors/background/index.ts @@ -0,0 +1,12 @@ +import type Bull from "bull"; +import indexAllNotes from "./index-all-notes.js"; + +const jobs = { + indexAllNotes, +} as Record>>; + +export default function (q: Bull.Queue) { + for (const [k, v] of Object.entries(jobs)) { + q.process(k, 16, v); + } +} diff --git a/packages/backend/src/queue/processors/db/delete-account.ts b/packages/backend/src/queue/processors/db/delete-account.ts index 764b83db2d..1cd7642ba5 100644 --- a/packages/backend/src/queue/processors/db/delete-account.ts +++ b/packages/backend/src/queue/processors/db/delete-account.ts @@ -7,6 +7,7 @@ import type { DriveFile } from "@/models/entities/drive-file.js"; import { MoreThan } from "typeorm"; import { deleteFileSync } from "@/services/drive/delete-file.js"; import { sendEmail } from "@/services/send-email.js"; +import meilisearch from "@/db/meilisearch.js"; const logger = queueLogger.createSubLogger("delete-account"); @@ -16,9 +17,7 @@ export async function deleteAccount( logger.info(`Deleting account of ${job.data.user.id} ...`); const user = await Users.findOneBy({ id: job.data.user.id }); - if (user == null) { - return; - } + if (!user) return; { // Delete notes @@ -43,6 +42,9 @@ export async function deleteAccount( cursor = notes[notes.length - 1].id; await Notes.delete(notes.map((note) => note.id)); + if (meilisearch) { + await meilisearch.deleteNotes(notes); + } } logger.succ("All of notes deleted"); diff --git a/packages/backend/src/queue/processors/db/export-notes.ts b/packages/backend/src/queue/processors/db/export-notes.ts index de8fac05b4..bf53f83603 100644 --- a/packages/backend/src/queue/processors/db/export-notes.ts +++ b/packages/backend/src/queue/processors/db/export-notes.ts @@ -4,7 +4,7 @@ import * as fs from "node:fs"; import { queueLogger } from "../../logger.js"; import { addFile } from "@/services/drive/add-file.js"; import { format as dateFormat } from "date-fns"; -import { Users, Notes, Polls } from "@/models/index.js"; +import { Users, Notes, Polls, DriveFiles } from "@/models/index.js"; import { MoreThan } from "typeorm"; import type { Note } from "@/models/entities/note.js"; import type { Poll } from "@/models/entities/poll.js"; @@ -75,7 +75,7 @@ export async function exportNotes( if (note.hasPoll) { poll = await Polls.findOneByOrFail({ noteId: note.id }); } - const content = JSON.stringify(serialize(note, poll)); + const content = JSON.stringify(await serialize(note, poll)); const isFirst = exportedNotesCount === 0; await write(isFirst ? content : ",\n" + content); exportedNotesCount++; @@ -112,15 +112,16 @@ export async function exportNotes( done(); } -function serialize( +async function serialize( note: Note, poll: Poll | null = null, -): Record { +): Promise> { return { id: note.id, text: note.text, createdAt: note.createdAt, fileIds: note.fileIds, + files: await DriveFiles.packMany(note.fileIds), replyId: note.replyId, renoteId: note.renoteId, poll: poll, diff --git a/packages/backend/src/queue/processors/db/import-custom-emojis.ts b/packages/backend/src/queue/processors/db/import-custom-emojis.ts index 45ab5ea9a1..9e8b3b174e 100644 --- a/packages/backend/src/queue/processors/db/import-custom-emojis.ts +++ b/packages/backend/src/queue/processors/db/import-custom-emojis.ts @@ -1,6 +1,6 @@ import type Bull from "bull"; import * as fs from "node:fs"; -import unzipper from "unzipper"; +import AdmZip from "adm-zip"; import { queueLogger } from "../../logger.js"; import { createTempDir } from "@/misc/create-temp.js"; @@ -10,6 +10,8 @@ import type { DbUserImportJobData } from "@/queue/types.js"; import { addFile } from "@/services/drive/add-file.js"; import { genId } from "@/misc/gen-id.js"; import { db } from "@/db/postgre.js"; +import probeImageSize from "probe-image-size"; +import * as path from "path"; const logger = queueLogger.createSubLogger("import-custom-emojis"); @@ -28,11 +30,11 @@ export async function importCustomEmojis( return; } - const [path, cleanup] = await createTempDir(); + const [tempPath, cleanup] = await createTempDir(); - logger.info(`Temp dir is ${path}`); + logger.info(`Temp dir is ${tempPath}`); - const destPath = `${path}/emojis.zip`; + const destPath = `${tempPath}/emojis.zip`; try { fs.writeFileSync(destPath, "", "binary"); @@ -45,37 +47,96 @@ export async function importCustomEmojis( throw e; } - const outputPath = `${path}/emojis`; + const outputPath = `${tempPath}/emojis`; const unzipStream = fs.createReadStream(destPath); - const extractor = unzipper.Extract({ path: outputPath }); - extractor.on("close", async () => { - const metaRaw = fs.readFileSync(`${outputPath}/meta.json`, "utf-8"); - const meta = JSON.parse(metaRaw); + const zip = new AdmZip(destPath); + zip.extractAllToAsync(outputPath, true, false, async (error) => { + if (error) throw error; - for (const record of meta.emojis) { - if (!record.downloaded) continue; - const emojiInfo = record.emoji; - const emojiPath = `${outputPath}/${record.fileName}`; - await Emojis.delete({ - name: emojiInfo.name, - }); - const driveFile = await addFile({ - user: null, - path: emojiPath, - name: record.fileName, - force: true, - }); - const emoji = await Emojis.insert({ - id: genId(), - updatedAt: new Date(), - name: emojiInfo.name, - category: emojiInfo.category, - host: null, - aliases: emojiInfo.aliases, - originalUrl: driveFile.url, - publicUrl: driveFile.webpublicUrl ?? driveFile.url, - type: driveFile.webpublicType ?? driveFile.type, - }).then((x) => Emojis.findOneByOrFail(x.identifiers[0])); + if (fs.existsSync(`${outputPath}/meta.json`)) { + logger.info("starting emoji import with metadata"); + const metaRaw = fs.readFileSync(`${outputPath}/meta.json`, "utf-8"); + const meta = JSON.parse(metaRaw); + + for (const record of meta.emojis) { + if (!record.downloaded) continue; + const emojiInfo = record.emoji; + const emojiPath = `${outputPath}/${record.fileName}`; + await Emojis.delete({ + name: emojiInfo.name, + }); + const driveFile = await addFile({ + user: null, + path: emojiPath, + name: record.fileName, + force: true, + }); + const file = fs.createReadStream(emojiPath); + const size = await probeImageSize(file); + file.destroy(); + await Emojis.insert({ + id: genId(), + updatedAt: new Date(), + name: emojiInfo.name, + category: emojiInfo.category, + host: null, + aliases: emojiInfo.aliases, + originalUrl: driveFile.url, + publicUrl: driveFile.webpublicUrl ?? driveFile.url, + type: driveFile.webpublicType ?? driveFile.type, + license: emojiInfo.license, + width: size.width || null, + height: size.height || null, + }).then((x) => Emojis.findOneByOrFail(x.identifiers[0])); + } + } else { + logger.info("starting emoji import without metadata"); + // Since we lack metadata, we import into a randomized category name instead + let categoryName = genId(); + + let containedEmojis = fs.readdirSync(outputPath); + + // Filter out accidental JSON files + containedEmojis = containedEmojis.filter( + (emoji) => !emoji.match(/\.(json)$/i), + ); + + for (const emojiFilename of containedEmojis) { + // strip extension and get filename to use as name + const name = path.basename(emojiFilename, path.extname(emojiFilename)); + const emojiPath = `${outputPath}/${emojiFilename}`; + + logger.info(`importing ${name}`); + + await Emojis.delete({ + name: name, + }); + const driveFile = await addFile({ + user: null, + path: emojiPath, + name: path.basename(emojiFilename), + force: true, + }); + const file = fs.createReadStream(emojiPath); + const size = await probeImageSize(file); + file.destroy(); + logger.info(`emoji size: ${size.width}x${size.height}`); + + await Emojis.insert({ + id: genId(), + updatedAt: new Date(), + name: name, + category: categoryName, + host: null, + aliases: [], + originalUrl: driveFile.url, + publicUrl: driveFile.webpublicUrl ?? driveFile.url, + type: driveFile.webpublicType ?? driveFile.type, + license: null, + width: size.width || null, + height: size.height || null, + }).then((x) => Emojis.findOneByOrFail(x.identifiers[0])); + } } await db.queryResultCache!.remove(["meta_emojis"]); @@ -85,6 +146,5 @@ export async function importCustomEmojis( logger.succ("Imported"); done(); }); - unzipStream.pipe(extractor); logger.succ(`Unzipping to ${outputPath}`); } diff --git a/packages/backend/src/queue/processors/db/import-firefish-post.ts b/packages/backend/src/queue/processors/db/import-firefish-post.ts new file mode 100644 index 0000000000..504cf9e507 --- /dev/null +++ b/packages/backend/src/queue/processors/db/import-firefish-post.ts @@ -0,0 +1,51 @@ +import * as Post from "@/misc/post.js"; +import create from "@/services/note/create.js"; +import { Users } from "@/models/index.js"; +import type { DbUserImportMastoPostJobData } from "@/queue/types.js"; +import { queueLogger } from "../../logger.js"; +import type Bull from "bull"; + +const logger = queueLogger.createSubLogger("import-firefish-post"); + +export async function importCkPost( + job: Bull.Job, + done: any, +): Promise { + const user = await Users.findOneBy({ id: job.data.user.id }); + if (user == null) { + done(); + return; + } + const post = job.data.post; + if (post.replyId != null) { + done(); + return; + } + if (post.renoteId != null) { + done(); + return; + } + if (post.visibility !== "public") { + done(); + return; + } + const { text, cw, localOnly, createdAt } = Post.parse(post); + const note = await create(user, { + createdAt: createdAt, + files: undefined, + poll: undefined, + text: text || undefined, + reply: null, + renote: null, + cw: cw, + localOnly, + visibility: "hidden", + visibleUsers: [], + channel: null, + apMentions: new Array(0), + apHashtags: undefined, + apEmojis: undefined, + }); + logger.succ("Imported"); + done(); +} diff --git a/packages/backend/src/queue/processors/db/import-masto-post.ts b/packages/backend/src/queue/processors/db/import-masto-post.ts new file mode 100644 index 0000000000..1d18008a0f --- /dev/null +++ b/packages/backend/src/queue/processors/db/import-masto-post.ts @@ -0,0 +1,91 @@ +import create from "@/services/note/create.js"; +import { Users } from "@/models/index.js"; +import type { DbUserImportMastoPostJobData } from "@/queue/types.js"; +import { queueLogger } from "../../logger.js"; +import type Bull from "bull"; +import { htmlToMfm } from "@/remote/activitypub/misc/html-to-mfm.js"; +import { resolveNote } from "@/remote/activitypub/models/note.js"; +import { Note } from "@/models/entities/note.js"; +import { uploadFromUrl } from "@/services/drive/upload-from-url.js"; +import type { DriveFile } from "@/models/entities/drive-file.js"; + +const logger = queueLogger.createSubLogger("import-masto-post"); + +export async function importMastoPost( + job: Bull.Job, + done: any, +): Promise { + const user = await Users.findOneBy({ id: job.data.user.id }); + if (user == null) { + done(); + return; + } + const post = job.data.post; + let reply: Note | null = null; + job.progress(20); + if (post.object.inReplyTo != null) { + reply = await resolveNote(post.object.inReplyTo); + } + job.progress(40); + if (post.directMessage) { + done(); + return; + } + if (job.data.signatureCheck) { + if (!post.signature) { + done(); + return; + } + } + job.progress(60); + let text; + try { + text = htmlToMfm(post.object.content, post.object.tag); + } catch (e) { + throw e; + } + job.progress(80); + + let files: DriveFile[] = (post.object.attachment || []) + .map((x: any) => x?.driveFile) + .filter((x: any) => x); + + if (files.length == 0) { + const urls = post.object.attachment + .map((x: any) => x.url) + .filter((x: String) => x.startsWith("http")); + files = []; + for (const url of urls) { + try { + const file = await uploadFromUrl({ + url: url, + user: user, + }); + files.push(file); + } catch (e) { + logger.error(`Skipped adding file to drive: ${url}`); + } + } + } + + const note = await create(user, { + createdAt: new Date(post.object.published), + files: files.length == 0 ? undefined : files, + poll: undefined, + text: text || undefined, + reply, + renote: null, + cw: post.object.sensitive ? post.object.summary : undefined, + localOnly: false, + visibility: "hidden", + visibleUsers: [], + channel: null, + apMentions: new Array(0), + apHashtags: undefined, + apEmojis: undefined, + }); + job.progress(100); + done(); + + logger.succ("Imported"); +} diff --git a/packages/backend/src/queue/processors/db/import-posts.ts b/packages/backend/src/queue/processors/db/import-posts.ts new file mode 100644 index 0000000000..9bde7479ed --- /dev/null +++ b/packages/backend/src/queue/processors/db/import-posts.ts @@ -0,0 +1,76 @@ +import { downloadTextFile } from "@/misc/download-text-file.js"; +import { processMastoNotes } from "@/misc/process-masto-notes.js"; +import { Users, DriveFiles } from "@/models/index.js"; +import type { DbUserImportPostsJobData } from "@/queue/types.js"; +import { queueLogger } from "../../logger.js"; +import type Bull from "bull"; +import { + createImportCkPostJob, + createImportMastoPostJob, +} from "@/queue/index.js"; + +const logger = queueLogger.createSubLogger("import-posts"); + +export async function importPosts( + job: Bull.Job, + done: any, +): Promise { + logger.info(`Importing posts of ${job.data.user.id} ...`); + + const user = await Users.findOneBy({ id: job.data.user.id }); + if (user == null) { + done(); + return; + } + + const file = await DriveFiles.findOneBy({ + id: job.data.fileId, + }); + if (file == null) { + done(); + return; + } + + if (file.name.endsWith("tar.gz") || file.name.endsWith("zip")) { + try { + logger.info("Reading Mastodon archive"); + const outbox = await processMastoNotes( + file.name, + file.url, + job.data.user.id, + ); + for (const post of outbox.orderedItems) { + createImportMastoPostJob(job.data.user, post, job.data.signatureCheck); + } + } catch (e) { + // handle error + logger.warn(`Failed reading Mastodon archive: ${e}`); + } + logger.succ("Mastodon archive imported"); + done(); + return; + } + + const json = await downloadTextFile(file.url); + + try { + const parsed = JSON.parse(json); + if (parsed instanceof Array) { + logger.info("Parsing key style posts"); + for (const post of JSON.parse(json)) { + createImportCkPostJob(job.data.user, post, job.data.signatureCheck); + } + } else if (parsed instanceof Object) { + logger.info("Parsing animal style posts"); + for (const post of parsed.orderedItems) { + createImportMastoPostJob(job.data.user, post, job.data.signatureCheck); + } + } + } catch (e) { + // handle error + logger.warn(`Error reading: ${e}`); + } + + logger.succ("Imported"); + done(); +} diff --git a/packages/backend/src/queue/processors/db/index.ts b/packages/backend/src/queue/processors/db/index.ts index 90173053fb..d20fc2c71a 100644 --- a/packages/backend/src/queue/processors/db/index.ts +++ b/packages/backend/src/queue/processors/db/index.ts @@ -11,6 +11,9 @@ import { importFollowing } from "./import-following.js"; import { importUserLists } from "./import-user-lists.js"; import { deleteAccount } from "./delete-account.js"; import { importMuting } from "./import-muting.js"; +import { importPosts } from "./import-posts.js"; +import { importMastoPost } from "./import-masto-post.js"; +import { importCkPost } from "./import-firefish-post.js"; import { importBlocking } from "./import-blocking.js"; import { importCustomEmojis } from "./import-custom-emojis.js"; @@ -26,6 +29,9 @@ const jobs = { importMuting, importBlocking, importUserLists, + importPosts, + importMastoPost, + importCkPost, importCustomEmojis, deleteAccount, } as Record< diff --git a/packages/backend/src/queue/processors/ended-poll-notification.ts b/packages/backend/src/queue/processors/ended-poll-notification.ts index 9fe57d8da3..3bf010a1b9 100644 --- a/packages/backend/src/queue/processors/ended-poll-notification.ts +++ b/packages/backend/src/queue/processors/ended-poll-notification.ts @@ -1,9 +1,9 @@ import type Bull from "bull"; -import { In } from "typeorm"; -import { Notes, Polls, PollVotes } from "@/models/index.js"; +import { Notes, PollVotes } from "@/models/index.js"; import { queueLogger } from "../logger.js"; import type { EndedPollNotificationJobData } from "@/queue/types.js"; import { createNotification } from "@/services/create-notification.js"; +import { deliverQuestionUpdate } from "@/services/note/polls/update.js"; const logger = queueLogger.createSubLogger("ended-poll-notification"); @@ -32,5 +32,8 @@ export async function endedPollNotification( }); } + // Broadcast the poll result once it ends + if (!note.localOnly) await deliverQuestionUpdate(note.id); + done(); } diff --git a/packages/backend/src/queue/processors/inbox.ts b/packages/backend/src/queue/processors/inbox.ts index ca063a6f3f..0e500b89ed 100644 --- a/packages/backend/src/queue/processors/inbox.ts +++ b/packages/backend/src/queue/processors/inbox.ts @@ -34,6 +34,12 @@ export default async (job: Bull.Job): Promise => { const info = Object.assign({}, activity) as any; info["@context"] = undefined; logger.debug(JSON.stringify(info, null, 2)); + + if (!signature?.keyId) { + const err = `Invalid signature: ${signature}`; + job.moveToFailed({ message: err }); + return err; + } //#endregion const host = toPuny(new URL(signature.keyId).hostname); diff --git a/packages/backend/src/queue/processors/system/index.ts b/packages/backend/src/queue/processors/system/index.ts index 68833d76f4..697d24d067 100644 --- a/packages/backend/src/queue/processors/system/index.ts +++ b/packages/backend/src/queue/processors/system/index.ts @@ -4,6 +4,8 @@ import { resyncCharts } from "./resync-charts.js"; 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, @@ -11,6 +13,8 @@ const jobs = { cleanCharts, checkExpiredMutings, clean, + setLocalEmojiSizes, + verifyLinks, } as Record< string, | Bull.ProcessCallbackFunction> diff --git a/packages/backend/src/queue/processors/system/local-emoji-size.ts b/packages/backend/src/queue/processors/system/local-emoji-size.ts new file mode 100644 index 0000000000..d696bbd863 --- /dev/null +++ b/packages/backend/src/queue/processors/system/local-emoji-size.ts @@ -0,0 +1,42 @@ +import type Bull from "bull"; +import { IsNull } from "typeorm"; +import { Emojis } from "@/models/index.js"; + +import { queueLogger } from "../../logger.js"; +import { getEmojiSize } from "@/misc/emoji-meta.js"; + +const logger = queueLogger.createSubLogger("local-emoji-size"); + +export async function setLocalEmojiSizes( + _job: Bull.Job>, + done: any, +): Promise { + logger.info("Setting sizes of local emojis..."); + + const emojis = await Emojis.findBy([ + { host: IsNull(), width: IsNull(), height: IsNull() }, + ]); + logger.info(`${emojis.length} emojis need to be fetched.`); + + for (let i = 0; i < emojis.length; i++) { + try { + const size = await getEmojiSize(emojis[i].publicUrl); + await Emojis.update(emojis[i].id, { + width: size.width || null, + height: size.height || null, + }); + } catch (e) { + logger.error( + `Unable to set emoji size (${i + 1}/${emojis.length}): ${e}`, + ); + /* skip if any error happens */ + } finally { + // wait for 1sec so that this would not overwhelm the object storage. + await new Promise((resolve) => setTimeout(resolve, 1000)); + if (i % 10 === 9) logger.succ(`fetched ${i + 1}/${emojis.length} emojis`); + } + } + + logger.succ("Done."); + done(); +} 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 0000000000..3ddda9baf4 --- /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/queue/processors/webhook-deliver.ts b/packages/backend/src/queue/processors/webhook-deliver.ts index a130fcd382..286b401ba7 100644 --- a/packages/backend/src/queue/processors/webhook-deliver.ts +++ b/packages/backend/src/queue/processors/webhook-deliver.ts @@ -16,10 +16,11 @@ export default async (job: Bull.Job) => { url: job.data.to, method: "POST", headers: { - "User-Agent": "Calckey-Hooks", - "X-Calckey-Host": config.host, - "X-Calckey-Hook-Id": job.data.webhookId, - "X-Calckey-Hook-Secret": job.data.secret, + "User-Agent": "Firefish-Hooks", + "X-Firefish-Host": config.host, + "X-Firefish-Hook-Id": job.data.webhookId, + "X-Firefish-Hook-Secret": job.data.secret, + "Content-Type": "application/json", }, body: JSON.stringify({ hookId: job.data.webhookId, diff --git a/packages/backend/src/queue/queues.ts b/packages/backend/src/queue/queues.ts index 12d9d66207..6b0eb2de42 100644 --- a/packages/backend/src/queue/queues.ts +++ b/packages/backend/src/queue/queues.ts @@ -20,13 +20,14 @@ export const inboxQueue = initializeQueue( "inbox", config.inboxJobPerSec || 16, ); -export const dbQueue = initializeQueue("db"); +export const dbQueue = initializeQueue("db", 256); export const objectStorageQueue = initializeQueue("objectStorage"); export const webhookDeliverQueue = initializeQueue( "webhookDeliver", 64, ); +export const backgroundQueue = initializeQueue>("bg"); export const queues = [ systemQueue, @@ -36,4 +37,5 @@ export const queues = [ dbQueue, objectStorageQueue, webhookDeliverQueue, + backgroundQueue, ]; diff --git a/packages/backend/src/queue/types.ts b/packages/backend/src/queue/types.ts index 90e88f7366..b72b127894 100644 --- a/packages/backend/src/queue/types.ts +++ b/packages/backend/src/queue/types.ts @@ -21,8 +21,10 @@ export type InboxJobData = { export type DbJobData = | DbUserJobData + | DbUserImportPostsJobData | DbUserImportJobData - | DbUserDeleteJobData; + | DbUserDeleteJobData + | DbUserImportMastoPostJobData; export type DbUserJobData = { user: ThinUser; @@ -40,6 +42,18 @@ export type DbUserImportJobData = { fileId: DriveFile["id"]; }; +export type DbUserImportPostsJobData = { + user: ThinUser; + fileId: DriveFile["id"]; + signatureCheck: boolean; +}; + +export type DbUserImportMastoPostJobData = { + user: ThinUser; + post: any; + signatureCheck: boolean; +}; + export type ObjectStorageJobData = | ObjectStorageFileJobData | Record; diff --git a/packages/backend/src/remote/activitypub/check-fetch.ts b/packages/backend/src/remote/activitypub/check-fetch.ts index a8bbe61b84..c885b4a199 100644 --- a/packages/backend/src/remote/activitypub/check-fetch.ts +++ b/packages/backend/src/remote/activitypub/check-fetch.ts @@ -7,6 +7,8 @@ import DbResolver from "@/remote/activitypub/db-resolver.js"; import { getApId } from "@/remote/activitypub/type.js"; import { shouldBlockInstance } from "@/misc/should-block-instance.js"; import type { IncomingMessage } from "http"; +import type { CacheableRemoteUser } from "@/models/entities/user.js"; +import type { UserPublickey } from "@/models/entities/user-publickey.js"; export async function hasSignature(req: IncomingMessage): Promise { const meta = await fetchMeta(); @@ -95,3 +97,22 @@ export async function checkFetch(req: IncomingMessage): Promise { } return 200; } + +export async function getSignatureUser(req: IncomingMessage): Promise<{ + user: CacheableRemoteUser; + key: UserPublickey | null; +} | null> { + const signature = httpSignature.parseRequest(req, { headers: [] }); + const keyId = new URL(signature.keyId); + const dbResolver = new DbResolver(); + + // Retrieve from DB by HTTP-Signature keyId + const authUser = await dbResolver.getAuthUserFromKeyId(signature.keyId); + if (authUser) { + return authUser; + } + + // Resolve if failed to retrieve by keyId + keyId.hash = ""; + return await dbResolver.getAuthUserFromApId(getApId(keyId.toString())); +} diff --git a/packages/backend/src/remote/activitypub/db-resolver.ts b/packages/backend/src/remote/activitypub/db-resolver.ts index 0a2aec9e85..a710b9f115 100644 --- a/packages/backend/src/remote/activitypub/db-resolver.ts +++ b/packages/backend/src/remote/activitypub/db-resolver.ts @@ -5,7 +5,6 @@ import type { CacheableRemoteUser, CacheableUser, } from "@/models/entities/user.js"; -import { User, IRemoteUser } from "@/models/entities/user.js"; import type { UserPublickey } from "@/models/entities/user-publickey.js"; import type { MessagingMessage } from "@/models/entities/messaging-message.js"; import { @@ -20,8 +19,11 @@ import type { IObject } from "./type.js"; import { getApId } from "./type.js"; import { resolvePerson } from "./models/person.js"; -const publicKeyCache = new Cache(Infinity); -const publicKeyByUserIdCache = new Cache(Infinity); +const publicKeyCache = new Cache("publicKey", 60 * 30); +const publicKeyByUserIdCache = new Cache( + "publicKeyByUserId", + 60 * 30, +); export type UriParseResult = | { @@ -67,8 +69,6 @@ export function parseUri(value: string | IObject): UriParseResult { } export default class DbResolver { - constructor() {} - /** * AP Note => Misskey Note in DB */ @@ -82,8 +82,15 @@ export default class DbResolver { id: parsed.id, }); } else { - return await Notes.findOneBy({ - uri: parsed.uri, + return await Notes.findOne({ + where: [ + { + uri: parsed.uri, + }, + { + url: parsed.uri, + }, + ], }); } } @@ -118,17 +125,23 @@ export default class DbResolver { if (parsed.type !== "users") return null; return ( - (await userByIdCache.fetchMaybe(parsed.id, () => - Users.findOneBy({ - id: parsed.id, - }).then((x) => x ?? undefined), + (await userByIdCache.fetchMaybe( + parsed.id, + () => + Users.findOneBy({ + id: parsed.id, + }).then((x) => x ?? undefined), + true, )) ?? null ); } else { - return await uriPersonCache.fetch(parsed.uri, () => - Users.findOneBy({ - uri: parsed.uri, - }), + return await uriPersonCache.fetch( + parsed.uri, + () => + Users.findOneBy({ + uri: parsed.uri, + }), + true, ); } } @@ -151,14 +164,17 @@ export default class DbResolver { return key; }, + true, (key) => key != null, ); if (key == null) return null; return { - user: (await userByIdCache.fetch(key.userId, () => - Users.findOneByOrFail({ id: key.userId }), + user: (await userByIdCache.fetch( + key.userId, + () => Users.findOneByOrFail({ id: key.userId }), + true, )) as CacheableRemoteUser, key, }; @@ -178,6 +194,7 @@ export default class DbResolver { const key = await publicKeyByUserIdCache.fetch( user.id, () => UserPublickeys.findOneBy({ userId: user.id }), + true, (v) => v != null, ); diff --git a/packages/backend/src/remote/activitypub/kernel/delete/actor.ts b/packages/backend/src/remote/activitypub/kernel/delete/actor.ts index 3571135aa5..83c6442dde 100644 --- a/packages/backend/src/remote/activitypub/kernel/delete/actor.ts +++ b/packages/backend/src/remote/activitypub/kernel/delete/actor.ts @@ -15,9 +15,11 @@ export async function deleteActor( return `skip: delete actor ${actor.uri} !== ${uri}`; } - const user = await Users.findOneByOrFail({ id: actor.id }); - if (user.isDeleted) { - logger.info("skip: already deleted"); + const user = await Users.findOneBy({ id: actor.id }); + if (!user) { + return `skip: actor ${actor.id} not found in the local database`; + } else if (user.isDeleted) { + return `skip: user ${user.id} already deleted`; } const job = await createDeleteAccountJob(actor); diff --git a/packages/backend/src/remote/activitypub/kernel/move/index.ts b/packages/backend/src/remote/activitypub/kernel/move/index.ts index 47a55b8ab3..800fd7bfea 100644 --- a/packages/backend/src/remote/activitypub/kernel/move/index.ts +++ b/packages/backend/src/remote/activitypub/kernel/move/index.ts @@ -1,19 +1,14 @@ import type { CacheableRemoteUser } from "@/models/entities/user.js"; -import { IRemoteUser, User } from "@/models/entities/user.js"; -import DbResolver from "@/remote/activitypub/db-resolver.js"; -import { getRemoteUser } from "@/server/api/common/getters.js"; -import { updatePerson } from "@/remote/activitypub/models/person.js"; import { Followings, Users } from "@/models/index.js"; -import { makePaginationQuery } from "@/server/api/common/make-pagination-query.js"; -import deleteFollowing from "@/services/following/delete.js"; +import { + resolvePerson, + updatePerson, +} from "@/remote/activitypub/models/person.js"; import create from "@/services/following/create.js"; -import { getUser } from "@/server/api/common/getters.js"; -import { IdentifiableError } from "@/misc/identifiable-error.js"; -import { ApiError } from "@/server/api/error.js"; -import { meta } from "@/server/api/endpoints/following/create.js"; -import { IObject } from "../../type.js"; -import type { IMove, IActor } from "../../type.js"; -import Resolver from "@/remote/activitypub/resolver.js"; +import deleteFollowing from "@/services/following/delete.js"; + +import type { IMove } from "../../type.js"; +import { getApHrefNullable } from "../../type.js"; export default async ( actor: CacheableRemoteUser, @@ -21,68 +16,50 @@ export default async ( ): Promise => { // ※ There is a block target in activity.object, which should be a local user that exists. - const dbResolver = new DbResolver(); - const resolver = new Resolver(); - let new_acc = await dbResolver.getUserFromApId(activity.target); - let actor_new; - if (!new_acc) - actor_new = (await resolver.resolve(activity.target)) as IActor; + // fetch the new and old accounts + const targetUri = getApHrefNullable(activity.target); + if (!targetUri) return "move: target uri is null"; + let new_acc = await resolvePerson(targetUri); + if (!actor.uri) return "move: actor uri is null"; + let old_acc = await resolvePerson(actor.uri); - if ( - (!new_acc || new_acc.uri === null) && - (!actor_new || actor_new.id === null) - ) { - return "move: new acc not found"; + // update them if they're remote + if (new_acc.uri) await updatePerson(new_acc.uri); + if (old_acc.uri) await updatePerson(old_acc.uri); + + // retrieve updated users + new_acc = await resolvePerson(targetUri); + old_acc = await resolvePerson(actor.uri); + + // check if alsoKnownAs of the new account is valid + let isValidMove = true; + if (old_acc.uri) { + if (!new_acc.alsoKnownAs?.includes(old_acc.uri)) { + isValidMove = false; + } + } else if (!new_acc.alsoKnownAs?.includes(old_acc.id)) { + isValidMove = false; + } + if (!isValidMove) { + return "skip: accounts invalid"; } - const newUri = new_acc ? new_acc.uri : actor_new?.url?.toString(); - - if (newUri === null || newUri === undefined) - return "move: new acc not found #2"; - - await updatePerson(newUri); - await updatePerson(actor.uri!); - - new_acc = await dbResolver.getUserFromApId(newUri); - const old = await dbResolver.getUserFromApId(actor.uri!); - - if ( - old === null || - old.uri === null || - !new_acc?.alsoKnownAs?.includes(old.uri) - ) - return "move: accounts invalid"; - - old.movedToUri = new_acc.uri; - - const followee = await getUser(actor.id).catch((e) => { - if (e.id === "15348ddd-432d-49c2-8a5a-8069753becff") - throw new ApiError(meta.errors.noSuchUser); - throw e; - }); - - const followeeNew = await getUser(new_acc.id).catch((e) => { - if (e.id === "15348ddd-432d-49c2-8a5a-8069753becff") - throw new ApiError(meta.errors.noSuchUser); - throw e; - }); + // add target uri to movedToUri in order to indicate that the user has moved + await Users.update(old_acc.id, { movedToUri: targetUri }); + // follow the new account and unfollow the old one const followings = await Followings.findBy({ - followeeId: followee.id, + followeeId: old_acc.id, }); - followings.forEach(async (following) => { - //if follower is local + // If follower is local if (!following.followerHost) { - const follower = await getUser(following.followerId).catch((e) => { - if (e.id === "15348ddd-432d-49c2-8a5a-8069753becff") - throw new ApiError(meta.errors.noSuchUser); - throw e; - }); - await deleteFollowing(follower!, followee); try { - await create(follower!, followeeNew); - } catch (e) { + const follower = await Users.findOneBy({ id: following.followerId }); + if (!follower) return; + await create(follower, new_acc); + await deleteFollowing(follower, old_acc); + } catch { /* empty */ } } diff --git a/packages/backend/src/remote/activitypub/kernel/update/index.ts b/packages/backend/src/remote/activitypub/kernel/update/index.ts index 4f1514ddd1..558a20ce0f 100644 --- a/packages/backend/src/remote/activitypub/kernel/update/index.ts +++ b/packages/backend/src/remote/activitypub/kernel/update/index.ts @@ -2,7 +2,7 @@ import type { CacheableRemoteUser } from "@/models/entities/user.js"; import type { IUpdate } from "../../type.js"; import { getApType, isActor } from "../../type.js"; import { apLogger } from "../../logger.js"; -import { updateQuestion } from "../../models/question.js"; +import { updateNote } from "../../models/note.js"; import Resolver from "../../resolver.js"; import { updatePerson } from "../../models/person.js"; @@ -29,10 +29,22 @@ export default async ( if (isActor(object)) { await updatePerson(actor.uri!, resolver, object); return "ok: Person updated"; - } else if (getApType(object) === "Question") { - await updateQuestion(object, resolver).catch((e) => console.log(e)); - return "ok: Question updated"; - } else { - return `skip: Unknown type: ${getApType(object)}`; + } + + const objectType = getApType(object); + switch (objectType) { + case "Question": + case "Note": + case "Article": + case "Document": + case "Page": + let failed = false; + await updateNote(object, resolver).catch((e: Error) => { + failed = true; + }); + return failed ? "skip: Note update failed" : "ok: Note updated"; + + default: + return `skip: Unknown type: ${objectType}`; } }; diff --git a/packages/backend/src/remote/activitypub/misc/ld-signature.ts b/packages/backend/src/remote/activitypub/misc/ld-signature.ts index 0a4ec3a539..62707624be 100644 --- a/packages/backend/src/remote/activitypub/misc/ld-signature.ts +++ b/packages/backend/src/remote/activitypub/misc/ld-signature.ts @@ -11,8 +11,6 @@ export class LdSignature { public preLoad = true; public loderTimeout = 10 * 1000; - constructor() {} - public async signRsaSignature2017( data: any, privateKey: string, @@ -70,13 +68,13 @@ export class LdSignature { ...options, "@context": "https://w3id.org/identity/v1", }; - transformedOptions["type"] = undefined; - transformedOptions["id"] = undefined; - transformedOptions["signatureValue"] = undefined; + delete transformedOptions["type"]; + delete transformedOptions["id"]; + delete transformedOptions["signatureValue"]; const canonizedOptions = await this.normalize(transformedOptions); const optionsHash = this.sha256(canonizedOptions); const transformedData = { ...data }; - transformedData["signature"] = undefined; + delete transformedData["signature"]; const cannonidedData = await this.normalize(transformedData); if (this.debug) console.debug(`cannonidedData: ${cannonidedData}`); const documentHash = this.sha256(cannonidedData); diff --git a/packages/backend/src/remote/activitypub/models/image.ts b/packages/backend/src/remote/activitypub/models/image.ts index 415f7c4006..67652d57de 100644 --- a/packages/backend/src/remote/activitypub/models/image.ts +++ b/packages/backend/src/remote/activitypub/models/image.ts @@ -26,7 +26,11 @@ export async function createImage( const image = (await new Resolver().resolve(value)) as any; if (image.url == null) { - throw new Error("invalid image: url not privided"); + throw new Error("Invalid image, URL not provided"); + } + + if (!image.url.startsWith("https://") && !image.url.startsWith("http://")) { + throw new Error(`Invalid image, unexpected schema: ${image.url}`); } logger.info(`Creating the Image: ${image.url}`); @@ -64,8 +68,8 @@ export async function createImage( /** * Resolve Image. * - * If the target Image is registered in Calckey, return it, otherwise - * Fetch from remote server, register with Calckey and return it. + * If the target Image is registered in Firefish, return it, otherwise + * Fetch from remote server, register with Firefish and return it. */ export async function resolveImage( actor: CacheableRemoteUser, diff --git a/packages/backend/src/remote/activitypub/models/note.ts b/packages/backend/src/remote/activitypub/models/note.ts index 34d8d0ba16..391f669f3f 100644 --- a/packages/backend/src/remote/activitypub/models/note.ts +++ b/packages/backend/src/remote/activitypub/models/note.ts @@ -1,22 +1,32 @@ import promiseLimit from "promise-limit"; - +import * as mfm from "mfm-js"; import config from "@/config/index.js"; import Resolver from "../resolver.js"; import post from "@/services/note/create.js"; +import { extractMentionedUsers } from "@/services/note/create.js"; import { resolvePerson } from "./person.js"; import { resolveImage } from "./image.js"; -import type { CacheableRemoteUser } from "@/models/entities/user.js"; +import type { + ILocalUser, + CacheableRemoteUser, +} from "@/models/entities/user.js"; import { htmlToMfm } from "../misc/html-to-mfm.js"; import { extractApHashtags } from "./tag.js"; import { unique, toArray, toSingle } from "@/prelude/array.js"; import { extractPollFromQuestion } from "./question.js"; import vote from "@/services/note/polls/vote.js"; import { apLogger } from "../logger.js"; -import type { DriveFile } from "@/models/entities/drive-file.js"; -import { deliverQuestionUpdate } from "@/services/note/polls/update.js"; +import { DriveFile } from "@/models/entities/drive-file.js"; import { extractDbHost, toPuny } from "@/misc/convert-host.js"; -import { Emojis, Polls, MessagingMessages } from "@/models/index.js"; -import type { Note } from "@/models/entities/note.js"; +import { + Emojis, + Polls, + MessagingMessages, + Notes, + NoteEdits, + DriveFiles, +} from "@/models/index.js"; +import type { IMentionedRemoteUsers, Note } from "@/models/entities/note.js"; import type { IObject, IPost } from "../type.js"; import { getOneApId, @@ -28,7 +38,6 @@ import { } from "../type.js"; import type { Emoji } from "@/models/entities/emoji.js"; import { genId } from "@/misc/gen-id.js"; -import { fetchMeta } from "@/misc/fetch-meta.js"; import { getApLock } from "@/misc/app-lock.js"; import { createMessage } from "@/services/messages/create.js"; import { parseAudience } from "../audience.js"; @@ -36,6 +45,14 @@ import { extractApMentions } from "./mention.js"; import DbResolver from "../db-resolver.js"; import { StatusError } from "@/misc/fetch.js"; import { shouldBlockInstance } from "@/misc/should-block-instance.js"; +import { publishNoteStream } from "@/services/stream.js"; +import { extractHashtags } from "@/misc/extract-hashtags.js"; +import { UserProfiles } from "@/models/index.js"; +import { In } from "typeorm"; +import { DB_MAX_IMAGE_COMMENT_LENGTH } from "@/misc/hard-limits.js"; +import { truncate } from "@/misc/truncate.js"; +import { type Size, getEmojiSize } from "@/misc/emoji-meta.js"; +import { fetchMeta } from "@/misc/fetch-meta.js"; const logger = apLogger; @@ -75,7 +92,7 @@ export function validateNote(object: any, uri: string) { /** * Fetch Notes. * - * If the target Note is registered in Calckey, it will be returned. + * If the target Note is registered in Firefish, it will be returned. */ export async function fetchNote( object: string | IObject, @@ -111,20 +128,37 @@ export async function createNote( const note: IPost = object; - if (note.id && !note.id.startsWith('https://')) { - throw new Error(`unexpected shcema of note.id: ${note.id}`); + if (note.id && !note.id.startsWith("https://")) { + throw new Error(`unexpected schema of note.id: ${note.id}`); } const url = getOneApHrefNullable(note.url); - if (url && !url.startsWith('https://')) { - throw new Error(`unexpected shcema of note url: ${url}`); + if (url && !url.startsWith("https://")) { + throw new Error(`unexpected schema of note url: ${url}`); } logger.debug(`Note fetched: ${JSON.stringify(note, null, 2)}`); - logger.info(`Creating the Note: ${note.id}`); + // Skip if note is made before 2007 (1yr before Fedi was created) + // OR skip if note is made 3 days in advance + if (note.published) { + const DateChecker = new Date(note.published); + const FutureCheck = new Date(); + FutureCheck.setDate(FutureCheck.getDate() + 3); // Allow some wiggle room for misconfigured hosts + if (DateChecker.getFullYear() < 2007) { + logger.warn( + "Note somehow made before Activitypub was created; discarding", + ); + return null; + } + if (DateChecker > FutureCheck) { + logger.warn("Note somehow made after today; discarding"); + return null; + } + } + // Fetch author const actor = (await resolvePerson( getOneApId(note.attributedTo), @@ -133,7 +167,9 @@ export async function createNote( // Skip if author is suspended. if (actor.isSuspended) { - logger.debug(`User ${actor.usernameLower}@${actor.host} suspended; discarding.`) + logger.debug( + `User ${actor.usernameLower}@${actor.host} suspended; discarding.`, + ); return null; } @@ -297,9 +333,6 @@ export async function createNote( `vote from AP: actor=${actor.username}@${actor.host}, note=${note.id}, choice=${name}`, ); await vote(actor, reply, index); - - // リモートフォロワーにUpdate配信 - deliverQuestionUpdate(reply.id); } return null; }; @@ -364,8 +397,8 @@ export async function createNote( /** * Resolve Note. * - * If the target Note is registered in Calckey, return it, otherwise - * Fetch from remote server, register with Calckey and return it. + * If the target Note is registered in Firefish, return it, otherwise + * Fetch from remote server, register with Firefish and return it. */ export async function resolveNote( value: string | IObject, @@ -437,8 +470,15 @@ export async function extractEmojis( (tag.updated != null && exists.updatedAt != null && new Date(tag.updated) > exists.updatedAt) || - tag.icon!.url !== exists.originalUrl + tag.icon!.url !== exists.originalUrl || + !(exists.width && exists.height) ) { + let size: Size = { width: 0, height: 0 }; + try { + size = await getEmojiSize(tag.icon!.url); + } catch { + /* skip if any error happens */ + } await Emojis.update( { host, @@ -449,6 +489,8 @@ export async function extractEmojis( originalUrl: tag.icon!.url, publicUrl: tag.icon!.url, updatedAt: new Date(), + width: size.width || null, + height: size.height || null, }, ); @@ -463,6 +505,12 @@ export async function extractEmojis( logger.info(`register emoji host=${host}, name=${name}`); + let size: Size = { width: 0, height: 0 }; + try { + size = await getEmojiSize(tag.icon!.url); + } catch { + /* skip if any error happens */ + } return await Emojis.insert({ id: genId(), host, @@ -472,9 +520,245 @@ export async function extractEmojis( publicUrl: tag.icon!.url, updatedAt: new Date(), aliases: [], + width: size.width || null, + height: size.height || null, } as Partial).then((x) => Emojis.findOneByOrFail(x.identifiers[0]), ); }), ); } + +type TagDetail = { + type: string; + name: string; +}; + +function notEmpty(partial: Partial) { + return Object.keys(partial).length > 0; +} + +export async function updateNote(value: string | IObject, resolver?: Resolver) { + const uri = typeof value === "string" ? value : value.id; + if (!uri) throw new Error("Missing note uri"); + + // Skip if URI points to this server + if (uri.startsWith(`${config.url}/`)) throw new Error("uri points local"); + + // A new resolver is created if not specified + if (resolver == null) resolver = new Resolver(); + + // Resolve the updated Note object + const post = (await resolver.resolve(value)) as IPost; + + const actor = (await resolvePerson( + getOneApId(post.attributedTo), + resolver, + )) as CacheableRemoteUser; + + // Already registered with this server? + const note = await Notes.findOneBy({ uri }); + if (note == null) { + return await createNote(post, resolver); + } + + // Whether to tell clients the note has been updated and requires refresh. + let publishing = false; + + // Text parsing + let text: string | null = null; + if ( + post.source?.mediaType === "text/x.misskeymarkdown" && + typeof post.source?.content === "string" + ) { + text = post.source.content; + } else if (typeof post._misskey_content !== "undefined") { + text = post._misskey_content; + } else if (typeof post.content === "string") { + text = htmlToMfm(post.content, post.tag); + } + + const cw = post.sensitive && post.summary; + + // File parsing + const fileList = post.attachment + ? Array.isArray(post.attachment) + ? post.attachment + : [post.attachment] + : []; + const files = fileList.map((f) => (f.sensitive = post.sensitive)); + + // Fetch files + const limit = promiseLimit(2); + + const driveFiles = ( + await Promise.all( + fileList.map( + (x) => + limit(async () => { + const file = await resolveImage(actor, x); + const update: Partial = {}; + + const altText = truncate(x.name, DB_MAX_IMAGE_COMMENT_LENGTH); + if (file.comment !== altText) { + update.comment = altText; + } + + // Don't unmark previously marked sensitive files, + // but if edited post contains sensitive marker, update it. + if (post.sensitive && !file.isSensitive) { + update.isSensitive = post.sensitive; + } + + if (notEmpty(update)) { + await DriveFiles.update(file.id, update); + publishing = true; + } + + return file; + }) as Promise, + ), + ) + ).filter((file) => file != null); + const fileIds = driveFiles.map((file) => file.id); + const fileTypes = driveFiles.map((file) => file.type); + + const apEmojis = ( + await extractEmojis(post.tag || [], actor.host).catch((e) => []) + ).map((emoji) => emoji.name); + const apMentions = await extractApMentions(post.tag); + const apHashtags = await extractApHashtags(post.tag); + + const poll = await extractPollFromQuestion(post, resolver).catch( + () => undefined, + ); + + const choices = poll?.choices.flatMap((choice) => mfm.parse(choice)) ?? []; + + const tokens = mfm + .parse(text || "") + .concat(mfm.parse(cw || "")) + .concat(choices); + + const hashTags: string[] = apHashtags || extractHashtags(tokens); + + const mentionUsers = + apMentions || (await extractMentionedUsers(actor, tokens)); + + const mentionUserIds = mentionUsers.map((user) => user.id); + const remoteUsers = mentionUsers.filter((user) => user.host != null); + const remoteUserIds = remoteUsers.map((user) => user.id); + const remoteProfiles = await UserProfiles.findBy({ + userId: In(remoteUserIds), + }); + const mentionedRemoteUsers = remoteUsers.map((user) => { + const profile = remoteProfiles.find( + (profile) => profile.userId === user.id, + ); + return { + username: user.username, + host: user.host ?? null, + uri: user.uri, + url: profile ? profile.url : undefined, + } as IMentionedRemoteUsers[0]; + }); + + const update = {} as Partial; + if (text && text !== note.text) { + update.text = text; + } + if (cw !== note.cw) { + update.cw = cw ? cw : null; + } + if (fileIds.sort().join(",") !== note.fileIds.sort().join(",")) { + update.fileIds = fileIds; + update.attachedFileTypes = fileTypes; + } + + if (hashTags.sort().join(",") !== note.tags.sort().join(",")) { + update.tags = hashTags; + } + + if (mentionUserIds.sort().join(",") !== note.mentions.sort().join(",")) { + update.mentions = mentionUserIds; + update.mentionedRemoteUsers = JSON.stringify(mentionedRemoteUsers); + } + + if (apEmojis.sort().join(",") !== note.emojis.sort().join(",")) { + update.emojis = apEmojis; + } + + if (note.hasPoll !== !!poll) { + update.hasPoll = !!poll; + } + + if (poll) { + const dbPoll = await Polls.findOneBy({ noteId: note.id }); + if (dbPoll == null) { + await Polls.insert({ + noteId: note.id, + choices: poll?.choices, + multiple: poll?.multiple, + votes: poll?.votes, + expiresAt: poll?.expiresAt, + noteVisibility: note.visibility === "hidden" ? "home" : note.visibility, + userId: actor.id, + userHost: actor.host, + }); + updating = true; + } else if ( + dbPoll.multiple !== poll.multiple || + dbPoll.expiresAt !== poll.expiresAt || + dbPoll.noteVisibility !== note.visibility || + JSON.stringify(dbPoll.choices) !== JSON.stringify(poll.choices) + ) { + await Polls.update( + { noteId: note.id }, + { + choices: poll?.choices, + multiple: poll?.multiple, + votes: poll?.votes, + expiresAt: poll?.expiresAt, + noteVisibility: + note.visibility === "hidden" ? "home" : note.visibility, + }, + ); + updating = true; + } else { + for (let i = 0; i < poll.choices.length; i++) { + if (dbPoll.votes[i] !== poll.votes?.[i]) { + await Polls.update({ noteId: note.id }, { votes: poll?.votes }); + publishing = true; + break; + } + } + } + } + + // Update Note + if (notEmpty(update)) { + update.updatedAt = new Date(); + + // Save updated note to the database + await Notes.update({ uri }, update); + + // Save an edit history for the previous note + await NoteEdits.insert({ + id: genId(), + noteId: note.id, + text: note.text, + cw: note.cw, + fileIds: note.fileIds, + updatedAt: update.updatedAt, + }); + + publishing = true; + } + + if (publishing) { + // Publish update event for the updated note details + publishNoteStream(note.id, "updated", { + updatedAt: update.updatedAt, + }); + } +} diff --git a/packages/backend/src/remote/activitypub/models/person.ts b/packages/backend/src/remote/activitypub/models/person.ts index 88bbca5c46..8f17cfae8b 100644 --- a/packages/backend/src/remote/activitypub/models/person.ts +++ b/packages/backend/src/remote/activitypub/models/person.ts @@ -127,7 +127,7 @@ function validateActor(x: IObject, uri: string): IActor { /** * Fetch a Person. * - * If the target Person is registered in Calckey, it will be returned. + * If the target Person is registered in Firefish, it will be returned. */ export async function fetchPerson( uri: string, @@ -135,23 +135,23 @@ export async function fetchPerson( ): Promise { if (typeof uri !== "string") throw new Error("uri is not string"); - const cached = uriPersonCache.get(uri); + const cached = await uriPersonCache.get(uri, true); if (cached) return cached; // Fetch from the database if the URI points to this server if (uri.startsWith(`${config.url}/`)) { const id = uri.split("/").pop(); const u = await Users.findOneBy({ id }); - if (u) uriPersonCache.set(uri, u); + if (u) await uriPersonCache.set(uri, u); return u; } //#region Returns if already registered with this server - const exist = await Users.findOneBy({ uri }); + const user = await Users.findOneBy({ uri }); - if (exist) { - uriPersonCache.set(uri, exist); - return exist; + if (user != null) { + await uriPersonCache.set(uri, user); + return user; } //#endregion @@ -191,14 +191,44 @@ export async function createPerson( .map((tag) => normalizeForSearch(tag)) .splice(0, 32); - const isBot = getApType(object) === "Service"; + const isBot = getApType(object) !== "Person"; const bday = person["vcard:bday"]?.match(/^\d{4}-\d{2}-\d{2}/); const url = getOneApHrefNullable(person.url); - if (url && !url.startsWith('https://')) { - throw new Error(`unexpected shcema of person url: ${url}`); + if (url && !url.startsWith("https://")) { + throw new Error(`unexpected schema of person url: ${url}`); + } + + let followersCount: number | undefined; + + if (typeof person.followers === "string") { + try { + let data = await fetch(person.followers, { + headers: { Accept: "application/json" }, + }); + let json_data = JSON.parse(await data.text()); + + followersCount = json_data.totalItems; + } catch { + followersCount = undefined; + } + } + + let followingCount: number | undefined; + + if (typeof person.following === "string") { + try { + let data = await fetch(person.following, { + headers: { Accept: "application/json" }, + }); + let json_data = JSON.parse(await data.text()); + + followingCount = json_data.totalItems; + } catch (e) { + followingCount = undefined; + } } // Create user @@ -228,12 +258,27 @@ export async function createPerson( followersUri: person.followers ? getApId(person.followers) : undefined, + followersCount: + followersCount !== undefined + ? followersCount + : person.followers && + typeof person.followers !== "string" && + isCollectionOrOrderedCollection(person.followers) + ? person.followers.totalItems + : undefined, + followingCount: + followingCount !== undefined + ? followingCount + : person.following && + typeof person.following !== "string" && + isCollectionOrOrderedCollection(person.following) + ? person.following.totalItems + : undefined, featured: person.featured ? getApId(person.featured) : undefined, uri: person.id, tags, isBot, isCat: (person as any).isCat === true, - showTimelineReplies: false, }), )) as IRemoteUser; @@ -333,7 +378,7 @@ export async function createPerson( /** * Update Person data from remote. - * If the target Person is not registered in Calckey, it is ignored. + * If the target Person is not registered in Firefish, it is ignored. * @param uri URI of Person * @param resolver Resolver * @param hint Hint of Person object (If this value is a valid Person, it is used for updating without Remote resolve) @@ -351,9 +396,9 @@ export async function updatePerson( } //#region Already registered on this server? - const exist = (await Users.findOneBy({ uri })) as IRemoteUser; + const user = (await Users.findOneBy({ uri })) as IRemoteUser; - if (exist == null) { + if (user == null) { return; } //#endregion @@ -371,17 +416,15 @@ export async function updatePerson( [person.icon, person.image].map((img) => img == null ? Promise.resolve(null) - : resolveImage(exist, img).catch(() => null), + : resolveImage(user, img).catch(() => null), ), ); // Custom pictogram acquisition - const emojis = await extractEmojis(person.tag || [], exist.host).catch( - (e) => { - logger.info(`extractEmojis: ${e}`); - return [] as Emoji[]; - }, - ); + const emojis = await extractEmojis(person.tag || [], user.host).catch((e) => { + logger.info(`extractEmojis: ${e}`); + return [] as Emoji[]; + }); const emojiNames = emojis.map((emoji) => emoji.name); @@ -395,8 +438,38 @@ export async function updatePerson( const url = getOneApHrefNullable(person.url); - if (url && !url.startsWith('https://')) { - throw new Error(`unexpected shcema of person url: ${url}`); + if (url && !url.startsWith("https://")) { + throw new Error(`unexpected schema of person url: ${url}`); + } + + let followersCount: number | undefined; + + if (typeof person.followers === "string") { + try { + let data = await fetch(person.followers, { + headers: { Accept: "application/json" }, + }); + let json_data = JSON.parse(await data.text()); + + followersCount = json_data.totalItems; + } catch { + followersCount = undefined; + } + } + + let followingCount: number | undefined; + + if (typeof person.following === "string") { + try { + let data = await fetch(person.following, { + headers: { Accept: "application/json" }, + }); + let json_data = JSON.parse(await data.text()); + + followingCount = json_data.totalItems; + } catch { + followingCount = undefined; + } } const updates = { @@ -406,15 +479,31 @@ export async function updatePerson( person.sharedInbox || (person.endpoints ? person.endpoints.sharedInbox : undefined), followersUri: person.followers ? getApId(person.followers) : undefined, + followersCount: + followersCount !== undefined + ? followersCount + : person.followers && + typeof person.followers !== "string" && + isCollectionOrOrderedCollection(person.followers) + ? person.followers.totalItems + : undefined, + followingCount: + followingCount !== undefined + ? followingCount + : person.following && + typeof person.following !== "string" && + isCollectionOrOrderedCollection(person.following) + ? person.following.totalItems + : undefined, featured: person.featured, emojis: emojiNames, name: truncate(person.name, nameLength), tags, - isBot: getApType(object) === "Service", + isBot: getApType(object) !== "Person", isCat: (person as any).isCat === true, isLocked: !!person.manuallyApprovesFollowers, - movedToUri: person.movedTo, - alsoKnownAs: person.alsoKnownAs, + movedToUri: person.movedTo || null, + alsoKnownAs: person.alsoKnownAs || null, isExplorable: !!person.discoverable, } as Partial; @@ -427,11 +516,11 @@ export async function updatePerson( } // Update user - await Users.update(exist.id, updates); + await Users.update(user.id, updates); if (person.publicKey) { await UserPublickeys.update( - { userId: exist.id }, + { userId: user.id }, { keyId: person.publicKey.id, keyPem: person.publicKey.publicKeyPem, @@ -440,7 +529,7 @@ export async function updatePerson( } await UserProfiles.update( - { userId: exist.id }, + { userId: user.id }, { url: url, fields, @@ -452,15 +541,15 @@ export async function updatePerson( }, ); - publishInternalEvent("remoteUserUpdated", { id: exist.id }); + publishInternalEvent("remoteUserUpdated", { id: user.id }); // Hashtag Update - updateUsertags(exist, tags); + updateUsertags(user, tags); // If the user in question is a follower, followers will also be updated. await Followings.update( { - followerId: exist.id, + followerId: user.id, }, { followerSharedInbox: @@ -469,14 +558,14 @@ export async function updatePerson( }, ); - await updateFeatured(exist.id, resolver).catch((err) => logger.error(err)); + await updateFeatured(user.id, resolver).catch((err) => logger.error(err)); } /** * Resolve Person. * - * If the target person is registered in Calckey, it returns it; - * otherwise, it fetches it from the remote server, registers it in Calckey, and returns it. + * If the target person is registered in Firefish, it returns it; + * otherwise, it fetches it from the remote server, registers it in Firefish, and returns it. */ export async function resolvePerson( uri: string, @@ -485,10 +574,10 @@ export async function resolvePerson( if (typeof uri !== "string") throw new Error("uri is not string"); //#region If already registered on this server, return it. - const exist = await fetchPerson(uri); + const user = await fetchPerson(uri); - if (exist) { - return exist; + if (user != null) { + return user; } //#endregion diff --git a/packages/backend/src/remote/activitypub/models/question.ts b/packages/backend/src/remote/activitypub/models/question.ts index 520b9fee94..f5855c3e7c 100644 --- a/packages/backend/src/remote/activitypub/models/question.ts +++ b/packages/backend/src/remote/activitypub/models/question.ts @@ -1,7 +1,7 @@ import config from "@/config/index.js"; import Resolver from "../resolver.js"; import type { IObject, IQuestion } from "../type.js"; -import { isQuestion } from "../type.js"; +import { getApId, isQuestion } from "../type.js"; import { apLogger } from "../logger.js"; import { Notes, Polls } from "@/models/index.js"; import type { IPoll } from "@/models/entities/poll.js"; @@ -47,11 +47,14 @@ export async function extractPollFromQuestion( /** * Update votes of Question - * @param uri URI of AP Question object + * @param value URI of AP Question object or object itself * @returns true if updated */ -export async function updateQuestion(value: any, resolver?: Resolver) { - const uri = typeof value === "string" ? value : value.id; +export async function updateQuestion( + value: string | IQuestion, + resolver?: Resolver, +): Promise { + const uri = typeof value === "string" ? value : getApId(value); // Skip if URI points to this server if (uri.startsWith(`${config.url}/`)) throw new Error("uri points local"); @@ -65,22 +68,23 @@ export async function updateQuestion(value: any, resolver?: Resolver) { //#endregion // resolve new Question object - if (resolver == null) resolver = new Resolver(); - const question = (await resolver.resolve(value)) as IQuestion; + const _resolver = resolver ?? new Resolver(); + const question = (await _resolver.resolve(value)) as IQuestion; apLogger.debug(`fetched question: ${JSON.stringify(question, null, 2)}`); if (question.type !== "Question") throw new Error("object is not a Question"); const apChoices = question.oneOf || question.anyOf; + if (!apChoices) return false; let changed = false; for (const choice of poll.choices) { const oldCount = poll.votes[poll.choices.indexOf(choice)]; - const newCount = apChoices!.filter((ap) => ap.name === choice)[0].replies! - .totalItems; + const newCount = apChoices.filter((ap) => ap.name === choice)[0].replies + ?.totalItems; - if (oldCount !== newCount) { + if (newCount !== undefined && oldCount !== newCount) { changed = true; poll.votes[poll.choices.indexOf(choice)] = newCount; } diff --git a/packages/backend/src/remote/activitypub/renderer/announce.ts b/packages/backend/src/remote/activitypub/renderer/announce.ts index cff79a3f72..1fd1842acf 100644 --- a/packages/backend/src/remote/activitypub/renderer/announce.ts +++ b/packages/backend/src/remote/activitypub/renderer/announce.ts @@ -4,6 +4,10 @@ import type { Note } from "@/models/entities/note.js"; export default (object: any, note: Note) => { const attributedTo = `${config.url}/users/${note.userId}`; + const mentions = ( + JSON.parse(note.mentionedRemoteUsers) as IMentionedRemoteUsers + ).map((x) => x.uri); + let to: string[] = []; let cc: string[] = []; @@ -13,6 +17,10 @@ export default (object: any, note: Note) => { } else if (note.visibility === "home") { to = [`${attributedTo}/followers`]; cc = ["https://www.w3.org/ns/activitystreams#Public"]; + } else if (note.visibility === "followers") { + to = [`${attributedTo}/followers`]; + } else if (note.visibility === "specified") { + to = mentions; } else { return null; } diff --git a/packages/backend/src/remote/activitypub/request.ts b/packages/backend/src/remote/activitypub/request.ts index ffb3e25a33..69c97a445d 100644 --- a/packages/backend/src/remote/activitypub/request.ts +++ b/packages/backend/src/remote/activitypub/request.ts @@ -3,6 +3,7 @@ import { getUserKeypair } from "@/misc/keypair-store.js"; import type { User } from "@/models/entities/user.js"; import { getResponse } from "../../misc/fetch.js"; import { createSignedPost, createSignedGet } from "./ap-request.js"; +import { apLogger } from "@/remote/activitypub/logger.js"; export default async (user: { id: User["id"] }, url: string, object: any) => { const body = JSON.stringify(object); @@ -35,6 +36,7 @@ export default async (user: { id: User["id"] }, url: string, object: any) => { * @param url URL to fetch */ export async function signedGet(url: string, user: { id: User["id"] }) { + apLogger.debug(`Running signedGet on url: ${url}`); const keypair = await getUserKeypair(user.id); const req = createSignedGet({ diff --git a/packages/backend/src/remote/activitypub/resolver.ts b/packages/backend/src/remote/activitypub/resolver.ts index 0547927609..608ca3e935 100644 --- a/packages/backend/src/remote/activitypub/resolver.ts +++ b/packages/backend/src/remote/activitypub/resolver.ts @@ -23,6 +23,7 @@ import renderCreate from "@/remote/activitypub/renderer/create.js"; import { renderActivity } from "@/remote/activitypub/renderer/index.js"; import renderFollow from "@/remote/activitypub/renderer/follow.js"; import { shouldBlockInstance } from "@/misc/should-block-instance.js"; +import { apLogger } from "@/remote/activitypub/logger.js"; export default class Resolver { private history: Set; @@ -34,6 +35,15 @@ export default class Resolver { this.recursionLimit = recursionLimit; } + public setUser(user) { + this.user = user; + } + + public reset(): Resolver { + this.history = new Set(); + return this; + } + public getHistory(): string[] { return Array.from(this.history); } @@ -56,15 +66,20 @@ export default class Resolver { } if (typeof value !== "string") { + apLogger.debug("Object to resolve is not a string"); if (typeof value.id !== "undefined") { const host = extractDbHost(getApId(value)); if (await shouldBlockInstance(host)) { throw new Error("instance is blocked"); } } + apLogger.debug("Returning existing object:"); + apLogger.debug(JSON.stringify(value, null, 2)); return value; } + apLogger.debug(`Resolving: ${value}`); + if (value.includes("#")) { // URLs with fragment parts cannot be resolved correctly because // the fragment part does not get transmitted over HTTP(S). @@ -102,6 +117,9 @@ export default class Resolver { this.user = await getInstanceActor(); } + apLogger.debug("Getting object from remote, authenticated as user:"); + apLogger.debug(JSON.stringify(this.user, null, 2)); + const object = ( this.user ? await signedGet(value, this.user) diff --git a/packages/backend/src/server/activitypub.ts b/packages/backend/src/server/activitypub.ts index 29ac726efc..f9d5eb99c3 100644 --- a/packages/backend/src/server/activitypub.ts +++ b/packages/backend/src/server/activitypub.ts @@ -10,11 +10,21 @@ import { renderPerson } from "@/remote/activitypub/renderer/person.js"; import renderEmoji from "@/remote/activitypub/renderer/emoji.js"; import { inbox as processInbox } from "@/queue/index.js"; import { isSelfHost, toPuny } from "@/misc/convert-host.js"; -import { Notes, Users, Emojis, NoteReactions } from "@/models/index.js"; +import { + Notes, + Users, + Emojis, + NoteReactions, + FollowRequests, +} from "@/models/index.js"; import type { ILocalUser, User } from "@/models/entities/user.js"; import { renderLike } from "@/remote/activitypub/renderer/like.js"; import { getUserKeypair } from "@/misc/keypair-store.js"; -import { checkFetch, hasSignature } from "@/remote/activitypub/check-fetch.js"; +import { + checkFetch, + hasSignature, + getSignatureUser, +} from "@/remote/activitypub/check-fetch.js"; import { getInstanceActor } from "@/services/instance-actor.js"; import { fetchMeta } from "@/misc/fetch-meta.js"; import renderFollow from "@/remote/activitypub/renderer/follow.js"; @@ -22,6 +32,7 @@ import Featured from "./activitypub/featured.js"; import Following from "./activitypub/following.js"; import Followers from "./activitypub/followers.js"; import Outbox, { packActivity } from "./activitypub/outbox.js"; +import { serverLogger } from "./index.js"; // Init router const router = new Router(); @@ -78,7 +89,7 @@ router.get("/notes/:note", async (ctx, next) => { const note = await Notes.findOneBy({ id: ctx.params.note, - visibility: In(["public" as const, "home" as const]), + visibility: In(["public" as const, "home" as const, "followers" as const]), localOnly: false, }); @@ -97,6 +108,37 @@ router.get("/notes/:note", async (ctx, next) => { return; } + if (note.visibility === "followers") { + serverLogger.debug( + "Responding to request for follower-only note, validating access...", + ); + const remoteUser = await getSignatureUser(ctx.req); + serverLogger.debug("Local note author user:"); + serverLogger.debug(JSON.stringify(note, null, 2)); + serverLogger.debug("Authenticated remote user:"); + serverLogger.debug(JSON.stringify(remoteUser, null, 2)); + + if (remoteUser == null) { + serverLogger.debug("Rejecting: no user"); + ctx.status = 401; + return; + } + + const relation = await Users.getRelation(remoteUser.user.id, note.userId); + serverLogger.debug("Relation:"); + serverLogger.debug(JSON.stringify(relation, null, 2)); + + if (!relation.isFollowing || relation.isBlocked) { + serverLogger.debug( + "Rejecting: authenticated user is not following us or was blocked by us", + ); + ctx.status = 403; + return; + } + + serverLogger.debug("Accepting: access criteria met"); + } + ctx.body = renderActivity(await renderNote(note, false)); const meta = await fetchMeta(); @@ -330,22 +372,68 @@ router.get("/likes/:like", async (ctx) => { }); // follow -router.get("/follows/:follower/:followee", async (ctx) => { +router.get( + "/follows/:follower/:followee", + async (ctx: Router.RouterContext) => { + const verify = await checkFetch(ctx.req); + if (verify !== 200) { + ctx.status = verify; + return; + } + // This may be used before the follow is completed, so we do not + // check if the following exists. + + const [follower, followee] = await Promise.all([ + Users.findOneBy({ + id: ctx.params.follower, + host: IsNull(), + }), + Users.findOneBy({ + id: ctx.params.followee, + host: Not(IsNull()), + }), + ]); + + if (follower == null || followee == null) { + ctx.status = 404; + return; + } + + ctx.body = renderActivity(renderFollow(follower, followee)); + const meta = await fetchMeta(); + if (meta.secureMode || meta.privateMode) { + ctx.set("Cache-Control", "private, max-age=0, must-revalidate"); + } else { + ctx.set("Cache-Control", "public, max-age=180"); + } + setResponseType(ctx); + }, +); + +// follow request +router.get("/follows/:followRequestId", async (ctx: Router.RouterContext) => { const verify = await checkFetch(ctx.req); if (verify !== 200) { ctx.status = verify; return; } - // This may be used before the follow is completed, so we do not - // check if the following exists. + + const followRequest = await FollowRequests.findOneBy({ + id: ctx.params.followRequestId, + }); + + if (followRequest == null) { + ctx.status = 404; + return; + } const [follower, followee] = await Promise.all([ Users.findOneBy({ - id: ctx.params.follower, + id: followRequest.followerId, host: IsNull(), }), Users.findOneBy({ - id: ctx.params.followee, + id: followRequest.followeeId, host: Not(IsNull()), }), ]); @@ -355,13 +443,13 @@ router.get("/follows/:follower/:followee", async (ctx) => { return; } - ctx.body = renderActivity(renderFollow(follower, followee)); const meta = await fetchMeta(); if (meta.secureMode || meta.privateMode) { ctx.set("Cache-Control", "private, max-age=0, must-revalidate"); } else { ctx.set("Cache-Control", "public, max-age=180"); } + ctx.body = renderActivity(renderFollow(follower, followee)); setResponseType(ctx); }); diff --git a/packages/backend/src/server/api/authenticate.ts b/packages/backend/src/server/api/authenticate.ts index 42274ad2a4..460a0ce84b 100644 --- a/packages/backend/src/server/api/authenticate.ts +++ b/packages/backend/src/server/api/authenticate.ts @@ -9,7 +9,7 @@ import { localUserByNativeTokenCache, } from "@/services/user-cache.js"; -const appCache = new Cache(Infinity); +const appCache = new Cache("app", 60 * 30); export class AuthenticationError extends Error { constructor(message: string) { @@ -49,6 +49,7 @@ export default async ( const user = await localUserByNativeTokenCache.fetch( token, () => Users.findOneBy({ token }) as Promise, + true, ); if (user == null) { @@ -82,11 +83,14 @@ export default async ( Users.findOneBy({ id: accessToken.userId, }) as Promise, + true, ); if (accessToken.appId) { - const app = await appCache.fetch(accessToken.appId, () => - Apps.findOneByOrFail({ id: accessToken.appId! }), + const app = await appCache.fetch( + accessToken.appId, + () => Apps.findOneByOrFail({ id: accessToken.appId! }), + true, ); return [ diff --git a/packages/backend/src/server/api/call.ts b/packages/backend/src/server/api/call.ts index 45471ed564..0a1027b835 100644 --- a/packages/backend/src/server/api/call.ts +++ b/packages/backend/src/server/api/call.ts @@ -66,8 +66,11 @@ export default async ( limit as IEndpointMeta["limit"] & { key: NonNullable }, limitActor, ).catch((e) => { + const remainingTime = e.remainingTime + ? `Please try again in ${e.remainingTime}.` + : "Please try again later."; throw new ApiError({ - message: "Rate limit exceeded. Please try again later.", + message: `Rate limit exceeded. ${remainingTime}`, code: "RATE_LIMIT_EXCEEDED", id: "d5826d14-3982-4d2e-8011-b9e9f02499ef", httpStatusCode: 429, @@ -94,7 +97,7 @@ export default async ( } if (ep.meta.requireAdmin && !user!.isAdmin) { - throw new ApiError(accessDenied, { reason: "You are not the admin." }); + throw new ApiError(accessDenied, { reason: "You are not an admin." }); } if (ep.meta.requireModerator && !isModerator) { diff --git a/packages/backend/src/server/api/common/generate-replies-query.ts b/packages/backend/src/server/api/common/generate-replies-query.ts index 140c1d74a0..845fef130f 100644 --- a/packages/backend/src/server/api/common/generate-replies-query.ts +++ b/packages/backend/src/server/api/common/generate-replies-query.ts @@ -4,7 +4,8 @@ import { Brackets } from "typeorm"; export function generateRepliesQuery( q: SelectQueryBuilder, - me?: Pick | null, + withReplies: boolean, + me?: Pick | null, ) { if (me == null) { q.andWhere( @@ -20,25 +21,21 @@ export function generateRepliesQuery( ); }), ); - } else if (!me.showTimelineReplies) { + } else if (!withReplies) { q.andWhere( new Brackets((qb) => { qb.where("note.replyId IS NULL") // 返信ではない .orWhere("note.replyUserId = :meId", { meId: me.id }) // 返信だけど自分のノートへの返信 .orWhere( new Brackets((qb) => { - qb.where( - // 返信だけど自分の行った返信 - "note.replyId IS NOT NULL", - ).andWhere("note.userId = :meId", { meId: me.id }); + qb.where("note.replyId IS NOT NULL") // 返信だけど自分の行った返信 + .andWhere("note.userId = :meId", { meId: me.id }); }), ) .orWhere( new Brackets((qb) => { - qb.where( - // 返信だけど投稿者自身への返信 - "note.replyId IS NOT NULL", - ).andWhere("note.replyUserId = note.userId"); + qb.where("note.replyId IS NOT NULL") // 返信だけど投稿者自身への返信 + .andWhere("note.replyUserId = note.userId"); }), ); }), diff --git a/packages/backend/src/server/api/common/generated-muted-renote-query.ts b/packages/backend/src/server/api/common/generated-muted-renote-query.ts new file mode 100644 index 0000000000..3fcd9b28e8 --- /dev/null +++ b/packages/backend/src/server/api/common/generated-muted-renote-query.ts @@ -0,0 +1,28 @@ +import { Brackets, SelectQueryBuilder } from "typeorm"; +import { User } from "@/models/entities/user.js"; +import { RenoteMutings } from "@/models/index.js"; + +export function generateMutedUserRenotesQueryForNotes( + q: SelectQueryBuilder, + me: { id: User["id"] }, +): void { + const mutingQuery = RenoteMutings.createQueryBuilder("renote_muting") + .select("renote_muting.muteeId") + .where("renote_muting.muterId = :muterId", { muterId: me.id }); + + q.andWhere( + new Brackets((qb) => { + qb.where( + new Brackets((qb) => { + qb.where("note.renoteId IS NOT NULL"); + qb.andWhere("note.text IS NULL"); + qb.andWhere(`note.userId NOT IN (${mutingQuery.getQuery()})`); + }), + ) + .orWhere("note.renoteId IS NULL") + .orWhere("note.text IS NOT NULL"); + }), + ); + + q.setParameters(mutingQuery.getParameters()); +} diff --git a/packages/backend/src/server/api/common/signup.ts b/packages/backend/src/server/api/common/signup.ts index 7ae9e10fba..6beae2c782 100644 --- a/packages/backend/src/server/api/common/signup.ts +++ b/packages/backend/src/server/api/common/signup.ts @@ -1,4 +1,3 @@ -import bcrypt from "bcryptjs"; import { generateKeyPair } from "node:crypto"; import generateUserToken from "./generate-native-user-token.js"; import { User } from "@/models/entities/user.js"; @@ -12,6 +11,7 @@ import { usersChart } from "@/services/chart/index.js"; import { UsedUsername } from "@/models/entities/used-username.js"; import { db } from "@/db/postgre.js"; import config from "@/config/index.js"; +import { hashPassword } from "@/misc/password.js"; export async function signup(opts: { username: User["username"]; @@ -42,8 +42,7 @@ export async function signup(opts: { } // Generate hash of password - const salt = await bcrypt.genSalt(8); - hash = await bcrypt.hash(password, salt); + hash = await hashPassword(password); } // Generate secret @@ -107,6 +106,7 @@ export async function signup(opts: { isAdmin: (await Users.countBy({ host: IsNull(), + isAdmin: true, })) === 0, }), ); diff --git a/packages/backend/src/server/api/compatibility.ts b/packages/backend/src/server/api/compatibility.ts index 7e44fa8b2e..42be40e104 100644 --- a/packages/backend/src/server/api/compatibility.ts +++ b/packages/backend/src/server/api/compatibility.ts @@ -1,11 +1,13 @@ import type { IEndpoint } from "./endpoints"; -import * as cp___instanceInfo from "./endpoints/compatibility/instance-info.js"; -import * as cp___customEmojis from "./endpoints/compatibility/custom-emojis.js"; +import * as cp___instance_info from "./endpoints/compatibility/instance-info.js"; +import * as cp___custom_emojis from "./endpoints/compatibility/custom-emojis.js"; +import * as ep___instance_peers from "./endpoints/compatibility/peers.js"; const cps = [ - ["v1/instance", cp___instanceInfo], - ["v1/custom_emojis", cp___customEmojis], + ["v1/instance", cp___instance_info], + ["v1/custom_emojis", cp___custom_emojis], + ["v1/instance/peers", ep___instance_peers], ]; const compatibility: IEndpoint[] = cps.map(([name, cp]) => { diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts index 353f137a76..311a196184 100644 --- a/packages/backend/src/server/api/endpoints.ts +++ b/packages/backend/src/server/api/endpoints.ts @@ -29,6 +29,7 @@ import * as ep___admin_emoji_list from "./endpoints/admin/emoji/list.js"; import * as ep___admin_emoji_removeAliasesBulk from "./endpoints/admin/emoji/remove-aliases-bulk.js"; import * as ep___admin_emoji_setAliasesBulk from "./endpoints/admin/emoji/set-aliases-bulk.js"; import * as ep___admin_emoji_setCategoryBulk from "./endpoints/admin/emoji/set-category-bulk.js"; +import * as ep___admin_emoji_setLicenseBulk from "./endpoints/admin/emoji/set-license-bulk.js"; import * as ep___admin_emoji_update from "./endpoints/admin/emoji/update.js"; import * as ep___admin_federation_deleteAllFiles from "./endpoints/admin/federation/delete-all-files.js"; import * as ep___admin_federation_refreshRemoteInstanceMetadata from "./endpoints/admin/federation/refresh-remote-instance-metadata.js"; @@ -50,7 +51,9 @@ import * as ep___admin_relays_list from "./endpoints/admin/relays/list.js"; import * as ep___admin_relays_remove from "./endpoints/admin/relays/remove.js"; import * as ep___admin_resetPassword from "./endpoints/admin/reset-password.js"; import * as ep___admin_resolveAbuseUserReport from "./endpoints/admin/resolve-abuse-user-report.js"; +import * as ep___admin_search_indexAll from "./endpoints/admin/search/index-all.js"; import * as ep___admin_sendEmail from "./endpoints/admin/send-email.js"; +import * as ep___admin_sendModMail from "./endpoints/admin/send-mod-mail.js"; import * as ep___admin_serverInfo from "./endpoints/admin/server-info.js"; import * as ep___admin_showModerationLogs from "./endpoints/admin/show-moderation-logs.js"; import * as ep___admin_showUser from "./endpoints/admin/show-user.js"; @@ -87,6 +90,7 @@ import * as ep___channels_featured from "./endpoints/channels/featured.js"; import * as ep___channels_follow from "./endpoints/channels/follow.js"; import * as ep___channels_followed from "./endpoints/channels/followed.js"; import * as ep___channels_owned from "./endpoints/channels/owned.js"; +import * as ep___channels_search from "./endpoints/channels/search.js"; import * as ep___channels_show from "./endpoints/channels/show.js"; import * as ep___channels_timeline from "./endpoints/channels/timeline.js"; import * as ep___channels_unfollow from "./endpoints/channels/unfollow.js"; @@ -131,6 +135,7 @@ import * as ep___drive_folders_show from "./endpoints/drive/folders/show.js"; import * as ep___drive_folders_update from "./endpoints/drive/folders/update.js"; import * as ep___drive_stream from "./endpoints/drive/stream.js"; import * as ep___emailAddress_available from "./endpoints/email-address/available.js"; +import * as ep___emoji from "./endpoints/emoji.js"; import * as ep___endpoint from "./endpoints/endpoint.js"; import * as ep___endpoints from "./endpoints/endpoints.js"; import * as ep___exportCustomEmojis from "./endpoints/export-custom-emojis.js"; @@ -169,6 +174,7 @@ import * as ep___i_2fa_keyDone from "./endpoints/i/2fa/key-done.js"; import * as ep___i_2fa_passwordLess from "./endpoints/i/2fa/password-less.js"; import * as ep___i_2fa_registerKey from "./endpoints/i/2fa/register-key.js"; import * as ep___i_2fa_register from "./endpoints/i/2fa/register.js"; +import * as ep___i_2fa_updateKey from "./endpoints/i/2fa/update-key.js"; import * as ep___i_2fa_removeKey from "./endpoints/i/2fa/remove-key.js"; import * as ep___i_2fa_unregister from "./endpoints/i/2fa/unregister.js"; import * as ep___i_apps from "./endpoints/i/apps.js"; @@ -179,6 +185,7 @@ import * as ep___i_exportBlocking from "./endpoints/i/export-blocking.js"; import * as ep___i_exportFollowing from "./endpoints/i/export-following.js"; import * as ep___i_exportMute from "./endpoints/i/export-mute.js"; import * as ep___i_exportNotes from "./endpoints/i/export-notes.js"; +import * as ep___i_importPosts from "./endpoints/i/import-posts.js"; import * as ep___i_exportUserLists from "./endpoints/i/export-user-lists.js"; import * as ep___i_favorites from "./endpoints/i/favorites.js"; import * as ep___i_gallery_likes from "./endpoints/i/gallery/likes.js"; @@ -198,7 +205,7 @@ import * as ep___i_readAnnouncement from "./endpoints/i/read-announcement.js"; import * as ep___i_regenerateToken from "./endpoints/i/regenerate-token.js"; import * as ep___i_registry_getAll from "./endpoints/i/registry/get-all.js"; import * as ep___i_registry_getDetail from "./endpoints/i/registry/get-detail.js"; -import * as ep___i_registry_getUnsecure from './endpoints/i/registry/get-unsecure.js'; +import * as ep___i_registry_getUnsecure from "./endpoints/i/registry/get-unsecure.js"; import * as ep___i_registry_get from "./endpoints/i/registry/get.js"; import * as ep___i_registry_keysWithType from "./endpoints/i/registry/keys-with-type.js"; import * as ep___i_registry_keys from "./endpoints/i/registry/keys.js"; @@ -222,10 +229,14 @@ import * as ep___messaging_messages_create from "./endpoints/messaging/messages/ import * as ep___messaging_messages_delete from "./endpoints/messaging/messages/delete.js"; import * as ep___messaging_messages_read from "./endpoints/messaging/messages/read.js"; import * as ep___meta from "./endpoints/meta.js"; +import * as ep___sounds from "./endpoints/get-sounds.js"; import * as ep___miauth_genToken from "./endpoints/miauth/gen-token.js"; import * as ep___mute_create from "./endpoints/mute/create.js"; import * as ep___mute_delete from "./endpoints/mute/delete.js"; import * as ep___mute_list from "./endpoints/mute/list.js"; +import * as ep___renote_mute_create from "./endpoints/renote-mute/create.js"; +import * as ep___renote_mute_delete from "./endpoints/renote-mute/delete.js"; +import * as ep___renote_mute_list from "./endpoints/renote-mute/list.js"; import * as ep___my_apps from "./endpoints/my/apps.js"; import * as ep___notes from "./endpoints/notes.js"; import * as ep___notes_children from "./endpoints/notes/children.js"; @@ -233,6 +244,7 @@ import * as ep___notes_clips from "./endpoints/notes/clips.js"; import * as ep___notes_conversation from "./endpoints/notes/conversation.js"; import * as ep___notes_create from "./endpoints/notes/create.js"; import * as ep___notes_delete from "./endpoints/notes/delete.js"; +import * as ep___notes_edit from "./endpoints/notes/edit.js"; import * as ep___notes_favorites_create from "./endpoints/notes/favorites/create.js"; import * as ep___notes_favorites_delete from "./endpoints/notes/favorites/delete.js"; import * as ep___notes_featured from "./endpoints/notes/featured.js"; @@ -285,6 +297,8 @@ import * as ep___resetDb from "./endpoints/reset-db.js"; import * as ep___resetPassword from "./endpoints/reset-password.js"; import * as ep___serverInfo from "./endpoints/server-info.js"; import * as ep___stats from "./endpoints/stats.js"; +import * as ep___sw_show_registration from "./endpoints/sw/show-registration.js"; +import * as ep___sw_update_registration from "./endpoints/sw/update-registration.js"; import * as ep___sw_register from "./endpoints/sw/register.js"; import * as ep___sw_unregister from "./endpoints/sw/unregister.js"; import * as ep___test from "./endpoints/test.js"; @@ -328,7 +342,7 @@ import * as ep___users_stats from "./endpoints/users/stats.js"; import * as ep___fetchRss from "./endpoints/fetch-rss.js"; import * as ep___admin_driveCapOverride from "./endpoints/admin/drive-capacity-override.js"; -//Calckey Move +//Firefish Move import * as ep___i_move from "./endpoints/i/move.js"; import * as ep___i_known_as from "./endpoints/i/known-as.js"; @@ -362,6 +376,7 @@ const eps = [ ["admin/emoji/remove-aliases-bulk", ep___admin_emoji_removeAliasesBulk], ["admin/emoji/set-aliases-bulk", ep___admin_emoji_setAliasesBulk], ["admin/emoji/set-category-bulk", ep___admin_emoji_setCategoryBulk], + ["admin/emoji/set-license-bulk", ep___admin_emoji_setLicenseBulk], ["admin/emoji/update", ep___admin_emoji_update], ["admin/federation/delete-all-files", ep___admin_federation_deleteAllFiles], [ @@ -389,7 +404,9 @@ const eps = [ ["admin/relays/remove", ep___admin_relays_remove], ["admin/reset-password", ep___admin_resetPassword], ["admin/resolve-abuse-user-report", ep___admin_resolveAbuseUserReport], + ["admin/search/index-all", ep___admin_search_indexAll], ["admin/send-email", ep___admin_sendEmail], + ["admin/send-mod-mail", ep___admin_sendModMail], ["admin/server-info", ep___admin_serverInfo], ["admin/show-moderation-logs", ep___admin_showModerationLogs], ["admin/show-user", ep___admin_showUser], @@ -426,6 +443,7 @@ const eps = [ ["channels/follow", ep___channels_follow], ["channels/followed", ep___channels_followed], ["channels/owned", ep___channels_owned], + ["channels/search", ep___channels_search], ["channels/show", ep___channels_show], ["channels/timeline", ep___channels_timeline], ["channels/unfollow", ep___channels_unfollow], @@ -470,6 +488,7 @@ const eps = [ ["drive/folders/update", ep___drive_folders_update], ["drive/stream", ep___drive_stream], ["email-address/available", ep___emailAddress_available], + ["emoji", ep___emoji], ["endpoint", ep___endpoint], ["endpoints", ep___endpoints], ["export-custom-emojis", ep___exportCustomEmojis], @@ -510,6 +529,7 @@ const eps = [ ["i/2fa/password-less", ep___i_2fa_passwordLess], ["i/2fa/register-key", ep___i_2fa_registerKey], ["i/2fa/register", ep___i_2fa_register], + ["i/2fa/update-key", ep___i_2fa_updateKey], ["i/2fa/remove-key", ep___i_2fa_removeKey], ["i/2fa/unregister", ep___i_2fa_unregister], ["i/apps", ep___i_apps], @@ -520,6 +540,7 @@ const eps = [ ["i/export-following", ep___i_exportFollowing], ["i/export-mute", ep___i_exportMute], ["i/export-notes", ep___i_exportNotes], + ["i/import-posts", ep___i_importPosts], ["i/export-user-lists", ep___i_exportUserLists], ["i/favorites", ep___i_favorites], ["i/gallery/likes", ep___i_gallery_likes], @@ -574,6 +595,7 @@ const eps = [ ["notes/conversation", ep___notes_conversation], ["notes/create", ep___notes_create], ["notes/delete", ep___notes_delete], + ["notes/edit", ep___notes_edit], ["notes/favorites/create", ep___notes_favorites_create], ["notes/favorites/delete", ep___notes_favorites_delete], ["notes/featured", ep___notes_featured], @@ -615,6 +637,9 @@ const eps = [ ["ping", ep___ping], ["pinned-users", ep___pinnedUsers], ["recommended-instances", ep___recommendedInstances], + ["renote-mute/create", ep___renote_mute_create], + ["renote-mute/delete", ep___renote_mute_delete], + ["renote-mute/list", ep___renote_mute_list], ["custom-motd", ep___customMOTD], ["custom-splash-icons", ep___customSplashIcons], ["latest-version", ep___latestVersion], @@ -628,6 +653,8 @@ const eps = [ ["stats", ep___stats], ["sw/register", ep___sw_register], ["sw/unregister", ep___sw_unregister], + ["sw/show-registration", ep___sw_show_registration], + ["sw/update-registration", ep___sw_update_registration], ["test", ep___test], ["username/available", ep___username_available], ["users", ep___users], @@ -668,6 +695,7 @@ const eps = [ ["users/stats", ep___users_stats], ["admin/drive-capacity-override", ep___admin_driveCapOverride], ["fetch-rss", ep___fetchRss], + ["get-sounds", ep___sounds], ]; export interface IEndpointMeta { @@ -767,10 +795,10 @@ export interface IEndpointMeta { } export interface IEndpoint { - name: string, - exec: any, // TODO: may be obosolete @ThatOneCalculator - meta: IEndpointMeta, - params: Schema, + name: string; + exec: any; // TODO: may be obosolete @ThatOneCalculator + meta: IEndpointMeta; + params: Schema; } const endpoints: IEndpoint[] = (eps as [string, any]).map(([name, ep]) => { diff --git a/packages/backend/src/server/api/endpoints/admin/accounts/create.ts b/packages/backend/src/server/api/endpoints/admin/accounts/create.ts index 11ef2273ca..2e035d1695 100644 --- a/packages/backend/src/server/api/endpoints/admin/accounts/create.ts +++ b/packages/backend/src/server/api/endpoints/admin/accounts/create.ts @@ -35,6 +35,7 @@ export default define(meta, paramDef, async (ps, _me) => { const noUsers = (await Users.countBy({ host: IsNull(), + isAdmin: true, })) === 0; if (!(noUsers || me?.isAdmin)) throw new Error("access denied"); diff --git a/packages/backend/src/server/api/endpoints/admin/accounts/hosted.ts b/packages/backend/src/server/api/endpoints/admin/accounts/hosted.ts index 15ad1f9a17..a7b6e95c28 100644 --- a/packages/backend/src/server/api/endpoints/admin/accounts/hosted.ts +++ b/packages/backend/src/server/api/endpoints/admin/accounts/hosted.ts @@ -30,6 +30,17 @@ export default define(meta, paramDef, async (ps, me) => { set.deeplIsPro = config.deepl.isPro; } } + if ( + config.libreTranslate.managed != null && + config.libreTranslate.managed === true + ) { + if (typeof config.libreTranslate.apiUrl === "string") { + set.libreTranslateApiUrl = config.libreTranslate.apiUrl; + } + if (typeof config.libreTranslate.apiKey === "string") { + set.libreTranslateApiKey = config.libreTranslate.apiKey; + } + } if (config.email.managed != null && config.email.managed === true) { set.enableEmail = true; if (typeof config.email.address === "string") { diff --git a/packages/backend/src/server/api/endpoints/admin/announcements/create.ts b/packages/backend/src/server/api/endpoints/admin/announcements/create.ts index a532b6677f..754cc6c893 100644 --- a/packages/backend/src/server/api/endpoints/admin/announcements/create.ts +++ b/packages/backend/src/server/api/endpoints/admin/announcements/create.ts @@ -47,6 +47,16 @@ export const meta = { optional: false, nullable: true, }, + showPopup: { + type: "boolean", + optional: true, + nullable: false, + }, + isGoodNews: { + type: "boolean", + optional: true, + nullable: false, + }, }, }, } as const; @@ -57,6 +67,8 @@ export const paramDef = { title: { type: "string", minLength: 1 }, text: { type: "string", minLength: 1 }, imageUrl: { type: "string", nullable: true, minLength: 1 }, + showPopup: { type: "boolean" }, + isGoodNews: { type: "boolean" }, }, required: ["title", "text", "imageUrl"], } as const; @@ -69,6 +81,8 @@ export default define(meta, paramDef, async (ps) => { title: ps.title, text: ps.text, imageUrl: ps.imageUrl, + showPopup: ps.showPopup ?? false, + isGoodNews: ps.isGoodNews ?? false, }).then((x) => Announcements.findOneByOrFail(x.identifiers[0])); return Object.assign({}, announcement, { diff --git a/packages/backend/src/server/api/endpoints/admin/announcements/list.ts b/packages/backend/src/server/api/endpoints/admin/announcements/list.ts index fc5b020706..e96517c68e 100644 --- a/packages/backend/src/server/api/endpoints/admin/announcements/list.ts +++ b/packages/backend/src/server/api/endpoints/admin/announcements/list.ts @@ -57,6 +57,16 @@ export const meta = { optional: false, nullable: false, }, + showPopup: { + type: "boolean", + optional: true, + nullable: false, + }, + isGoodNews: { + type: "boolean", + optional: true, + nullable: false, + }, }, }, }, @@ -100,5 +110,7 @@ export default define(meta, paramDef, async (ps) => { text: announcement.text, imageUrl: announcement.imageUrl, reads: reads.get(announcement)!, + showPopup: announcement.showPopup, + isGoodNews: announcement.isGoodNews, })); }); diff --git a/packages/backend/src/server/api/endpoints/admin/announcements/update.ts b/packages/backend/src/server/api/endpoints/admin/announcements/update.ts index 35e64f2819..616b94d699 100644 --- a/packages/backend/src/server/api/endpoints/admin/announcements/update.ts +++ b/packages/backend/src/server/api/endpoints/admin/announcements/update.ts @@ -24,6 +24,8 @@ export const paramDef = { title: { type: "string", minLength: 1 }, text: { type: "string", minLength: 1 }, imageUrl: { type: "string", nullable: true, minLength: 1 }, + showPopup: { type: "boolean" }, + isGoodNews: { type: "boolean" }, }, required: ["id", "title", "text", "imageUrl"], } as const; @@ -38,5 +40,7 @@ export default define(meta, paramDef, async (ps, me) => { title: ps.title, text: ps.text, imageUrl: ps.imageUrl, + showPopup: ps.showPopup ?? false, + isGoodNews: ps.isGoodNews ?? false, }); }); diff --git a/packages/backend/src/server/api/endpoints/admin/drive-capacity-override.ts b/packages/backend/src/server/api/endpoints/admin/drive-capacity-override.ts index 1f6ac5f27e..c8be344696 100644 --- a/packages/backend/src/server/api/endpoints/admin/drive-capacity-override.ts +++ b/packages/backend/src/server/api/endpoints/admin/drive-capacity-override.ts @@ -1,7 +1,7 @@ import define from "../../define.js"; import { Users } from "@/models/index.js"; -import { User } from "@/models/entities/user.js"; import { insertModerationLog } from "@/services/insert-moderation-log.js"; +import { publishInternalEvent } from "@/services/stream.js"; export const meta = { tags: ["admin"], @@ -29,17 +29,14 @@ export default define(meta, paramDef, async (ps, me) => { throw new Error("user is not local user"); } - /*if (user.isAdmin) { - throw new Error('cannot suspend admin'); - } - if (user.isModerator) { - throw new Error('cannot suspend moderator'); - }*/ - await Users.update(user.id, { driveCapacityOverrideMb: ps.overrideMb, }); + publishInternalEvent("localUserUpdated", { + id: user.id, + }); + insertModerationLog(me, "change-drive-capacity-override", { targetId: user.id, }); diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/add.ts b/packages/backend/src/server/api/endpoints/admin/emoji/add.ts index 601d754a0d..4366406ec3 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/add.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/add.ts @@ -6,6 +6,7 @@ import { ApiError } from "../../../error.js"; import rndstr from "rndstr"; import { publishBroadcastStream } from "@/services/stream.js"; import { db } from "@/db/postgre.js"; +import { getEmojiSize } from "@/misc/emoji-meta.js"; export const meta = { tags: ["admin"], @@ -39,6 +40,8 @@ export default define(meta, paramDef, async (ps, me) => { ? file.name.split(".")[0] : `_${rndstr("a-z0-9", 8)}_`; + const size = await getEmojiSize(file.url); + const emoji = await Emojis.insert({ id: genId(), updatedAt: new Date(), @@ -49,6 +52,9 @@ export default define(meta, paramDef, async (ps, me) => { originalUrl: file.url, publicUrl: file.webpublicUrl ?? file.url, type: file.webpublicType ?? file.type, + license: null, + width: size.width || null, + height: size.height || null, }).then((x) => Emojis.findOneByOrFail(x.identifiers[0])); await db.queryResultCache!.remove(["meta_emojis"]); diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts b/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts index 4a7f2bc611..c90e606335 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts @@ -6,6 +6,7 @@ import type { DriveFile } from "@/models/entities/drive-file.js"; import { uploadFromUrl } from "@/services/drive/upload-from-url.js"; import { publishBroadcastStream } from "@/services/stream.js"; import { db } from "@/db/postgre.js"; +import { getEmojiSize } from "@/misc/emoji-meta.js"; export const meta = { tags: ["admin"], @@ -64,6 +65,8 @@ export default define(meta, paramDef, async (ps, me) => { throw new ApiError(); } + const size = await getEmojiSize(driveFile.url); + const copied = await Emojis.insert({ id: genId(), updatedAt: new Date(), @@ -73,6 +76,9 @@ export default define(meta, paramDef, async (ps, me) => { originalUrl: driveFile.url, publicUrl: driveFile.webpublicUrl ?? driveFile.url, type: driveFile.webpublicType ?? driveFile.type, + license: emoji.license, + width: size.width || null, + height: size.height || null, }).then((x) => Emojis.findOneByOrFail(x.identifiers[0])); await db.queryResultCache!.remove(["meta_emojis"]); 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 63f60bc991..6252e7e929 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 @@ -2,6 +2,7 @@ import define from "../../../define.js"; import { Emojis } from "@/models/index.js"; import { toPuny } from "@/misc/convert-host.js"; import { makePaginationQuery } from "../../../common/make-pagination-query.js"; +import { sqlLikeEscape } from "@/misc/sql-like-escape.js"; export const meta = { tags: ["admin"], @@ -55,6 +56,21 @@ export const meta = { optional: false, nullable: false, }, + license: { + type: "string", + optional: false, + nullable: true, + }, + width: { + type: "number", + optional: false, + nullable: true, + }, + height: { + type: "number", + optional: false, + nullable: true, + }, }, }, }, @@ -91,7 +107,9 @@ export default define(meta, paramDef, async (ps) => { } if (ps.query) { - q.andWhere("emoji.name like :query", { query: `%${ps.query}%` }); + q.andWhere("emoji.name like :query", { + query: `%${sqlLikeEscape(ps.query)}%`, + }); } const emojis = await q.orderBy("emoji.id", "DESC").take(ps.limit).getMany(); diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/list.ts b/packages/backend/src/server/api/endpoints/admin/emoji/list.ts index bc4f1d29fa..f8269588ca 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/list.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/list.ts @@ -2,6 +2,7 @@ import define from "../../../define.js"; import { Emojis } from "@/models/index.js"; import { makePaginationQuery } from "../../../common/make-pagination-query.js"; import type { Emoji } from "@/models/entities/emoji.js"; +//import { sqlLikeEscape } from "@/misc/sql-like-escape.js"; export const meta = { tags: ["admin"], @@ -55,6 +56,21 @@ export const meta = { optional: false, nullable: false, }, + license: { + type: "string", + optional: false, + nullable: true, + }, + width: { + type: "number", + optional: false, + nullable: true, + }, + height: { + type: "number", + optional: false, + nullable: true, + }, }, }, }, @@ -81,7 +97,7 @@ export default define(meta, paramDef, async (ps) => { let emojis: Emoji[]; if (ps.query) { - //q.andWhere('emoji.name ILIKE :q', { q: `%${ps.query}%` }); + //q.andWhere('emoji.name ILIKE :q', { q: `%${sqlLikeEscape(ps.query)}%` }); //const emojis = await q.take(ps.limit).getMany(); emojis = await q.getMany(); diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/set-license-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/set-license-bulk.ts new file mode 100644 index 0000000000..c98ca03fae --- /dev/null +++ b/packages/backend/src/server/api/endpoints/admin/emoji/set-license-bulk.ts @@ -0,0 +1,45 @@ +import define from "../../../define.js"; +import { Emojis } from "@/models/index.js"; +import { In } from "typeorm"; +import { ApiError } from "../../../error.js"; +import { db } from "@/db/postgre.js"; + +export const meta = { + tags: ["admin"], + + requireCredential: true, + requireModerator: true, +} as const; + +export const paramDef = { + type: "object", + properties: { + ids: { + type: "array", + items: { + type: "string", + format: "misskey:id", + }, + }, + license: { + type: "string", + nullable: true, + description: "Use `null` to reset the license.", + }, + }, + required: ["ids"], +} as const; + +export default define(meta, paramDef, async (ps) => { + await Emojis.update( + { + id: In(ps.ids), + }, + { + updatedAt: new Date(), + license: ps.license, + }, + ); + + await db.queryResultCache!.remove(["meta_emojis"]); +}); diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/update.ts b/packages/backend/src/server/api/endpoints/admin/emoji/update.ts index 3f7f6639f5..9e2e854760 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/update.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/update.ts @@ -34,6 +34,10 @@ export const paramDef = { type: "string", }, }, + license: { + type: "string", + nullable: true, + }, }, required: ["id", "name", "aliases"], } as const; @@ -48,6 +52,7 @@ export default define(meta, paramDef, async (ps) => { name: ps.name, category: ps.category, aliases: ps.aliases, + license: ps.license, }); await db.queryResultCache!.remove(["meta_emojis"]); diff --git a/packages/backend/src/server/api/endpoints/admin/meta.ts b/packages/backend/src/server/api/endpoints/admin/meta.ts index 1808de118a..9abb57b1b3 100644 --- a/packages/backend/src/server/api/endpoints/admin/meta.ts +++ b/packages/backend/src/server/api/endpoints/admin/meta.ts @@ -1,7 +1,8 @@ import config from "@/config/index.js"; import { fetchMeta } from "@/misc/fetch-meta.js"; -import { MAX_NOTE_TEXT_LENGTH } from "@/const.js"; +import { MAX_NOTE_TEXT_LENGTH, MAX_CAPTION_TEXT_LENGTH } from "@/const.js"; import define from "../../define.js"; +import { Exp } from "@tensorflow/tfjs"; export const meta = { tags: ["meta"], @@ -63,7 +64,7 @@ export const meta = { type: "string", optional: false, nullable: false, - default: "/assets/ai.png", + default: "/static-assets/badges/info.png", }, bannerUrl: { type: "string", @@ -74,7 +75,7 @@ export const meta = { type: "string", optional: false, nullable: false, - default: "https://xn--931a.moe/aiart/yubitun.png", + default: "/static-assets/badges/error.png", }, iconUrl: { type: "string", @@ -86,6 +87,11 @@ export const meta = { optional: false, nullable: false, }, + maxCaptionTextLength: { + type: "number", + optional: false, + nullable: false, + }, emojis: { type: "array", optional: false, @@ -254,6 +260,16 @@ export const meta = { nullable: false, }, }, + silencedHosts: { + type: "array", + optional: true, + nullable: false, + items: { + type: "string", + optional: false, + nullable: false, + }, + }, allowedHosts: { type: "array", optional: true, @@ -455,6 +471,31 @@ export const meta = { optional: false, nullable: false, }, + experimentalFeatures: { + type: "object", + optional: true, + nullable: true, + properties: { + postImports: { + type: "boolean", + }, + }, + }, + enableServerMachineStats: { + type: "boolean", + optional: false, + nullable: false, + }, + enableIdenticonGeneration: { + type: "boolean", + optional: false, + nullable: false, + }, + donationLink: { + type: "string", + optional: true, + nullable: true, + }, }, }, } as const; @@ -499,6 +540,7 @@ export default define(meta, paramDef, async (ps, me) => { backgroundImageUrl: instance.backgroundImageUrl, logoImageUrl: instance.logoImageUrl, maxNoteTextLength: MAX_NOTE_TEXT_LENGTH, // 後方互換性のため + maxCaptionTextLength: MAX_CAPTION_TEXT_LENGTH, defaultLightTheme: instance.defaultLightTheme, defaultDarkTheme: instance.defaultDarkTheme, enableEmail: instance.enableEmail, @@ -506,7 +548,8 @@ export default define(meta, paramDef, async (ps, me) => { enableGithubIntegration: instance.enableGithubIntegration, enableDiscordIntegration: instance.enableDiscordIntegration, enableServiceWorker: instance.enableServiceWorker, - translatorAvailable: instance.deeplAuthKey != null, + translatorAvailable: + instance.deeplAuthKey != null || instance.libreTranslateApiUrl != null, pinnedPages: instance.pinnedPages, pinnedClipId: instance.pinnedClipId, cacheRemoteFiles: instance.cacheRemoteFiles, @@ -517,6 +560,7 @@ export default define(meta, paramDef, async (ps, me) => { customSplashIcons: instance.customSplashIcons, hiddenTags: instance.hiddenTags, blockedHosts: instance.blockedHosts, + silencedHosts: instance.silencedHosts, allowedHosts: instance.allowedHosts, privateMode: instance.privateMode, secureMode: instance.secureMode, @@ -558,7 +602,13 @@ export default define(meta, paramDef, async (ps, me) => { objectStorageS3ForcePathStyle: instance.objectStorageS3ForcePathStyle, deeplAuthKey: instance.deeplAuthKey, deeplIsPro: instance.deeplIsPro, + libreTranslateApiUrl: instance.libreTranslateApiUrl, + libreTranslateApiKey: instance.libreTranslateApiKey, enableIpLogging: instance.enableIpLogging, enableActiveEmailValidation: instance.enableActiveEmailValidation, + experimentalFeatures: instance.experimentalFeatures, + enableServerMachineStats: instance.enableServerMachineStats, + enableIdenticonGeneration: instance.enableIdenticonGeneration, + donationLink: instance.donationLink, }; }); diff --git a/packages/backend/src/server/api/endpoints/admin/promo/create.ts b/packages/backend/src/server/api/endpoints/admin/promo/create.ts index a6d1f35191..00244a777a 100644 --- a/packages/backend/src/server/api/endpoints/admin/promo/create.ts +++ b/packages/backend/src/server/api/endpoints/admin/promo/create.ts @@ -40,9 +40,9 @@ export default define(meta, paramDef, async (ps, user) => { throw err; }); - const exist = await PromoNotes.findOneBy({ noteId: note.id }); + const exist = await PromoNotes.exist({ where: { noteId: note.id } }); - if (exist != null) { + if (exist) { throw new ApiError(meta.errors.alreadyPromoted); } diff --git a/packages/backend/src/server/api/endpoints/admin/queue/stats.ts b/packages/backend/src/server/api/endpoints/admin/queue/stats.ts index ecd67d8936..4a437c3d12 100644 --- a/packages/backend/src/server/api/endpoints/admin/queue/stats.ts +++ b/packages/backend/src/server/api/endpoints/admin/queue/stats.ts @@ -3,6 +3,7 @@ import { inboxQueue, dbQueue, objectStorageQueue, + backgroundQueue, } from "@/queue/queues.js"; import define from "../../../define.js"; @@ -37,6 +38,11 @@ export const meta = { nullable: false, ref: "QueueCount", }, + backgroundQueue: { + optional: false, + nullable: false, + ref: "QueueCount", + }, }, }, } as const; @@ -52,11 +58,13 @@ export default define(meta, paramDef, async (ps) => { const inboxJobCounts = await inboxQueue.getJobCounts(); const dbJobCounts = await dbQueue.getJobCounts(); const objectStorageJobCounts = await objectStorageQueue.getJobCounts(); + const backgroundJobCounts = await backgroundQueue.getJobCounts(); return { deliver: deliverJobCounts, inbox: inboxJobCounts, db: dbJobCounts, objectStorage: objectStorageJobCounts, + backgroundQueue: backgroundJobCounts, }; }); diff --git a/packages/backend/src/server/api/endpoints/admin/reset-password.ts b/packages/backend/src/server/api/endpoints/admin/reset-password.ts index dd3f258f74..cbe6735ce5 100644 --- a/packages/backend/src/server/api/endpoints/admin/reset-password.ts +++ b/packages/backend/src/server/api/endpoints/admin/reset-password.ts @@ -1,7 +1,8 @@ import define from "../../define.js"; -import bcrypt from "bcryptjs"; +// import bcrypt from "bcryptjs"; import rndstr from "rndstr"; import { Users, UserProfiles } from "@/models/index.js"; +import { hashPassword } from "@/misc/password.js"; export const meta = { tags: ["admin"], @@ -47,7 +48,8 @@ export default define(meta, paramDef, async (ps) => { const passwd = rndstr("a-zA-Z0-9", 8); // Generate hash of password - const hash = bcrypt.hashSync(passwd); + // const hash = bcrypt.hashSync(passwd); + const hash = await hashPassword(passwd); await UserProfiles.update( { diff --git a/packages/backend/src/server/api/endpoints/admin/search/index-all.ts b/packages/backend/src/server/api/endpoints/admin/search/index-all.ts new file mode 100644 index 0000000000..135b48eccd --- /dev/null +++ b/packages/backend/src/server/api/endpoints/admin/search/index-all.ts @@ -0,0 +1,28 @@ +import define from "../../../define.js"; +import { createIndexAllNotesJob } from "@/queue/index.js"; + +export const meta = { + tags: ["admin"], + + requireCredential: true, + requireModerator: true, +} as const; + +export const paramDef = { + type: "object", + properties: { + cursor: { + type: "string", + format: "misskey:id", + nullable: true, + default: null, + }, + }, + required: [], +} as const; + +export default define(meta, paramDef, async (ps, _me) => { + createIndexAllNotesJob({ + cursor: ps.cursor ?? undefined, + }); +}); diff --git a/packages/backend/src/server/api/endpoints/admin/send-mod-mail.ts b/packages/backend/src/server/api/endpoints/admin/send-mod-mail.ts new file mode 100644 index 0000000000..f7a9ad7782 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/admin/send-mod-mail.ts @@ -0,0 +1,68 @@ +import * as sanitizeHtml from "sanitize-html"; +import define from "../../define.js"; +import { Users, UserProfiles } from "@/models/index.js"; +import { ApiError } from "../../error.js"; +import { sendEmail } from "@/services/send-email.js"; +import { createNotification } from "@/services/create-notification.js"; + +export const meta = { + tags: ["users"], + + requireCredential: true, + requireModerator: true, + + description: "Send a moderation notice.", + + errors: { + noSuchUser: { + message: "No such user.", + code: "NO_SUCH_USER", + id: "1acefcb5-0959-43fd-9685-b48305736cb5", + }, + noEmail: { + message: "No email for user.", + code: "NO_EMAIL", + id: "ac9d2d22-ef73-11ed-a05b-0242ac120003", + }, + }, +} as const; + +export const paramDef = { + type: "object", + properties: { + userId: { type: "string", format: "misskey:id" }, + comment: { type: "string", minLength: 1, maxLength: 2048 }, + }, + required: ["userId", "comment"], +} as const; + +export default define(meta, paramDef, async (ps) => { + const [user, profile] = await Promise.all([ + Users.findOneBy({ id: ps.userId }), + UserProfiles.findOneBy({ userId: ps.userId }), + ]); + + if (user == null || profile == null) { + throw new ApiError(meta.errors.noSuchUser); + } + + createNotification(user.id, "app", { + customBody: ps.comment, + customHeader: "Moderation Notice", + customIcon: "/static-assets/badges/info.png", + }); + + setImmediate(async () => { + const email = profile.email; + if (email == null) { + throw new ApiError(meta.errors.noEmail); + } + + sendEmail( + email, + "Moderation notice", + sanitizeHtml(ps.comment), + sanitizeHtml(ps.comment), + ); + }); +}); diff --git a/packages/backend/src/server/api/endpoints/admin/show-user.ts b/packages/backend/src/server/api/endpoints/admin/show-user.ts index e91f07f838..7a2bf23651 100644 --- a/packages/backend/src/server/api/endpoints/admin/show-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/show-user.ts @@ -42,6 +42,7 @@ export default define(meta, paramDef, async (ps, me) => { isModerator: user.isModerator, isSilenced: user.isSilenced, isSuspended: user.isSuspended, + moderationNote: profile.moderationNote, }; } @@ -59,6 +60,7 @@ export default define(meta, paramDef, async (ps, me) => { emailVerified: profile.emailVerified, autoAcceptFollowed: profile.autoAcceptFollowed, noCrawle: profile.noCrawle, + preventAiLearning: profile.preventAiLearning, alwaysMarkNsfw: profile.alwaysMarkNsfw, autoSensitive: profile.autoSensitive, carefulBot: profile.carefulBot, diff --git a/packages/backend/src/server/api/endpoints/admin/show-users.ts b/packages/backend/src/server/api/endpoints/admin/show-users.ts index 868df9dc9b..b777bd26d9 100644 --- a/packages/backend/src/server/api/endpoints/admin/show-users.ts +++ b/packages/backend/src/server/api/endpoints/admin/show-users.ts @@ -1,5 +1,6 @@ import { Users } from "@/models/index.js"; import define from "../../define.js"; +import { sqlLikeEscape } from "@/misc/sql-like-escape.js"; export const meta = { tags: ["admin"], @@ -106,7 +107,7 @@ export default define(meta, paramDef, async (ps, me) => { if (ps.username) { query.andWhere("user.usernameLower like :username", { - username: `${ps.username.toLowerCase()}%`, + username: `${sqlLikeEscape(ps.username.toLowerCase())}%`, }); } diff --git a/packages/backend/src/server/api/endpoints/admin/update-meta.ts b/packages/backend/src/server/api/endpoints/admin/update-meta.ts index 28aac4a4d4..2142c7df73 100644 --- a/packages/backend/src/server/api/endpoints/admin/update-meta.ts +++ b/packages/backend/src/server/api/endpoints/admin/update-meta.ts @@ -1,6 +1,5 @@ import { Meta } from "@/models/entities/meta.js"; import { insertModerationLog } from "@/services/insert-moderation-log.js"; -import { DB_MAX_NOTE_TEXT_LENGTH } from "@/misc/hard-limits.js"; import { db } from "@/db/postgre.js"; import define from "../../define.js"; @@ -61,6 +60,13 @@ export const paramDef = { type: "string", }, }, + silencedHosts: { + type: "array", + nullable: true, + items: { + type: "string", + }, + }, allowedHosts: { type: "array", nullable: true, @@ -124,6 +130,8 @@ export const paramDef = { summalyProxy: { type: "string", nullable: true }, deeplAuthKey: { type: "string", nullable: true }, deeplIsPro: { type: "boolean" }, + libreTranslateApiUrl: { type: "string", nullable: true }, + libreTranslateApiKey: { type: "string", nullable: true }, enableTwitterIntegration: { type: "boolean" }, twitterConsumerKey: { type: "string", nullable: true }, twitterConsumerSecret: { type: "string", nullable: true }, @@ -161,6 +169,16 @@ export const paramDef = { objectStorageS3ForcePathStyle: { type: "boolean" }, enableIpLogging: { type: "boolean" }, enableActiveEmailValidation: { type: "boolean" }, + experimentalFeatures: { + type: "object", + nullable: true, + properties: { + postImports: { type: "boolean" }, + }, + }, + enableServerMachineStats: { type: "boolean" }, + enableIdenticonGeneration: { type: "boolean" }, + donationLink: { type: "string", nullable: true }, }, required: [], } as const; @@ -202,6 +220,15 @@ export default define(meta, paramDef, async (ps, me) => { if (Array.isArray(ps.recommendedInstances)) { set.recommendedInstances = ps.recommendedInstances.filter(Boolean); + if (set.recommendedInstances?.length > 0) { + set.recommendedInstances.forEach((instance, index) => { + if (/^https?:\/\//i.test(instance)) { + set.recommendedInstances![index] = instance + .replace(/^https?:\/\//i, "") + .replace(/\/$/, ""); + } + }); + } } if (Array.isArray(ps.hiddenTags)) { @@ -209,7 +236,21 @@ export default define(meta, paramDef, async (ps, me) => { } if (Array.isArray(ps.blockedHosts)) { - set.blockedHosts = ps.blockedHosts.filter(Boolean); + let lastValue = ""; + set.blockedHosts = ps.blockedHosts.sort().filter((h) => { + const lv = lastValue; + lastValue = h; + return h !== "" && h !== lv; + }); + } + + if (Array.isArray(ps.silencedHosts)) { + let lastValue = ""; + set.silencedHosts = ps.silencedHosts.sort().filter((h) => { + const lv = lastValue; + lastValue = h; + return h !== "" && h !== lv; + }); } if (ps.themeColor !== undefined) { @@ -510,6 +551,22 @@ export default define(meta, paramDef, async (ps, me) => { set.deeplIsPro = ps.deeplIsPro; } + if (ps.libreTranslateApiUrl !== undefined) { + if (ps.libreTranslateApiUrl === "") { + set.libreTranslateApiUrl = null; + } else { + set.libreTranslateApiUrl = ps.libreTranslateApiUrl; + } + } + + if (ps.libreTranslateApiKey !== undefined) { + if (ps.libreTranslateApiKey === "") { + set.libreTranslateApiKey = null; + } else { + set.libreTranslateApiKey = ps.libreTranslateApiKey; + } + } + if (ps.enableIpLogging !== undefined) { set.enableIpLogging = ps.enableIpLogging; } @@ -518,6 +575,25 @@ export default define(meta, paramDef, async (ps, me) => { set.enableActiveEmailValidation = ps.enableActiveEmailValidation; } + if (ps.experimentalFeatures !== undefined) { + set.experimentalFeatures = ps.experimentalFeatures || undefined; + } + + if (ps.enableServerMachineStats !== undefined) { + set.enableServerMachineStats = ps.enableServerMachineStats; + } + + if (ps.enableIdenticonGeneration !== undefined) { + set.enableIdenticonGeneration = ps.enableIdenticonGeneration; + } + + if (ps.donationLink !== undefined) { + set.donationLink = ps.donationLink; + if (set.donationLink && !/^https?:\/\//i.test(set.donationLink)) { + set.donationLink = `https://${set.donationLink}`; + } + } + await db.transaction(async (transactionalEntityManager) => { const metas = await transactionalEntityManager.find(Meta, { order: { diff --git a/packages/backend/src/server/api/endpoints/announcements.ts b/packages/backend/src/server/api/endpoints/announcements.ts index 00634cc421..1bab61ba2c 100644 --- a/packages/backend/src/server/api/endpoints/announcements.ts +++ b/packages/backend/src/server/api/endpoints/announcements.ts @@ -56,6 +56,16 @@ export const meta = { optional: true, nullable: false, }, + showPopup: { + type: "boolean", + optional: false, + nullable: false, + }, + isGoodNews: { + type: "boolean", + optional: false, + nullable: false, + }, }, }, }, diff --git a/packages/backend/src/server/api/endpoints/antennas/create.ts b/packages/backend/src/server/api/endpoints/antennas/create.ts index 171fc2c643..ed16450120 100644 --- a/packages/backend/src/server/api/endpoints/antennas/create.ts +++ b/packages/backend/src/server/api/endpoints/antennas/create.ts @@ -23,6 +23,17 @@ export const meta = { code: "NO_SUCH_USER_GROUP", id: "aa3c0b9a-8cae-47c0-92ac-202ce5906682", }, + + tooManyAntennas: { + message: "Too many antennas.", + code: "TOO_MANY_ANTENNAS", + id: "c3a5a51e-04d4-11ee-be56-0242ac120002", + }, + noKeywords: { + message: "No keywords.", + code: "NO_KEYWORDS", + id: "aa975b74-1ddb-11ee-be56-0242ac120002", + }, }, res: { @@ -37,7 +48,10 @@ export const paramDef = { type: "object", properties: { name: { type: "string", minLength: 1, maxLength: 100 }, - src: { type: "string", enum: ["home", "all", "users", "list", "group"] }, + src: { + type: "string", + enum: ["home", "all", "users", "list", "group", "instances"], + }, userListId: { type: "string", format: "misskey:id", nullable: true }, userGroupId: { type: "string", format: "misskey:id", nullable: true }, keywords: { @@ -64,6 +78,12 @@ export const paramDef = { type: "string", }, }, + instances: { + type: "array", + items: { + type: "string", + }, + }, caseSensitive: { type: "boolean" }, withReplies: { type: "boolean" }, withFile: { type: "boolean" }, @@ -75,6 +95,7 @@ export const paramDef = { "keywords", "excludeKeywords", "users", + "instances", "caseSensitive", "withReplies", "withFile", @@ -84,9 +105,17 @@ export const paramDef = { export default define(meta, paramDef, async (ps, user) => { if (user.movedToUri != null) throw new ApiError(meta.errors.noSuchUserGroup); + if (ps.keywords.length === 0) throw new ApiError(meta.errors.noKeywords); let userList; let userGroupJoining; + const antennas = await Antennas.findBy({ + userId: user.id, + }); + if (antennas.length > 5 && !user.isAdmin) { + throw new ApiError(meta.errors.tooManyAntennas); + } + if (ps.src === "list" && ps.userListId) { userList = await UserLists.findOneBy({ id: ps.userListId, @@ -118,6 +147,7 @@ export default define(meta, paramDef, async (ps, user) => { keywords: ps.keywords, excludeKeywords: ps.excludeKeywords, users: ps.users, + instances: ps.instances, caseSensitive: ps.caseSensitive, withReplies: ps.withReplies, withFile: ps.withFile, diff --git a/packages/backend/src/server/api/endpoints/antennas/markread.ts b/packages/backend/src/server/api/endpoints/antennas/markread.ts index e29e13bbbb..db8e683e49 100644 --- a/packages/backend/src/server/api/endpoints/antennas/markread.ts +++ b/packages/backend/src/server/api/endpoints/antennas/markread.ts @@ -1,7 +1,6 @@ import define from "../../define.js"; -import { Antennas, AntennaNotes } from "@/models/index.js"; +import { Antennas } from "@/models/index.js"; import { FindOptionsWhere } from "typeorm"; -import { AntennaNote } from "@/models/entities/antenna-note.js"; export const meta = { tags: ["antennas", "account"], @@ -29,15 +28,15 @@ export default define(meta, paramDef, async (ps, me) => { return null; } - await AntennaNotes.update( - { - antennaId: antenna.id, - read: false, - }, - { - read: true, - }, - ); + // await AntennaNotes.update( + // { + // antennaId: antenna.id, + // read: false, + // }, + // { + // read: true, + // }, + // ); return true; }); diff --git a/packages/backend/src/server/api/endpoints/antennas/notes.ts b/packages/backend/src/server/api/endpoints/antennas/notes.ts index d011c5fb80..6ca71c08e2 100644 --- a/packages/backend/src/server/api/endpoints/antennas/notes.ts +++ b/packages/backend/src/server/api/endpoints/antennas/notes.ts @@ -1,6 +1,8 @@ import define from "../../define.js"; import readNote from "@/services/note/read.js"; -import { Antennas, Notes, AntennaNotes } from "@/models/index.js"; +import { Antennas, Notes } from "@/models/index.js"; +import { redisClient } from "@/db/redis.js"; +import { genId } from "@/misc/gen-id.js"; import { makePaginationQuery } from "../../common/make-pagination-query.js"; import { generateVisibilityQuery } from "../../common/generate-visibility-query.js"; import { generateMutedUserQuery } from "../../common/generate-muted-user-query.js"; @@ -58,6 +60,26 @@ export default define(meta, paramDef, async (ps, user) => { throw new ApiError(meta.errors.noSuchAntenna); } + const noteIdsRes = await redisClient.xrevrange( + `antennaTimeline:${antenna.id}`, + ps.untilDate || "+", + "-", + "COUNT", + ps.limit + 1, + ); // untilIdに指定したものも含まれるため+1 + + if (noteIdsRes.length === 0) { + return []; + } + + const noteIds = noteIdsRes + .map((x) => x[1][1]) + .filter((x) => x !== ps.untilId); + + if (noteIds.length === 0) { + return []; + } + const query = makePaginationQuery( Notes.createQueryBuilder("note"), ps.sinceId, @@ -65,11 +87,7 @@ export default define(meta, paramDef, async (ps, user) => { ps.sinceDate, ps.untilDate, ) - .innerJoin( - AntennaNotes.metadata.targetName, - "antennaNote", - "antennaNote.noteId = note.id", - ) + .where("note.id IN (:...noteIds)", { noteIds: noteIds }) .innerJoinAndSelect("note.user", "user") .leftJoinAndSelect("user.avatar", "avatar") .leftJoinAndSelect("user.banner", "banner") @@ -81,7 +99,7 @@ export default define(meta, paramDef, async (ps, user) => { .leftJoinAndSelect("renote.user", "renoteUser") .leftJoinAndSelect("renoteUser.avatar", "renoteUserAvatar") .leftJoinAndSelect("renoteUser.banner", "renoteUserBanner") - .andWhere("antennaNote.antennaId = :antennaId", { antennaId: antenna.id }); + .andWhere("note.visibility != 'home'"); generateVisibilityQuery(query, user); generateMutedUserQuery(query, user); diff --git a/packages/backend/src/server/api/endpoints/antennas/update.ts b/packages/backend/src/server/api/endpoints/antennas/update.ts index 6c48cd3696..f491c0b638 100644 --- a/packages/backend/src/server/api/endpoints/antennas/update.ts +++ b/packages/backend/src/server/api/endpoints/antennas/update.ts @@ -43,7 +43,10 @@ export const paramDef = { properties: { antennaId: { type: "string", format: "misskey:id" }, name: { type: "string", minLength: 1, maxLength: 100 }, - src: { type: "string", enum: ["home", "all", "users", "list", "group"] }, + src: { + type: "string", + enum: ["home", "all", "users", "list", "group", "instances"], + }, userListId: { type: "string", format: "misskey:id", nullable: true }, userGroupId: { type: "string", format: "misskey:id", nullable: true }, keywords: { @@ -70,6 +73,12 @@ export const paramDef = { type: "string", }, }, + instances: { + type: "array", + items: { + type: "string", + }, + }, caseSensitive: { type: "boolean" }, withReplies: { type: "boolean" }, withFile: { type: "boolean" }, @@ -82,6 +91,7 @@ export const paramDef = { "keywords", "excludeKeywords", "users", + "instances", "caseSensitive", "withReplies", "withFile", @@ -131,6 +141,7 @@ export default define(meta, paramDef, async (ps, user) => { keywords: ps.keywords, excludeKeywords: ps.excludeKeywords, users: ps.users, + instances: ps.instances, caseSensitive: ps.caseSensitive, withReplies: ps.withReplies, withFile: ps.withFile, diff --git a/packages/backend/src/server/api/endpoints/ap/show.ts b/packages/backend/src/server/api/endpoints/ap/show.ts index fa804802eb..c25f0a8018 100644 --- a/packages/backend/src/server/api/endpoints/ap/show.ts +++ b/packages/backend/src/server/api/endpoints/ap/show.ts @@ -1,5 +1,4 @@ import define from "../../define.js"; -import config from "@/config/index.js"; import { createPerson } from "@/remote/activitypub/models/person.js"; import { createNote } from "@/remote/activitypub/models/note.js"; import DbResolver from "@/remote/activitypub/db-resolver.js"; @@ -9,11 +8,13 @@ import { extractDbHost } from "@/misc/convert-host.js"; import { Users, Notes } from "@/models/index.js"; import type { Note } from "@/models/entities/note.js"; import type { CacheableLocalUser, User } from "@/models/entities/user.js"; -import { fetchMeta } from "@/misc/fetch-meta.js"; import { isActor, isPost, getApId } from "@/remote/activitypub/type.js"; import type { SchemaType } from "@/misc/schema.js"; -import { HOUR } from "@/const.js"; +import { MINUTE } from "@/const.js"; import { shouldBlockInstance } from "@/misc/should-block-instance.js"; +import { updateQuestion } from "@/remote/activitypub/models/question.js"; +import { populatePoll } from "@/models/repositories/note.js"; +import { redisClient } from "@/db/redis.js"; export const meta = { tags: ["federation"], @@ -21,8 +22,8 @@ export const meta = { requireCredential: true, limit: { - duration: HOUR, - max: 30, + duration: MINUTE, + max: 10, }, errors: { @@ -104,18 +105,30 @@ async function fetchAny( const dbResolver = new DbResolver(); - let local = await mergePack( - me, - ...(await Promise.all([ - dbResolver.getUserFromApId(uri), - dbResolver.getNoteFromApId(uri), - ])), - ); - if (local != null) return local; + const [user, note] = await Promise.all([ + dbResolver.getUserFromApId(uri), + dbResolver.getNoteFromApId(uri), + ]); + let local = await mergePack(me, user, note); + if (local) { + if (local.type === "Note" && note?.uri && note.hasPoll) { + // Update questions if the stored (remote) note contains the poll + const key = `pollFetched:${note.uri}`; + if ((await redisClient.exists(key)) === 0) { + if (await updateQuestion(note.uri)) { + local.object.poll = await populatePoll(note, me?.id ?? null); + } + // Allow fetching the poll again after 1 minute + await redisClient.set(key, 1, "EX", 60); + } + } + return local; + } // fetching Object once from remote const resolver = new Resolver(); - const object = (await resolver.resolve(uri)) as any; + resolver.setUser(me); + const object = await resolver.resolve(uri); // /@user If a URI other than the id is specified, // the URI is determined here @@ -123,8 +136,8 @@ async function fetchAny( local = await mergePack( me, ...(await Promise.all([ - dbResolver.getUserFromApId(object.id), - dbResolver.getNoteFromApId(object.id), + dbResolver.getUserFromApId(getApId(object)), + dbResolver.getNoteFromApId(getApId(object)), ])), ); if (local != null) return local; @@ -132,8 +145,12 @@ async function fetchAny( return await mergePack( me, - isActor(object) ? await createPerson(getApId(object)) : null, - isPost(object) ? await createNote(getApId(object), undefined, true) : null, + isActor(object) + ? await createPerson(getApId(object), resolver.reset()) + : null, + isPost(object) + ? await createNote(getApId(object), resolver.reset(), true) + : null, ); } diff --git a/packages/backend/src/server/api/endpoints/auth/accept.ts b/packages/backend/src/server/api/endpoints/auth/accept.ts index 35565e2560..8e6ad6527a 100644 --- a/packages/backend/src/server/api/endpoints/auth/accept.ts +++ b/packages/backend/src/server/api/endpoints/auth/accept.ts @@ -41,12 +41,14 @@ export default define(meta, paramDef, async (ps, user) => { const accessToken = secureRndstr(32, true); // Fetch exist access token - const exist = await AccessTokens.findOneBy({ - appId: session.appId, - userId: user.id, + const exist = await AccessTokens.exist({ + where: { + appId: session.appId, + userId: user.id, + }, }); - if (exist == null) { + if (!exist) { // Lookup app const app = await Apps.findOneByOrFail({ id: session.appId }); diff --git a/packages/backend/src/server/api/endpoints/blocking/create.ts b/packages/backend/src/server/api/endpoints/blocking/create.ts index 4bd58d5ef5..f00a2923d5 100644 --- a/packages/backend/src/server/api/endpoints/blocking/create.ts +++ b/packages/backend/src/server/api/endpoints/blocking/create.ts @@ -69,12 +69,14 @@ export default define(meta, paramDef, async (ps, user) => { }); // Check if already blocking - const exist = await Blockings.findOneBy({ - blockerId: blocker.id, - blockeeId: blockee.id, + const exist = await Blockings.exist({ + where: { + blockerId: blocker.id, + blockeeId: blockee.id, + }, }); - if (exist != null) { + if (exist) { throw new ApiError(meta.errors.alreadyBlocking); } diff --git a/packages/backend/src/server/api/endpoints/blocking/delete.ts b/packages/backend/src/server/api/endpoints/blocking/delete.ts index 6c4ca27755..037d5af22c 100644 --- a/packages/backend/src/server/api/endpoints/blocking/delete.ts +++ b/packages/backend/src/server/api/endpoints/blocking/delete.ts @@ -69,12 +69,14 @@ export default define(meta, paramDef, async (ps, user) => { }); // Check not blocking - const exist = await Blockings.findOneBy({ - blockerId: blocker.id, - blockeeId: blockee.id, + const exist = await Blockings.exist({ + where: { + blockerId: blocker.id, + blockeeId: blockee.id, + }, }); - if (exist == null) { + if (!exist) { throw new ApiError(meta.errors.notBlocking); } diff --git a/packages/backend/src/server/api/endpoints/channels/followed.ts b/packages/backend/src/server/api/endpoints/channels/followed.ts index 63483a79aa..993a211f7e 100644 --- a/packages/backend/src/server/api/endpoints/channels/followed.ts +++ b/packages/backend/src/server/api/endpoints/channels/followed.ts @@ -1,6 +1,5 @@ import define from "../../define.js"; import { Channels, ChannelFollowings } from "@/models/index.js"; -import { makePaginationQuery } from "../../common/make-pagination-query.js"; export const meta = { tags: ["channels", "account"], @@ -33,11 +32,24 @@ export const paramDef = { } as const; export default define(meta, paramDef, async (ps, me) => { - const query = makePaginationQuery( - ChannelFollowings.createQueryBuilder(), - ps.sinceId, - ps.untilId, - ).andWhere({ followerId: me.id }); + const query = ChannelFollowings.createQueryBuilder("following").andWhere({ + followerId: me.id, + }); + if (ps.sinceId) { + query.andWhere('following."followeeId" > :sinceId', { + sinceId: ps.sinceId, + }); + } + if (ps.untilId) { + query.andWhere('following."followeeId" < :untilId', { + untilId: ps.untilId, + }); + } + if (ps.sinceId && !ps.untilId) { + query.orderBy('following."followeeId"', "ASC"); + } else { + query.orderBy('following."followeeId"', "DESC"); + } const followings = await query.take(ps.limit).getMany(); diff --git a/packages/backend/src/server/api/endpoints/channels/search.ts b/packages/backend/src/server/api/endpoints/channels/search.ts new file mode 100644 index 0000000000..b21fa76206 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/channels/search.ts @@ -0,0 +1,69 @@ +import define from "../../define.js"; +import { Brackets } from "typeorm"; +import { Endpoint } from "@/server/api/endpoint-base.js"; +import { makePaginationQuery } from "@/server/api/common/make-pagination-query.js"; +import { Channels } from "@/models/index.js"; +import { DI } from "@/di-symbols.js"; +import { sqlLikeEscape } from "@/misc/sql-like-escape.js"; + +export const meta = { + tags: ["channels"], + + requireCredential: false, + + res: { + type: "array", + optional: false, + nullable: false, + items: { + type: "object", + optional: false, + nullable: false, + ref: "Channel", + }, + }, +} as const; + +export const paramDef = { + type: "object", + properties: { + query: { type: "string" }, + type: { + type: "string", + enum: ["nameAndDescription", "nameOnly"], + default: "nameAndDescription", + }, + sinceId: { type: "string", format: "misskey:id" }, + untilId: { type: "string", format: "misskey:id" }, + limit: { type: "integer", minimum: 1, maximum: 100, default: 5 }, + }, + required: ["query"], +} as const; + +export default define(meta, paramDef, async (ps, me) => { + const query = makePaginationQuery( + Channels.createQueryBuilder("channel"), + ps.sinceId, + ps.untilId, + ); + + if (ps.type === "nameAndDescription") { + query.andWhere( + new Brackets((qb) => { + qb.where("channel.name ILIKE :q", { + q: `%${sqlLikeEscape(ps.query)}%`, + }).orWhere("channel.description ILIKE :q", { + q: `%${sqlLikeEscape(ps.query)}%`, + }); + }), + ); + } else { + query.andWhere("channel.name ILIKE :q", { + q: `%${sqlLikeEscape(ps.query)}%`, + }); + } + + const channels = await query.take(ps.limit).getMany(); + + return await Promise.all(channels.map((x) => Channels.pack(x, me))); +}); diff --git a/packages/backend/src/server/api/endpoints/clips/add-note.ts b/packages/backend/src/server/api/endpoints/clips/add-note.ts index b9d6b54c95..416af6faa2 100644 --- a/packages/backend/src/server/api/endpoints/clips/add-note.ts +++ b/packages/backend/src/server/api/endpoints/clips/add-note.ts @@ -57,12 +57,14 @@ export default define(meta, paramDef, async (ps, user) => { throw err; }); - const exist = await ClipNotes.findOneBy({ - noteId: note.id, - clipId: clip.id, + const exist = await ClipNotes.exist({ + where: { + noteId: note.id, + clipId: clip.id, + }, }); - if (exist != null) { + if (exist) { throw new ApiError(meta.errors.alreadyClipped); } diff --git a/packages/backend/src/server/api/endpoints/compatibility/peers.ts b/packages/backend/src/server/api/endpoints/compatibility/peers.ts new file mode 100644 index 0000000000..30f6e0e937 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/compatibility/peers.ts @@ -0,0 +1,25 @@ +import { Instances } from "@/models/index.js"; +import define from "../../define.js"; + +export const meta = { + tags: ["meta"], + requireCredential: false, + requireCredentialPrivateMode: true, + allowGet: true, + cacheSec: 60, +} as const; + +export const paramDef = { + type: "object", +} as const; + +export default define(meta, paramDef, async (ps) => { + const instances = await Instances.find({ + select: ["host"], + where: { + isSuspended: false, + }, + }); + + return instances.map((instance) => instance.host); +}); diff --git a/packages/backend/src/server/api/endpoints/drive/files/check-existence.ts b/packages/backend/src/server/api/endpoints/drive/files/check-existence.ts index df89685201..e26da30eb5 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/check-existence.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/check-existence.ts @@ -26,10 +26,12 @@ export const paramDef = { } as const; export default define(meta, paramDef, async (ps, user) => { - const file = await DriveFiles.findOneBy({ - md5: ps.md5, - userId: user.id, + const exist = await DriveFiles.exist({ + where: { + md5: ps.md5, + userId: user.id, + }, }); - return file != null; + return exist; }); diff --git a/packages/backend/src/server/api/endpoints/drive/files/update.ts b/packages/backend/src/server/api/endpoints/drive/files/update.ts index d113b978f6..e833fddb58 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/update.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/update.ts @@ -54,7 +54,11 @@ export const paramDef = { folderId: { type: "string", format: "misskey:id", nullable: true }, name: { type: "string" }, isSensitive: { type: "boolean" }, - comment: { type: "string", nullable: true, maxLength: 512 }, + comment: { + type: "string", + nullable: true, + maxLength: DB_MAX_IMAGE_COMMENT_LENGTH, + }, }, required: ["fileId"], } as const; diff --git a/packages/backend/src/server/api/endpoints/emoji.ts b/packages/backend/src/server/api/endpoints/emoji.ts new file mode 100644 index 0000000000..ddfad77374 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/emoji.ts @@ -0,0 +1,39 @@ +import { IsNull } from "typeorm"; +import { Emojis } from "@/models/index.js"; +import define from "../define.js"; + +export const meta = { + tags: ["meta"], + + requireCredential: false, + allowGet: true, + cacheSec: 3600, + + res: { + type: "object", + optional: false, + nullable: false, + ref: "Emoji", + }, +} as const; + +export const paramDef = { + type: "object", + properties: { + name: { + type: "string", + }, + }, + required: ["name"], +} as const; + +export default define(meta, paramDef, async (ps, me) => { + const emoji = await Emojis.findOneOrFail({ + where: { + name: ps.name, + host: IsNull(), + }, + }); + + return Emojis.pack(emoji); +}); diff --git a/packages/backend/src/server/api/endpoints/federation/instances.ts b/packages/backend/src/server/api/endpoints/federation/instances.ts index 3ab37ff6f2..fa1a415312 100644 --- a/packages/backend/src/server/api/endpoints/federation/instances.ts +++ b/packages/backend/src/server/api/endpoints/federation/instances.ts @@ -2,6 +2,7 @@ import config from "@/config/index.js"; import define from "../../define.js"; import { Instances } from "@/models/index.js"; import { fetchMeta } from "@/misc/fetch-meta.js"; +import { sqlLikeEscape } from "@/misc/sql-like-escape.js"; export const meta = { tags: ["federation"], @@ -34,6 +35,7 @@ export const paramDef = { notResponding: { type: "boolean", nullable: true }, suspended: { type: "boolean", nullable: true }, federating: { type: "boolean", nullable: true }, + silenced: { type: "boolean", nullable: true }, subscribing: { type: "boolean", nullable: true }, publishing: { type: "boolean", nullable: true }, limit: { type: "integer", minimum: 1, maximum: 100, default: 30 }, @@ -102,16 +104,35 @@ export default define(meta, paramDef, async (ps, me) => { if (typeof ps.blocked === "boolean") { const meta = await fetchMeta(true); if (ps.blocked) { + if (meta.blockedHosts.length === 0) { + return []; + } query.andWhere("instance.host IN (:...blocks)", { blocks: meta.blockedHosts, }); - } else { + } else if (meta.blockedHosts.length > 0) { query.andWhere("instance.host NOT IN (:...blocks)", { blocks: meta.blockedHosts, }); } } + if (typeof ps.silenced === "boolean") { + const meta = await fetchMeta(true); + if (ps.silenced) { + if (meta.silencedHosts.length === 0) { + return []; + } + query.andWhere("instance.host IN (:...silences)", { + silences: meta.silencedHosts, + }); + } else if (meta.silencedHosts.length > 0) { + query.andWhere("instance.host NOT IN (:...silences)", { + silences: meta.silencedHosts, + }); + } + } + if (typeof ps.notResponding === "boolean") { if (ps.notResponding) { query.andWhere("instance.isNotResponding = TRUE"); @@ -158,7 +179,7 @@ export default define(meta, paramDef, async (ps, me) => { if (ps.host) { query.andWhere("instance.host like :host", { - host: `%${ps.host.toLowerCase()}%`, + host: `%${sqlLikeEscape(ps.host.toLowerCase())}%`, }); } diff --git a/packages/backend/src/server/api/endpoints/following/create.ts b/packages/backend/src/server/api/endpoints/following/create.ts index e617c1ffb3..48ae6ae7af 100644 --- a/packages/backend/src/server/api/endpoints/following/create.ts +++ b/packages/backend/src/server/api/endpoints/following/create.ts @@ -82,12 +82,14 @@ export default define(meta, paramDef, async (ps, user) => { }); // Check if already following - const exist = await Followings.findOneBy({ - followerId: follower.id, - followeeId: followee.id, + const exist = await Followings.exist({ + where: { + followerId: follower.id, + followeeId: followee.id, + }, }); - if (exist != null) { + if (exist) { throw new ApiError(meta.errors.alreadyFollowing); } diff --git a/packages/backend/src/server/api/endpoints/following/delete.ts b/packages/backend/src/server/api/endpoints/following/delete.ts index 2eebe8a903..cbc6097f4d 100644 --- a/packages/backend/src/server/api/endpoints/following/delete.ts +++ b/packages/backend/src/server/api/endpoints/following/delete.ts @@ -69,12 +69,14 @@ export default define(meta, paramDef, async (ps, user) => { }); // Check not following - const exist = await Followings.findOneBy({ - followerId: follower.id, - followeeId: followee.id, + const exist = await Followings.exist({ + where: { + followerId: follower.id, + followeeId: followee.id, + }, }); - if (exist == null) { + if (!exist) { throw new ApiError(meta.errors.notFollowing); } diff --git a/packages/backend/src/server/api/endpoints/following/invalidate.ts b/packages/backend/src/server/api/endpoints/following/invalidate.ts index 979d298f7d..01ccc2761b 100644 --- a/packages/backend/src/server/api/endpoints/following/invalidate.ts +++ b/packages/backend/src/server/api/endpoints/following/invalidate.ts @@ -69,12 +69,14 @@ export default define(meta, paramDef, async (ps, user) => { }); // Check not following - const exist = await Followings.findOneBy({ - followerId: follower.id, - followeeId: followee.id, + const exist = await Followings.exist({ + where: { + followerId: follower.id, + followeeId: followee.id, + }, }); - if (exist == null) { + if (!exist) { throw new ApiError(meta.errors.notFollowing); } diff --git a/packages/backend/src/server/api/endpoints/gallery/posts/like.ts b/packages/backend/src/server/api/endpoints/gallery/posts/like.ts index fd46406bdf..2506e40aaa 100644 --- a/packages/backend/src/server/api/endpoints/gallery/posts/like.ts +++ b/packages/backend/src/server/api/endpoints/gallery/posts/like.ts @@ -40,12 +40,14 @@ export default define(meta, paramDef, async (ps, user) => { } // if already liked - const exist = await GalleryLikes.findOneBy({ - postId: post.id, - userId: user.id, + const exist = await GalleryLikes.exist({ + where: { + postId: post.id, + userId: user.id, + }, }); - if (exist != null) { + if (exist) { throw new ApiError(meta.errors.alreadyLiked); } diff --git a/packages/backend/src/server/api/endpoints/gallery/posts/unlike.ts b/packages/backend/src/server/api/endpoints/gallery/posts/unlike.ts index 772dc92028..03bc299b94 100644 --- a/packages/backend/src/server/api/endpoints/gallery/posts/unlike.ts +++ b/packages/backend/src/server/api/endpoints/gallery/posts/unlike.ts @@ -38,17 +38,17 @@ export default define(meta, paramDef, async (ps, user) => { throw new ApiError(meta.errors.noSuchPost); } - const exist = await GalleryLikes.findOneBy({ + const like = await GalleryLikes.findOneBy({ postId: post.id, userId: user.id, }); - if (exist == null) { + if (like == null) { throw new ApiError(meta.errors.notLiked); } // Delete like - await GalleryLikes.delete(exist.id); + await GalleryLikes.delete(like.id); GalleryPosts.decrement({ id: post.id }, "likedCount", 1); }); diff --git a/packages/backend/src/server/api/endpoints/get-sounds.ts b/packages/backend/src/server/api/endpoints/get-sounds.ts new file mode 100644 index 0000000000..f7edd38609 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/get-sounds.ts @@ -0,0 +1,30 @@ +import { readdir } from "fs/promises"; +import define from "../define.js"; + +export const meta = { + tags: ["meta"], + requireCredential: false, + requireCredentialPrivateMode: false, +} as const; + +export const paramDef = { + type: "object", + properties: {}, + required: [], +} as const; + +export default define(meta, paramDef, async () => { + const music_files: (string | null)[] = [null]; + const directory = ( + await readdir("./assets/sounds", { withFileTypes: true }) + ).filter((potentialFolder) => potentialFolder.isDirectory()); + for await (const folder of directory) { + const files = (await readdir(`./assets/sounds/${folder.name}`)).filter( + (potentialSong) => potentialSong.endsWith(".mp3"), + ); + for await (const file of files) { + music_files.push(`${folder.name}/${file.replace(".mp3", "")}`); + } + } + return music_files; +}); diff --git a/packages/backend/src/server/api/endpoints/hashtags/search.ts b/packages/backend/src/server/api/endpoints/hashtags/search.ts index 95bc608ece..cde586af0c 100644 --- a/packages/backend/src/server/api/endpoints/hashtags/search.ts +++ b/packages/backend/src/server/api/endpoints/hashtags/search.ts @@ -1,5 +1,6 @@ import define from "../../define.js"; import { Hashtags } from "@/models/index.js"; +import { sqlLikeEscape } from "@/misc/sql-like-escape.js"; export const meta = { tags: ["hashtags"], @@ -31,7 +32,9 @@ export const paramDef = { export default define(meta, paramDef, async (ps) => { const hashtags = await Hashtags.createQueryBuilder("tag") - .where("tag.name like :q", { q: `${ps.query.toLowerCase()}%` }) + .where("tag.name like :q", { + q: `${sqlLikeEscape(ps.query.toLowerCase())}%`, + }) .orderBy("tag.count", "DESC") .groupBy("tag.id") .take(ps.limit) diff --git a/packages/backend/src/server/api/endpoints/i/2fa/done.ts b/packages/backend/src/server/api/endpoints/i/2fa/done.ts index 1e9892f03b..05d57d2821 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/done.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/done.ts @@ -1,6 +1,7 @@ -import * as speakeasy from "speakeasy"; +import { publishMainStream } from "@/services/stream.js"; +import * as OTPAuth from "otpauth"; import define from "../../../define.js"; -import { UserProfiles } from "@/models/index.js"; +import { Users, UserProfiles } from "@/models/index.js"; export const meta = { requireCredential: true, @@ -25,13 +26,14 @@ export default define(meta, paramDef, async (ps, user) => { throw new Error("二段階認証の設定が開始されていません"); } - const verified = (speakeasy as any).totp.verify({ - secret: profile.twoFactorTempSecret, - encoding: "base32", - token: token, + const delta = OTPAuth.TOTP.validate({ + secret: OTPAuth.Secret.fromBase32(profile.twoFactorTempSecret), + digits: 6, + token, + window: 1, }); - if (!verified) { + if (delta === null) { throw new Error("not verified"); } @@ -39,4 +41,11 @@ export default define(meta, paramDef, async (ps, user) => { twoFactorSecret: profile.twoFactorTempSecret, twoFactorEnabled: true, }); + + const iObj = await Users.pack(user.id, user, { + detail: true, + includeSecrets: true, + }); + + publishMainStream(user.id, "meUpdated", iObj); }); diff --git a/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts b/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts index e80dc4d711..34660c6f2e 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/key-done.ts @@ -1,4 +1,3 @@ -import bcrypt from "bcryptjs"; import { promisify } from "node:util"; import * as cbor from "cbor"; import define from "../../../define.js"; @@ -11,6 +10,7 @@ import { import config from "@/config/index.js"; import { procedures, hash } from "../../../2fa.js"; import { publishMainStream } from "@/services/stream.js"; +import { comparePassword } from "@/misc/password.js"; const cborDecodeFirst = promisify(cbor.decodeFirst) as any; const rpIdHashReal = hash(Buffer.from(config.hostname, "utf-8")); @@ -28,7 +28,7 @@ export const paramDef = { attestationObject: { type: "string" }, password: { type: "string" }, challengeId: { type: "string" }, - name: { type: "string" }, + name: { type: "string", minLength: 1, maxLength: 30 }, }, required: [ "clientDataJSON", @@ -43,7 +43,7 @@ export default define(meta, paramDef, async (ps, user) => { const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); // Compare password - const same = await bcrypt.compare(ps.password, profile.password!); + const same = await comparePassword(ps.password, profile.password!); if (!same) { throw new Error("incorrect password"); diff --git a/packages/backend/src/server/api/endpoints/i/2fa/password-less.ts b/packages/backend/src/server/api/endpoints/i/2fa/password-less.ts index 11b2e9a2e3..b9f3426804 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/password-less.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/password-less.ts @@ -1,10 +1,20 @@ import define from "../../../define.js"; -import { UserProfiles } from "@/models/index.js"; +import { Users, UserProfiles, UserSecurityKeys } from "@/models/index.js"; +import { publishMainStream } from "@/services/stream.js"; +import { ApiError } from "../../../error.js"; export const meta = { requireCredential: true, secure: true, + + errors: { + noKey: { + message: "No security key.", + code: "NO_SECURITY_KEY", + id: "f9c54d7f-d4c2-4d3c-9a8g-a70daac86512", + }, + }, } as const; export const paramDef = { @@ -16,7 +26,36 @@ export const paramDef = { } as const; export default define(meta, paramDef, async (ps, user) => { + if (ps.value === true) { + // セキュリティキーがなければパスワードレスを有効にはできない + const keyCount = await UserSecurityKeys.count({ + where: { + userId: user.id, + }, + select: { + id: true, + name: true, + lastUsed: true, + }, + }); + + if (keyCount === 0) { + await UserProfiles.update(user.id, { + usePasswordLessLogin: false, + }); + + throw new ApiError(meta.errors.noKey); + } + } + await UserProfiles.update(user.id, { usePasswordLessLogin: ps.value, }); + + const iObj = await Users.pack(user.id, user, { + detail: true, + includeSecrets: true, + }); + + publishMainStream(user.id, "meUpdated", iObj); }); diff --git a/packages/backend/src/server/api/endpoints/i/2fa/register-key.ts b/packages/backend/src/server/api/endpoints/i/2fa/register-key.ts index 8c0af28ee4..a10dc9b256 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/register-key.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/register-key.ts @@ -1,10 +1,10 @@ -import bcrypt from "bcryptjs"; import define from "../../../define.js"; import { UserProfiles, AttestationChallenges } from "@/models/index.js"; import { promisify } from "node:util"; import * as crypto from "node:crypto"; import { genId } from "@/misc/gen-id.js"; import { hash } from "../../../2fa.js"; +import { comparePassword } from "@/misc/password.js"; const randomBytes = promisify(crypto.randomBytes); @@ -26,15 +26,15 @@ export default define(meta, paramDef, async (ps, user) => { const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); // Compare password - const same = await bcrypt.compare(ps.password, profile.password!); + const same = await comparePassword(ps.password, profile.password!); if (!same) { throw new Error("incorrect password"); } - if (!profile.twoFactorEnabled) { - throw new Error("2fa not enabled"); - } + // if (!profile.twoFactorEnabled) { + // throw new Error("2fa not enabled"); + // } // 32 byte challenge const entropy = await randomBytes(32); diff --git a/packages/backend/src/server/api/endpoints/i/2fa/register.ts b/packages/backend/src/server/api/endpoints/i/2fa/register.ts index 9019787f23..cf391ca2fd 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/register.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/register.ts @@ -1,9 +1,9 @@ -import bcrypt from "bcryptjs"; -import * as speakeasy from "speakeasy"; +import * as OTPAuth from "otpauth"; import * as QRCode from "qrcode"; import config from "@/config/index.js"; import { UserProfiles } from "@/models/index.js"; import define from "../../../define.js"; +import { comparePassword } from "@/misc/password.js"; export const meta = { requireCredential: true, @@ -23,32 +23,31 @@ export default define(meta, paramDef, async (ps, user) => { const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); // Compare password - const same = await bcrypt.compare(ps.password, profile.password!); + const same = await comparePassword(ps.password, profile.password!); if (!same) { throw new Error("incorrect password"); } // Generate user's secret key - const secret = speakeasy.generateSecret({ - length: 32, - }); + const secret = new OTPAuth.Secret(); await UserProfiles.update(user.id, { twoFactorTempSecret: secret.base32, }); // Get the data URL of the authenticator URL - const url = speakeasy.otpauthURL({ - secret: secret.base32, - encoding: "base32", + const totp = new OTPAuth.TOTP({ + secret, + digits: 6, label: user.username, issuer: config.host, }); - const dataUrl = await QRCode.toDataURL(url); + const url = totp.toString(); + const qr = await QRCode.toDataURL(url); return { - qr: dataUrl, + qr, url, secret: secret.base32, label: user.username, diff --git a/packages/backend/src/server/api/endpoints/i/2fa/remove-key.ts b/packages/backend/src/server/api/endpoints/i/2fa/remove-key.ts index d491f0a6ee..d91c8f214b 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/remove-key.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/remove-key.ts @@ -1,4 +1,4 @@ -import bcrypt from "bcryptjs"; +import { comparePassword } from "@/misc/password.js"; import define from "../../../define.js"; import { UserProfiles, UserSecurityKeys, Users } from "@/models/index.js"; import { publishMainStream } from "@/services/stream.js"; @@ -22,7 +22,7 @@ export default define(meta, paramDef, async (ps, user) => { const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); // Compare password - const same = await bcrypt.compare(ps.password, profile.password!); + const same = await comparePassword(ps.password, profile.password!); if (!same) { throw new Error("incorrect password"); @@ -34,6 +34,24 @@ export default define(meta, paramDef, async (ps, user) => { id: ps.credentialId, }); + // 使われているキーがなくなったらパスワードレスログインをやめる + const keyCount = await UserSecurityKeys.count({ + where: { + userId: user.id, + }, + select: { + id: true, + name: true, + lastUsed: true, + }, + }); + + if (keyCount === 0) { + await UserProfiles.update(me.id, { + usePasswordLessLogin: false, + }); + } + // Publish meUpdated event publishMainStream( user.id, diff --git a/packages/backend/src/server/api/endpoints/i/2fa/unregister.ts b/packages/backend/src/server/api/endpoints/i/2fa/unregister.ts index 9bb1538b00..54f1422d4c 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/unregister.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/unregister.ts @@ -1,6 +1,7 @@ -import bcrypt from "bcryptjs"; +import { publishMainStream } from "@/services/stream.js"; import define from "../../../define.js"; -import { UserProfiles } from "@/models/index.js"; +import { Users, UserProfiles } from "@/models/index.js"; +import { comparePassword } from "@/misc/password.js"; export const meta = { requireCredential: true, @@ -20,7 +21,7 @@ export default define(meta, paramDef, async (ps, user) => { const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); // Compare password - const same = await bcrypt.compare(ps.password, profile.password!); + const same = await comparePassword(ps.password, profile.password!); if (!same) { throw new Error("incorrect password"); @@ -29,5 +30,13 @@ export default define(meta, paramDef, async (ps, user) => { await UserProfiles.update(user.id, { twoFactorSecret: null, twoFactorEnabled: false, + usePasswordLessLogin: false, }); + + const iObj = await Users.pack(user.id, user, { + detail: true, + includeSecrets: true, + }); + + publishMainStream(user.id, "meUpdated", iObj); }); diff --git a/packages/backend/src/server/api/endpoints/i/2fa/update-key.ts b/packages/backend/src/server/api/endpoints/i/2fa/update-key.ts new file mode 100644 index 0000000000..7587ec780e --- /dev/null +++ b/packages/backend/src/server/api/endpoints/i/2fa/update-key.ts @@ -0,0 +1,58 @@ +import { publishMainStream } from "@/services/stream.js"; +import define from "../../../define.js"; +import { Users, UserSecurityKeys } from "@/models/index.js"; +import { ApiError } from "../../../error.js"; + +export const meta = { + requireCredential: true, + + secure: true, + + errors: { + noSuchKey: { + message: "No such key.", + code: "NO_SUCH_KEY", + id: "f9c5467f-d492-4d3c-9a8g-a70dacc86512", + }, + + accessDenied: { + message: "You do not have edit privilege of the channel.", + code: "ACCESS_DENIED", + id: "1fb7cb09-d46a-4fff-b8df-057708cce513", + }, + }, +} as const; + +export const paramDef = { + type: "object", + properties: { + name: { type: "string", minLength: 1, maxLength: 30 }, + credentialId: { type: "string" }, + }, + required: ["name", "credentialId"], +} as const; + +export default define(meta, paramDef, async (ps, user) => { + const key = await UserSecurityKeys.findOneBy({ + id: ps.credentialId, + }); + + if (key == null) { + throw new ApiError(meta.errors.noSuchKey); + } + + if (key.userId !== user.id) { + throw new ApiError(meta.errors.accessDenied); + } + + await UserSecurityKeys.update(key.id, { + name: ps.name, + }); + + const iObj = await Users.pack(user.id, user, { + detail: true, + includeSecrets: true, + }); + + publishMainStream(user.id, "meUpdated", iObj); +}); diff --git a/packages/backend/src/server/api/endpoints/i/change-password.ts b/packages/backend/src/server/api/endpoints/i/change-password.ts index fcfc38bd14..8bbb3ad93a 100644 --- a/packages/backend/src/server/api/endpoints/i/change-password.ts +++ b/packages/backend/src/server/api/endpoints/i/change-password.ts @@ -1,6 +1,6 @@ -import bcrypt from "bcryptjs"; import define from "../../define.js"; import { UserProfiles } from "@/models/index.js"; +import { hashPassword, comparePassword } from "@/misc/password.js"; export const meta = { requireCredential: true, @@ -21,15 +21,14 @@ export default define(meta, paramDef, async (ps, user) => { const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); // Compare password - const same = await bcrypt.compare(ps.currentPassword, profile.password!); + const same = await comparePassword(ps.currentPassword, profile.password!); if (!same) { throw new Error("incorrect password"); } // Generate hash of password - const salt = await bcrypt.genSalt(8); - const hash = await bcrypt.hash(ps.newPassword, salt); + const hash = await hashPassword(ps.newPassword); await UserProfiles.update(user.id, { password: hash, diff --git a/packages/backend/src/server/api/endpoints/i/delete-account.ts b/packages/backend/src/server/api/endpoints/i/delete-account.ts index 81aee9a41a..781abe0b38 100644 --- a/packages/backend/src/server/api/endpoints/i/delete-account.ts +++ b/packages/backend/src/server/api/endpoints/i/delete-account.ts @@ -1,7 +1,7 @@ -import bcrypt from "bcryptjs"; import { UserProfiles, Users } from "@/models/index.js"; import { deleteAccount } from "@/services/delete-account.js"; import define from "../../define.js"; +import { comparePassword } from "@/misc/password.js"; export const meta = { requireCredential: true, @@ -25,7 +25,7 @@ export default define(meta, paramDef, async (ps, user) => { } // Compare password - const same = await bcrypt.compare(ps.password, profile.password!); + const same = await comparePassword(ps.password, profile.password!); if (!same) { throw new Error("incorrect password"); diff --git a/packages/backend/src/server/api/endpoints/i/import-posts.ts b/packages/backend/src/server/api/endpoints/i/import-posts.ts new file mode 100644 index 0000000000..3adba0514e --- /dev/null +++ b/packages/backend/src/server/api/endpoints/i/import-posts.ts @@ -0,0 +1,55 @@ +import define from "../../define.js"; +import { createImportPostsJob } from "@/queue/index.js"; +import { ApiError } from "../../error.js"; +import { DriveFiles } from "@/models/index.js"; +import { DAY } from "@/const.js"; +import { fetchMeta } from "@/misc/fetch-meta.js"; + +export const meta = { + secure: true, + requireCredential: true, + limit: { + duration: DAY * 30, + max: 2, + }, + errors: { + noSuchFile: { + message: "No such file.", + code: "NO_SUCH_FILE", + id: "e674141e-bd2a-ba85-e616-aefb187c9c2a", + }, + + emptyFile: { + message: "That file is empty.", + code: "EMPTY_FILE", + id: "d2f12af1-e7b4-feac-86a3-519548f2728e", + }, + + importsDisabled: { + message: "Post imports are disabled.", + code: "IMPORTS_DISABLED", + id: " bc9227e4-fb82-11ed-be56-0242ac120002", + }, + }, +} as const; + +export const paramDef = { + type: "object", + properties: { + fileId: { type: "string", format: "misskey:id" }, + signatureCheck: { type: "boolean" }, + }, + required: ["fileId"], +} as const; + +export default define(meta, paramDef, async (ps, user) => { + const file = await DriveFiles.findOneBy({ id: ps.fileId }); + + const instanceMeta = await fetchMeta(); + if (instanceMeta.experimentalFeatures?.postImports === false) + throw new ApiError(meta.errors.importsDisabled); + + if (file == null) throw new ApiError(meta.errors.noSuchFile); + if (file.size === 0) throw new ApiError(meta.errors.emptyFile); + createImportPostsJob(user, file.id, ps.signatureCheck); +}); diff --git a/packages/backend/src/server/api/endpoints/i/known-as.ts b/packages/backend/src/server/api/endpoints/i/known-as.ts index 5e86e8b955..0d0c06180a 100644 --- a/packages/backend/src/server/api/endpoints/i/known-as.ts +++ b/packages/backend/src/server/api/endpoints/i/known-as.ts @@ -1,4 +1,4 @@ -import type { User, UserDetailedNotMeOnly } from "@/models/entities/user.js"; +import type { User } from "@/models/entities/user.js"; import { Users } from "@/models/index.js"; import { resolveUser } from "@/remote/resolve-user.js"; import acceptAllFollowRequests from "@/services/following/requests/accept-all.js"; @@ -6,10 +6,9 @@ import { publishToFollowers } from "@/services/i/update.js"; import { publishMainStream } from "@/services/stream.js"; import { DAY } from "@/const.js"; import { apiLogger } from "../../logger.js"; -import { UserProfiles } from "@/models/index.js"; -import config from "@/config/index.js"; import define from "../../define.js"; import { ApiError } from "../../error.js"; +import { parse } from "@/misc/acct.js"; export const meta = { tags: ["users"], @@ -38,49 +37,57 @@ export const meta = { code: "URI_NULL", id: "bf326f31-d430-4f97-9933-5d61e4d48a23", }, + alreadyMoved: { + message: "You have already moved your account.", + code: "ALREADY_MOVED", + id: "56f20ec9-fd06-4fa5-841b-edd6d7d4fa31", + }, + yourself: { + message: "You can't set yourself as your own alias.", + code: "FORBIDDEN_TO_SET_YOURSELF", + id: "25c90186-4ab0-49c8-9bba-a1fa6c202ba4", + }, }, } as const; export const paramDef = { type: "object", properties: { - alsoKnownAs: { type: "string" }, + alsoKnownAs: { + type: "array", + maxItems: 10, + uniqueItems: true, + items: { type: "string" }, + }, }, required: ["alsoKnownAs"], } as const; export default define(meta, paramDef, async (ps, user) => { if (!ps.alsoKnownAs) throw new ApiError(meta.errors.noSuchUser); + if (user.movedToUri) throw new ApiError(meta.errors.alreadyMoved); - let unfiltered: string = ps.alsoKnownAs; - const updates = {} as Partial; + const newAka = new Set(); - if (!unfiltered) { - updates.alsoKnownAs = null; - } else { - if (unfiltered.startsWith("acct:")) unfiltered = unfiltered.substring(5); - if (unfiltered.startsWith("@")) unfiltered = unfiltered.substring(1); - if (!unfiltered.includes("@")) throw new ApiError(meta.errors.notRemote); + for (const line of ps.alsoKnownAs) { + if (!line) throw new ApiError(meta.errors.noSuchUser); + const { username, host } = parse(line); - const userAddress: string[] = unfiltered.split("@"); - const knownAs = await resolveUser(userAddress[0], userAddress[1]).catch( - (e) => { - apiLogger.warn(`failed to resolve remote user: ${e}`); - throw new ApiError(meta.errors.noSuchUser); - }, - ); + const aka = await resolveUser(username, host).catch((e) => { + apiLogger.warn(`failed to resolve remote user: ${e}`); + throw new ApiError(meta.errors.noSuchUser); + }); - const toUrl: string | null = knownAs.uri; - if (!toUrl) { - throw new ApiError(meta.errors.uriNull); - } - if (updates.alsoKnownAs == null || updates.alsoKnownAs.length === 0) { - updates.alsoKnownAs = [toUrl]; - } else { - updates.alsoKnownAs.push(toUrl); - } + if (aka.id === user.id) throw new ApiError(meta.errors.yourself); + if (!aka.uri) throw new ApiError(meta.errors.uriNull); + + newAka.add(aka.uri); } + const updates = { + alsoKnownAs: newAka.size > 0 ? Array.from(newAka) : null, + } as Partial; + await Users.update(user.id, updates); const iObj = await Users.pack(user.id, user, { diff --git a/packages/backend/src/server/api/endpoints/i/move.ts b/packages/backend/src/server/api/endpoints/i/move.ts index bfeba04595..d972aaf1d9 100644 --- a/packages/backend/src/server/api/endpoints/i/move.ts +++ b/packages/backend/src/server/api/endpoints/i/move.ts @@ -3,7 +3,6 @@ import { resolveUser } from "@/remote/resolve-user.js"; import { DAY } from "@/const.js"; import DeliverManager from "@/remote/activitypub/deliver-manager.js"; import { renderActivity } from "@/remote/activitypub/renderer/index.js"; -import { genId } from "@/misc/gen-id.js"; import define from "../../define.js"; import { ApiError } from "../../error.js"; import { apiLogger } from "../../logger.js"; @@ -11,9 +10,9 @@ import deleteFollowing from "@/services/following/delete.js"; import create from "@/services/following/create.js"; import { getUser } from "@/server/api/common/getters.js"; import { Followings, Users } from "@/models/index.js"; -import { UserProfiles } from "@/models/index.js"; import config from "@/config/index.js"; import { publishMainStream } from "@/services/stream.js"; +import { parse } from "@/misc/acct.js"; export const meta = { tags: ["users"], @@ -81,7 +80,7 @@ export const paramDef = { function moveActivity(toUrl: string, fromUrl: string) { const activity = { - id: genId(), + id: null, actor: fromUrl, type: "Move", object: fromUrl, @@ -96,22 +95,13 @@ export default define(meta, paramDef, async (ps, user) => { if (user.isAdmin) throw new ApiError(meta.errors.adminForbidden); if (user.movedToUri) throw new ApiError(meta.errors.alreadyMoved); - let unfiltered: string = ps.moveToAccount; - if (!unfiltered) { + const { username, host } = parse(ps.moveToAccount); + if (!host) throw new ApiError(meta.errors.notRemote); + + const moveTo: User = await resolveUser(username, host).catch((e) => { + apiLogger.warn(`failed to resolve remote user: ${e}`); throw new ApiError(meta.errors.noSuchMoveTarget); - } - - if (unfiltered.startsWith("acct:")) unfiltered = unfiltered.substring(5); - if (unfiltered.startsWith("@")) unfiltered = unfiltered.substring(1); - if (!unfiltered.includes("@")) throw new ApiError(meta.errors.notRemote); - - const userAddress: string[] = unfiltered.split("@"); - const moveTo: User = await resolveUser(userAddress[0], userAddress[1]).catch( - (e) => { - apiLogger.warn(`failed to resolve remote user: ${e}`); - throw new ApiError(meta.errors.noSuchMoveTarget); - }, - ); + }); let fromUrl: string | null = user.uri; if (!fromUrl) { fromUrl = `${config.url}/users/${user.id}`; @@ -135,6 +125,7 @@ export default define(meta, paramDef, async (ps, user) => { if (!toUrl) toUrl = ""; updates.movedToUri = toUrl; + updates.alsoKnownAs = user.alsoKnownAs?.concat(toUrl) ?? [toUrl]; await Users.update(user.id, updates); const iObj = await Users.pack(user.id, user, { diff --git a/packages/backend/src/server/api/endpoints/i/read-announcement.ts b/packages/backend/src/server/api/endpoints/i/read-announcement.ts index 5218dba871..d0dfa66579 100644 --- a/packages/backend/src/server/api/endpoints/i/read-announcement.ts +++ b/packages/backend/src/server/api/endpoints/i/read-announcement.ts @@ -30,19 +30,23 @@ export const paramDef = { export default define(meta, paramDef, async (ps, user) => { // Check if announcement exists - const announcement = await Announcements.findOneBy({ id: ps.announcementId }); + const exist = await Announcements.exist({ + where: { id: ps.announcementId }, + }); - if (announcement == null) { + if (!exist) { throw new ApiError(meta.errors.noSuchAnnouncement); } // Check if already read - const read = await AnnouncementReads.findOneBy({ - announcementId: ps.announcementId, - userId: user.id, + const read = await AnnouncementReads.exist({ + where: { + announcementId: ps.announcementId, + userId: user.id, + }, }); - if (read != null) { + if (read) { return; } diff --git a/packages/backend/src/server/api/endpoints/i/regenerate-token.ts b/packages/backend/src/server/api/endpoints/i/regenerate-token.ts index dff37cf3d3..b5b34c0902 100644 --- a/packages/backend/src/server/api/endpoints/i/regenerate-token.ts +++ b/packages/backend/src/server/api/endpoints/i/regenerate-token.ts @@ -1,4 +1,3 @@ -import bcrypt from "bcryptjs"; import { publishInternalEvent, publishMainStream, @@ -7,6 +6,7 @@ import { import generateUserToken from "../../common/generate-native-user-token.js"; import define from "../../define.js"; import { Users, UserProfiles } from "@/models/index.js"; +import { comparePassword } from "@/misc/password.js"; export const meta = { requireCredential: true, @@ -29,7 +29,7 @@ export default define(meta, paramDef, async (ps, user) => { const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); // Compare password - const same = await bcrypt.compare(ps.password, profile.password!); + const same = await comparePassword(ps.password, profile.password!); if (!same) { throw new Error("incorrect password"); diff --git a/packages/backend/src/server/api/endpoints/i/registry/get-unsecure.ts b/packages/backend/src/server/api/endpoints/i/registry/get-unsecure.ts index 40065c83ec..a9bcf69351 100644 --- a/packages/backend/src/server/api/endpoints/i/registry/get-unsecure.ts +++ b/packages/backend/src/server/api/endpoints/i/registry/get-unsecure.ts @@ -1,6 +1,6 @@ import { ApiError } from "../../../error.js"; import define from "../../../define.js"; -import { RegistryItems } from "../../../../../models/index.js"; +import { RegistryItems } from "@/models/index.js"; export const meta = { requireCredential: true, @@ -33,7 +33,7 @@ export const paramDef = { } as const; export default define(meta, paramDef, async (ps, user) => { - if (ps.key !== "reactions") return; + if (ps.key !== "reactions" && ps.key !== "defaultNoteVisibility") return; const query = RegistryItems.createQueryBuilder("item") .where("item.domain IS NULL") .andWhere("item.userId = :userId", { userId: user.id }) diff --git a/packages/backend/src/server/api/endpoints/i/revoke-token.ts b/packages/backend/src/server/api/endpoints/i/revoke-token.ts index 308442bf7b..3a410fa0e5 100644 --- a/packages/backend/src/server/api/endpoints/i/revoke-token.ts +++ b/packages/backend/src/server/api/endpoints/i/revoke-token.ts @@ -17,9 +17,9 @@ export const paramDef = { } as const; export default define(meta, paramDef, async (ps, user) => { - const token = await AccessTokens.findOneBy({ id: ps.tokenId }); + const exist = await AccessTokens.exist({ where: { id: ps.tokenId } }); - if (token) { + if (exist) { await AccessTokens.delete({ id: ps.tokenId, userId: user.id, diff --git a/packages/backend/src/server/api/endpoints/i/update-email.ts b/packages/backend/src/server/api/endpoints/i/update-email.ts index c3c24d4d3d..94ad6b3c72 100644 --- a/packages/backend/src/server/api/endpoints/i/update-email.ts +++ b/packages/backend/src/server/api/endpoints/i/update-email.ts @@ -2,12 +2,12 @@ import { publishMainStream } from "@/services/stream.js"; import define from "../../define.js"; import rndstr from "rndstr"; import config from "@/config/index.js"; -import bcrypt from "bcryptjs"; import { Users, UserProfiles } from "@/models/index.js"; import { sendEmail } from "@/services/send-email.js"; import { ApiError } from "../../error.js"; import { validateEmailForAccount } from "@/services/validate-email-for-account.js"; import { HOUR } from "@/const.js"; +import { comparePassword } from "@/misc/password.js"; export const meta = { requireCredential: true, @@ -47,7 +47,7 @@ export default define(meta, paramDef, async (ps, user) => { const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); // Compare password - const same = await bcrypt.compare(ps.password, profile.password!); + const same = await comparePassword(ps.password, profile.password!); if (!same) { throw new ApiError(meta.errors.incorrectPassword); diff --git a/packages/backend/src/server/api/endpoints/i/update.ts b/packages/backend/src/server/api/endpoints/i/update.ts index 48868de37e..6d3bde2b8d 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: { @@ -102,9 +116,10 @@ export const paramDef = { carefulBot: { type: "boolean" }, autoAcceptFollowed: { type: "boolean" }, noCrawle: { type: "boolean" }, + preventAiLearning: { type: "boolean" }, isBot: { type: "boolean" }, isCat: { type: "boolean" }, - showTimelineReplies: { type: "boolean" }, + speakAsCat: { type: "boolean" }, injectFeaturedNote: { type: "boolean" }, receiveAnnouncementEmail: { type: "boolean" }, alwaysMarkNsfw: { type: "boolean" }, @@ -183,14 +198,15 @@ export default define(meta, paramDef, async (ps, _user, token) => { if (typeof ps.publicReactions === "boolean") profileUpdates.publicReactions = ps.publicReactions; if (typeof ps.isBot === "boolean") updates.isBot = ps.isBot; - if (typeof ps.showTimelineReplies === "boolean") - updates.showTimelineReplies = ps.showTimelineReplies; if (typeof ps.carefulBot === "boolean") profileUpdates.carefulBot = ps.carefulBot; if (typeof ps.autoAcceptFollowed === "boolean") profileUpdates.autoAcceptFollowed = ps.autoAcceptFollowed; if (typeof ps.noCrawle === "boolean") profileUpdates.noCrawle = ps.noCrawle; + if (typeof ps.preventAiLearning === "boolean") + profileUpdates.preventAiLearning = ps.preventAiLearning; if (typeof ps.isCat === "boolean") updates.isCat = ps.isCat; + if (typeof ps.speakAsCat === "boolean") updates.speakAsCat = ps.speakAsCat; if (typeof ps.injectFeaturedNote === "boolean") profileUpdates.injectFeaturedNote = ps.injectFeaturedNote; if (typeof ps.receiveAnnouncementEmail === "boolean") @@ -232,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/server/api/endpoints/messaging/messages/create.ts b/packages/backend/src/server/api/endpoints/messaging/messages/create.ts index 99d392ce45..ed9ae16df0 100644 --- a/packages/backend/src/server/api/endpoints/messaging/messages/create.ts +++ b/packages/backend/src/server/api/endpoints/messaging/messages/create.ts @@ -151,7 +151,7 @@ export default define(meta, paramDef, async (ps, user) => { } // テキストが無いかつ添付ファイルも無かったらエラー - if (ps.text == null && file == null) { + if ((ps.text == null || ps.text.trim() === "") && file == null) { throw new ApiError(meta.errors.contentRequired); } diff --git a/packages/backend/src/server/api/endpoints/meta.ts b/packages/backend/src/server/api/endpoints/meta.ts index 005d0800ac..00adbc6bfa 100644 --- a/packages/backend/src/server/api/endpoints/meta.ts +++ b/packages/backend/src/server/api/endpoints/meta.ts @@ -1,9 +1,9 @@ +import JSON5 from "json5"; import { IsNull, MoreThan } from "typeorm"; import config from "@/config/index.js"; import { fetchMeta } from "@/misc/fetch-meta.js"; import { Ads, Emojis, Users } from "@/models/index.js"; -import { DB_MAX_NOTE_TEXT_LENGTH } from "@/misc/hard-limits.js"; -import { MAX_NOTE_TEXT_LENGTH } from "@/const.js"; +import { MAX_NOTE_TEXT_LENGTH, MAX_CAPTION_TEXT_LENGTH } from "@/const.js"; import define from "../define.js"; export const meta = { @@ -42,7 +42,7 @@ export const meta = { optional: false, nullable: false, format: "url", - example: "https://calckey.example.com", + example: "https://firefish.example.com", }, description: { type: "string", @@ -68,13 +68,13 @@ export const meta = { type: "string", optional: false, nullable: false, - default: "https://codeberg.org/calckey/calckey", + default: "https://gitlab.prometheus.systems/firefish/firefish", }, feedbackUrl: { type: "string", optional: false, nullable: false, - default: "https://codeberg.org/calckey/calckey/issues", + default: "https://gitlab.prometheus.systems/firefish/firefish/issues", }, defaultDarkTheme: { type: "string", @@ -155,7 +155,7 @@ export const meta = { type: "string", optional: false, nullable: false, - default: "/assets/ai.png", + default: "/static-assets/badges/info.png", }, bannerUrl: { type: "string", @@ -166,7 +166,7 @@ export const meta = { type: "string", optional: false, nullable: false, - default: "https://xn--931a.moe/aiart/yubitun.png", + default: "/static-assets/badges/error.png", }, iconUrl: { type: "string", @@ -178,6 +178,11 @@ export const meta = { optional: false, nullable: false, }, + maxCaptionTextLength: { + type: "number", + optional: false, + nullable: false, + }, emojis: { type: "array", optional: false, @@ -318,7 +323,7 @@ export const meta = { optional: false, nullable: false, }, - elasticsearch: { + searchFilters: { type: "boolean", optional: false, nullable: false, @@ -384,6 +389,11 @@ export const meta = { nullable: false, default: "⭐", }, + donationLink: { + type: "string", + optional: "true", + nullable: true, + }, }, }, } as const; @@ -456,9 +466,15 @@ export default define(meta, paramDef, async (ps, me) => { backgroundImageUrl: instance.backgroundImageUrl, logoImageUrl: instance.logoImageUrl, maxNoteTextLength: MAX_NOTE_TEXT_LENGTH, // 後方互換性のため + maxCaptionTextLength: MAX_CAPTION_TEXT_LENGTH, emojis: instance.privateMode && !me ? [] : await Emojis.packMany(emojis), - defaultLightTheme: instance.defaultLightTheme, - defaultDarkTheme: instance.defaultDarkTheme, + // クライアントの手間を減らすためあらかじめJSONに変換しておく + defaultLightTheme: instance.defaultLightTheme + ? JSON.stringify(JSON5.parse(instance.defaultLightTheme)) + : null, + defaultDarkTheme: instance.defaultDarkTheme + ? JSON.stringify(JSON5.parse(instance.defaultDarkTheme)) + : null, ads: instance.privateMode && !me ? [] @@ -477,8 +493,10 @@ export default define(meta, paramDef, async (ps, me) => { enableServiceWorker: instance.enableServiceWorker, - translatorAvailable: instance.deeplAuthKey != null, + translatorAvailable: + instance.deeplAuthKey != null || instance.libreTranslateApiUrl != null, defaultReaction: instance.defaultReaction, + donationLink: instance.donationLink, ...(ps.detail ? { @@ -489,6 +507,7 @@ export default define(meta, paramDef, async (ps, me) => { requireSetup: (await Users.countBy({ host: IsNull(), + isAdmin: true, })) === 0, } : {}), @@ -508,7 +527,7 @@ export default define(meta, paramDef, async (ps, me) => { recommendedTimeline: !instance.disableRecommendedTimeline, globalTimeLine: !instance.disableGlobalTimeline, emailRequiredForSignup: instance.emailRequiredForSignup, - elasticsearch: config.elasticsearch ? true : false, + searchFilters: config.meilisearch ? true : false, hcaptcha: instance.enableHcaptcha, recaptcha: instance.enableRecaptcha, objectStorage: instance.useObjectStorage, @@ -516,6 +535,8 @@ export default define(meta, paramDef, async (ps, me) => { github: instance.enableGithubIntegration, discord: instance.enableDiscordIntegration, serviceWorker: instance.enableServiceWorker, + postEditing: true, + postImports: instance.experimentalFeatures?.postImports || false, miauth: true, }; } diff --git a/packages/backend/src/server/api/endpoints/mute/create.ts b/packages/backend/src/server/api/endpoints/mute/create.ts index bacab9b458..7b2f109053 100644 --- a/packages/backend/src/server/api/endpoints/mute/create.ts +++ b/packages/backend/src/server/api/endpoints/mute/create.ts @@ -64,12 +64,14 @@ export default define(meta, paramDef, async (ps, user) => { }); // Check if already muting - const exist = await Mutings.findOneBy({ - muterId: muter.id, - muteeId: mutee.id, + const exist = await Mutings.exist({ + where: { + muterId: muter.id, + muteeId: mutee.id, + }, }); - if (exist != null) { + if (exist) { throw new ApiError(meta.errors.alreadyMuting); } diff --git a/packages/backend/src/server/api/endpoints/mute/delete.ts b/packages/backend/src/server/api/endpoints/mute/delete.ts index cc67a44c26..cd00c1a8ab 100644 --- a/packages/backend/src/server/api/endpoints/mute/delete.ts +++ b/packages/backend/src/server/api/endpoints/mute/delete.ts @@ -56,18 +56,18 @@ export default define(meta, paramDef, async (ps, user) => { }); // Check not muting - const exist = await Mutings.findOneBy({ + const muting = await Mutings.findOneBy({ muterId: muter.id, muteeId: mutee.id, }); - if (exist == null) { + if (muting == null) { throw new ApiError(meta.errors.notMuting); } // Delete mute await Mutings.delete({ - id: exist.id, + id: muting.id, }); publishUserEvent(user.id, "unmute", mutee); diff --git a/packages/backend/src/server/api/endpoints/notes/children.ts b/packages/backend/src/server/api/endpoints/notes/children.ts index 9047fcce1d..a35b17a022 100644 --- a/packages/backend/src/server/api/endpoints/notes/children.ts +++ b/packages/backend/src/server/api/endpoints/notes/children.ts @@ -1,4 +1,3 @@ -import { Brackets } from "typeorm"; import { Notes } from "@/models/index.js"; import define from "../../define.js"; import { makePaginationQuery } from "../../common/make-pagination-query.js"; @@ -11,6 +10,7 @@ export const meta = { requireCredential: false, requireCredentialPrivateMode: true, + description: "Get threaded/chained replies to a note", res: { type: "array", @@ -23,13 +23,14 @@ export const meta = { ref: "Note", }, }, -}; +} as const; export const paramDef = { type: "object", properties: { noteId: { type: "string", format: "misskey:id" }, limit: { type: "integer", minimum: 1, maximum: 100, default: 10 }, + depth: { type: "integer", minimum: 1, maximum: 100, default: 12 }, sinceId: { type: "string", format: "misskey:id" }, untilId: { type: "string", format: "misskey:id" }, }, diff --git a/packages/backend/src/server/api/endpoints/notes/conversation.ts b/packages/backend/src/server/api/endpoints/notes/conversation.ts index 2e8f5ef73b..c74da2ec71 100644 --- a/packages/backend/src/server/api/endpoints/notes/conversation.ts +++ b/packages/backend/src/server/api/endpoints/notes/conversation.ts @@ -9,6 +9,7 @@ export const meta = { requireCredential: false, requireCredentialPrivateMode: true, + description: "Get conversation of a note thread/chain by a reply", res: { type: "array", @@ -34,7 +35,11 @@ export const meta = { export const paramDef = { type: "object", properties: { - noteId: { type: "string", format: "misskey:id" }, + noteId: { + type: "string", + format: "misskey:id", + description: "Should be a reply", + }, limit: { type: "integer", minimum: 1, maximum: 100, default: 10 }, offset: { type: "integer", default: 0 }, }, @@ -51,7 +56,7 @@ export default define(meta, paramDef, async (ps, user) => { const conversation: Note[] = []; let i = 0; - async function get(id: any) { + async function get(id: string) { i++; const p = await getNote(id, user).catch((e) => { if (e.id === "9725d0ce-ba28-4dde-95a7-2cbb2c15de24") return null; @@ -60,7 +65,7 @@ export default define(meta, paramDef, async (ps, user) => { if (p == null) return; - if (i > ps.offset!) { + if (i > ps.offset) { conversation.push(p); } diff --git a/packages/backend/src/server/api/endpoints/notes/create.ts b/packages/backend/src/server/api/endpoints/notes/create.ts index 41b8ab9796..e15221046f 100644 --- a/packages/backend/src/server/api/endpoints/notes/create.ts +++ b/packages/backend/src/server/api/endpoints/notes/create.ts @@ -224,11 +224,13 @@ export default define(meta, paramDef, async (ps, user) => { // Check blocking if (renote.userId !== user.id) { - const block = await Blockings.findOneBy({ - blockerId: renote.userId, - blockeeId: user.id, + const isBlocked = await Blockings.exist({ + where: { + blockerId: renote.userId, + blockeeId: user.id, + }, }); - if (block) { + if (isBlocked) { throw new ApiError(meta.errors.youHaveBeenBlocked); } } @@ -249,11 +251,13 @@ export default define(meta, paramDef, async (ps, user) => { // Check blocking if (reply.userId !== user.id) { - const block = await Blockings.findOneBy({ - blockerId: reply.userId, - blockeeId: user.id, + const isBlocked = await Blockings.exist({ + where: { + blockerId: reply.userId, + blockeeId: user.id, + }, }); - if (block) { + if (isBlocked) { throw new ApiError(meta.errors.youHaveBeenBlocked); } } diff --git a/packages/backend/src/server/api/endpoints/notes/edit.ts b/packages/backend/src/server/api/endpoints/notes/edit.ts new file mode 100644 index 0000000000..70c5ceffb4 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/notes/edit.ts @@ -0,0 +1,654 @@ +import { In } from "typeorm"; +import create, { index } from "@/services/note/create.js"; +import type { IRemoteUser, User } from "@/models/entities/user.js"; +import { + Users, + DriveFiles, + Notes, + Channels, + Blockings, + UserProfiles, + Polls, + NoteEdits, +} from "@/models/index.js"; +import type { DriveFile } from "@/models/entities/drive-file.js"; +import type { IMentionedRemoteUsers, Note } from "@/models/entities/note.js"; +import type { Channel } from "@/models/entities/channel.js"; +import { MAX_NOTE_TEXT_LENGTH } from "@/const.js"; +import { noteVisibilities } from "../../../../types.js"; +import { ApiError } from "../../error.js"; +import define from "../../define.js"; +import { HOUR } from "@/const.js"; +import { getNote } from "../../common/getters.js"; +import { Poll } from "@/models/entities/poll.js"; +import * as mfm from "mfm-js"; +import { concat } from "@/prelude/array.js"; +import { extractHashtags } from "@/misc/extract-hashtags.js"; +import { extractCustomEmojisFromMfm } from "@/misc/extract-custom-emojis-from-mfm.js"; +import { extractMentionedUsers } from "@/services/note/create.js"; +import { genId } from "@/misc/gen-id.js"; +import { publishNoteStream } from "@/services/stream.js"; +import DeliverManager from "@/remote/activitypub/deliver-manager.js"; +import { renderActivity } from "@/remote/activitypub/renderer/index.js"; +import renderNote from "@/remote/activitypub/renderer/note.js"; +import renderUpdate from "@/remote/activitypub/renderer/update.js"; +import { deliverToRelays } from "@/services/relay.js"; +// import { deliverQuestionUpdate } from "@/services/note/polls/update.js"; +import { fetchMeta } from "@/misc/fetch-meta.js"; + +export const meta = { + tags: ["notes"], + + requireCredential: true, + + limit: { + duration: HOUR, + max: 300, + }, + + kind: "write:notes", + + res: { + type: "object", + optional: false, + nullable: false, + properties: { + createdNote: { + type: "object", + optional: false, + nullable: false, + ref: "Note", + }, + }, + }, + + errors: { + noSuchRenoteTarget: { + message: "No such renote target.", + code: "NO_SUCH_RENOTE_TARGET", + id: "b5c90186-4ab0-49c8-9bba-a1f76c282ba4", + }, + + cannotReRenote: { + message: "You can not Renote a pure Renote.", + code: "CANNOT_RENOTE_TO_A_PURE_RENOTE", + id: "fd4cc33e-2a37-48dd-99cc-9b806eb2031a", + }, + + noSuchReplyTarget: { + message: "No such reply target.", + code: "NO_SUCH_REPLY_TARGET", + id: "749ee0f6-d3da-459a-bf02-282e2da4292c", + }, + + cannotReplyToPureRenote: { + message: "You can not reply to a pure Renote.", + code: "CANNOT_REPLY_TO_A_PURE_RENOTE", + id: "3ac74a84-8fd5-4bb0-870f-01804f82ce15", + }, + + cannotCreateAlreadyExpiredPoll: { + message: "Poll is already expired.", + code: "CANNOT_CREATE_ALREADY_EXPIRED_POLL", + id: "04da457d-b083-4055-9082-955525eda5a5", + }, + + noSuchChannel: { + message: "No such channel.", + code: "NO_SUCH_CHANNEL", + id: "b1653923-5453-4edc-b786-7c4f39bb0bbb", + }, + + youHaveBeenBlocked: { + message: "You have been blocked by this user.", + code: "YOU_HAVE_BEEN_BLOCKED", + id: "b390d7e1-8a5e-46ed-b625-06271cafd3d3", + }, + + accountLocked: { + message: "You migrated. Your account is now locked.", + code: "ACCOUNT_LOCKED", + id: "d390d7e1-8a5e-46ed-b625-06271cafd3d3", + }, + + needsEditId: { + message: "You need to specify `editId`.", + code: "NEEDS_EDIT_ID", + id: "d697edc8-8c73-4de8-bded-35fd198b79e5", + }, + + noSuchNote: { + message: "No such note.", + code: "NO_SUCH_NOTE", + id: "eef6c173-3010-4a23-8674-7c4fcaeba719", + }, + + youAreNotTheAuthor: { + message: "You are not the author of this note.", + code: "YOU_ARE_NOT_THE_AUTHOR", + id: "c6e61685-411d-43d0-b90a-a448d2539001", + }, + + cannotPrivateRenote: { + message: "You can not perform a private renote.", + code: "CANNOT_PRIVATE_RENOTE", + id: "19a50f1c-84fa-4e33-81d3-17834ccc0ad8", + }, + + notLocalUser: { + message: "You are not a local user.", + code: "NOT_LOCAL_USER", + id: "b907f407-2aa0-4283-800b-a2c56290b822", + }, + }, +} as const; + +export const paramDef = { + type: "object", + properties: { + editId: { type: "string", format: "misskey:id" }, + visibility: { type: "string", enum: noteVisibilities, default: "public" }, + visibleUserIds: { + type: "array", + uniqueItems: true, + items: { + type: "string", + format: "misskey:id", + }, + }, + text: { type: "string", maxLength: MAX_NOTE_TEXT_LENGTH, nullable: true }, + cw: { type: "string", nullable: true, maxLength: 250 }, + localOnly: { type: "boolean", default: false }, + noExtractMentions: { type: "boolean", default: false }, + noExtractHashtags: { type: "boolean", default: false }, + noExtractEmojis: { type: "boolean", default: false }, + fileIds: { + type: "array", + uniqueItems: true, + minItems: 1, + maxItems: 16, + items: { type: "string", format: "misskey:id" }, + }, + mediaIds: { + deprecated: true, + description: + "Use `fileIds` instead. If both are specified, this property is discarded.", + type: "array", + uniqueItems: true, + minItems: 1, + maxItems: 16, + items: { type: "string", format: "misskey:id" }, + }, + replyId: { type: "string", format: "misskey:id", nullable: true }, + renoteId: { type: "string", format: "misskey:id", nullable: true }, + channelId: { type: "string", format: "misskey:id", nullable: true }, + poll: { + type: "object", + nullable: true, + properties: { + choices: { + type: "array", + uniqueItems: true, + minItems: 2, + maxItems: 10, + items: { type: "string", minLength: 1, maxLength: 50 }, + }, + multiple: { type: "boolean", default: false }, + expiresAt: { type: "integer", nullable: true }, + expiredAfter: { type: "integer", nullable: true, minimum: 1 }, + }, + required: ["choices"], + }, + }, + anyOf: [ + { + // (re)note with text, files and poll are optional + properties: { + text: { + type: "string", + minLength: 1, + maxLength: MAX_NOTE_TEXT_LENGTH, + nullable: false, + }, + }, + required: ["text"], + }, + { + // (re)note with files, text and poll are optional + required: ["fileIds"], + }, + { + // (re)note with files, text and poll are optional + required: ["mediaIds"], + }, + { + // (re)note with poll, text and files are optional + properties: { + poll: { type: "object", nullable: false }, + }, + required: ["poll"], + }, + { + // pure renote + required: ["renoteId"], + }, + ], +} as const; + +export default define(meta, paramDef, async (ps, user) => { + if (user.movedToUri != null) throw new ApiError(meta.errors.accountLocked); + + if (!Users.isLocalUser(user)) { + throw new ApiError(meta.errors.notLocalUser); + } + + if (!ps.editId) { + throw new ApiError(meta.errors.needsEditId); + } + + let publishing = false; + let note = await Notes.findOneBy({ + id: ps.editId, + }); + + if (note == null) { + throw new ApiError(meta.errors.noSuchNote); + } + + if (note.userId !== user.id) { + throw new ApiError(meta.errors.youAreNotTheAuthor); + } + + let renote: Note | null = null; + if (ps.renoteId != null) { + // Fetch renote to note + renote = await getNote(ps.renoteId, user).catch((e) => { + if (e.id === "9725d0ce-ba28-4dde-95a7-2cbb2c15de24") + throw new ApiError(meta.errors.noSuchRenoteTarget); + throw e; + }); + + if (renote.renoteId && !renote.text && !renote.fileIds && !renote.hasPoll) { + throw new ApiError(meta.errors.cannotReRenote); + } + + // Check blocking + if (renote.userId !== user.id) { + const block = await Blockings.findOneBy({ + blockerId: renote.userId, + blockeeId: user.id, + }); + if (block) { + throw new ApiError(meta.errors.youHaveBeenBlocked); + } + } + } + + let reply: Note | null = null; + if (ps.replyId != null) { + // Fetch reply + reply = await getNote(ps.replyId, user).catch((e) => { + if (e.id === "9725d0ce-ba28-4dde-95a7-2cbb2c15de24") + throw new ApiError(meta.errors.noSuchReplyTarget); + throw e; + }); + + if (reply.renoteId && !reply.text && !reply.fileIds && !reply.hasPoll) { + throw new ApiError(meta.errors.cannotReplyToPureRenote); + } + + // Check blocking + if (reply.userId !== user.id) { + const block = await Blockings.findOneBy({ + blockerId: reply.userId, + blockeeId: user.id, + }); + if (block) { + throw new ApiError(meta.errors.youHaveBeenBlocked); + } + } + } + + let channel: Channel | null = null; + if (ps.channelId != null) { + channel = await Channels.findOneBy({ id: ps.channelId }); + + if (channel == null) { + throw new ApiError(meta.errors.noSuchChannel); + } + } + + // enforce silent clients on server + if (user.isSilenced && ps.visibility === "public" && ps.channelId == null) { + ps.visibility = "home"; + } + + // Reject if the target of the renote is a public range other than "Home or Entire". + if ( + renote && + renote.visibility !== "public" && + renote.visibility !== "home" && + renote.userId !== user.id + ) { + throw new ApiError(meta.errors.cannotPrivateRenote); + } + + // If the target of the renote is not public, make it home. + if (renote && renote.visibility !== "public" && ps.visibility === "public") { + ps.visibility = "home"; + } + + // If the reply target is not public, make it home. + if (reply && reply.visibility !== "public" && ps.visibility === "public") { + ps.visibility = "home"; + } + + // Renote local only if you Renote local only. + if (renote?.localOnly && ps.channelId == null) { + ps.localOnly = true; + } + + // If you reply to local only, make it local only. + if (reply?.localOnly && ps.channelId == null) { + ps.localOnly = true; + } + + if (ps.text) { + ps.text = ps.text.trim(); + } else { + ps.text = null; + } + + let tags = []; + let emojis = []; + let mentionedUsers = []; + + const tokens = ps.text ? mfm.parse(ps.text) : []; + const cwTokens = ps.cw ? mfm.parse(ps.cw) : []; + const choiceTokens = ps.poll?.choices + ? concat(ps.poll.choices.map((choice) => mfm.parse(choice))) + : []; + + const combinedTokens = tokens.concat(cwTokens).concat(choiceTokens); + + tags = extractHashtags(combinedTokens); + + emojis = extractCustomEmojisFromMfm(combinedTokens); + + mentionedUsers = await extractMentionedUsers(user, combinedTokens); + + tags = [...new Set(tags)] + .sort() + .filter((tag) => Array.from(tag || "").length <= 128) + .splice(0, 32); + + emojis = [...new Set(emojis)].sort(); + + if ( + reply && + user.id !== reply.userId && + !mentionedUsers.some((u) => u.id === reply?.userId) + ) { + mentionedUsers.push(await Users.findOneByOrFail({ id: reply.userId })); + } + + let visibleUsers: User[] = []; + if (ps.visibleUserIds) { + visibleUsers = await Users.findBy({ + id: In(ps.visibleUserIds), + }); + } + + if (ps.visibility === "specified") { + if (visibleUsers == null) throw new Error("invalid param"); + + for (const u of visibleUsers) { + if (!mentionedUsers.some((x) => x.id === u.id)) { + mentionedUsers.push(u); + } + } + + if (reply && !visibleUsers.some((x) => x.id === reply?.userId)) { + visibleUsers.push(await Users.findOneByOrFail({ id: reply.userId })); + } + } + + let files: DriveFile[] = []; + const fileIds = + ps.fileIds != null ? ps.fileIds : ps.mediaIds != null ? ps.mediaIds : null; + if (fileIds != null) { + files = await DriveFiles.createQueryBuilder("file") + .where("file.userId = :userId AND file.id IN (:...fileIds)", { + userId: user.id, + fileIds, + }) + .orderBy('array_position(ARRAY[:...fileIds], "id"::text)') + .setParameters({ fileIds }) + .getMany(); + } + + if (ps.poll) { + let expires = ps.poll.expiresAt; + if (typeof expires === "number") { + if (expires < Date.now()) { + throw new ApiError(meta.errors.cannotCreateAlreadyExpiredPoll); + } + } else if (typeof ps.poll.expiredAfter === "number") { + expires = Date.now() + ps.poll.expiredAfter; + } + + let poll = await Polls.findOneBy({ noteId: note.id }); + const pp = ps.poll; + if (!poll && pp) { + poll = new Poll({ + noteId: note.id, + choices: pp.choices, + expiresAt: expires ? new Date(expires) : null, + multiple: pp.multiple, + votes: new Array(pp.choices.length).fill(0), + noteVisibility: ps.visibility, + userId: user.id, + userHost: user.host, + }); + await Polls.insert(poll); + publishing = true; + } else if (poll && !pp) { + await Polls.remove(poll); + publishing = true; + } else if (poll && pp) { + const pollUpdate: Partial = {}; + if (poll.expiresAt !== expires) { + pollUpdate.expiresAt = expires ? new Date(expires) : null; + } + if (poll.multiple !== pp.multiple) { + pollUpdate.multiple = pp.multiple; + } + if (poll.noteVisibility !== ps.visibility) { + pollUpdate.noteVisibility = ps.visibility; + } + // Keep votes for unmodified choices, reset votes if choice is modified or new + const oldVoteCounts = new Map(); + for (let i = 0; i < poll.choices.length; i++) { + oldVoteCounts.set(poll.choices[i], poll.votes[i]); + } + const newVotes = pp.choices.map( + (choice) => oldVoteCounts.get(choice) || 0, + ); + pollUpdate.choices = pp.choices; + pollUpdate.votes = newVotes; + if (notEmpty(pollUpdate)) { + await Polls.update(note.id, pollUpdate); + // Seemingly already handled by by the rendered update activity + // await deliverQuestionUpdate(note.id); + } + publishing = true; + } + } + + const mentionedUserLookup: Record = {}; + mentionedUsers.forEach((u) => { + mentionedUserLookup[u.id] = u; + }); + + const mentionedUserIds = [...new Set(mentionedUsers.map((u) => u.id))].sort(); + + const remoteUsers = mentionedUserIds + .map((id) => mentionedUserLookup[id]) + .filter((u) => u.host != null); + + const remoteUserIds = remoteUsers.map((user) => user.id); + const remoteProfiles = await UserProfiles.findBy({ + userId: In(remoteUserIds), + }); + const mentionedRemoteUsers = remoteUsers.map((user) => { + const profile = remoteProfiles.find( + (profile) => profile.userId === user.id, + ); + return { + username: user.username, + host: user.host ?? null, + uri: user.uri, + url: profile ? profile.url : undefined, + } as IMentionedRemoteUsers[0]; + }); + + const update: Partial = {}; + if (ps.text !== note.text) { + update.text = ps.text; + } + if (ps.cw !== note.cw || (ps.cw && !note.cw)) { + update.cw = ps.cw; + } + if (!ps.cw && note.cw) { + update.cw = null; + } + if (ps.visibility !== note.visibility) { + update.visibility = ps.visibility; + } + if (ps.localOnly !== note.localOnly) { + update.localOnly = ps.localOnly; + } + if (ps.visibleUserIds !== note.visibleUserIds) { + update.visibleUserIds = ps.visibleUserIds; + } + if (!unorderedEqual(mentionedUserIds, note.mentions)) { + update.mentions = mentionedUserIds; + update.mentionedRemoteUsers = JSON.stringify(mentionedRemoteUsers); + } + if (ps.channelId !== note.channelId) { + update.channelId = ps.channelId; + } + if (ps.replyId !== note.replyId) { + update.replyId = ps.replyId; + } + if (ps.renoteId !== note.renoteId) { + update.renoteId = ps.renoteId; + } + if (note.hasPoll !== !!ps.poll) { + update.hasPoll = !!ps.poll; + } + if (!unorderedEqual(emojis, note.emojis)) { + update.emojis = emojis; + } + if (!unorderedEqual(tags, note.tags)) { + update.tags = tags; + } + if (!unorderedEqual(ps.fileIds || [], note.fileIds)) { + update.fileIds = fileIds || undefined; + + if (fileIds) { + // Get attachedFileTypes for each file with fileId from fileIds + const attachedFiles = fileIds.map((fileId) => + files.find((file) => file.id === fileId), + ); + update.attachedFileTypes = attachedFiles.map( + (file) => file?.type || "application/octet-stream", + ); + } else { + update.attachedFileTypes = undefined; + } + } + + if (notEmpty(update)) { + update.updatedAt = new Date(); + await Notes.update(note.id, update); + + // Add NoteEdit history + await NoteEdits.insert({ + id: genId(), + noteId: note.id, + text: ps.text || undefined, + cw: ps.cw, + fileIds: ps.fileIds, + updatedAt: new Date(), + }); + + publishing = true; + } + + note = await Notes.findOneBy({ id: note.id }); + if (!note) { + throw new ApiError(meta.errors.noSuchNote); + } + + if (publishing) { + index(note, true); + + // Publish update event for the updated note details + publishNoteStream(note.id, "updated", { + updatedAt: update.updatedAt, + }); + + (async () => { + const noteActivity = await renderNote(note, false); + noteActivity.updated = note.updatedAt.toISOString(); + const updateActivity = renderUpdate(noteActivity, user); + updateActivity.to = noteActivity.to; + updateActivity.cc = noteActivity.cc; + const activity = renderActivity(updateActivity); + const dm = new DeliverManager(user, activity); + + // Delivery to remote mentioned users + for (const u of mentionedUsers.filter((u) => Users.isRemoteUser(u))) { + dm.addDirectRecipe(u as IRemoteUser); + } + + // Post is a reply and remote user is the contributor of the original post + if (note.reply && note.reply.userHost !== null) { + const u = await Users.findOneBy({ id: note.reply.userId }); + if (u && Users.isRemoteUser(u)) dm.addDirectRecipe(u); + } + + // Post is a renote and remote user is the contributor of the original post + if (note.renote && note.renote.userHost !== null) { + const u = await Users.findOneBy({ id: note.renote.userId }); + if (u && Users.isRemoteUser(u)) dm.addDirectRecipe(u); + } + + // Deliver to followers for non-direct posts. + if (["public", "home", "followers"].includes(note.visibility)) { + dm.addFollowersRecipe(); + } + + // Deliver to relays for public posts. + if (["public"].includes(note.visibility)) { + deliverToRelays(user, activity); + } + + // GO! + dm.execute(); + })(); + } + + return { + createdNote: await Notes.pack(note, user), + }; +}); + +function unorderedEqual(a: T[], b: T[]) { + return a.length === b.length && a.every((v) => b.includes(v)); +} + +function notEmpty(partial: Partial) { + return Object.keys(partial).length > 0; +} diff --git a/packages/backend/src/server/api/endpoints/notes/favorites/create.ts b/packages/backend/src/server/api/endpoints/notes/favorites/create.ts index 835594f03a..64862a3733 100644 --- a/packages/backend/src/server/api/endpoints/notes/favorites/create.ts +++ b/packages/backend/src/server/api/endpoints/notes/favorites/create.ts @@ -43,12 +43,14 @@ export default define(meta, paramDef, async (ps, user) => { }); // if already favorited - const exist = await NoteFavorites.findOneBy({ - noteId: note.id, - userId: user.id, + const exist = await NoteFavorites.exist({ + where: { + noteId: note.id, + userId: user.id, + }, }); - if (exist != null) { + if (exist) { throw new ApiError(meta.errors.alreadyFavorited); } diff --git a/packages/backend/src/server/api/endpoints/notes/favorites/delete.ts b/packages/backend/src/server/api/endpoints/notes/favorites/delete.ts index 9a09767482..e05d04a969 100644 --- a/packages/backend/src/server/api/endpoints/notes/favorites/delete.ts +++ b/packages/backend/src/server/api/endpoints/notes/favorites/delete.ts @@ -42,15 +42,15 @@ export default define(meta, paramDef, async (ps, user) => { }); // if already favorited - const exist = await NoteFavorites.findOneBy({ + const favorite = await NoteFavorites.findOneBy({ noteId: note.id, userId: user.id, }); - if (exist == null) { + if (favorite == null) { throw new ApiError(meta.errors.notFavorited); } // Delete favorite - await NoteFavorites.delete(exist.id); + await NoteFavorites.delete(favorite.id); }); diff --git a/packages/backend/src/server/api/endpoints/notes/featured.ts b/packages/backend/src/server/api/endpoints/notes/featured.ts index cd7e44296e..47c1e13812 100644 --- a/packages/backend/src/server/api/endpoints/notes/featured.ts +++ b/packages/backend/src/server/api/endpoints/notes/featured.ts @@ -27,17 +27,22 @@ export const paramDef = { properties: { limit: { type: "integer", minimum: 1, maximum: 100, default: 10 }, offset: { type: "integer", default: 0 }, + origin: { + type: "string", + enum: ["combined", "local", "remote"], + default: "local", + }, + days: { type: "integer", minimum: 1, maximum: 365, default: 3 }, }, required: [], } as const; export default define(meta, paramDef, async (ps, user) => { const max = 30; - const day = 1000 * 60 * 60 * 24 * 3; // 3日前まで + const day = 1000 * 60 * 60 * 24 * ps.days; const query = Notes.createQueryBuilder("note") .addSelect("note.score") - .where("note.userHost IS NULL") .andWhere("note.score > 0") .andWhere("note.createdAt > :date", { date: new Date(Date.now() - day) }) .andWhere("note.visibility = 'public'") @@ -53,6 +58,15 @@ export default define(meta, paramDef, async (ps, user) => { .leftJoinAndSelect("renoteUser.avatar", "renoteUserAvatar") .leftJoinAndSelect("renoteUser.banner", "renoteUserBanner"); + switch (ps.origin) { + case "local": + query.andWhere("note.userHost IS NULL"); + break; + case "remote": + query.andWhere("note.userHost IS NOT NULL"); + break; + } + if (user) generateMutedUserQuery(query, user); if (user) generateBlockedUserQuery(query, user); diff --git a/packages/backend/src/server/api/endpoints/notes/global-timeline.ts b/packages/backend/src/server/api/endpoints/notes/global-timeline.ts index e4a38cffb1..0a365a6dfd 100644 --- a/packages/backend/src/server/api/endpoints/notes/global-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/global-timeline.ts @@ -8,6 +8,7 @@ import { generateMutedUserQuery } from "../../common/generate-muted-user-query.j import { generateRepliesQuery } from "../../common/generate-replies-query.js"; import { generateMutedNoteQuery } from "../../common/generate-muted-note-query.js"; import { generateBlockedUserQuery } from "../../common/generate-block-query.js"; +import { generateMutedUserRenotesQueryForNotes } from "../../common/generated-muted-renote-query.js"; export const meta = { tags: ["notes"], @@ -31,6 +32,11 @@ export const meta = { code: "GTL_DISABLED", id: "0332fc13-6ab2-4427-ae80-a9fadffd1a6b", }, + queryError: { + message: "Please follow more users.", + code: "QUERY_ERROR", + id: "620763f4-f621-4533-ab33-0577a1a3c343", + }, }, } as const; @@ -47,6 +53,11 @@ export const paramDef = { untilId: { type: "string", format: "misskey:id" }, sinceDate: { type: "integer" }, untilDate: { type: "integer" }, + withReplies: { + type: "boolean", + default: false, + description: "Show replies in the timeline", + }, }, required: [], } as const; @@ -81,25 +92,45 @@ export default define(meta, paramDef, async (ps, user) => { .leftJoinAndSelect("renoteUser.avatar", "renoteUserAvatar") .leftJoinAndSelect("renoteUser.banner", "renoteUserBanner"); - generateRepliesQuery(query, user); + generateRepliesQuery(query, ps.withReplies, user); if (user) { generateMutedUserQuery(query, user); generateMutedNoteQuery(query, user); generateBlockedUserQuery(query, user); + generateMutedUserRenotesQueryForNotes(query, user); } if (ps.withFiles) { query.andWhere("note.fileIds != '{}'"); } + query.andWhere("note.visibility != 'hidden'"); //#endregion - const timeline = await query.take(ps.limit).getMany(); - process.nextTick(() => { if (user) { activeUsersChart.read(user); } }); - return await Notes.packMany(timeline, user); + // We fetch more than requested because some may be filtered out, and if there's less than + // requested, the pagination stops. + const found = []; + const take = Math.floor(ps.limit * 1.5); + let skip = 0; + try { + while (found.length < ps.limit) { + const notes = await query.take(take).skip(skip).getMany(); + found.push(...(await Notes.packMany(notes, user))); + skip += take; + if (notes.length < take) break; + } + } catch (error) { + throw new ApiError(meta.errors.queryError); + } + + if (found.length > ps.limit) { + found.length = ps.limit; + } + + return found; }); diff --git a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts index 3d6103da87..4e32b0ab29 100644 --- a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts @@ -11,6 +11,7 @@ import { generateRepliesQuery } from "../../common/generate-replies-query.js"; import { generateMutedNoteQuery } from "../../common/generate-muted-note-query.js"; import { generateChannelQuery } from "../../common/generate-channel-query.js"; import { generateBlockedUserQuery } from "../../common/generate-block-query.js"; +import { generateMutedUserRenotesQueryForNotes } from "../../common/generated-muted-renote-query.js"; export const meta = { tags: ["notes"], @@ -35,6 +36,11 @@ export const meta = { code: "STL_DISABLED", id: "620763f4-f621-4533-ab33-0577a1a3c342", }, + queryError: { + message: "Please follow more users.", + code: "QUERY_ERROR", + id: "620763f4-f621-4533-ab33-0577a1a3c343", + }, }, } as const; @@ -54,6 +60,11 @@ export const paramDef = { default: false, description: "Only show notes that have attached files.", }, + withReplies: { + type: "boolean", + default: false, + description: "Show replies in the timeline", + }, }, required: [], } as const; @@ -98,11 +109,12 @@ export default define(meta, paramDef, async (ps, user) => { .setParameters(followingQuery.getParameters()); generateChannelQuery(query, user); - generateRepliesQuery(query, user); + generateRepliesQuery(query, ps.withReplies, user); generateVisibilityQuery(query, user); generateMutedUserQuery(query, user); generateMutedNoteQuery(query, user); generateBlockedUserQuery(query, user); + generateMutedUserRenotesQueryForNotes(query, user); if (ps.includeMyRenotes === false) { query.andWhere( @@ -149,13 +161,33 @@ export default define(meta, paramDef, async (ps, user) => { if (ps.withFiles) { query.andWhere("note.fileIds != '{}'"); } - //#endregion - const timeline = await query.take(ps.limit).getMany(); + query.andWhere("note.visibility != 'hidden'"); + //#endregion process.nextTick(() => { activeUsersChart.read(user); }); - return await Notes.packMany(timeline, user); + // We fetch more than requested because some may be filtered out, and if there's less than + // requested, the pagination stops. + const found = []; + const take = Math.floor(ps.limit * 1.5); + let skip = 0; + try { + while (found.length < ps.limit) { + const notes = await query.take(take).skip(skip).getMany(); + found.push(...(await Notes.packMany(notes, user))); + skip += take; + if (notes.length < take) break; + } + } catch (error) { + throw new ApiError(meta.errors.queryError); + } + + if (found.length > ps.limit) { + found.length = ps.limit; + } + + return found; }); diff --git a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts index 22e5965fce..82e93e371f 100644 --- a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts @@ -11,6 +11,7 @@ import { generateRepliesQuery } from "../../common/generate-replies-query.js"; import { generateMutedNoteQuery } from "../../common/generate-muted-note-query.js"; import { generateChannelQuery } from "../../common/generate-channel-query.js"; import { generateBlockedUserQuery } from "../../common/generate-block-query.js"; +import { generateMutedUserRenotesQueryForNotes } from "../../common/generated-muted-renote-query.js"; export const meta = { tags: ["notes"], @@ -34,6 +35,11 @@ export const meta = { code: "LTL_DISABLED", id: "45a6eb02-7695-4393-b023-dd3be9aaaefd", }, + queryError: { + message: "Please follow more users.", + code: "QUERY_ERROR", + id: "620763f4-f621-4533-ab33-0577a1a3c343", + }, }, } as const; @@ -57,6 +63,11 @@ export const paramDef = { untilId: { type: "string", format: "misskey:id" }, sinceDate: { type: "integer" }, untilDate: { type: "integer" }, + withReplies: { + type: "boolean", + default: false, + description: "Show replies in the timeline", + }, }, required: [], } as const; @@ -91,11 +102,12 @@ export default define(meta, paramDef, async (ps, user) => { .leftJoinAndSelect("renoteUser.banner", "renoteUserBanner"); generateChannelQuery(query, user); - generateRepliesQuery(query, user); + generateRepliesQuery(query, ps.withReplies, user); generateVisibilityQuery(query, user); if (user) generateMutedUserQuery(query, user); if (user) generateMutedNoteQuery(query, user); if (user) generateBlockedUserQuery(query, user); + if (user) generateMutedUserRenotesQueryForNotes(query, user); if (ps.withFiles) { query.andWhere("note.fileIds != '{}'"); @@ -121,15 +133,34 @@ export default define(meta, paramDef, async (ps, user) => { ); } } + query.andWhere("note.visibility != 'hidden'"); //#endregion - const timeline = await query.take(ps.limit).getMany(); - process.nextTick(() => { if (user) { activeUsersChart.read(user); } }); - return await Notes.packMany(timeline, user); + // We fetch more than requested because some may be filtered out, and if there's less than + // requested, the pagination stops. + const found = []; + const take = Math.floor(ps.limit * 1.5); + let skip = 0; + try { + while (found.length < ps.limit) { + const notes = await query.take(take).skip(skip).getMany(); + found.push(...(await Notes.packMany(notes, user))); + skip += take; + if (notes.length < take) break; + } + } catch (error) { + throw new ApiError(meta.errors.queryError); + } + + if (found.length > ps.limit) { + found.length = ps.limit; + } + + return found; }); diff --git a/packages/backend/src/server/api/endpoints/notes/mentions.ts b/packages/backend/src/server/api/endpoints/notes/mentions.ts index 3e5c4f18b2..68688b504c 100644 --- a/packages/backend/src/server/api/endpoints/notes/mentions.ts +++ b/packages/backend/src/server/api/endpoints/notes/mentions.ts @@ -86,9 +86,23 @@ export default define(meta, paramDef, async (ps, user) => { query.setParameters(followingQuery.getParameters()); } - const mentions = await query.take(ps.limit).getMany(); + // We fetch more than requested because some may be filtered out, and if there's less than + // requested, the pagination stops. + const found = []; + const take = Math.floor(ps.limit * 1.5); + let skip = 0; + while (found.length < ps.limit) { + const notes = await query.take(take).skip(skip).getMany(); + found.push(...(await Notes.packMany(notes, user))); + skip += take; + if (notes.length < take) break; + } - read(user.id, mentions); + if (found.length > ps.limit) { + found.length = ps.limit; + } - return await Notes.packMany(mentions, user); + read(user.id, found); + + return found; }); diff --git a/packages/backend/src/server/api/endpoints/notes/polls/vote.ts b/packages/backend/src/server/api/endpoints/notes/polls/vote.ts index 0558aa1b8f..40535d3401 100644 --- a/packages/backend/src/server/api/endpoints/notes/polls/vote.ts +++ b/packages/backend/src/server/api/endpoints/notes/polls/vote.ts @@ -4,7 +4,6 @@ import { createNotification } from "@/services/create-notification.js"; import { deliver } from "@/queue/index.js"; import { renderActivity } from "@/remote/activitypub/renderer/index.js"; import renderVote from "@/remote/activitypub/renderer/vote.js"; -import { deliverQuestionUpdate } from "@/services/note/polls/update.js"; import { PollVotes, NoteWatchings, @@ -178,7 +177,4 @@ export default define(meta, paramDef, async (ps, user) => { pollOwner.inbox, ); } - - // リモートフォロワーにUpdate配信 - deliverQuestionUpdate(note.id); }); diff --git a/packages/backend/src/server/api/endpoints/notes/recommended-timeline.ts b/packages/backend/src/server/api/endpoints/notes/recommended-timeline.ts index 6dacadec2a..d3b5cbff50 100644 --- a/packages/backend/src/server/api/endpoints/notes/recommended-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/recommended-timeline.ts @@ -11,6 +11,7 @@ import { generateRepliesQuery } from "../../common/generate-replies-query.js"; import { generateMutedNoteQuery } from "../../common/generate-muted-note-query.js"; import { generateChannelQuery } from "../../common/generate-channel-query.js"; import { generateBlockedUserQuery } from "../../common/generate-block-query.js"; +import { generateMutedUserRenotesQueryForNotes } from "../../common/generated-muted-renote-query.js"; export const meta = { tags: ["notes"], @@ -34,6 +35,11 @@ export const meta = { code: "RTL_DISABLED", id: "45a6eb02-7695-4393-b023-dd3be9aaaefe", }, + queryError: { + message: "Please follow more users.", + code: "QUERY_ERROR", + id: "620763f4-f621-4533-ab33-0577a1a3c343", + }, }, } as const; @@ -57,6 +63,11 @@ export const paramDef = { untilId: { type: "string", format: "misskey:id" }, sinceDate: { type: "integer" }, untilDate: { type: "integer" }, + withReplies: { + type: "boolean", + default: false, + description: "Show replies in the timeline", + }, }, required: [], } as const; @@ -94,11 +105,12 @@ export default define(meta, paramDef, async (ps, user) => { .leftJoinAndSelect("renoteUser.banner", "renoteUserBanner"); generateChannelQuery(query, user); - generateRepliesQuery(query, user); + generateRepliesQuery(query, ps.withReplies, user); generateVisibilityQuery(query, user); if (user) generateMutedUserQuery(query, user); if (user) generateMutedNoteQuery(query, user); if (user) generateBlockedUserQuery(query, user); + if (user) generateMutedUserRenotesQueryForNotes(query, user); if (ps.withFiles) { query.andWhere("note.fileIds != '{}'"); @@ -124,15 +136,34 @@ export default define(meta, paramDef, async (ps, user) => { ); } } + query.andWhere("note.visibility != 'hidden'"); //#endregion - const timeline = await query.take(ps.limit).getMany(); - process.nextTick(() => { if (user) { activeUsersChart.read(user); } }); - return await Notes.packMany(timeline, user); + // We fetch more than requested because some may be filtered out, and if there's less than + // requested, the pagination stops. + const found = []; + const take = Math.floor(ps.limit * 1.5); + let skip = 0; + try { + while (found.length < ps.limit) { + const notes = await query.take(take).skip(skip).getMany(); + found.push(...(await Notes.packMany(notes, user))); + skip += take; + if (notes.length < take) break; + } + } catch (error) { + throw new ApiError(meta.errors.queryError); + } + + if (found.length > ps.limit) { + found.length = ps.limit; + } + + return found; }); diff --git a/packages/backend/src/server/api/endpoints/notes/renotes.ts b/packages/backend/src/server/api/endpoints/notes/renotes.ts index b09243e7e6..df801c7fc4 100644 --- a/packages/backend/src/server/api/endpoints/notes/renotes.ts +++ b/packages/backend/src/server/api/endpoints/notes/renotes.ts @@ -38,6 +38,7 @@ export const paramDef = { type: "object", properties: { noteId: { type: "string", format: "misskey:id" }, + userId: { type: "string", format: "misskey:id" }, limit: { type: "integer", minimum: 1, maximum: 100, default: 10 }, sinceId: { type: "string", format: "misskey:id" }, untilId: { type: "string", format: "misskey:id" }, @@ -52,13 +53,19 @@ export default define(meta, paramDef, async (ps, user) => { throw err; }); - const query = makePaginationQuery( + let query = makePaginationQuery( Notes.createQueryBuilder("note"), ps.sinceId, ps.untilId, ) .andWhere("note.renoteId = :renoteId", { renoteId: note.id }) - .innerJoinAndSelect("note.user", "user") + .innerJoinAndSelect("note.user", "user"); + + if (ps.userId) { + query.andWhere("user.id = :userId", { userId: ps.userId }); + } + + query .leftJoinAndSelect("user.avatar", "avatar") .leftJoinAndSelect("user.banner", "banner") .leftJoinAndSelect("note.reply", "reply") @@ -74,7 +81,21 @@ export default define(meta, paramDef, async (ps, user) => { if (user) generateMutedUserQuery(query, user); if (user) generateBlockedUserQuery(query, user); - const renotes = await query.take(ps.limit).getMany(); + // We fetch more than requested because some may be filtered out, and if there's less than + // requested, the pagination stops. + const found = []; + const take = Math.floor(ps.limit * 1.5); + let skip = 0; + while (found.length < ps.limit) { + const notes = await query.take(take).skip(skip).getMany(); + found.push(...(await Notes.packMany(notes, user))); + skip += take; + if (notes.length < take) break; + } - return await Notes.packMany(renotes, user); + if (found.length > ps.limit) { + found.length = ps.limit; + } + + return found; }); diff --git a/packages/backend/src/server/api/endpoints/notes/replies.ts b/packages/backend/src/server/api/endpoints/notes/replies.ts index 0a8e909496..5ea4d479c5 100644 --- a/packages/backend/src/server/api/endpoints/notes/replies.ts +++ b/packages/backend/src/server/api/endpoints/notes/replies.ts @@ -58,7 +58,21 @@ export default define(meta, paramDef, async (ps, user) => { if (user) generateMutedUserQuery(query, user); if (user) generateBlockedUserQuery(query, user); - const timeline = await query.take(ps.limit).getMany(); + // We fetch more than requested because some may be filtered out, and if there's less than + // requested, the pagination stops. + const found = []; + const take = Math.floor(ps.limit * 1.5); + let skip = 0; + while (found.length < ps.limit) { + const notes = await query.take(take).skip(skip).getMany(); + found.push(...(await Notes.packMany(notes, user))); + skip += take; + if (notes.length < take) break; + } - return await Notes.packMany(timeline, user); + if (found.length > ps.limit) { + found.length = ps.limit; + } + + return found; }); diff --git a/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts b/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts index d8d0dbbf73..f988acaa51 100644 --- a/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts +++ b/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts @@ -145,8 +145,21 @@ export default define(meta, paramDef, async (ps, me) => { } } - // Search notes - const notes = await query.take(ps.limit).getMany(); + // We fetch more than requested because some may be filtered out, and if there's less than + // requested, the pagination stops. + const found = []; + const take = Math.floor(ps.limit * 1.5); + let skip = 0; + while (found.length < ps.limit) { + const notes = await query.take(take).skip(skip).getMany(); + found.push(...(await Notes.packMany(notes, me))); + skip += take; + if (notes.length < take) break; + } - return await Notes.packMany(notes, me); + if (found.length > ps.limit) { + found.length = ps.limit; + } + + return found; }); diff --git a/packages/backend/src/server/api/endpoints/notes/search.ts b/packages/backend/src/server/api/endpoints/notes/search.ts index ce60436db8..b4d83aa0bc 100644 --- a/packages/backend/src/server/api/endpoints/notes/search.ts +++ b/packages/backend/src/server/api/endpoints/notes/search.ts @@ -1,12 +1,16 @@ import { In } from "typeorm"; import { Notes } from "@/models/index.js"; +import { Note } from "@/models/entities/note.js"; import config from "@/config/index.js"; -import es from "../../../../db/elasticsearch.js"; +import es from "@/db/elasticsearch.js"; +import sonic from "@/db/sonic.js"; +import meilisearch, { MeilisearchNote } from "@/db/meilisearch.js"; import define from "../../define.js"; import { makePaginationQuery } from "../../common/make-pagination-query.js"; import { generateVisibilityQuery } from "../../common/generate-visibility-query.js"; import { generateMutedUserQuery } from "../../common/generate-muted-user-query.js"; import { generateBlockedUserQuery } from "../../common/generate-block-query.js"; +import { sqlLikeEscape } from "@/misc/sql-like-escape.js"; export const meta = { tags: ["notes"], @@ -59,7 +63,7 @@ export const paramDef = { } as const; export default define(meta, paramDef, async (ps, me) => { - if (es == null) { + if (es == null && sonic == null && meilisearch == null) { const query = makePaginationQuery( Notes.createQueryBuilder("note"), ps.sinceId, @@ -75,7 +79,7 @@ export default define(meta, paramDef, async (ps, me) => { } query - .andWhere("note.text ILIKE :q", { q: `%${ps.query}%` }) + .andWhere("note.text ILIKE :q", { q: `%${sqlLikeEscape(ps.query)}%` }) .innerJoinAndSelect("note.user", "user") .leftJoinAndSelect("user.avatar", "avatar") .leftJoinAndSelect("user.banner", "banner") @@ -92,9 +96,146 @@ export default define(meta, paramDef, async (ps, me) => { if (me) generateMutedUserQuery(query, me); if (me) generateBlockedUserQuery(query, me); - const notes = await query.take(ps.limit).getMany(); + const notes: Note[] = await query.take(ps.limit).getMany(); return await Notes.packMany(notes, me); + } else if (sonic) { + let start = 0; + const chunkSize = 100; + + // Use sonic to fetch and step through all search results that could match the requirements + const ids = []; + while (true) { + const results = await sonic.search.query( + sonic.collection, + sonic.bucket, + ps.query, + { + limit: chunkSize, + offset: start, + }, + ); + + start += chunkSize; + + if (results.length === 0) { + break; + } + + const res = results + .map((k) => JSON.parse(k)) + .filter((key) => { + if (ps.userId && key.userId !== ps.userId) { + return false; + } + if (ps.channelId && key.channelId !== ps.channelId) { + return false; + } + if (ps.sinceId && key.id <= ps.sinceId) { + return false; + } + if (ps.untilId && key.id >= ps.untilId) { + return false; + } + return true; + }) + .map((key) => key.id); + + ids.push(...res); + } + + // Sort all the results by note id DESC (newest first) + ids.sort((a, b) => b - a); + + // Fetch the notes from the database until we have enough to satisfy the limit + start = 0; + const found = []; + while (found.length < ps.limit && start < ids.length) { + const chunk = ids.slice(start, start + chunkSize); + const notes: Note[] = await Notes.find({ + where: { + id: In(chunk), + }, + order: { + id: "DESC", + }, + }); + + // The notes are checked for visibility and muted/blocked users when packed + found.push(...(await Notes.packMany(notes, me))); + start += chunkSize; + } + + // If we have more results than the limit, trim them + if (found.length > ps.limit) { + found.length = ps.limit; + } + + return found; + } else if (meilisearch) { + let start = 0; + const chunkSize = 100; + + // Use meilisearch to fetch and step through all search results that could match the requirements + const ids = []; + while (true) { + const results = await meilisearch.search(ps.query, chunkSize, start, me); + + start += chunkSize; + + if (results.hits.length === 0) { + break; + } + + const res = results.hits + .filter((key: MeilisearchNote) => { + if (ps.userId && key.userId !== ps.userId) { + return false; + } + if (ps.channelId && key.channelId !== ps.channelId) { + return false; + } + if (ps.sinceId && key.id <= ps.sinceId) { + return false; + } + if (ps.untilId && key.id >= ps.untilId) { + return false; + } + return true; + }) + .map((key) => key.id); + + ids.push(...res); + } + + // Sort all the results by note id DESC (newest first) + ids.sort((a, b) => b - a); + + // Fetch the notes from the database until we have enough to satisfy the limit + start = 0; + const found = []; + while (found.length < ps.limit && start < ids.length) { + const chunk = ids.slice(start, start + chunkSize); + const notes: Note[] = await Notes.find({ + where: { + id: In(chunk), + }, + order: { + id: "DESC", + }, + }); + + // The notes are checked for visibility and muted/blocked users when packed + found.push(...(await Notes.packMany(notes, me))); + start += chunkSize; + } + + // If we have more results than the limit, trim them + if (found.length > ps.limit) { + found.length = ps.limit; + } + + return found; } else { const userQuery = ps.userId != null diff --git a/packages/backend/src/server/api/endpoints/notes/show.ts b/packages/backend/src/server/api/endpoints/notes/show.ts index 39d128134f..8c5f91c5c1 100644 --- a/packages/backend/src/server/api/endpoints/notes/show.ts +++ b/packages/backend/src/server/api/endpoints/notes/show.ts @@ -21,6 +21,7 @@ export const meta = { message: "No such note.", code: "NO_SUCH_NOTE", id: "24fcbfc6-2e37-42b6-8388-c29b3861a08d", + httpStatusCode: 404, }, }, } as const; diff --git a/packages/backend/src/server/api/endpoints/notes/timeline.ts b/packages/backend/src/server/api/endpoints/notes/timeline.ts index 083f41365a..d629deebb6 100644 --- a/packages/backend/src/server/api/endpoints/notes/timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/timeline.ts @@ -9,6 +9,8 @@ import { generateRepliesQuery } from "../../common/generate-replies-query.js"; import { generateMutedNoteQuery } from "../../common/generate-muted-note-query.js"; import { generateChannelQuery } from "../../common/generate-channel-query.js"; import { generateBlockedUserQuery } from "../../common/generate-block-query.js"; +import { generateMutedUserRenotesQueryForNotes } from "../../common/generated-muted-renote-query.js"; +import { ApiError } from "../../error.js"; export const meta = { tags: ["notes"], @@ -26,6 +28,14 @@ export const meta = { ref: "Note", }, }, + + errors: { + queryError: { + message: "Please follow more users.", + code: "QUERY_ERROR", + id: "620763f4-f621-4533-ab33-0577a1a3c343", + }, + }, } as const; export const paramDef = { @@ -44,6 +54,11 @@ export const paramDef = { default: false, description: "Only show notes that have attached files.", }, + withReplies: { + type: "boolean", + default: false, + description: "Show replies in the timeline", + }, }, required: [], } as const; @@ -90,11 +105,12 @@ export default define(meta, paramDef, async (ps, user) => { .setParameters(followingQuery.getParameters()); generateChannelQuery(query, user); - generateRepliesQuery(query, user); + generateRepliesQuery(query, ps.withReplies, user); generateVisibilityQuery(query, user); generateMutedUserQuery(query, user); generateMutedNoteQuery(query, user); generateBlockedUserQuery(query, user); + generateMutedUserRenotesQueryForNotes(query, user); if (ps.includeMyRenotes === false) { query.andWhere( @@ -141,13 +157,33 @@ export default define(meta, paramDef, async (ps, user) => { if (ps.withFiles) { query.andWhere("note.fileIds != '{}'"); } - //#endregion - const timeline = await query.take(ps.limit).getMany(); + query.andWhere("note.visibility != 'hidden'"); + //#endregion process.nextTick(() => { activeUsersChart.read(user); }); - return await Notes.packMany(timeline, user); + // We fetch more than requested because some may be filtered out, and if there's less than + // requested, the pagination stops. + const found = []; + const take = Math.floor(ps.limit * 1.5); + let skip = 0; + try { + while (found.length < ps.limit) { + const notes = await query.take(take).skip(skip).getMany(); + found.push(...(await Notes.packMany(notes, user))); + skip += take; + if (notes.length < take) break; + } + } catch (error) { + throw new ApiError(meta.errors.queryError); + } + + if (found.length > ps.limit) { + found.length = ps.limit; + } + + return found; }); diff --git a/packages/backend/src/server/api/endpoints/notes/translate.ts b/packages/backend/src/server/api/endpoints/notes/translate.ts index c6415ceef2..d86fc12a2e 100644 --- a/packages/backend/src/server/api/endpoints/notes/translate.ts +++ b/packages/backend/src/server/api/endpoints/notes/translate.ts @@ -51,15 +51,54 @@ export default define(meta, paramDef, async (ps, user) => { const instance = await fetchMeta(); - if (instance.deeplAuthKey == null) { + if (instance.deeplAuthKey == null && instance.libreTranslateApiUrl == null) { return 204; // TODO: 良い感じのエラー返す } let targetLang = ps.targetLang; if (targetLang.includes("-")) targetLang = targetLang.split("-")[0]; + if (instance.libreTranslateApiUrl != null) { + const jsonBody = { + q: note.text, + source: "auto", + target: targetLang, + format: "text", + api_key: instance.libreTranslateApiKey ?? "", + }; + + const url = new URL(instance.libreTranslateApiUrl); + if (url.pathname.endsWith("/")) { + url.pathname = url.pathname.slice(0, -1); + } + if (!url.pathname.endsWith("/translate")) { + url.pathname += "/translate"; + } + const res = await fetch(url.toString(), { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(jsonBody), + agent: getAgentByUrl, + }); + + const json = (await res.json()) as { + detectedLanguage?: { + confidence: number; + language: string; + }; + translatedText: string; + }; + + return { + sourceLang: json.detectedLanguage?.language, + text: json.translatedText, + }; + } + const params = new URLSearchParams(); - params.append("auth_key", instance.deeplAuthKey); + params.append("auth_key", instance.deeplAuthKey ?? ""); params.append("text", note.text); params.append("target_lang", targetLang); diff --git a/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts b/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts index c1e5357222..5c3fc55bef 100644 --- a/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts @@ -29,6 +29,11 @@ export const meta = { code: "NO_SUCH_LIST", id: "8fb1fbd5-e476-4c37-9fb0-43d55b63a2ff", }, + queryError: { + message: "Please follow more users.", + code: "QUERY_ERROR", + id: "620763f4-f621-4533-ab33-0577a1a3c343", + }, }, } as const; @@ -138,9 +143,31 @@ export default define(meta, paramDef, async (ps, user) => { } //#endregion - const timeline = await query.take(ps.limit).getMany(); + process.nextTick(() => { + if (user) { + activeUsersChart.read(user); + } + }); - activeUsersChart.read(user); + // We fetch more than requested because some may be filtered out, and if there's less than + // requested, the pagination stops. + const found = []; + const take = Math.floor(ps.limit * 1.5); + let skip = 0; + try { + while (found.length < ps.limit) { + const notes = await query.take(take).skip(skip).getMany(); + found.push(...(await Notes.packMany(notes, user))); + skip += take; + if (notes.length < take) break; + } + } catch (error) { + throw new ApiError(meta.errors.queryError); + } - return await Notes.packMany(timeline, user); + if (found.length > ps.limit) { + found.length = ps.limit; + } + + return found; }); diff --git a/packages/backend/src/server/api/endpoints/pages/like.ts b/packages/backend/src/server/api/endpoints/pages/like.ts index f14ed39eb0..03482c9616 100644 --- a/packages/backend/src/server/api/endpoints/pages/like.ts +++ b/packages/backend/src/server/api/endpoints/pages/like.ts @@ -40,12 +40,14 @@ export default define(meta, paramDef, async (ps, user) => { } // if already liked - const exist = await PageLikes.findOneBy({ - pageId: page.id, - userId: user.id, + const exist = await PageLikes.exist({ + where: { + pageId: page.id, + userId: user.id, + }, }); - if (exist != null) { + if (exist) { throw new ApiError(meta.errors.alreadyLiked); } diff --git a/packages/backend/src/server/api/endpoints/pages/unlike.ts b/packages/backend/src/server/api/endpoints/pages/unlike.ts index 07bf3fbf48..e607d7a546 100644 --- a/packages/backend/src/server/api/endpoints/pages/unlike.ts +++ b/packages/backend/src/server/api/endpoints/pages/unlike.ts @@ -38,17 +38,17 @@ export default define(meta, paramDef, async (ps, user) => { throw new ApiError(meta.errors.noSuchPage); } - const exist = await PageLikes.findOneBy({ + const like = await PageLikes.findOneBy({ pageId: page.id, userId: user.id, }); - if (exist == null) { + if (like == null) { throw new ApiError(meta.errors.notLiked); } // Delete like - await PageLikes.delete(exist.id); + await PageLikes.delete(like.id); Pages.decrement({ id: page.id }, "likedCount", 1); }); diff --git a/packages/backend/src/server/api/endpoints/patrons.ts b/packages/backend/src/server/api/endpoints/patrons.ts index d6ac6c3971..b5feea20e8 100644 --- a/packages/backend/src/server/api/endpoints/patrons.ts +++ b/packages/backend/src/server/api/endpoints/patrons.ts @@ -1,8 +1,15 @@ import define from "../define.js"; +import { redisClient } from "@/db/redis.js"; +import * as fs from "node:fs"; +import { fileURLToPath } from "node:url"; +import { dirname } from "node:path"; + +const _filename = fileURLToPath(import.meta.url); +const _dirname = dirname(_filename); export const meta = { tags: ["meta"], - description: "Get list of Calckey patrons from Codeberg", + description: "Get Firefish patrons", requireCredential: false, requireCredentialPrivateMode: false, @@ -10,19 +17,42 @@ export const meta = { export const paramDef = { type: "object", - properties: {}, + properties: { + forceUpdate: { type: "boolean", default: false }, + }, required: [], } as const; -export default define(meta, paramDef, async () => { +export default define(meta, paramDef, async (ps) => { let patrons; - await fetch( - "https://codeberg.org/calckey/calckey/raw/branch/develop/patrons.json", - ) - .then((response) => response.json()) - .then((data) => { - patrons = data["patrons"]; - }); + const cachedPatrons = await redisClient.get("patrons"); + if (!ps.forceUpdate && cachedPatrons) { + patrons = JSON.parse(cachedPatrons); + } else { + AbortSignal.timeout ??= function timeout(ms) { + const ctrl = new AbortController(); + setTimeout(() => ctrl.abort(), ms); + return ctrl.signal; + }; - return patrons; + patrons = await fetch( + "https://gitlab.prometheus.systems/firefish/firefish/-/raw/develop/patrons.json", + { signal: AbortSignal.timeout(2000) }, + ) + .then((response) => response.json()) + .catch(() => { + const staticPatrons = JSON.parse( + fs.readFileSync( + `${_dirname}/../../../../../../patrons.json`, + "utf-8", + ), + ); + patrons = cachedPatrons ? JSON.parse(cachedPatrons) : staticPatrons; + }); + await redisClient.set("patrons", JSON.stringify(patrons), "EX", 3600); + } + return { + patrons: patrons["patrons"], + sponsors: patrons["sponsors"], + }; }); diff --git a/packages/backend/src/server/api/endpoints/promo/read.ts b/packages/backend/src/server/api/endpoints/promo/read.ts index 09c8cb6fab..5310382a43 100644 --- a/packages/backend/src/server/api/endpoints/promo/read.ts +++ b/packages/backend/src/server/api/endpoints/promo/read.ts @@ -33,12 +33,14 @@ export default define(meta, paramDef, async (ps, user) => { throw err; }); - const exist = await PromoReads.findOneBy({ - noteId: note.id, - userId: user.id, + const exist = await PromoReads.exist({ + where: { + noteId: note.id, + userId: user.id, + }, }); - if (exist != null) { + if (exist) { return; } diff --git a/packages/backend/src/server/api/endpoints/release.ts b/packages/backend/src/server/api/endpoints/release.ts index e5ebbb79a6..620bb23d69 100644 --- a/packages/backend/src/server/api/endpoints/release.ts +++ b/packages/backend/src/server/api/endpoints/release.ts @@ -18,7 +18,7 @@ export default define(meta, paramDef, async () => { let release; await fetch( - "https://codeberg.org/calckey/calckey/raw/branch/develop/release.json", + "https://gitlab.prometheus.systems/firefish/firefish/-/raw/develop/release.json", ) .then((response) => response.json()) .then((data) => { diff --git a/packages/backend/src/server/api/endpoints/renote-mute/create.ts b/packages/backend/src/server/api/endpoints/renote-mute/create.ts new file mode 100644 index 0000000000..f09f197c01 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/renote-mute/create.ts @@ -0,0 +1,70 @@ +import { genId } from "@/misc/gen-id.js"; +import { RenoteMutings } from "@/models/index.js"; +import { RenoteMuting } from "@/models/entities/renote-muting.js"; +import define from "../../define.js"; +import { ApiError } from "../../error.js"; +import { getUser } from "../../common/getters.js"; + +export const meta = { + tags: ["account"], + + requireCredential: true, + + kind: "write:mutes", + + errors: { + noSuchUser: { + message: "No such user.", + code: "NO_SUCH_USER", + id: "6fef56f3-e765-4957-88e5-c6f65329b8a5", + }, + + alreadyMuting: { + message: "You are already muting that user.", + code: "ALREADY_MUTING", + id: "7e7359cb-160c-4956-b08f-4d1c653cd007", + }, + }, +} as const; + +export const paramDef = { + type: "object", + properties: { + userId: { type: "string", format: "misskey:id" }, + }, + required: ["userId"], +} as const; + +// eslint-disable-next-line import/no-default-export +export default define(meta, paramDef, async (ps, user) => { + const muter = user; + + // Get mutee + const mutee = await getUser(ps.userId).catch((e) => { + if (e.id === "15348ddd-432d-49c2-8a5a-8069753becff") + throw new ApiError(meta.errors.noSuchUser); + throw e; + }); + + // Check if already muting + const exist = await RenoteMutings.exist({ + where: { + muterId: muter.id, + muteeId: mutee.id, + }, + }); + + if (exist) { + throw new ApiError(meta.errors.alreadyMuting); + } + + // Create mute + await RenoteMutings.insert({ + id: genId(), + createdAt: new Date(), + muterId: muter.id, + muteeId: mutee.id, + } as RenoteMuting); + + // publishUserEvent(user.id, "mute", mutee); +}); diff --git a/packages/backend/src/server/api/endpoints/renote-mute/delete.ts b/packages/backend/src/server/api/endpoints/renote-mute/delete.ts new file mode 100644 index 0000000000..7a898141c3 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/renote-mute/delete.ts @@ -0,0 +1,63 @@ +import { RenoteMutings } from "@/models/index.js"; +import define from "../../define.js"; +import { ApiError } from "../../error.js"; +import { getUser } from "../../common/getters.js"; + +export const meta = { + tags: ["account"], + + requireCredential: true, + + kind: "write:mutes", + + errors: { + noSuchUser: { + message: "No such user.", + code: "NO_SUCH_USER", + id: "b851d00b-8ab1-4a56-8b1b-e24187cb48ef", + }, + + notMuting: { + message: "You are not muting that user.", + code: "NOT_MUTING", + id: "5467d020-daa9-4553-81e1-135c0c35a96d", + }, + }, +} as const; + +export const paramDef = { + type: "object", + properties: { + userId: { type: "string", format: "misskey:id" }, + }, + required: ["userId"], +} as const; + +// eslint-disable-next-line import/no-default-export +export default define(meta, paramDef, async (ps, user) => { + const muter = user; + + // Get mutee + const mutee = await getUser(ps.userId).catch((e) => { + if (e.id === "15348ddd-432d-49c2-8a5a-8069753becff") + throw new ApiError(meta.errors.noSuchUser); + throw e; + }); + + // Check not muting + const muting = await RenoteMutings.findOneBy({ + muterId: muter.id, + muteeId: mutee.id, + }); + + if (muting == null) { + throw new ApiError(meta.errors.notMuting); + } + + // Delete mute + await RenoteMutings.delete({ + id: muting.id, + }); + + // publishUserEvent(user.id, "unmute", mutee); +}); diff --git a/packages/backend/src/server/api/endpoints/renote-mute/list.ts b/packages/backend/src/server/api/endpoints/renote-mute/list.ts new file mode 100644 index 0000000000..9149dd9753 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/renote-mute/list.ts @@ -0,0 +1,46 @@ +import { RenoteMutings } from "@/models/index.js"; +import define from "../../define.js"; +import { makePaginationQuery } from "../../common/make-pagination-query.js"; + +export const meta = { + tags: ["account"], + + requireCredential: true, + + kind: "read:mutes", + + res: { + type: "array", + optional: false, + nullable: false, + items: { + type: "object", + optional: false, + nullable: false, + ref: "RenoteMuting", + }, + }, +} as const; + +export const paramDef = { + type: "object", + properties: { + limit: { type: "integer", minimum: 1, maximum: 100, default: 30 }, + sinceId: { type: "string", format: "misskey:id" }, + untilId: { type: "string", format: "misskey:id" }, + }, + required: [], +} as const; + +// eslint-disable-next-line import/no-default-export +export default define(meta, paramDef, async (ps, me) => { + const query = makePaginationQuery( + RenoteMutings.createQueryBuilder("muting"), + ps.sinceId, + ps.untilId, + ).andWhere("muting.muterId = :meId", { meId: me.id }); + + const mutings = await query.take(ps.limit).getMany(); + + return await RenoteMutings.packMany(mutings, me); +}); diff --git a/packages/backend/src/server/api/endpoints/reset-password.ts b/packages/backend/src/server/api/endpoints/reset-password.ts index 51755727a3..f695ae41f1 100644 --- a/packages/backend/src/server/api/endpoints/reset-password.ts +++ b/packages/backend/src/server/api/endpoints/reset-password.ts @@ -1,8 +1,8 @@ -import bcrypt from "bcryptjs"; import { publishMainStream } from "@/services/stream.js"; import { Users, UserProfiles, PasswordResetRequests } from "@/models/index.js"; import define from "../define.js"; import { ApiError } from "../error.js"; +import { hashPassword } from "@/misc/password.js"; export const meta = { tags: ["reset password"], @@ -34,8 +34,7 @@ export default define(meta, paramDef, async (ps, user) => { } // Generate hash of password - const salt = await bcrypt.genSalt(8); - const hash = await bcrypt.hash(ps.password, salt); + const hash = await hashPassword(ps.password); await UserProfiles.update(req.userId, { password: hash, diff --git a/packages/backend/src/server/api/endpoints/server-info.ts b/packages/backend/src/server/api/endpoints/server-info.ts index 1ce27e2621..87132758fe 100644 --- a/packages/backend/src/server/api/endpoints/server-info.ts +++ b/packages/backend/src/server/api/endpoints/server-info.ts @@ -1,11 +1,14 @@ import * as os from "node:os"; import si from "systeminformation"; import define from "../define.js"; +import meilisearch from "@/db/meilisearch.js"; +import { fetchMeta } from "@/misc/fetch-meta.js"; export const meta = { requireCredential: false, requireCredentialPrivateMode: true, - + allowGet: true, + cacheSec: 30, tags: ["meta"], } as const; @@ -19,6 +22,32 @@ export default define(meta, paramDef, async () => { const memStats = await si.mem(); const fsStats = await si.fsSize(); + let fsIndex = 0; + // Get the first index of fs sizes that are actualy used. + for (const [i, stat] of fsStats.entries()) { + if (stat.rw === true && stat.used > 0) { + fsIndex = i; + break; + } + } + + const instanceMeta = await fetchMeta(); + if (!instanceMeta.enableServerMachineStats) { + return { + machine: "Not specified", + cpu: { + model: "Not specified", + cores: 0, + }, + mem: { + total: 0, + }, + fs: { + total: 0, + used: 0, + }, + }; + } return { machine: os.hostname(), cpu: { @@ -29,8 +58,20 @@ export default define(meta, paramDef, async () => { total: memStats.total, }, fs: { - total: fsStats[0].size, - used: fsStats[0].used, + total: fsStats[fsIndex].size, + used: fsStats[fsIndex].used, }, }; }); + +async function meilisearchStatus() { + if (meilisearch) { + return meilisearch.serverStats(); + } else { + return { + health: "unconfigured", + size: 0, + indexed_count: 0, + }; + } +} diff --git a/packages/backend/src/server/api/endpoints/stats.ts b/packages/backend/src/server/api/endpoints/stats.ts index 8bd5597689..97889c42ed 100644 --- a/packages/backend/src/server/api/endpoints/stats.ts +++ b/packages/backend/src/server/api/endpoints/stats.ts @@ -1,6 +1,6 @@ import { Instances, NoteReactions, Notes, Users } from "@/models/index.js"; import define from "../define.js"; -import {} from "@/services/chart/index.js"; +import { driveChart, notesChart, usersChart } from "@/services/chart/index.js"; import { IsNull } from "typeorm"; export const meta = { @@ -60,19 +60,25 @@ export const paramDef = { } as const; export default define(meta, paramDef, async () => { + const notesChartData = await notesChart.getChart("hour", 1, null); + const notesCount = + notesChartData.local.total[0] + notesChartData.remote.total[0]; + const originalNotesCount = notesChartData.local.total[0]; + + const usersChartData = await usersChart.getChart("hour", 1, null); + const usersCount = + usersChartData.local.total[0] + usersChartData.remote.total[0]; + const originalUsersCount = usersChartData.local.total[0]; + const driveChartData = await driveChart.getChart("hour", 1, null); + //TODO: fixme currently returns 0 + const driveUsageLocal = driveChartData.local.incSize[0]; + const driveUsageRemote = driveChartData.remote.incSize[0]; + const [ - notesCount, - originalNotesCount, - usersCount, - originalUsersCount, reactionsCount, //originalReactionsCount, instances, ] = await Promise.all([ - Notes.count({ cache: 3600000 }), // 1 hour - Notes.count({ where: { userHost: IsNull() }, cache: 3600000 }), - Users.count({ cache: 3600000 }), - Users.count({ where: { host: IsNull() }, cache: 3600000 }), NoteReactions.count({ cache: 3600000 }), // 1 hour //NoteReactions.count({ where: { userHost: IsNull() }, cache: 3600000 }), Instances.count({ cache: 3600000 }), @@ -86,7 +92,7 @@ export default define(meta, paramDef, async () => { reactionsCount, //originalReactionsCount, instances, - driveUsageLocal: 0, - driveUsageRemote: 0, + driveUsageLocal, + driveUsageRemote, }; }); diff --git a/packages/backend/src/server/api/endpoints/sw/register.ts b/packages/backend/src/server/api/endpoints/sw/register.ts index 7218b0d50a..6268ae26d7 100644 --- a/packages/backend/src/server/api/endpoints/sw/register.ts +++ b/packages/backend/src/server/api/endpoints/sw/register.ts @@ -26,6 +26,21 @@ export const meta = { optional: false, nullable: true, }, + userId: { + type: "string", + optional: true, + nullable: false, + }, + endpoint: { + type: "string", + optional: false, + nullable: false, + }, + sendReadMessage: { + type: "boolean", + optional: false, + nullable: false, + }, }, }, } as const; @@ -36,14 +51,14 @@ export const paramDef = { endpoint: { type: "string" }, auth: { type: "string" }, publickey: { type: "string" }, + sendReadMessage: { type: "boolean", default: false }, }, required: ["endpoint", "auth", "publickey"], } as const; -export default define(meta, paramDef, async (ps, user) => { - // if already subscribed - const exist = await SwSubscriptions.findOneBy({ - userId: user.id, +export default define(meta, paramDef, async (ps, me) => { + const subscription = await SwSubscriptions.findOneBy({ + userId: me.id, endpoint: ps.endpoint, auth: ps.auth, publickey: ps.publickey, @@ -51,24 +66,32 @@ export default define(meta, paramDef, async (ps, user) => { const instance = await fetchMeta(true); - if (exist != null) { + // if already subscribed + if (subscription != null) { return { state: "already-subscribed" as const, key: instance.swPublicKey, + userId: me.id, + endpoint: subscription.endpoint, + sendReadMessage: subscription.sendReadMessage, }; } await SwSubscriptions.insert({ id: genId(), createdAt: new Date(), - userId: user.id, + userId: me.id, endpoint: ps.endpoint, auth: ps.auth, publickey: ps.publickey, + sendReadMessage: ps.sendReadMessage, }); return { state: "subscribed" as const, key: instance.swPublicKey, + userId: me.id, + endpoint: ps.endpoint, + sendReadMessage: ps.sendReadMessage, }; }); diff --git a/packages/backend/src/server/api/endpoints/sw/show-registration.ts b/packages/backend/src/server/api/endpoints/sw/show-registration.ts new file mode 100644 index 0000000000..3ccb7de948 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/sw/show-registration.ts @@ -0,0 +1,59 @@ +import { SwSubscriptions } from "@/models/index.js"; +import define from "../../define.js"; + +export const meta = { + tags: ["account"], + + requireCredential: true, + + description: "Check push notification registration exists.", + + res: { + type: "object", + optional: false, + nullable: true, + properties: { + userId: { + type: "string", + optional: false, + nullable: false, + }, + endpoint: { + type: "string", + optional: false, + nullable: false, + }, + sendReadMessage: { + type: "boolean", + optional: false, + nullable: false, + }, + }, + }, +} as const; + +export const paramDef = { + type: "object", + properties: { + endpoint: { type: "string" }, + }, + required: ["endpoint"], +} as const; + +// eslint-disable-next-line import/no-default-export +export default define(meta, paramDef, async (ps, me) => { + const subscription = await SwSubscriptions.findOneBy({ + userId: me.id, + endpoint: ps.endpoint, + }); + + if (subscription != null) { + return { + userId: subscription.userId, + endpoint: subscription.endpoint, + sendReadMessage: subscription.sendReadMessage, + }; + } + + return null; +}); diff --git a/packages/backend/src/server/api/endpoints/sw/unregister.ts b/packages/backend/src/server/api/endpoints/sw/unregister.ts index b025630e4b..e2a40f51cb 100644 --- a/packages/backend/src/server/api/endpoints/sw/unregister.ts +++ b/packages/backend/src/server/api/endpoints/sw/unregister.ts @@ -4,7 +4,7 @@ import define from "../../define.js"; export const meta = { tags: ["account"], - requireCredential: true, + requireCredential: false, description: "Unregister from receiving push notifications.", } as const; @@ -17,9 +17,9 @@ export const paramDef = { required: ["endpoint"], } as const; -export default define(meta, paramDef, async (ps, user) => { +export default define(meta, paramDef, async (ps, me) => { await SwSubscriptions.delete({ - userId: user.id, + ...(me ? { userId: me.id } : {}), endpoint: ps.endpoint, }); }); diff --git a/packages/backend/src/server/api/endpoints/sw/update-registration.ts b/packages/backend/src/server/api/endpoints/sw/update-registration.ts new file mode 100644 index 0000000000..5ba53ee8a7 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/sw/update-registration.ts @@ -0,0 +1,44 @@ +import { SwSubscriptions } from "@/models/index.js"; +import define from "../../define.js"; + +export const meta = { + tags: ["account"], + + requireCredential: true, + + description: "Unregister from receiving push notifications.", +} as const; + +export const paramDef = { + type: "object", + properties: { + endpoint: { type: "string" }, + sendReadMessage: { type: "boolean" }, + }, + required: ["endpoint"], +} as const; + +export default define(meta, paramDef, async (ps, me) => { + const swSubscription = await SwSubscriptions.findOneBy({ + userId: me.id, + endpoint: ps.endpoint, + }); + + if (swSubscription === null) { + throw new Error("No such registration"); + } + + if (ps.sendReadMessage !== undefined) { + swSubscription.sendReadMessage = ps.sendReadMessage; + } + + await SwSubscriptions.update(swSubscription.id, { + sendReadMessage: swSubscription.sendReadMessage, + }); + + return { + userId: swSubscription.userId, + endpoint: swSubscription.endpoint, + sendReadMessage: swSubscription.sendReadMessage, + }; +}); diff --git a/packages/backend/src/server/api/endpoints/username/available.ts b/packages/backend/src/server/api/endpoints/username/available.ts index f5aa4ed1ea..6fa09ba369 100644 --- a/packages/backend/src/server/api/endpoints/username/available.ts +++ b/packages/backend/src/server/api/endpoints/username/available.ts @@ -1,5 +1,6 @@ import { IsNull } from "typeorm"; import { Users, UsedUsernames } from "@/models/index.js"; +import config from "@/config/index.js"; import define from "../../define.js"; export const meta = { @@ -40,7 +41,11 @@ export default define(meta, paramDef, async (ps) => { username: ps.username.toLowerCase(), }); + const reserved = config.reservedUsernames?.includes( + ps.username.toLowerCase(), + ); + return { - available: exist === 0 && exist2 === 0, + available: exist === 0 && exist2 === 0 && !reserved, }; }); diff --git a/packages/backend/src/server/api/endpoints/users/followers.ts b/packages/backend/src/server/api/endpoints/users/followers.ts index 138343d9f4..31719bad32 100644 --- a/packages/backend/src/server/api/endpoints/users/followers.ts +++ b/packages/backend/src/server/api/endpoints/users/followers.ts @@ -37,6 +37,12 @@ export const meta = { code: "FORBIDDEN", id: "3c6a84db-d619-26af-ca14-06232a21df8a", }, + + nullFollowers: { + message: "No followers found.", + code: "NULL_FOLLOWERS", + id: "174a6507-a6c2-4925-8e5d-92fd08aedc9e", + }, }, } as const; @@ -92,12 +98,14 @@ export default define(meta, paramDef, async (ps, me) => { if (me == null) { throw new ApiError(meta.errors.forbidden); } else if (me.id !== user.id) { - const following = await Followings.findOneBy({ - followeeId: user.id, - followerId: me.id, + const isFollowed = await Followings.exist({ + where: { + followeeId: user.id, + followerId: me.id, + }, }); - if (following == null) { - throw new ApiError(meta.errors.forbidden); + if (!isFollowed) { + throw new ApiError(meta.errors.nullFollowers); } } } diff --git a/packages/backend/src/server/api/endpoints/users/following.ts b/packages/backend/src/server/api/endpoints/users/following.ts index 967379d0c4..1c1da0e117 100644 --- a/packages/backend/src/server/api/endpoints/users/following.ts +++ b/packages/backend/src/server/api/endpoints/users/following.ts @@ -37,6 +37,11 @@ export const meta = { code: "FORBIDDEN", id: "f6cdb0df-c19f-ec5c-7dbb-0ba84a1f92ba", }, + cannot_find: { + message: "Cannot find the following.", + code: "CANNOT_FIND", + id: "7a55f0d7-8e06-4a7e-9c77-ee7d59b25a82", + }, }, } as const; @@ -92,12 +97,14 @@ export default define(meta, paramDef, async (ps, me) => { if (me == null) { throw new ApiError(meta.errors.forbidden); } else if (me.id !== user.id) { - const following = await Followings.findOneBy({ - followeeId: user.id, - followerId: me.id, + const isFollowing = await Followings.exist({ + where: { + followeeId: user.id, + followerId: me.id, + }, }); - if (following == null) { - throw new ApiError(meta.errors.forbidden); + if (!isFollowing) { + throw new ApiError(meta.errors.cannot_find); } } } diff --git a/packages/backend/src/server/api/endpoints/users/lists/push.ts b/packages/backend/src/server/api/endpoints/users/lists/push.ts index a14195bbc3..899754aafb 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/push.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/push.ts @@ -57,7 +57,7 @@ export default define(meta, paramDef, async (ps, me) => { userId: me.id, }); - if (userList == null) { + if (!userList) { throw new ApiError(meta.errors.noSuchList); } @@ -70,18 +70,22 @@ export default define(meta, paramDef, async (ps, me) => { // Check blocking if (user.id !== me.id) { - const block = await Blockings.findOneBy({ - blockerId: user.id, - blockeeId: me.id, + const isBlocked = await Blockings.exist({ + where: { + blockerId: user.id, + blockeeId: me.id, + }, }); - if (block) { + if (isBlocked) { throw new ApiError(meta.errors.youHaveBeenBlocked); } } - const exist = await UserListJoinings.findOneBy({ - userListId: userList.id, - userId: user.id, + const exist = await UserListJoinings.exist({ + where: { + userListId: ps.listId, + userId: user.id, + }, }); if (exist) { diff --git a/packages/backend/src/server/api/endpoints/users/lists/show.ts b/packages/backend/src/server/api/endpoints/users/lists/show.ts index 716fd405dc..cb4893b0e4 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/show.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/show.ts @@ -42,7 +42,7 @@ export default define(meta, paramDef, async (ps, me) => { userId: me.id, }); - if (userList == null) { + if (!userList) { throw new ApiError(meta.errors.noSuchList); } diff --git a/packages/backend/src/server/api/endpoints/users/reactions.ts b/packages/backend/src/server/api/endpoints/users/reactions.ts index 17b7a04a06..6b6d32e8ad 100644 --- a/packages/backend/src/server/api/endpoints/users/reactions.ts +++ b/packages/backend/src/server/api/endpoints/users/reactions.ts @@ -49,7 +49,7 @@ export const paramDef = { export default define(meta, paramDef, async (ps, me) => { const profile = await UserProfiles.findOneByOrFail({ userId: ps.userId }); - if (me == null || (me.id !== ps.userId && !profile.publicReactions)) { + if (me.id !== ps.userId && !profile.publicReactions) { throw new ApiError(meta.errors.reactionsNotPublic); } diff --git a/packages/backend/src/server/api/endpoints/users/relation.ts b/packages/backend/src/server/api/endpoints/users/relation.ts index 01f39396de..5580eaea0b 100644 --- a/packages/backend/src/server/api/endpoints/users/relation.ts +++ b/packages/backend/src/server/api/endpoints/users/relation.ts @@ -57,6 +57,11 @@ export const meta = { optional: false, nullable: false, }, + isRenoteMuted: { + type: "boolean", + optional: false, + nullable: false, + }, }, }, { @@ -107,6 +112,11 @@ export const meta = { optional: false, nullable: false, }, + isRenoteMuted: { + type: "boolean", + optional: false, + nullable: false, + }, }, }, }, diff --git a/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts b/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts index 99aa2f1af3..f34083233f 100644 --- a/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts +++ b/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts @@ -3,6 +3,7 @@ import { Followings, Users } from "@/models/index.js"; import { USER_ACTIVE_THRESHOLD } from "@/const.js"; import type { User } from "@/models/entities/user.js"; import define from "../../define.js"; +import { sqlLikeEscape } from "@/misc/sql-like-escape.js"; export const meta = { tags: ["users"], @@ -31,6 +32,12 @@ export const paramDef = { username: { type: "string", nullable: true }, host: { type: "string", nullable: true }, limit: { type: "integer", minimum: 1, maximum: 100, default: 10 }, + maxDaysSinceLastActive: { + type: "integer", + minimum: 1, + maximum: 1000, + default: 30, + }, detail: { type: "boolean", default: true }, }, anyOf: [{ required: ["username"] }, { required: ["host"] }], @@ -39,16 +46,20 @@ export const paramDef = { // TODO: avatar,bannerをJOINしたいけどエラーになる export default define(meta, paramDef, async (ps, me) => { - const activeThreshold = new Date(Date.now() - 1000 * 60 * 60 * 24 * 30); // 30日 + const activeThreshold = ps.maxDaysSinceLastActive + ? new Date(Date.now() - 1000 * 60 * 60 * 24 * ps.maxDaysSinceLastActive) + : null; if (ps.host) { const q = Users.createQueryBuilder("user") .where("user.isSuspended = FALSE") - .andWhere("user.host LIKE :host", { host: `${ps.host.toLowerCase()}%` }); + .andWhere("user.host LIKE :host", { + host: `${sqlLikeEscape(ps.host.toLowerCase())}%`, + }); if (ps.username) { q.andWhere("user.usernameLower LIKE :username", { - username: `${ps.username.toLowerCase()}%`, + username: `${sqlLikeEscape(ps.username.toLowerCase())}%`, }); } @@ -71,9 +82,11 @@ export default define(meta, paramDef, async (ps, me) => { .andWhere("user.id != :meId", { meId: me.id }) .andWhere("user.isSuspended = FALSE") .andWhere("user.usernameLower LIKE :username", { - username: `${ps.username.toLowerCase()}%`, - }) - .andWhere( + username: `${sqlLikeEscape(ps.username.toLowerCase())}%`, + }); + + if (activeThreshold) { + query.andWhere( new Brackets((qb) => { qb.where("user.updatedAt IS NULL").orWhere( "user.updatedAt > :activeThreshold", @@ -81,6 +94,7 @@ export default define(meta, paramDef, async (ps, me) => { ); }), ); + } query.setParameters(followingQuery.getParameters()); @@ -95,7 +109,7 @@ export default define(meta, paramDef, async (ps, me) => { .andWhere("user.id != :meId", { meId: me.id }) .andWhere("user.isSuspended = FALSE") .andWhere("user.usernameLower LIKE :username", { - username: `${ps.username.toLowerCase()}%`, + username: `${sqlLikeEscape(ps.username.toLowerCase())}%`, }) .andWhere("user.updatedAt IS NOT NULL"); @@ -112,7 +126,7 @@ export default define(meta, paramDef, async (ps, me) => { users = await Users.createQueryBuilder("user") .where("user.isSuspended = FALSE") .andWhere("user.usernameLower LIKE :username", { - username: `${ps.username.toLowerCase()}%`, + username: `${sqlLikeEscape(ps.username.toLowerCase())}%`, }) .andWhere("user.updatedAt IS NOT NULL") .orderBy("user.updatedAt", "DESC") diff --git a/packages/backend/src/server/api/endpoints/users/search.ts b/packages/backend/src/server/api/endpoints/users/search.ts index db687a1075..2d84d5bfeb 100644 --- a/packages/backend/src/server/api/endpoints/users/search.ts +++ b/packages/backend/src/server/api/endpoints/users/search.ts @@ -2,6 +2,7 @@ import { Brackets } from "typeorm"; import { UserProfiles, Users } from "@/models/index.js"; import type { User } from "@/models/entities/user.js"; import define from "../../define.js"; +import { sqlLikeEscape } from "@/misc/sql-like-escape.js"; export const meta = { tags: ["users"], @@ -50,7 +51,7 @@ export default define(meta, paramDef, async (ps, me) => { if (isUsername) { const usernameQuery = Users.createQueryBuilder("user") .where("user.usernameLower LIKE :username", { - username: `${ps.query.replace("@", "").toLowerCase()}%`, + username: `${sqlLikeEscape(ps.query.replace("@", "").toLowerCase())}%`, }) .andWhere( new Brackets((qb) => { @@ -77,12 +78,14 @@ export default define(meta, paramDef, async (ps, me) => { const nameQuery = Users.createQueryBuilder("user") .where( new Brackets((qb) => { - qb.where("user.name ILIKE :query", { query: `%${ps.query}%` }); + qb.where("user.name ILIKE :query", { + query: `%${sqlLikeEscape(ps.query)}%`, + }); // Also search username if it qualifies as username if (Users.validateLocalUsername(ps.query)) { qb.orWhere("user.usernameLower LIKE :username", { - username: `%${ps.query.toLowerCase()}%`, + username: `%${sqlLikeEscape(ps.query.toLowerCase())}%`, }); } }), @@ -113,7 +116,7 @@ export default define(meta, paramDef, async (ps, me) => { const profQuery = UserProfiles.createQueryBuilder("prof") .select("prof.userId") .where("prof.description ILIKE :query", { - query: `%${ps.query}%`, + query: `%${sqlLikeEscape(ps.query)}%`, }); if (ps.origin === "local") { diff --git a/packages/backend/src/server/api/endpoints/users/show.ts b/packages/backend/src/server/api/endpoints/users/show.ts index 49cac81fdb..bead8df0a4 100644 --- a/packages/backend/src/server/api/endpoints/users/show.ts +++ b/packages/backend/src/server/api/endpoints/users/show.ts @@ -54,7 +54,7 @@ export const paramDef = { anyOf: [ { properties: { - userId: { type: "string", format: "misskey:id" }, + userId: { type: "string" }, }, required: ["userId"], }, @@ -65,7 +65,6 @@ export const paramDef = { uniqueItems: true, items: { type: "string", - format: "misskey:id", }, }, }, @@ -95,21 +94,27 @@ export default define(meta, paramDef, async (ps, me) => { return []; } - const users = await Users.findBy( - isAdminOrModerator - ? { - id: In(ps.userIds), - } - : { - id: In(ps.userIds), - isSuspended: false, - }, - ); + const isUrl = ps.userIds[0].startsWith("http"); + let users: User[]; + if (isUrl) { + users = await Users.findBy( + isAdminOrModerator + ? { uri: In(ps.userIds) } + : { uri: In(ps.userIds), isSuspended: false }, + ); + } else { + users = await Users.findBy( + isAdminOrModerator + ? { id: In(ps.userIds) } + : { id: In(ps.userIds), isSuspended: false }, + ); + } // リクエストされた通りに並べ替え const _users: User[] = []; for (const id of ps.userIds) { - _users.push(users.find((x) => x.id === id)!); + const res = users.find((x) => (isUrl ? x.uri === id : x.id === id)); + if (res) _users.push(res); } return await Promise.all( @@ -129,7 +134,9 @@ export default define(meta, paramDef, async (ps, me) => { } else { const q: FindOptionsWhere = ps.userId != null - ? { id: ps.userId } + ? ps.userId.startsWith("http") + ? { uri: ps.userId } + : { id: ps.userId } : { usernameLower: ps.username!.toLowerCase(), host: IsNull() }; user = await Users.findOneBy(q); diff --git a/packages/backend/src/server/api/index.ts b/packages/backend/src/server/api/index.ts index da98a9df19..9e8c458868 100644 --- a/packages/backend/src/server/api/index.ts +++ b/packages/backend/src/server/api/index.ts @@ -7,18 +7,32 @@ import Router from "@koa/router"; import multer from "@koa/multer"; import bodyParser from "koa-bodyparser"; import cors from "@koa/cors"; -import { apiMastodonCompatible } from './mastodon/ApiMastodonCompatibleService.js'; +import { + apiMastodonCompatible, + getClient, +} from "./mastodon/ApiMastodonCompatibleService.js"; import { Instances, AccessTokens, Users } from "@/models/index.js"; import config from "@/config/index.js"; +import fs from "fs"; import endpoints from "./endpoints.js"; import compatibility from "./compatibility.js"; import handler from "./api-handler.js"; import signup from "./private/signup.js"; import signin from "./private/signin.js"; import signupPending from "./private/signup-pending.js"; +import verifyEmail from "./private/verify-email.js"; import discord from "./service/discord.js"; import github from "./service/github.js"; import twitter from "./service/twitter.js"; +import { koaBody } from "koa-body"; +import { + convertId, + IdConvertType as IdType, +} from "../../../native-utils/built/index.js"; +import { convertAttachment } from "./mastodon/converters.js"; + +// re-export native rust id conversion (function and enum) +export { IdType, convertId }; // Init app const app = new Koa(); @@ -35,16 +49,11 @@ app.use(async (ctx, next) => { await next(); }); -app.use( - bodyParser({ - // リクエストが multipart/form-data でない限りはJSONだと見なす - detectJSON: (ctx) => - !( - ctx.is("multipart/form-data") || - ctx.is("application/x-www-form-urlencoded") - ), - }), -); +// Init router +const router = new Router(); +const mastoRouter = new Router(); +const mastoFileRouter = new Router(); +const errorRouter = new Router(); // Init multer instance const upload = multer({ @@ -55,10 +64,75 @@ const upload = multer({ }, }); -// Init router -const router = new Router(); +router.use( + bodyParser({ + // リクエストが multipart/form-data でない限りはJSONだと見なす + detectJSON: (ctx) => + !( + ctx.is("multipart/form-data") || + ctx.is("application/x-www-form-urlencoded") + ), + }), +); -apiMastodonCompatible(router); +mastoRouter.use( + koaBody({ + multipart: true, + urlencoded: true, + }), +); + +mastoFileRouter.post("/v1/media", upload.single("file"), async (ctx) => { + const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; + const accessTokens = ctx.headers.authorization; + const client = getClient(BASE_URL, accessTokens); + try { + const multipartData = await ctx.file; + if (!multipartData) { + ctx.body = { error: "No image" }; + ctx.status = 401; + return; + } + const data = await client.uploadMedia(multipartData); + ctx.body = convertAttachment(data.data as Entity.Attachment); + } catch (e: any) { + console.error(e); + ctx.status = 401; + ctx.body = e.response.data; + } +}); +mastoFileRouter.post("/v2/media", upload.single("file"), async (ctx) => { + const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; + const accessTokens = ctx.headers.authorization; + const client = getClient(BASE_URL, accessTokens); + try { + const multipartData = await ctx.file; + if (!multipartData) { + ctx.body = { error: "No image" }; + ctx.status = 401; + return; + } + const data = await client.uploadMedia(multipartData, ctx.request.body); + ctx.body = convertAttachment(data.data as Entity.Attachment); + } catch (e: any) { + console.error(e); + ctx.status = 401; + ctx.body = e.response.data; + } +}); + +mastoRouter.use(async (ctx, next) => { + if (ctx.request.query) { + if (!ctx.request.body || Object.keys(ctx.request.body).length === 0) { + ctx.request.body = ctx.request.query; + } else { + ctx.request.body = { ...ctx.request.body, ...ctx.request.query }; + } + } + await next(); +}); + +apiMastodonCompatible(mastoRouter); /** * Register endpoint handlers @@ -105,22 +179,12 @@ for (const endpoint of [...endpoints, ...compatibility]) { router.post("/signup", signup); router.post("/signin", signin); router.post("/signup-pending", signupPending); +router.post("/verify-email", verifyEmail); router.use(discord.routes()); router.use(github.routes()); router.use(twitter.routes()); -router.get("/v1/instance/peers", async (ctx) => { - const instances = await Instances.find({ - select: ["host"], - where: { - isSuspended: false, - }, - }); - - ctx.body = instances.map((instance) => instance.host); -}); - router.post("/miauth/:session/check", async (ctx) => { const token = await AccessTokens.findOneBy({ session: ctx.params.session, @@ -144,11 +208,15 @@ router.post("/miauth/:session/check", async (ctx) => { }); // Return 404 for unknown API -router.all("(.*)", async (ctx) => { +errorRouter.all("(.*)", async (ctx) => { ctx.status = 404; }); // Register router +app.use(mastoFileRouter.routes()); +app.use(mastoRouter.routes()); +app.use(mastoRouter.allowedMethods()); app.use(router.routes()); +app.use(errorRouter.routes()); export default app; diff --git a/packages/backend/src/server/api/limiter.ts b/packages/backend/src/server/api/limiter.ts index dd005ad136..367fb3d279 100644 --- a/packages/backend/src/server/api/limiter.ts +++ b/packages/backend/src/server/api/limiter.ts @@ -3,6 +3,7 @@ import { CacheableLocalUser, User } from "@/models/entities/user.js"; import Logger from "@/services/logger.js"; import { redisClient } from "../../db/redis.js"; import type { IEndpointMeta } from "./endpoints.js"; +import { convertMilliseconds } from "@/misc/convert-milliseconds.js"; const logger = new Logger("limiter"); @@ -76,7 +77,10 @@ export const limiter = ( ); if (info.remaining === 0) { - reject("RATE_LIMIT_EXCEEDED"); + reject({ + message: "RATE_LIMIT_EXCEEDED", + remainingTime: convertMilliseconds(info.resetMs - Date.now()), + }); } else { ok(); } diff --git a/packages/backend/src/server/api/mastodon/ApiMastodonCompatibleService.ts b/packages/backend/src/server/api/mastodon/ApiMastodonCompatibleService.ts index 57a86c96d2..f3fbd4badb 100644 --- a/packages/backend/src/server/api/mastodon/ApiMastodonCompatibleService.ts +++ b/packages/backend/src/server/api/mastodon/ApiMastodonCompatibleService.ts @@ -1,32 +1,37 @@ import Router from "@koa/router"; -import megalodon, { MegalodonInterface } from '@cutls/megalodon'; -import { apiAuthMastodon } from './endpoints/auth.js'; -import { apiAccountMastodon } from './endpoints/account.js'; -import { apiStatusMastodon } from './endpoints/status.js'; -import { apiFilterMastodon } from './endpoints/filter.js'; -import { apiTimelineMastodon } from './endpoints/timeline.js'; -import { apiNotificationsMastodon } from './endpoints/notifications.js'; -import { apiSearchMastodon } from './endpoints/search.js'; -import { getInstance } from './endpoints/meta.js'; +import megalodon, { MegalodonInterface } from "megalodon"; +import { apiAuthMastodon } from "./endpoints/auth.js"; +import { apiAccountMastodon } from "./endpoints/account.js"; +import { apiStatusMastodon } from "./endpoints/status.js"; +import { apiFilterMastodon } from "./endpoints/filter.js"; +import { apiTimelineMastodon } from "./endpoints/timeline.js"; +import { apiNotificationsMastodon } from "./endpoints/notifications.js"; +import { apiSearchMastodon } from "./endpoints/search.js"; +import { getInstance } from "./endpoints/meta.js"; +import { convertAnnouncement, convertFilter } from "./converters.js"; +import { convertId, IdType } from "../index.js"; -export function getClient(BASE_URL: string, authorization: string | undefined): MegalodonInterface { - const accessTokenArr = authorization?.split(' ') ?? [null]; +export function getClient( + BASE_URL: string, + authorization: string | undefined, +): MegalodonInterface { + const accessTokenArr = authorization?.split(" ") ?? [null]; const accessToken = accessTokenArr[accessTokenArr.length - 1]; - const generator = (megalodon as any).default - const client = generator('misskey', BASE_URL, accessToken) as MegalodonInterface; - return client + const generator = (megalodon as any).default; + const client = generator(BASE_URL, accessToken) as MegalodonInterface; + return client; } export function apiMastodonCompatible(router: Router): void { - apiAuthMastodon(router) - apiAccountMastodon(router) - apiStatusMastodon(router) - apiFilterMastodon(router) - apiTimelineMastodon(router) - apiNotificationsMastodon(router) - apiSearchMastodon(router) + apiAuthMastodon(router); + apiAccountMastodon(router); + apiStatusMastodon(router); + apiFilterMastodon(router); + apiTimelineMastodon(router); + apiNotificationsMastodon(router); + apiSearchMastodon(router); - router.get('/v1/custom_emojis', async (ctx) => { + router.get("/v1/custom_emojis", async (ctx) => { const BASE_URL = `${ctx.request.protocol}://${ctx.request.hostname}`; const accessTokens = ctx.request.headers.authorization; const client = getClient(BASE_URL, accessTokens); @@ -34,25 +39,104 @@ export function apiMastodonCompatible(router: Router): void { const data = await client.getInstanceCustomEmojis(); ctx.body = data.data; } catch (e: any) { - console.error(e) + console.error(e); ctx.status = 401; ctx.body = e.response.data; } }); - router.get('/v1/instance', async (ctx) => { + router.get("/v1/instance", async (ctx) => { const BASE_URL = `${ctx.request.protocol}://${ctx.request.hostname}`; const accessTokens = ctx.request.headers.authorization; - const client = getClient(BASE_URL, accessTokens); // we are using this here, because in private mode some info isnt - // displayed without being logged in + const client = getClient(BASE_URL, accessTokens); // we are using this here, because in private mode some info isnt + // displayed without being logged in try { const data = await client.getInstance(); - ctx.body = getInstance(data.data); + ctx.body = await getInstance(data.data); } catch (e: any) { - console.error(e) + console.error(e); ctx.status = 401; ctx.body = e.response.data; } }); -} + router.get("/v1/announcements", async (ctx) => { + const BASE_URL = `${ctx.request.protocol}://${ctx.request.hostname}`; + const accessTokens = ctx.request.headers.authorization; + const client = getClient(BASE_URL, accessTokens); + try { + const data = await client.getInstanceAnnouncements(); + ctx.body = data.data.map((announcement) => + convertAnnouncement(announcement), + ); + } catch (e: any) { + console.error(e); + ctx.status = 401; + ctx.body = e.response.data; + } + }); + + router.post<{ Params: { id: string } }>( + "/v1/announcements/:id/dismiss", + async (ctx) => { + const BASE_URL = `${ctx.request.protocol}://${ctx.request.hostname}`; + const accessTokens = ctx.request.headers.authorization; + const client = getClient(BASE_URL, accessTokens); + try { + const data = await client.dismissInstanceAnnouncement( + convertId(ctx.params.id, IdType.FirefishId), + ); + ctx.body = data.data; + } catch (e: any) { + console.error(e); + ctx.status = 401; + ctx.body = e.response.data; + } + }, + ); + + router.get("/v1/filters", async (ctx) => { + const BASE_URL = `${ctx.request.protocol}://${ctx.request.hostname}`; + const accessTokens = ctx.request.headers.authorization; + const client = getClient(BASE_URL, accessTokens); // we are using this here, because in private mode some info isnt + // displayed without being logged in + try { + const data = await client.getFilters(); + ctx.body = data.data.map((filter) => convertFilter(filter)); + } catch (e: any) { + console.error(e); + ctx.status = 401; + ctx.body = e.response.data; + } + }); + + router.get("/v1/trends", async (ctx) => { + const BASE_URL = `${ctx.request.protocol}://${ctx.request.hostname}`; + const accessTokens = ctx.request.headers.authorization; + const client = getClient(BASE_URL, accessTokens); // we are using this here, because in private mode some info isnt + // displayed without being logged in + try { + const data = await client.getInstanceTrends(); + ctx.body = data.data; + } catch (e: any) { + console.error(e); + ctx.status = 401; + ctx.body = e.response.data; + } + }); + + router.get("/v1/preferences", async (ctx) => { + const BASE_URL = `${ctx.request.protocol}://${ctx.request.hostname}`; + const accessTokens = ctx.request.headers.authorization; + const client = getClient(BASE_URL, accessTokens); // we are using this here, because in private mode some info isnt + // displayed without being logged in + try { + const data = await client.getPreferences(); + ctx.body = data.data; + } catch (e: any) { + console.error(e); + ctx.status = 401; + ctx.body = e.response.data; + } + }); +} diff --git a/packages/backend/src/server/api/mastodon/converters.ts b/packages/backend/src/server/api/mastodon/converters.ts new file mode 100644 index 0000000000..cbaf5287f6 --- /dev/null +++ b/packages/backend/src/server/api/mastodon/converters.ts @@ -0,0 +1,66 @@ +import { Entity } from "megalodon"; +import { convertId, IdType } from "../index.js"; + +function simpleConvert(data: any) { + // copy the object to bypass weird pass by reference bugs + const result = Object.assign({}, data); + result.id = convertId(data.id, IdType.MastodonId); + return result; +} + +export function convertAccount(account: Entity.Account) { + return simpleConvert(account); +} +export function convertAnnouncement(announcement: Entity.Announcement) { + return simpleConvert(announcement); +} +export function convertAttachment(attachment: Entity.Attachment) { + return simpleConvert(attachment); +} +export function convertFilter(filter: Entity.Filter) { + return simpleConvert(filter); +} +export function convertList(list: Entity.List) { + return simpleConvert(list); +} +export function convertFeaturedTag(tag: Entity.FeaturedTag) { + return simpleConvert(tag); +} + +export function convertNotification(notification: Entity.Notification) { + notification.account = convertAccount(notification.account); + notification.id = convertId(notification.id, IdType.MastodonId); + if (notification.status) + notification.status = convertStatus(notification.status); + return notification; +} + +export function convertPoll(poll: Entity.Poll) { + return simpleConvert(poll); +} +export function convertRelationship(relationship: Entity.Relationship) { + return simpleConvert(relationship); +} + +export function convertStatus(status: Entity.Status) { + status.account = convertAccount(status.account); + status.id = convertId(status.id, IdType.MastodonId); + if (status.in_reply_to_account_id) + status.in_reply_to_account_id = convertId( + status.in_reply_to_account_id, + IdType.MastodonId, + ); + if (status.in_reply_to_id) + status.in_reply_to_id = convertId(status.in_reply_to_id, IdType.MastodonId); + status.media_attachments = status.media_attachments.map((attachment) => + convertAttachment(attachment), + ); + status.mentions = status.mentions.map((mention) => ({ + ...mention, + id: convertId(mention.id, IdType.MastodonId), + })); + if (status.poll) status.poll = convertPoll(status.poll); + if (status.reblog) status.reblog = convertStatus(status.reblog); + + return status; +} diff --git a/packages/backend/src/server/api/mastodon/endpoints/account.ts b/packages/backend/src/server/api/mastodon/endpoints/account.ts index 61d4da8a8a..36548cd3b2 100644 --- a/packages/backend/src/server/api/mastodon/endpoints/account.ts +++ b/packages/backend/src/server/api/mastodon/endpoints/account.ts @@ -1,323 +1,511 @@ -import megalodon, { MegalodonInterface } from '@cutls/megalodon'; +import { Users } from "@/models/index.js"; +import { resolveUser } from "@/remote/resolve-user.js"; import Router from "@koa/router"; -import { koaBody } from 'koa-body'; -import { getClient } from '../ApiMastodonCompatibleService.js'; -import { toLimitToInt } from './timeline.js'; +import { FindOptionsWhere, IsNull } from "typeorm"; +import { getClient } from "../ApiMastodonCompatibleService.js"; +import { argsToBools, convertTimelinesArgsId, limitToInt } from "./timeline.js"; +import { convertId, IdType } from "../../index.js"; +import { + convertAccount, + convertFeaturedTag, + convertList, + convertRelationship, + convertStatus, +} from "../converters.js"; + +const relationshipModel = { + id: "", + following: false, + followed_by: false, + delivery_following: false, + blocking: false, + blocked_by: false, + muting: false, + muting_notifications: false, + requested: false, + domain_blocking: false, + showing_reblogs: false, + endorsed: false, + notifying: false, + note: "", +}; export function apiAccountMastodon(router: Router): void { + router.get("/v1/accounts/verify_credentials", async (ctx) => { + const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; + const accessTokens = ctx.headers.authorization; + const client = getClient(BASE_URL, accessTokens); + try { + const data = await client.verifyAccountCredentials(); + let acct = data.data; + acct.id = convertId(acct.id, IdType.MastodonId); + acct.display_name = acct.display_name || acct.username; + acct.url = `${BASE_URL}/@${acct.url}`; + acct.note = acct.note || ""; + acct.avatar_static = acct.avatar; + acct.header = acct.header || "/static-assets/transparent.png"; + acct.header_static = acct.header || "/static-assets/transparent.png"; + acct.source = { + note: acct.note, + fields: acct.fields, + privacy: await client.getDefaultPostPrivacy(), + sensitive: false, + language: "", + }; + console.log(acct); + ctx.body = acct; + } catch (e: any) { + console.error(e); + console.error(e.response.data); + ctx.status = 401; + ctx.body = e.response.data; + } + }); + router.patch("/v1/accounts/update_credentials", async (ctx) => { + const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; + const accessTokens = ctx.headers.authorization; + const client = getClient(BASE_URL, accessTokens); + try { + const data = await client.updateCredentials( + (ctx.request as any).body as any, + ); + ctx.body = convertAccount(data.data); + } catch (e: any) { + console.error(e); + console.error(e.response.data); + ctx.status = 401; + ctx.body = e.response.data; + } + }); + router.get("/v1/accounts/lookup", async (ctx) => { + const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; + const accessTokens = ctx.headers.authorization; + const client = getClient(BASE_URL, accessTokens); + try { + const data = await client.search( + (ctx.request.query as any).acct, + "accounts", + ); + ctx.body = convertAccount(data.data.accounts[0]); + } catch (e: any) { + console.error(e); + console.error(e.response.data); + ctx.status = 401; + ctx.body = e.response.data; + } + }); + router.get("/v1/accounts/relationships", async (ctx) => { + const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; + const accessTokens = ctx.headers.authorization; + const client = getClient(BASE_URL, accessTokens); + let users; + try { + // TODO: this should be body + let ids = ctx.request.query ? ctx.request.query["id[]"] : null; + if (typeof ids === "string") { + ids = [ids]; + } + users = ids; + relationshipModel.id = ids?.toString() || "1"; + if (!ids) { + ctx.body = [relationshipModel]; + return; + } - router.get('/v1/accounts/verify_credentials', async (ctx, next) => { - const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; - const accessTokens = ctx.headers.authorization; - const client = getClient(BASE_URL, accessTokens); - try { - const data = await client.verifyAccountCredentials(); - const acct = data.data; - acct.url = `${BASE_URL}/@${acct.url}` - acct.note = '' - acct.avatar_static = acct.avatar - acct.header = acct.header || '' - acct.header_static = acct.header || '' - acct.source = { - note: acct.note, - fields: acct.fields, - privacy: 'public', - sensitive: false, - language: '' - } - ctx.body = acct - } catch (e: any) { - console.error(e) - console.error(e.response.data) - ctx.status =(401); - ctx.body = e.response.data; - } - }); - router.patch('/v1/accounts/update_credentials', async (ctx) => { - const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; - const accessTokens = ctx.headers.authorization; - const client = getClient(BASE_URL, accessTokens); - try { - const data = await client.updateCredentials((ctx.request as any).body as any); - ctx.body = data.data; - } catch (e: any) { - console.error(e) - console.error(e.response.data) - ctx.status =(401); - ctx.body = e.response.data; - } - }); - router.get<{ Params: { id: string } }>('/v1/accounts/:id', async (ctx, next) => { - const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; - const accessTokens = ctx.headers.authorization; - const client = getClient(BASE_URL, accessTokens); - try { - const data = await client.getAccount(ctx.params.id); - ctx.body = data.data; - } catch (e: any) { - console.error(e) - console.error(e.response.data) - ctx.status =(401); - ctx.body = e.response.data; - } - }); - router.get<{ Params: { id: string } }>('/v1/accounts/:id/statuses', async (ctx, next) => { - const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; - const accessTokens = ctx.headers.authorization; - const client = getClient(BASE_URL, accessTokens); - try { - const data = await client.getAccountStatuses(ctx.params.id, toLimitToInt(ctx.query as any)); - ctx.body = data.data; - } catch (e: any) { - console.error(e) - console.error(e.response.data) - ctx.status =(401); - ctx.body = e.response.data; - } - }); - router.get<{ Params: { id: string } }>('/v1/accounts/:id/followers', async (ctx, next) => { - const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; - const accessTokens = ctx.headers.authorization; - const client = getClient(BASE_URL, accessTokens); - try { - const data = await client.getAccountFollowers(ctx.params.id, ctx.query as any); - ctx.body = data.data; - } catch (e: any) { - console.error(e) - console.error(e.response.data) - ctx.status =(401); - ctx.body = e.response.data; - } - }); - router.get<{ Params: { id: string } }>('/v1/accounts/:id/following', async (ctx, next) => { - const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; - const accessTokens = ctx.headers.authorization; - const client = getClient(BASE_URL, accessTokens); - try { - const data = await client.getAccountFollowing(ctx.params.id, ctx.query as any); - ctx.body = data.data; - } catch (e: any) { - console.error(e) - console.error(e.response.data) - ctx.status =(401); - ctx.body = e.response.data; - } - }); - router.get<{ Params: { id: string } }>('/v1/accounts/:id/lists', async (ctx, next) => { - const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; - const accessTokens = ctx.headers.authorization; - const client = getClient(BASE_URL, accessTokens); - try { - const data = await client.getAccountLists(ctx.params.id); - ctx.body = data.data; - } catch (e: any) { - console.error(e) - console.error(e.response.data) - ctx.status =(401); - ctx.body = e.response.data; - } - }); - router.post<{ Params: { id: string } }>('/v1/accounts/:id/follow', async (ctx, next) => { - const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; - const accessTokens = ctx.headers.authorization; - const client = getClient(BASE_URL, accessTokens); - try { - const data = await client.followAccount(ctx.params.id); - const acct = data.data; - acct.following = true; - ctx.body = data.data; - } catch (e: any) { - console.error(e) - console.error(e.response.data) - ctx.status =(401); - ctx.body = e.response.data; - } - }); - router.post<{ Params: { id: string } }>('/v1/accounts/:id/unfollow', async (ctx, next) => { - const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; - const accessTokens = ctx.headers.authorization; - const client = getClient(BASE_URL, accessTokens); - try { - const data = await client.unfollowAccount(ctx.params.id); - const acct = data.data; - acct.following = false; - ctx.body = data.data; - } catch (e: any) { - console.error(e) - console.error(e.response.data) - ctx.status =(401); - ctx.body = e.response.data; - } - }); - router.post<{ Params: { id: string } }>('/v1/accounts/:id/block', async (ctx, next) => { - const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; - const accessTokens = ctx.headers.authorization; - const client = getClient(BASE_URL, accessTokens); - try { - const data = await client.blockAccount(ctx.params.id); - ctx.body = data.data; - } catch (e: any) { - console.error(e) - console.error(e.response.data) - ctx.status =(401); - ctx.body = e.response.data; - } - }); - router.post<{ Params: { id: string } }>('/v1/accounts/:id/unblock', async (ctx, next) => { - const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; - const accessTokens = ctx.headers.authorization; - const client = getClient(BASE_URL, accessTokens); - try { - const data = await client.unblockAccount(ctx.params.id); - ctx.body = data.data; - } catch (e: any) { - console.error(e) - console.error(e.response.data) - ctx.status =(401); - ctx.body = e.response.data; - } - }); - router.post<{ Params: { id: string } }>('/v1/accounts/:id/mute', async (ctx) => { - const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; - const accessTokens = ctx.headers.authorization; - const client = getClient(BASE_URL, accessTokens); - try { - const data = await client.muteAccount(ctx.params.id, (ctx.request as any).body as any); - ctx.body = data.data; - } catch (e: any) { - console.error(e) - console.error(e.response.data) - ctx.status =(401); - ctx.body = e.response.data; - } - }); - router.post<{ Params: { id: string } }>('/v1/accounts/:id/unmute', async (ctx, next) => { - const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; - const accessTokens = ctx.headers.authorization; - const client = getClient(BASE_URL, accessTokens); - try { - const data = await client.unmuteAccount(ctx.params.id); - ctx.body = data.data; - } catch (e: any) { - console.error(e) - console.error(e.response.data) - ctx.status =(401); - ctx.body = e.response.data; - } - }); - router.get('/v1/accounts/relationships', async (ctx, next) => { - const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; - const accessTokens = ctx.headers.authorization; - const client = getClient(BASE_URL, accessTokens); - try { - const idsRaw = (ctx.query as any)['id[]'] - const ids = typeof idsRaw === 'string' ? [idsRaw] : idsRaw - const data = await client.getRelationships(ids) as any; - ctx.body = data.data; - } catch (e: any) { - console.error(e) - console.error(e.response.data) - ctx.status =(401); - ctx.body = e.response.data; - } - }); - router.get('/v1/bookmarks', async (ctx, next) => { - const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; - const accessTokens = ctx.headers.authorization; - const client = getClient(BASE_URL, accessTokens); - try { - const data = await client.getBookmarks(ctx.query as any) as any; - ctx.body = data.data; - } catch (e: any) { - console.error(e) - console.error(e.response.data) - ctx.status =(401); - ctx.body = e.response.data; - } - }); - router.get('/v1/favourites', async (ctx, next) => { - const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; - const accessTokens = ctx.headers.authorization; - const client = getClient(BASE_URL, accessTokens); - try { - const data = await client.getFavourites(ctx.query as any); - ctx.body = data.data; - } catch (e: any) { - console.error(e) - console.error(e.response.data) - ctx.status =(401); - ctx.body = e.response.data; - } - }); - router.get('/v1/mutes', async (ctx, next) => { - const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; - const accessTokens = ctx.headers.authorization; - const client = getClient(BASE_URL, accessTokens); - try { - const data = await client.getMutes(ctx.query as any); - ctx.body = data.data; - } catch (e: any) { - console.error(e) - console.error(e.response.data) - ctx.status =(401); - ctx.body = e.response.data; - } - }); - router.get('/v1/blocks', async (ctx, next) => { - const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; - const accessTokens = ctx.headers.authorization; - const client = getClient(BASE_URL, accessTokens); - try { - const data = await client.getBlocks(ctx.query as any); - ctx.body = data.data; - } catch (e: any) { - console.error(e) - console.error(e.response.data) - ctx.status =(401); - ctx.body = e.response.data; - } - }); - router.get('/v1/follow_ctxs', async (ctx, next) => { - const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; - const accessTokens = ctx.headers.authorization; - const client = getClient(BASE_URL, accessTokens); - try { - const data = await client.getFollowRequests((ctx.query as any || { limit: 20 }).limit); - ctx.body = data.data; - } catch (e: any) { - console.error(e) - console.error(e.response.data) - ctx.status =(401); - ctx.body = e.response.data; - } - }); - router.post<{ Params: { id: string } }>('/v1/follow_ctxs/:id/authorize', async (ctx, next) => { - const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; - const accessTokens = ctx.headers.authorization; - const client = getClient(BASE_URL, accessTokens); - try { - const data = await client.acceptFollowRequest(ctx.params.id); - ctx.body = data.data; - } catch (e: any) { - console.error(e) - console.error(e.response.data) - ctx.status =(401); - ctx.body = e.response.data; - } - }); - router.post<{ Params: { id: string } }>('/v1/follow_ctxs/:id/reject', async (ctx, next) => { - const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; - const accessTokens = ctx.headers.authorization; - const client = getClient(BASE_URL, accessTokens); - try { - const data = await client.rejectFollowRequest(ctx.params.id); - ctx.body = data.data; - } catch (e: any) { - console.error(e) - console.error(e.response.data) - ctx.status =(401); - ctx.body = e.response.data; - } - }); + let reqIds = []; + for (let i = 0; i < ids.length; i++) { + reqIds.push(convertId(ids[i], IdType.FirefishId)); + } -} + const data = await client.getRelationships(reqIds); + ctx.body = data.data.map((relationship) => + convertRelationship(relationship), + ); + } catch (e: any) { + console.error(e); + let data = e.response.data; + data.users = users; + console.error(data); + ctx.status = 401; + ctx.body = data; + } + }); + router.get<{ Params: { id: string } }>("/v1/accounts/:id", async (ctx) => { + const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; + const accessTokens = ctx.headers.authorization; + const client = getClient(BASE_URL, accessTokens); + try { + const calcId = convertId(ctx.params.id, IdType.FirefishId); + const data = await client.getAccount(calcId); + ctx.body = convertAccount(data.data); + } catch (e: any) { + console.error(e); + console.error(e.response.data); + ctx.status = 401; + ctx.body = e.response.data; + } + }); + router.get<{ Params: { id: string } }>( + "/v1/accounts/:id/statuses", + async (ctx) => { + const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; + const accessTokens = ctx.headers.authorization; + const client = getClient(BASE_URL, accessTokens); + try { + const data = await client.getAccountStatuses( + convertId(ctx.params.id, IdType.FirefishId), + convertTimelinesArgsId(argsToBools(limitToInt(ctx.query as any))), + ); + ctx.body = data.data.map((status) => convertStatus(status)); + } catch (e: any) { + console.error(e); + console.error(e.response.data); + ctx.status = 401; + ctx.body = e.response.data; + } + }, + ); + router.get<{ Params: { id: string } }>( + "/v1/accounts/:id/featured_tags", + async (ctx) => { + const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; + const accessTokens = ctx.headers.authorization; + const client = getClient(BASE_URL, accessTokens); + try { + const data = await client.getAccountFeaturedTags( + convertId(ctx.params.id, IdType.FirefishId), + ); + ctx.body = data.data.map((tag) => convertFeaturedTag(tag)); + } catch (e: any) { + console.error(e); + console.error(e.response.data); + ctx.status = 401; + ctx.body = e.response.data; + } + }, + ); + router.get<{ Params: { id: string } }>( + "/v1/accounts/:id/followers", + async (ctx) => { + const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; + const accessTokens = ctx.headers.authorization; + const client = getClient(BASE_URL, accessTokens); + try { + const data = await client.getAccountFollowers( + convertId(ctx.params.id, IdType.FirefishId), + convertTimelinesArgsId(limitToInt(ctx.query as any)), + ); + ctx.body = data.data.map((account) => convertAccount(account)); + } catch (e: any) { + console.error(e); + console.error(e.response.data); + ctx.status = 401; + ctx.body = e.response.data; + } + }, + ); + router.get<{ Params: { id: string } }>( + "/v1/accounts/:id/following", + async (ctx) => { + const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; + const accessTokens = ctx.headers.authorization; + const client = getClient(BASE_URL, accessTokens); + try { + const data = await client.getAccountFollowing( + convertId(ctx.params.id, IdType.FirefishId), + convertTimelinesArgsId(limitToInt(ctx.query as any)), + ); + ctx.body = data.data.map((account) => convertAccount(account)); + } catch (e: any) { + console.error(e); + console.error(e.response.data); + ctx.status = 401; + ctx.body = e.response.data; + } + }, + ); + router.get<{ Params: { id: string } }>( + "/v1/accounts/:id/lists", + async (ctx) => { + const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; + const accessTokens = ctx.headers.authorization; + const client = getClient(BASE_URL, accessTokens); + try { + const data = await client.getAccountLists( + convertId(ctx.params.id, IdType.FirefishId), + ); + ctx.body = data.data.map((list) => convertList(list)); + } catch (e: any) { + console.error(e); + console.error(e.response.data); + ctx.status = 401; + ctx.body = e.response.data; + } + }, + ); + router.post<{ Params: { id: string } }>( + "/v1/accounts/:id/follow", + async (ctx) => { + const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; + const accessTokens = ctx.headers.authorization; + const client = getClient(BASE_URL, accessTokens); + try { + const data = await client.followAccount( + convertId(ctx.params.id, IdType.FirefishId), + ); + let acct = convertRelationship(data.data); + acct.following = true; + ctx.body = acct; + } catch (e: any) { + console.error(e); + console.error(e.response.data); + ctx.status = 401; + ctx.body = e.response.data; + } + }, + ); + router.post<{ Params: { id: string } }>( + "/v1/accounts/:id/unfollow", + async (ctx) => { + const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; + const accessTokens = ctx.headers.authorization; + const client = getClient(BASE_URL, accessTokens); + try { + const data = await client.unfollowAccount( + convertId(ctx.params.id, IdType.FirefishId), + ); + let acct = convertRelationship(data.data); + acct.following = false; + ctx.body = acct; + } catch (e: any) { + console.error(e); + console.error(e.response.data); + ctx.status = 401; + ctx.body = e.response.data; + } + }, + ); + router.post<{ Params: { id: string } }>( + "/v1/accounts/:id/block", + async (ctx) => { + const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; + const accessTokens = ctx.headers.authorization; + const client = getClient(BASE_URL, accessTokens); + try { + const data = await client.blockAccount( + convertId(ctx.params.id, IdType.FirefishId), + ); + ctx.body = convertRelationship(data.data); + } catch (e: any) { + console.error(e); + console.error(e.response.data); + ctx.status = 401; + ctx.body = e.response.data; + } + }, + ); + router.post<{ Params: { id: string } }>( + "/v1/accounts/:id/unblock", + async (ctx) => { + const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; + const accessTokens = ctx.headers.authorization; + const client = getClient(BASE_URL, accessTokens); + try { + const data = await client.unblockAccount( + convertId(ctx.params.id, IdType.MastodonId), + ); + ctx.body = convertRelationship(data.data); + } catch (e: any) { + console.error(e); + console.error(e.response.data); + ctx.status = 401; + ctx.body = e.response.data; + } + }, + ); + router.post<{ Params: { id: string } }>( + "/v1/accounts/:id/mute", + async (ctx) => { + const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; + const accessTokens = ctx.headers.authorization; + const client = getClient(BASE_URL, accessTokens); + try { + const data = await client.muteAccount( + convertId(ctx.params.id, IdType.FirefishId), + (ctx.request as any).body as any, + ); + ctx.body = convertRelationship(data.data); + } catch (e: any) { + console.error(e); + console.error(e.response.data); + ctx.status = 401; + ctx.body = e.response.data; + } + }, + ); + router.post<{ Params: { id: string } }>( + "/v1/accounts/:id/unmute", + async (ctx) => { + const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; + const accessTokens = ctx.headers.authorization; + const client = getClient(BASE_URL, accessTokens); + try { + const data = await client.unmuteAccount( + convertId(ctx.params.id, IdType.FirefishId), + ); + ctx.body = convertRelationship(data.data); + } catch (e: any) { + console.error(e); + console.error(e.response.data); + ctx.status = 401; + ctx.body = e.response.data; + } + }, + ); + router.get("/v1/featured_tags", async (ctx) => { + const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; + const accessTokens = ctx.headers.authorization; + const client = getClient(BASE_URL, accessTokens); + try { + const data = await client.getFeaturedTags(); + ctx.body = data.data.map((tag) => convertFeaturedTag(tag)); + } catch (e: any) { + console.error(e); + console.error(e.response.data); + ctx.status = 401; + ctx.body = e.response.data; + } + }); + router.get("/v1/followed_tags", async (ctx) => { + const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; + const accessTokens = ctx.headers.authorization; + const client = getClient(BASE_URL, accessTokens); + try { + const data = await client.getFollowedTags(); + ctx.body = data.data; + } catch (e: any) { + console.error(e); + console.error(e.response.data); + ctx.status = 401; + ctx.body = e.response.data; + } + }); + router.get("/v1/bookmarks", async (ctx) => { + const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; + const accessTokens = ctx.headers.authorization; + const client = getClient(BASE_URL, accessTokens); + try { + const data = await client.getBookmarks( + convertTimelinesArgsId(limitToInt(ctx.query as any)), + ); + ctx.body = data.data.map((status) => convertStatus(status)); + } catch (e: any) { + console.error(e); + console.error(e.response.data); + ctx.status = 401; + ctx.body = e.response.data; + } + }); + router.get("/v1/favourites", async (ctx) => { + const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; + const accessTokens = ctx.headers.authorization; + const client = getClient(BASE_URL, accessTokens); + try { + const data = await client.getFavourites( + convertTimelinesArgsId(limitToInt(ctx.query as any)), + ); + ctx.body = data.data.map((status) => convertStatus(status)); + } catch (e: any) { + console.error(e); + console.error(e.response.data); + ctx.status = 401; + ctx.body = e.response.data; + } + }); + router.get("/v1/mutes", async (ctx) => { + const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; + const accessTokens = ctx.headers.authorization; + const client = getClient(BASE_URL, accessTokens); + try { + const data = await client.getMutes( + convertTimelinesArgsId(limitToInt(ctx.query as any)), + ); + ctx.body = data.data.map((account) => convertAccount(account)); + } catch (e: any) { + console.error(e); + console.error(e.response.data); + ctx.status = 401; + ctx.body = e.response.data; + } + }); + router.get("/v1/blocks", async (ctx) => { + const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; + const accessTokens = ctx.headers.authorization; + const client = getClient(BASE_URL, accessTokens); + try { + const data = await client.getBlocks( + convertTimelinesArgsId(limitToInt(ctx.query as any)), + ); + ctx.body = data.data.map((account) => convertAccount(account)); + } catch (e: any) { + console.error(e); + console.error(e.response.data); + ctx.status = 401; + ctx.body = e.response.data; + } + }); + router.get("/v1/follow_requests", async (ctx) => { + const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; + const accessTokens = ctx.headers.authorization; + const client = getClient(BASE_URL, accessTokens); + try { + const data = await client.getFollowRequests( + ((ctx.query as any) || { limit: 20 }).limit, + ); + ctx.body = data.data.map((account) => convertAccount(account)); + } catch (e: any) { + console.error(e); + console.error(e.response.data); + ctx.status = 401; + ctx.body = e.response.data; + } + }); + router.post<{ Params: { id: string } }>( + "/v1/follow_requests/:id/authorize", + async (ctx) => { + const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; + const accessTokens = ctx.headers.authorization; + const client = getClient(BASE_URL, accessTokens); + try { + const data = await client.acceptFollowRequest( + convertId(ctx.params.id, IdType.FirefishId), + ); + ctx.body = convertRelationship(data.data); + } catch (e: any) { + console.error(e); + console.error(e.response.data); + ctx.status = 401; + ctx.body = e.response.data; + } + }, + ); + router.post<{ Params: { id: string } }>( + "/v1/follow_requests/:id/reject", + async (ctx) => { + const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; + const accessTokens = ctx.headers.authorization; + const client = getClient(BASE_URL, accessTokens); + try { + const data = await client.rejectFollowRequest( + convertId(ctx.params.id, IdType.FirefishId), + ); + ctx.body = convertRelationship(data.data); + } catch (e: any) { + console.error(e); + console.error(e.response.data); + ctx.status = 401; + ctx.body = e.response.data; + } + }, + ); +} diff --git a/packages/backend/src/server/api/mastodon/endpoints/auth.ts b/packages/backend/src/server/api/mastodon/endpoints/auth.ts index ff8b8a5188..b55cb6388c 100644 --- a/packages/backend/src/server/api/mastodon/endpoints/auth.ts +++ b/packages/backend/src/server/api/mastodon/endpoints/auth.ts @@ -1,81 +1,81 @@ -import megalodon, { MegalodonInterface } from '@cutls/megalodon'; +import megalodon, { MegalodonInterface } from "megalodon"; import Router from "@koa/router"; -import { koaBody } from 'koa-body'; -import { getClient } from '../ApiMastodonCompatibleService.js'; +import { koaBody } from "koa-body"; +import { getClient } from "../ApiMastodonCompatibleService.js"; +import bodyParser from "koa-bodyparser"; const readScope = [ - 'read:account', - 'read:drive', - 'read:blocks', - 'read:favorites', - 'read:following', - 'read:messaging', - 'read:mutes', - 'read:notifications', - 'read:reactions', - 'read:pages', - 'read:page-likes', - 'read:user-groups', - 'read:channels', - 'read:gallery', - 'read:gallery-likes' -] + "read:account", + "read:drive", + "read:blocks", + "read:favorites", + "read:following", + "read:messaging", + "read:mutes", + "read:notifications", + "read:reactions", + "read:pages", + "read:page-likes", + "read:user-groups", + "read:channels", + "read:gallery", + "read:gallery-likes", +]; const writeScope = [ - 'write:account', - 'write:drive', - 'write:blocks', - 'write:favorites', - 'write:following', - 'write:messaging', - 'write:mutes', - 'write:notes', - 'write:notifications', - 'write:reactions', - 'write:votes', - 'write:pages', - 'write:page-likes', - 'write:user-groups', - 'write:channels', - 'write:gallery', - 'write:gallery-likes' -] + "write:account", + "write:drive", + "write:blocks", + "write:favorites", + "write:following", + "write:messaging", + "write:mutes", + "write:notes", + "write:notifications", + "write:reactions", + "write:votes", + "write:pages", + "write:page-likes", + "write:user-groups", + "write:channels", + "write:gallery", + "write:gallery-likes", +]; export function apiAuthMastodon(router: Router): void { - - router.post('/v1/apps', async (ctx) => { + router.post("/v1/apps", async (ctx) => { const BASE_URL = `${ctx.request.protocol}://${ctx.request.hostname}`; - const accessTokens = ctx.request.headers.authorization; - const client = getClient(BASE_URL, accessTokens); - const body: any = ctx.request.body; + const client = getClient(BASE_URL, ""); + const body: any = ctx.request.body || ctx.request.query; try { - let scope = body.scopes - console.log(body) - if (typeof scope === 'string') scope = scope.split(' ') - const pushScope = new Set() + let scope = body.scopes; + if (typeof scope === "string") scope = scope.split(" "); + const pushScope = new Set(); for (const s of scope) { - if (s.match(/^read/)) for (const r of readScope) pushScope.add(r) - if (s.match(/^write/)) for (const r of writeScope) pushScope.add(r) + if (s.match(/^read/)) for (const r of readScope) pushScope.add(r); + if (s.match(/^write/)) for (const r of writeScope) pushScope.add(r); } - const scopeArr = Array.from(pushScope) + const scopeArr = Array.from(pushScope); - let red = body.redirect_uris - if (red === 'urn:ietf:wg:oauth:2.0:oob') { - red = 'https://thedesk.top/hello.html' - } - const appData = await client.registerApp(body.client_name, { scopes: scopeArr, redirect_uris: red, website: body.website }); - ctx.body = { - id: appData.id, + const red = body.redirect_uris; + const appData = await client.registerApp(body.client_name, { + scopes: scopeArr, + redirect_uris: red, + website: body.website, + }); + const returns = { + id: Math.floor(Math.random() * 100).toString(), name: appData.name, - website: appData.website, + website: body.website, redirect_uri: red, - client_id: Buffer.from(appData.url || '').toString('base64'), + client_id: Buffer.from(appData.url || "").toString("base64"), client_secret: appData.clientSecret, - } + }; + console.log(returns); + ctx.body = returns; } catch (e: any) { - console.error(e) + console.error(e); ctx.status = 401; ctx.body = e.response.data; } }); - } diff --git a/packages/backend/src/server/api/mastodon/endpoints/filter.ts b/packages/backend/src/server/api/mastodon/endpoints/filter.ts index 810b8be110..e27b7e22ae 100644 --- a/packages/backend/src/server/api/mastodon/endpoints/filter.ts +++ b/packages/backend/src/server/api/mastodon/endpoints/filter.ts @@ -1,83 +1,90 @@ -import megalodon, { MegalodonInterface } from '@cutls/megalodon'; +import megalodon, { MegalodonInterface } from "megalodon"; import Router from "@koa/router"; -import { koaBody } from 'koa-body'; -import { getClient } from '../ApiMastodonCompatibleService.js'; +import { getClient } from "../ApiMastodonCompatibleService.js"; +import { IdType, convertId } from "../../index.js"; +import { convertFilter } from "../converters.js"; export function apiFilterMastodon(router: Router): void { - - router.get('/v1/filters', async (ctx) => { + router.get("/v1/filters", async (ctx) => { const BASE_URL = `${ctx.request.protocol}://${ctx.request.hostname}`; const accessTokens = ctx.request.headers.authorization; const client = getClient(BASE_URL, accessTokens); const body: any = ctx.request.body; try { const data = await client.getFilters(); - ctx.body = data.data; + ctx.body = data.data.map((filter) => convertFilter(filter)); } catch (e: any) { - console.error(e) + console.error(e); ctx.status = 401; ctx.body = e.response.data; } }); - router.get('/v1/filters/:id', async (ctx) => { + router.get("/v1/filters/:id", async (ctx) => { const BASE_URL = `${ctx.request.protocol}://${ctx.request.hostname}`; const accessTokens = ctx.request.headers.authorization; const client = getClient(BASE_URL, accessTokens); const body: any = ctx.request.body; try { - const data = await client.getFilter(ctx.params.id); - ctx.body = data.data; + const data = await client.getFilter( + convertId(ctx.params.id, IdType.FirefishId), + ); + ctx.body = convertFilter(data.data); } catch (e: any) { - console.error(e) + console.error(e); ctx.status = 401; ctx.body = e.response.data; } }); - router.post('/v1/filters', async (ctx) => { + router.post("/v1/filters", async (ctx) => { const BASE_URL = `${ctx.request.protocol}://${ctx.request.hostname}`; const accessTokens = ctx.request.headers.authorization; const client = getClient(BASE_URL, accessTokens); const body: any = ctx.request.body; try { const data = await client.createFilter(body.phrase, body.context, body); - ctx.body = data.data; + ctx.body = convertFilter(data.data); } catch (e: any) { - console.error(e) + console.error(e); ctx.status = 401; ctx.body = e.response.data; } }); - router.post('/v1/filters/:id', async (ctx) => { + router.post("/v1/filters/:id", async (ctx) => { const BASE_URL = `${ctx.request.protocol}://${ctx.request.hostname}`; const accessTokens = ctx.request.headers.authorization; const client = getClient(BASE_URL, accessTokens); const body: any = ctx.request.body; try { - const data = await client.updateFilter(ctx.params.id, body.phrase, body.context); - ctx.body = data.data; + const data = await client.updateFilter( + convertId(ctx.params.id, IdType.FirefishId), + body.phrase, + body.context, + ); + ctx.body = convertFilter(data.data); } catch (e: any) { - console.error(e) + console.error(e); ctx.status = 401; ctx.body = e.response.data; } }); - router.delete('/v1/filters/:id', async (ctx) => { + router.delete("/v1/filters/:id", async (ctx) => { const BASE_URL = `${ctx.request.protocol}://${ctx.request.hostname}`; const accessTokens = ctx.request.headers.authorization; const client = getClient(BASE_URL, accessTokens); const body: any = ctx.request.body; try { - const data = await client.deleteFilter(ctx.params.id); + const data = await client.deleteFilter( + convertId(ctx.params.id, IdType.FirefishId), + ); ctx.body = data.data; } catch (e: any) { - console.error(e) + console.error(e); ctx.status = 401; ctx.body = e.response.data; } }); - } diff --git a/packages/backend/src/server/api/mastodon/endpoints/meta.ts b/packages/backend/src/server/api/mastodon/endpoints/meta.ts index 3496272b9e..9a81aad844 100644 --- a/packages/backend/src/server/api/mastodon/endpoints/meta.ts +++ b/packages/backend/src/server/api/mastodon/endpoints/meta.ts @@ -1,18 +1,33 @@ -import { Entity } from "@cutls/megalodon"; -// TODO: add calckey features -export function getInstance(response: Entity.Instance) { +import { Entity } from "megalodon"; +import config from "@/config/index.js"; +import { fetchMeta } from "@/misc/fetch-meta.js"; +import { Users, Notes } from "@/models/index.js"; +import { IsNull, MoreThan } from "typeorm"; + +// TODO: add firefish features +export async function getInstance(response: Entity.Instance) { + const meta = await fetchMeta(true); + const totalUsers = Users.count({ where: { host: IsNull() } }); + const totalStatuses = Notes.count({ where: { userHost: IsNull() } }); return { uri: response.uri, - title: response.title || "", - short_description: response.description || "", - description: response.description || "", + title: response.title || "Firefish", + short_description: + response.description?.substring(0, 50) || "See real server website", + description: + response.description || + "This is a vanilla Firefish Instance. It doesn't seem to have a description.", email: response.email || "", - version: "3.0.0 compatible (Calckey)", + version: `3.0.0 (compatible; Firefish ${config.version})`, urls: response.urls, - stats: response.stats, - thumbnail: response.thumbnail || "", - languages: ["en", "de", "ja"], - registrations: response.registrations, + stats: { + user_count: await totalUsers, + status_count: await totalStatuses, + domain_count: response.stats.domain_count, + }, + thumbnail: response.thumbnail || "/static-assets/transparent.png", + languages: meta.langs, + registrations: !meta.disableRegistration || response.registrations, approval_required: !response.registrations, invites_enabled: response.registrations, configuration: { @@ -77,17 +92,17 @@ export function getInstance(response: Entity.Instance) { bot: true, discoverable: false, group: false, - created_at: "1971-01-01T00:00:00.000Z", - note: "", - url: "https://http.cat/404", - avatar: "https://http.cat/404", - avatar_static: "https://http.cat/404", - header: "https://http.cat/404", - header_static: "https://http.cat/404", + created_at: new Date().toISOString(), + note: "

Please refer to the original instance for the actual admin contact.

", + url: `${response.uri}/`, + avatar: `${response.uri}/static-assets/badges/info.png`, + avatar_static: `${response.uri}/static-assets/badges/info.png`, + header: "/static-assets/transparent.png", + header_static: "/static-assets/transparent.png", followers_count: -1, following_count: 0, statuses_count: 0, - last_status_at: "1971-01-01T00:00:00.000Z", + last_status_at: new Date().toISOString(), noindex: true, emojis: [], fields: [], diff --git a/packages/backend/src/server/api/mastodon/endpoints/notifications.ts b/packages/backend/src/server/api/mastodon/endpoints/notifications.ts index 625ff386c1..f0a0bab984 100644 --- a/packages/backend/src/server/api/mastodon/endpoints/notifications.ts +++ b/packages/backend/src/server/api/mastodon/endpoints/notifications.ts @@ -1,62 +1,69 @@ -import megalodon, { MegalodonInterface } from '@cutls/megalodon'; +import megalodon, { MegalodonInterface } from "megalodon"; import Router from "@koa/router"; -import { koaBody } from 'koa-body'; -import { getClient } from '../ApiMastodonCompatibleService.js'; -import { toTextWithReaction } from './timeline.js'; +import { koaBody } from "koa-body"; +import { convertId, IdType } from "../../index.js"; +import { getClient } from "../ApiMastodonCompatibleService.js"; +import { convertTimelinesArgsId } from "./timeline.js"; +import { convertNotification } from "../converters.js"; function toLimitToInt(q: any) { - if (q.limit) if (typeof q.limit === 'string') q.limit = parseInt(q.limit, 10) - return q + if (q.limit) if (typeof q.limit === "string") q.limit = parseInt(q.limit, 10); + return q; } export function apiNotificationsMastodon(router: Router): void { - - router.get('/v1/notifications', async (ctx) => { + router.get("/v1/notifications", async (ctx) => { const BASE_URL = `${ctx.request.protocol}://${ctx.request.hostname}`; const accessTokens = ctx.request.headers.authorization; const client = getClient(BASE_URL, accessTokens); const body: any = ctx.request.body; try { - const data = await client.getNotifications(toLimitToInt(ctx.query)); + const data = await client.getNotifications( + convertTimelinesArgsId(toLimitToInt(ctx.query)), + ); const notfs = data.data; const ret = notfs.map((n) => { - if(n.type !== 'follow' && n.type !== 'follow_request') { - if (n.type === 'reaction') n.type = 'favourite' - n.status = toTextWithReaction(n.status ? [n.status] : [], ctx.hostname)[0] - return n - } else { - return n - } - }) + n = convertNotification(n); + if (n.type !== "follow" && n.type !== "follow_request") { + if (n.type === "reaction") n.type = "favourite"; + return n; + } else { + return n; + } + }); ctx.body = ret; } catch (e: any) { - console.error(e) + console.error(e); ctx.status = 401; ctx.body = e.response.data; } }); - router.get('/v1/notification/:id', async (ctx) => { + router.get("/v1/notification/:id", async (ctx) => { const BASE_URL = `${ctx.request.protocol}://${ctx.request.hostname}`; const accessTokens = ctx.request.headers.authorization; const client = getClient(BASE_URL, accessTokens); const body: any = ctx.request.body; try { - const dataRaw = await client.getNotification(ctx.params.id); - const data = dataRaw.data; - if(data.type !== 'follow' && data.type !== 'follow_request') { - if (data.type === 'reaction') data.type = 'favourite' - ctx.body = toTextWithReaction([data as any], ctx.request.hostname)[0] - } else { - ctx.body = data + const dataRaw = await client.getNotification( + convertId(ctx.params.id, IdType.FirefishId), + ); + const data = convertNotification(dataRaw.data); + ctx.body = data; + if ( + data.type !== "follow" && + data.type !== "follow_request" && + data.type === "reaction" + ) { + data.type = "favourite"; } } catch (e: any) { - console.error(e) + console.error(e); ctx.status = 401; ctx.body = e.response.data; } }); - router.post('/v1/notifications/clear', async (ctx) => { + router.post("/v1/notifications/clear", async (ctx) => { const BASE_URL = `${ctx.request.protocol}://${ctx.request.hostname}`; const accessTokens = ctx.request.headers.authorization; const client = getClient(BASE_URL, accessTokens); @@ -65,25 +72,26 @@ export function apiNotificationsMastodon(router: Router): void { const data = await client.dismissNotifications(); ctx.body = data.data; } catch (e: any) { - console.error(e) + console.error(e); ctx.status = 401; ctx.body = e.response.data; } }); - router.post('/v1/notification/:id/dismiss', async (ctx) => { + router.post("/v1/notification/:id/dismiss", async (ctx) => { const BASE_URL = `${ctx.request.protocol}://${ctx.request.hostname}`; const accessTokens = ctx.request.headers.authorization; const client = getClient(BASE_URL, accessTokens); const body: any = ctx.request.body; try { - const data = await client.dismissNotification(ctx.params.id); + const data = await client.dismissNotification( + convertId(ctx.params.id, IdType.FirefishId), + ); ctx.body = data.data; } catch (e: any) { - console.error(e) + console.error(e); ctx.status = 401; ctx.body = e.response.data; } }); - } diff --git a/packages/backend/src/server/api/mastodon/endpoints/search.ts b/packages/backend/src/server/api/mastodon/endpoints/search.ts index dce3ff57c8..8a48175579 100644 --- a/packages/backend/src/server/api/mastodon/endpoints/search.ts +++ b/packages/backend/src/server/api/mastodon/endpoints/search.ts @@ -1,25 +1,147 @@ -import megalodon, { MegalodonInterface } from '@cutls/megalodon'; +import megalodon, { MegalodonInterface } from "megalodon"; import Router from "@koa/router"; -import { koaBody } from 'koa-body'; -import { getClient } from '../ApiMastodonCompatibleService.js'; +import { getClient } from "../ApiMastodonCompatibleService.js"; +import axios from "axios"; +import { Converter } from "megalodon"; +import { convertTimelinesArgsId, limitToInt } from "./timeline.js"; +import { convertAccount, convertStatus } from "../converters.js"; export function apiSearchMastodon(router: Router): void { - - router.get('/v1/search', async (ctx) => { + router.get("/v1/search", async (ctx) => { const BASE_URL = `${ctx.request.protocol}://${ctx.request.hostname}`; const accessTokens = ctx.request.headers.authorization; const client = getClient(BASE_URL, accessTokens); const body: any = ctx.request.body; try { - const query: any = ctx.query - const type = query.type || '' + const query: any = convertTimelinesArgsId(limitToInt(ctx.query)); + const type = query.type || ""; const data = await client.search(query.q, type, query); ctx.body = data.data; } catch (e: any) { - console.error(e) + console.error(e); ctx.status = 401; ctx.body = e.response.data; } }); + router.get("/v2/search", async (ctx) => { + const BASE_URL = `${ctx.request.protocol}://${ctx.request.hostname}`; + const accessTokens = ctx.headers.authorization; + const client = getClient(BASE_URL, accessTokens); + try { + const query: any = convertTimelinesArgsId(limitToInt(ctx.query)); + const type = query.type; + const acct = + !type || type === "accounts" + ? await client.search(query.q, "accounts", query) + : null; + const stat = + !type || type === "statuses" + ? await client.search(query.q, "statuses", query) + : null; + const tags = + !type || type === "hashtags" + ? await client.search(query.q, "hashtags", query) + : null; + ctx.body = { + accounts: + acct?.data?.accounts.map((account) => convertAccount(account)) ?? [], + statuses: + stat?.data?.statuses.map((status) => convertStatus(status)) ?? [], + hashtags: tags?.data?.hashtags ?? [], + }; + } catch (e: any) { + console.error(e); + ctx.status = 401; + ctx.body = e.response.data; + } + }); + router.get("/v1/trends/statuses", async (ctx) => { + const BASE_URL = `${ctx.request.protocol}://${ctx.request.hostname}`; + const accessTokens = ctx.headers.authorization; + try { + const data = await getHighlight( + BASE_URL, + ctx.request.hostname, + accessTokens, + ); + ctx.body = data.map((status) => convertStatus(status)); + } catch (e: any) { + console.error(e); + ctx.status = 401; + ctx.body = e.response.data; + } + }); + router.get("/v2/suggestions", async (ctx) => { + const BASE_URL = `${ctx.request.protocol}://${ctx.request.hostname}`; + const accessTokens = ctx.headers.authorization; + try { + const query: any = ctx.query; + let data = await getFeaturedUser( + BASE_URL, + ctx.request.hostname, + accessTokens, + query.limit || 20, + ); + data = data.map((suggestion) => { + suggestion.account = convertAccount(suggestion.account); + return suggestion; + }); + console.log(data); + ctx.body = data; + } catch (e: any) { + console.error(e); + ctx.status = 401; + ctx.body = e.response.data; + } + }); +} +async function getHighlight( + BASE_URL: string, + domain: string, + accessTokens: string | undefined, +) { + const accessTokenArr = accessTokens?.split(" ") ?? [null]; + const accessToken = accessTokenArr[accessTokenArr.length - 1]; + try { + const api = await axios.post(`${BASE_URL}/api/notes/featured`, { + i: accessToken, + }); + const data: MisskeyEntity.Note[] = api.data; + return data.map((note) => new Converter(BASE_URL).note(note, domain)); + } catch (e: any) { + console.log(e); + console.log(e.response.data); + return []; + } +} +async function getFeaturedUser( + BASE_URL: string, + host: string, + accessTokens: string | undefined, + limit: number, +) { + const accessTokenArr = accessTokens?.split(" ") ?? [null]; + const accessToken = accessTokenArr[accessTokenArr.length - 1]; + try { + const api = await axios.post(`${BASE_URL}/api/users`, { + i: accessToken, + limit, + origin: "local", + sort: "+follower", + state: "alive", + }); + const data: MisskeyEntity.UserDetail[] = api.data; + console.log(data); + return data.map((u) => { + return { + source: "past_interactions", + account: new Converter(BASE_URL).userDetail(u, host), + }; + }); + } catch (e: any) { + console.log(e); + console.log(e.response.data); + return []; + } } diff --git a/packages/backend/src/server/api/mastodon/endpoints/status.ts b/packages/backend/src/server/api/mastodon/endpoints/status.ts index 8dc4ba5f7d..2fcd28c9a9 100644 --- a/packages/backend/src/server/api/mastodon/endpoints/status.ts +++ b/packages/backend/src/server/api/mastodon/endpoints/status.ts @@ -1,403 +1,457 @@ import Router from "@koa/router"; -import { koaBody } from 'koa-body'; -import megalodon, { MegalodonInterface } from '@cutls/megalodon'; -import { getClient } from '../ApiMastodonCompatibleService.js'; -import fs from 'fs' -import { pipeline } from 'node:stream'; -import { promisify } from 'node:util'; -import { createTemp } from '@/misc/create-temp.js'; -import { emojiRegex, emojiRegexAtStartToEnd } from '@/misc/emoji-regex.js'; -import axios from 'axios'; -const pump = promisify(pipeline); +import { getClient } from "../ApiMastodonCompatibleService.js"; +import { emojiRegexAtStartToEnd } from "@/misc/emoji-regex.js"; +import axios from "axios"; +import querystring from "node:querystring"; +import qs from "qs"; +import { convertTimelinesArgsId, limitToInt } from "./timeline.js"; +import { convertId, IdType } from "../../index.js"; +import { + convertAccount, + convertAttachment, + convertPoll, + convertStatus, +} from "../converters.js"; + +function normalizeQuery(data: any) { + const str = querystring.stringify(data); + return qs.parse(str); +} export function apiStatusMastodon(router: Router): void { - router.post('/v1/statuses', async (ctx, reply) => { - const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; - const accessTokens = ctx.headers.authorization; - const client = getClient(BASE_URL, accessTokens); - try { - const body: any = ctx.request.body - const text = body.status - const removed = text.replace(/@\S+/g, '').replaceAll(' ', '') - const isDefaultEmoji = emojiRegexAtStartToEnd.test(removed) - const isCustomEmoji = /^:[a-zA-Z0-9@_]+:$/.test(removed) - if (body.in_reply_to_id && isDefaultEmoji || isCustomEmoji) { - const a = await client.createEmojiReaction(body.in_reply_to_id, removed) - ctx.body = a.data - } - if (body.in_reply_to_id && removed === '/unreact') { - try { - const id = body.in_reply_to_id - const post = await client.getStatus(id) - const react = post.data.emoji_reactions.filter((e) => e.me)[0].name - const data = await client.deleteEmojiReaction(id, react); - ctx.body = data.data; - } catch (e: any) { - console.error(e) - ctx.status = (401); - ctx.body = e.response.data; - } - } - if (!body.media_ids) body.media_ids = undefined - if (body.media_ids && !body.media_ids.length) body.media_ids = undefined - const data = await client.postStatus(text, body); - ctx.body = data.data; - } catch (e: any) { - console.error(e) - ctx.status = (401); - ctx.body = e.response.data; - } - }); - router.get<{ Params: { id: string } }>('/v1/statuses/:id', async (ctx, reply) => { - const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; - const accessTokens = ctx.headers.authorization; - const client = getClient(BASE_URL, accessTokens); - try { - const data = await client.getStatus(ctx.params.id); - ctx.body = data.data; - } catch (e: any) { - console.error(e) - ctx.status = (401); - ctx.body = e.response.data; - } - }); - router.delete<{ Params: { id: string } }>('/v1/statuses/:id', async (ctx, reply) => { - const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; - const accessTokens = ctx.headers.authorization; - const client = getClient(BASE_URL, accessTokens); - try { - const data = await client.deleteStatus(ctx.params.id); - ctx.body = data.data; - } catch (e: any) { - console.error(e) - ctx.status = (401); - ctx.body = e.response.data; - } - }); - interface IReaction { - id: string - createdAt: string - user: MisskeyEntity.User, - type: string - } - router.get<{ Params: { id: string } }>('/v1/statuses/:id/context', async (ctx, reply) => { - const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; - const accessTokens = ctx.headers.authorization; - const client = getClient(BASE_URL, accessTokens); - try { - const id = ctx.params.id - const data = await client.getStatusContext(id, ctx.query as any); - const status = await client.getStatus(id); - const reactionsAxios = await axios.get(`${BASE_URL}/api/notes/reactions?noteId=${id}`) - const reactions: IReaction[] = reactionsAxios.data - const text = reactions.map((r) => `${r.type.replace('@.', '')} ${r.user.username}`).join('
') - data.data.descendants.unshift(statusModel(status.data.id, status.data.account.id, status.data.emojis, text)) - ctx.body = data.data; - } catch (e: any) { - console.error(e) - ctx.status = (401); - ctx.body = e.response.data; - } - }); - router.get<{ Params: { id: string } }>('/v1/statuses/:id/reblogged_by', async (ctx, reply) => { - const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; - const accessTokens = ctx.headers.authorization; - const client = getClient(BASE_URL, accessTokens); - try { - const data = await client.getStatusRebloggedBy(ctx.params.id); - ctx.body = data.data; - } catch (e: any) { - console.error(e) - ctx.status = (401); - ctx.body = e.response.data; - } - }); - router.get<{ Params: { id: string } }>('/v1/statuses/:id/favourited_by', async (ctx, reply) => { - ctx.body = [] - }); - router.post<{ Params: { id: string } }>('/v1/statuses/:id/favourite', async (ctx, reply) => { - const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; - const accessTokens = ctx.headers.authorization; - const client = getClient(BASE_URL, accessTokens); - const react = await getFirstReaction(BASE_URL, accessTokens); - try { - const a = await client.createEmojiReaction(ctx.params.id, react) as any; - //const data = await client.favouriteStatus(ctx.params.id) as any; - ctx.body = a.data; - } catch (e: any) { - console.error(e) - console.error(e.response.data) - ctx.status = (401); - ctx.body = e.response.data; - } - }); - router.post<{ Params: { id: string } }>('/v1/statuses/:id/unfavourite', async (ctx, reply) => { - const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; - const accessTokens = ctx.headers.authorization; - const client = getClient(BASE_URL, accessTokens); - const react = await getFirstReaction(BASE_URL, accessTokens); - try { - const data = await client.deleteEmojiReaction(ctx.params.id, react); - ctx.body = data.data; - } catch (e: any) { - console.error(e) - ctx.status = (401); - ctx.body = e.response.data; - } - }); + router.post("/v1/statuses", async (ctx) => { + const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; + const accessTokens = ctx.headers.authorization; + const client = getClient(BASE_URL, accessTokens); + try { + let body: any = ctx.request.body; + if (body.in_reply_to_id) + body.in_reply_to_id = convertId(body.in_reply_to_id, IdType.FirefishId); + if ( + (!body.poll && body["poll[options][]"]) || + (!body.media_ids && body["media_ids[]"]) + ) { + body = normalizeQuery(body); + } + const text = body.status; + const removed = text.replace(/@\S+/g, "").replace(/\s|​/g, ""); + const isDefaultEmoji = emojiRegexAtStartToEnd.test(removed); + const isCustomEmoji = /^:[a-zA-Z0-9@_]+:$/.test(removed); + if ((body.in_reply_to_id && isDefaultEmoji) || isCustomEmoji) { + const a = await client.createEmojiReaction( + body.in_reply_to_id, + removed, + ); + ctx.body = a.data; + } + if (body.in_reply_to_id && removed === "/unreact") { + try { + const id = body.in_reply_to_id; + const post = await client.getStatus(id); + const react = post.data.emoji_reactions.filter((e) => e.me)[0].name; + const data = await client.deleteEmojiReaction(id, react); + ctx.body = data.data; + } catch (e: any) { + console.error(e); + ctx.status = 401; + ctx.body = e.response.data; + } + } + if (!body.media_ids) body.media_ids = undefined; + if (body.media_ids && !body.media_ids.length) body.media_ids = undefined; + if (body.media_ids) { + body.media_ids = (body.media_ids as string[]).map((p) => + convertId(p, IdType.FirefishId), + ); + } + const { sensitive } = body; + body.sensitive = + typeof sensitive === "string" ? sensitive === "true" : sensitive; - router.post<{ Params: { id: string } }>('/v1/statuses/:id/reblog', async (ctx, reply) => { - const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; - const accessTokens = ctx.headers.authorization; - const client = getClient(BASE_URL, accessTokens); - try { - const data = await client.reblogStatus(ctx.params.id); - ctx.body = data.data; - } catch (e: any) { - console.error(e) - ctx.status = (401); - ctx.body = e.response.data; - } - }); + if (body.poll) { + if ( + body.poll.expires_in != null && + typeof body.poll.expires_in === "string" + ) + body.poll.expires_in = parseInt(body.poll.expires_in); + if ( + body.poll.multiple != null && + typeof body.poll.multiple === "string" + ) + body.poll.multiple = body.poll.multiple == "true"; + if ( + body.poll.hide_totals != null && + typeof body.poll.hide_totals === "string" + ) + body.poll.hide_totals = body.poll.hide_totals == "true"; + } - router.post<{ Params: { id: string } }>('/v1/statuses/:id/unreblog', async (ctx, reply) => { - const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; - const accessTokens = ctx.headers.authorization; - const client = getClient(BASE_URL, accessTokens); - try { - const data = await client.unreblogStatus(ctx.params.id); - ctx.body = data.data; - } catch (e: any) { - console.error(e) - ctx.status = (401); - ctx.body = e.response.data; - } - }); + const data = await client.postStatus(text, body); + ctx.body = convertStatus(data.data); + } catch (e: any) { + console.error(e); + ctx.status = 401; + ctx.body = e.response.data; + } + }); + router.get<{ Params: { id: string } }>("/v1/statuses/:id", async (ctx) => { + const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; + const accessTokens = ctx.headers.authorization; + const client = getClient(BASE_URL, accessTokens); + try { + const data = await client.getStatus( + convertId(ctx.params.id, IdType.FirefishId), + ); + ctx.body = convertStatus(data.data); + } catch (e: any) { + console.error(e); + ctx.status = ctx.status == 404 ? 404 : 401; + ctx.body = e.response.data; + } + }); + router.delete<{ Params: { id: string } }>("/v1/statuses/:id", async (ctx) => { + const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; + const accessTokens = ctx.headers.authorization; + const client = getClient(BASE_URL, accessTokens); + try { + const data = await client.deleteStatus( + convertId(ctx.params.id, IdType.FirefishId), + ); + ctx.body = data.data; + } catch (e: any) { + console.error(e.response.data, request.params.id); + ctx.status = 401; + ctx.body = e.response.data; + } + }); + interface IReaction { + id: string; + createdAt: string; + user: MisskeyEntity.User; + type: string; + } + router.get<{ Params: { id: string } }>( + "/v1/statuses/:id/context", + async (ctx) => { + const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; + const accessTokens = ctx.headers.authorization; + const client = getClient(BASE_URL, accessTokens); + try { + const id = convertId(ctx.params.id, IdType.FirefishId); + const data = await client.getStatusContext( + id, + convertTimelinesArgsId(limitToInt(ctx.query as any)), + ); - router.post<{ Params: { id: string } }>('/v1/statuses/:id/bookmark', async (ctx, reply) => { - const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; - const accessTokens = ctx.headers.authorization; - const client = getClient(BASE_URL, accessTokens); - try { - const data = await client.bookmarkStatus(ctx.params.id); - ctx.body = data.data; - } catch (e: any) { - console.error(e) - ctx.status = (401); - ctx.body = e.response.data; - } - }); + data.data.ancestors = data.data.ancestors.map((status) => + convertStatus(status), + ); + data.data.descendants = data.data.descendants.map((status) => + convertStatus(status), + ); + ctx.body = data.data; + } catch (e: any) { + console.error(e); + ctx.status = 401; + ctx.body = e.response.data; + } + }, + ); + router.get<{ Params: { id: string } }>( + "/v1/statuses/:id/history", + async (ctx) => { + const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; + const accessTokens = ctx.headers.authorization; + const client = getClient(BASE_URL, accessTokens); + try { + const data = await client.getStatusHistory( + convertId(ctx.params.id, IdType.FirefishId), + ); + ctx.body = data.data.map((account) => convertAccount(account)); + } catch (e: any) { + console.error(e); + ctx.status = 401; + ctx.body = e.response.data; + } + }, + ); + router.get<{ Params: { id: string } }>( + "/v1/statuses/:id/reblogged_by", + async (ctx) => { + const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; + const accessTokens = ctx.headers.authorization; + const client = getClient(BASE_URL, accessTokens); + try { + const data = await client.getStatusRebloggedBy( + convertId(ctx.params.id, IdType.FirefishId), + ); + ctx.body = data.data.map((account) => convertAccount(account)); + } catch (e: any) { + console.error(e); + ctx.status = 401; + ctx.body = e.response.data; + } + }, + ); + router.get<{ Params: { id: string } }>( + "/v1/statuses/:id/favourited_by", + async (ctx) => { + const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; + const accessTokens = ctx.headers.authorization; + const client = getClient(BASE_URL, accessTokens); + try { + const data = await client.getStatusFavouritedBy( + convertId(ctx.params.id, IdType.FirefishId), + ); + ctx.body = data.data.map((account) => convertAccount(account)); + } catch (e: any) { + console.error(e); + ctx.status = 401; + ctx.body = e.response.data; + } + }, + ); + router.post<{ Params: { id: string } }>( + "/v1/statuses/:id/favourite", + async (ctx) => { + const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; + const accessTokens = ctx.headers.authorization; + const client = getClient(BASE_URL, accessTokens); + const react = await getFirstReaction(BASE_URL, accessTokens); + try { + const a = (await client.createEmojiReaction( + convertId(ctx.params.id, IdType.FirefishId), + react, + )) as any; + //const data = await client.favouriteStatus(ctx.params.id) as any; + ctx.body = convertStatus(a.data); + } catch (e: any) { + console.error(e); + console.error(e.response.data); + ctx.status = 401; + ctx.body = e.response.data; + } + }, + ); + router.post<{ Params: { id: string } }>( + "/v1/statuses/:id/unfavourite", + async (ctx) => { + const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; + const accessTokens = ctx.headers.authorization; + const client = getClient(BASE_URL, accessTokens); + const react = await getFirstReaction(BASE_URL, accessTokens); + try { + const data = await client.deleteEmojiReaction( + convertId(ctx.params.id, IdType.FirefishId), + react, + ); + ctx.body = convertStatus(data.data); + } catch (e: any) { + console.error(e); + ctx.status = 401; + ctx.body = e.response.data; + } + }, + ); - router.post<{ Params: { id: string } }>('/v1/statuses/:id/unbookmark', async (ctx, reply) => { - const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; - const accessTokens = ctx.headers.authorization; - const client = getClient(BASE_URL, accessTokens); - try { - const data = await client.unbookmarkStatus(ctx.params.id) as any; - ctx.body = data.data; - } catch (e: any) { - console.error(e) - ctx.status = (401); - ctx.body = e.response.data; - } - }); + router.post<{ Params: { id: string } }>( + "/v1/statuses/:id/reblog", + async (ctx) => { + const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; + const accessTokens = ctx.headers.authorization; + const client = getClient(BASE_URL, accessTokens); + try { + const data = await client.reblogStatus( + convertId(ctx.params.id, IdType.FirefishId), + ); + ctx.body = convertStatus(data.data); + } catch (e: any) { + console.error(e); + ctx.status = 401; + ctx.body = e.response.data; + } + }, + ); - router.post<{ Params: { id: string } }>('/v1/statuses/:id/pin', async (ctx, reply) => { - const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; - const accessTokens = ctx.headers.authorization; - const client = getClient(BASE_URL, accessTokens); - try { - const data = await client.pinStatus(ctx.params.id); - ctx.body = data.data; - } catch (e: any) { - console.error(e) - ctx.status = (401); - ctx.body = e.response.data; - } - }); + router.post<{ Params: { id: string } }>( + "/v1/statuses/:id/unreblog", + async (ctx) => { + const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; + const accessTokens = ctx.headers.authorization; + const client = getClient(BASE_URL, accessTokens); + try { + const data = await client.unreblogStatus( + convertId(ctx.params.id, IdType.FirefishId), + ); + ctx.body = convertStatus(data.data); + } catch (e: any) { + console.error(e); + ctx.status = 401; + ctx.body = e.response.data; + } + }, + ); - router.post<{ Params: { id: string } }>('/v1/statuses/:id/unpin', async (ctx, reply) => { - const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; - const accessTokens = ctx.headers.authorization; - const client = getClient(BASE_URL, accessTokens); - try { - const data = await client.unpinStatus(ctx.params.id); - ctx.body = data.data; - } catch (e: any) { - console.error(e) - ctx.status = (401); - ctx.body = e.response.data; - } - }); - router.post('/v1/media', async (ctx, reply) => { - const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; - const accessTokens = ctx.headers.authorization; - const client = getClient(BASE_URL, accessTokens); - try { - const multipartData = await ctx.file; - if (!multipartData) { - ctx.body = { error: 'No image' }; - return; - } - const [path] = await createTemp(); - await pump(multipartData.buffer, fs.createWriteStream(path)); - const image = fs.readFileSync(path); - const data = await client.uploadMedia(image); - ctx.body = data.data; - } catch (e: any) { - console.error(e) - ctx.status = (401); - ctx.body = e.response.data; - } - }); - router.post('/v2/media', async (ctx, reply) => { - const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; - const accessTokens = ctx.headers.authorization; - const client = getClient(BASE_URL, accessTokens); - try { - const multipartData = await ctx.file; - if (!multipartData) { - ctx.body = { error: 'No image' }; - return; - } - const [path] = await createTemp(); - await pump(multipartData.buffer, fs.createWriteStream(path)); - const image = fs.readFileSync(path); - const data = await client.uploadMedia(image); - ctx.body = data.data; - } catch (e: any) { - console.error(e) - ctx.status = (401); - ctx.body = e.response.data; - } - }); - router.get<{ Params: { id: string } }>('/v1/media/:id', async (ctx, reply) => { - const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; - const accessTokens = ctx.headers.authorization; - const client = getClient(BASE_URL, accessTokens); - try { - const data = await client.getMedia(ctx.params.id); - ctx.body = data.data; - } catch (e: any) { - console.error(e) - ctx.status = (401); - ctx.body = e.response.data; - } - }); - router.put<{ Params: { id: string } }>('/v1/media/:id', async (ctx, reply) => { - const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; - const accessTokens = ctx.headers.authorization; - const client = getClient(BASE_URL, accessTokens); - try { - const data = await client.updateMedia(ctx.params.id, ctx.request.body as any); - ctx.body = data.data; - } catch (e: any) { - console.error(e) - ctx.status = (401); - ctx.body = e.response.data; - } - }); - router.get<{ Params: { id: string } }>('/v1/polls/:id', async (ctx, reply) => { - const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; - const accessTokens = ctx.headers.authorization; - const client = getClient(BASE_URL, accessTokens); - try { - const data = await client.getPoll(ctx.params.id); - ctx.body = data.data; - } catch (e: any) { - console.error(e) - ctx.status = (401); - ctx.body = e.response.data; - } - }); - router.post<{ Params: { id: string } }>('/v1/polls/:id/votes', async (ctx, reply) => { - const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; - const accessTokens = ctx.headers.authorization; - const client = getClient(BASE_URL, accessTokens); - try { - const data = await client.votePoll(ctx.params.id, (ctx.request.body as any).choices); - ctx.body = data.data; - } catch (e: any) { - console.error(e) - ctx.status = (401); - ctx.body = e.response.data; - } - }); + router.post<{ Params: { id: string } }>( + "/v1/statuses/:id/bookmark", + async (ctx) => { + const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; + const accessTokens = ctx.headers.authorization; + const client = getClient(BASE_URL, accessTokens); + try { + const data = await client.bookmarkStatus( + convertId(ctx.params.id, IdType.FirefishId), + ); + ctx.body = convertStatus(data.data); + } catch (e: any) { + console.error(e); + ctx.status = 401; + ctx.body = e.response.data; + } + }, + ); + router.post<{ Params: { id: string } }>( + "/v1/statuses/:id/unbookmark", + async (ctx) => { + const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; + const accessTokens = ctx.headers.authorization; + const client = getClient(BASE_URL, accessTokens); + try { + const data = await client.unbookmarkStatus( + convertId(ctx.params.id, IdType.FirefishId), + ); + ctx.body = convertStatus(data.data); + } catch (e: any) { + console.error(e); + ctx.status = 401; + ctx.body = e.response.data; + } + }, + ); + + router.post<{ Params: { id: string } }>( + "/v1/statuses/:id/pin", + async (ctx) => { + const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; + const accessTokens = ctx.headers.authorization; + const client = getClient(BASE_URL, accessTokens); + try { + const data = await client.pinStatus( + convertId(ctx.params.id, IdType.FirefishId), + ); + ctx.body = convertStatus(data.data); + } catch (e: any) { + console.error(e); + ctx.status = 401; + ctx.body = e.response.data; + } + }, + ); + + router.post<{ Params: { id: string } }>( + "/v1/statuses/:id/unpin", + async (ctx) => { + const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; + const accessTokens = ctx.headers.authorization; + const client = getClient(BASE_URL, accessTokens); + try { + const data = await client.unpinStatus( + convertId(ctx.params.id, IdType.FirefishId), + ); + ctx.body = convertStatus(data.data); + } catch (e: any) { + console.error(e); + ctx.status = 401; + ctx.body = e.response.data; + } + }, + ); + router.get<{ Params: { id: string } }>("/v1/media/:id", async (ctx) => { + const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; + const accessTokens = ctx.headers.authorization; + const client = getClient(BASE_URL, accessTokens); + try { + const data = await client.getMedia( + convertId(ctx.params.id, IdType.FirefishId), + ); + ctx.body = convertAttachment(data.data); + } catch (e: any) { + console.error(e); + ctx.status = 401; + ctx.body = e.response.data; + } + }); + router.put<{ Params: { id: string } }>("/v1/media/:id", async (ctx) => { + const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; + const accessTokens = ctx.headers.authorization; + const client = getClient(BASE_URL, accessTokens); + try { + const data = await client.updateMedia( + convertId(ctx.params.id, IdType.FirefishId), + ctx.request.body as any, + ); + ctx.body = convertAttachment(data.data); + } catch (e: any) { + console.error(e); + ctx.status = 401; + ctx.body = e.response.data; + } + }); + router.get<{ Params: { id: string } }>("/v1/polls/:id", async (ctx) => { + const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; + const accessTokens = ctx.headers.authorization; + const client = getClient(BASE_URL, accessTokens); + try { + const data = await client.getPoll( + convertId(ctx.params.id, IdType.FirefishId), + ); + ctx.body = convertPoll(data.data); + } catch (e: any) { + console.error(e); + ctx.status = 401; + ctx.body = e.response.data; + } + }); + router.post<{ Params: { id: string } }>( + "/v1/polls/:id/votes", + async (ctx) => { + const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; + const accessTokens = ctx.headers.authorization; + const client = getClient(BASE_URL, accessTokens); + try { + const data = await client.votePoll( + convertId(ctx.params.id, IdType.FirefishId), + (ctx.request.body as any).choices, + ); + ctx.body = convertPoll(data.data); + } catch (e: any) { + console.error(e); + ctx.status = 401; + ctx.body = e.response.data; + } + }, + ); } -async function getFirstReaction(BASE_URL: string, accessTokens: string | undefined) { - const accessTokenArr = accessTokens?.split(' ') ?? [null]; - const accessToken = accessTokenArr[accessTokenArr.length - 1]; - let react = '👍' - try { - const api = await axios.post(`${BASE_URL}/api/i/registry/get-unsecure`, { - scope: ['client', 'base'], - key: 'reactions', - i: accessToken - }) - const reactRaw = api.data - react = Array.isArray(reactRaw) ? api.data[0] : '👍' - console.log(api.data) - return react - } catch (e) { - return react - } +async function getFirstReaction( + BASE_URL: string, + accessTokens: string | undefined, +) { + const accessTokenArr = accessTokens?.split(" ") ?? [null]; + const accessToken = accessTokenArr[accessTokenArr.length - 1]; + let react = "⭐"; + try { + const api = await axios.post(`${BASE_URL}/api/i/registry/get-unsecure`, { + scope: ["client", "base"], + key: "reactions", + i: accessToken, + }); + const reactRaw = api.data; + react = Array.isArray(reactRaw) ? api.data[0] : "⭐"; + console.log(api.data); + return react; + } catch (e) { + return react; + } } - -export function statusModel(id: string | null, acctId: string | null, emojis: MastodonEntity.Emoji[], content: string) { - const now = "1970-01-02T00:00:00.000Z" - return { - id: '9atm5frjhb', - uri: 'https://http.cat/404', // "" - url: 'https://http.cat/404', // "", - account: { - id: '9arzuvv0sw', - username: 'ReactionBot', - acct: 'ReactionBot', - display_name: 'ReactionOfThisPost', - locked: false, - created_at: now, - followers_count: 0, - following_count: 0, - statuses_count: 0, - note: '', - url: 'https://http.cat/404', - avatar: 'https://http.cat/404', - avatar_static: 'https://http.cat/404', - header: 'https://http.cat/404', // "" - header_static: 'https://http.cat/404', // "" - emojis: [], - fields: [], - moved: null, - bot: false, - }, - in_reply_to_id: id, - in_reply_to_account_id: acctId, - reblog: null, - content: `

${content}

`, - plain_content: null, - created_at: now, - emojis: emojis, - replies_count: 0, - reblogs_count: 0, - favourites_count: 0, - favourited: false, - reblogged: false, - muted: false, - sensitive: false, - spoiler_text: '', - visibility: 'public' as const, - media_attachments: [], - mentions: [], - tags: [], - card: null, - poll: null, - application: null, - language: null, - pinned: false, - emoji_reactions: [], - bookmarked: false, - quote: false, - } -} diff --git a/packages/backend/src/server/api/mastodon/endpoints/timeline.ts b/packages/backend/src/server/api/mastodon/endpoints/timeline.ts index 3fdb6ce881..4b49d8acc5 100644 --- a/packages/backend/src/server/api/mastodon/endpoints/timeline.ts +++ b/packages/backend/src/server/api/mastodon/endpoints/timeline.ts @@ -1,246 +1,305 @@ import Router from "@koa/router"; -import { koaBody } from 'koa-body'; -import megalodon, { Entity, MegalodonInterface } from '@cutls/megalodon'; -import { getClient } from '../ApiMastodonCompatibleService.js' -import { statusModel } from './status.js'; -import Autolinker from 'autolinker'; +import { getClient } from "../ApiMastodonCompatibleService.js"; import { ParsedUrlQuery } from "querystring"; +import { convertAccount, convertList, convertStatus } from "../converters.js"; +import { convertId, IdType } from "../../index.js"; -export function toLimitToInt(q: ParsedUrlQuery) { - if (q.limit) if (typeof q.limit === 'string') q.limit = parseInt(q.limit, 10).toString() - return q +export function limitToInt(q: ParsedUrlQuery) { + let object: any = q; + if (q.limit) + if (typeof q.limit === "string") object.limit = parseInt(q.limit, 10); + if (q.offset) + if (typeof q.offset === "string") object.offset = parseInt(q.offset, 10); + return object; } -export function toTextWithReaction(status: Entity.Status[], host: string) { - return status.map((t) => { - if (!t) return statusModel(null, null, [], 'no content') - if (!t.emoji_reactions) return t - if (t.reblog) t.reblog = toTextWithReaction([t.reblog], host)[0] - const reactions = t.emoji_reactions.map((r) => `${r.name.replace('@.', '')} (${r.count}${r.me ? "* " : ''})`); - //t.emojis = getEmoji(t.content, host) - t.content = `

${autoLinker(t.content, host)}

${reactions.join(', ')}

` - return t - }) +export function argsToBools(q: ParsedUrlQuery) { + // Values taken from https://docs.joinmastodon.org/client/intro/#boolean + const toBoolean = (value: string) => + !["0", "f", "F", "false", "FALSE", "off", "OFF"].includes(value); + + let object: any = q; + if (q.only_media) + if (typeof q.only_media === "string") + object.only_media = toBoolean(q.only_media); + if (q.exclude_replies) + if (typeof q.exclude_replies === "string") + object.exclude_replies = toBoolean(q.exclude_replies); + return q; } -export function autoLinker(input: string, host: string) { - return Autolinker.link(input, { - hashtag: 'twitter', - mention: 'twitter', - email: false, - stripPrefix: false, - replaceFn : function (match) { - switch(match.type) { - case 'url': - return true - case 'mention': - console.log("Mention: ", match.getMention()); - console.log("Mention Service Name: ", match.getServiceName()); - return `@${match.getMention()}`; - case 'hashtag': - console.log("Hashtag: ", match.getHashtag()); - return `#${match.getHashtag()}`; - } - return false - } - } ); + +export function convertTimelinesArgsId(q: ParsedUrlQuery) { + if (typeof q.min_id === "string") + q.min_id = convertId(q.min_id, IdType.FirefishId); + if (typeof q.max_id === "string") + q.max_id = convertId(q.max_id, IdType.FirefishId); + if (typeof q.since_id === "string") + q.since_id = convertId(q.since_id, IdType.FirefishId); + return q; } export function apiTimelineMastodon(router: Router): void { - router.get('/v1/timelines/public', async (ctx, reply) => { - const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; - const accessTokens = ctx.headers.authorization; - const client = getClient(BASE_URL, accessTokens); - try { - const query: any = ctx.query - const data = query.local ? await client.getLocalTimeline(toLimitToInt(query)) : await client.getPublicTimeline(toLimitToInt(query)); - ctx.body = toTextWithReaction(data.data, ctx.hostname); - } catch (e: any) { - console.error(e) - console.error(e.response.data) - ctx.status = (401); - ctx.body = e.response.data; - } - }); - router.get<{ Params: { hashtag: string } }>('/v1/timelines/tag/:hashtag', async (ctx, reply) => { - const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; - const accessTokens = ctx.headers.authorization; - const client = getClient(BASE_URL, accessTokens); - try { - const data = await client.getTagTimeline(ctx.params.hashtag, toLimitToInt(ctx.query)); - ctx.body = toTextWithReaction(data.data, ctx.hostname); - } catch (e: any) { - console.error(e) - console.error(e.response.data) - ctx.status = (401); - ctx.body = e.response.data; - } - }); - router.get<{ Params: { hashtag: string } }>('/v1/timelines/home', async (ctx, reply) => { - const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; - const accessTokens = ctx.headers.authorization; - const client = getClient(BASE_URL, accessTokens); - try { - const data = await client.getHomeTimeline(toLimitToInt(ctx.query)); - ctx.body = toTextWithReaction(data.data, ctx.hostname); - } catch (e: any) { - console.error(e) - console.error(e.response.data) - ctx.status = (401); - ctx.body = e.response.data; - } - }); - router.get<{ Params: { listId: string } }>('/v1/timelines/list/:listId', async (ctx, reply) => { - const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; - const accessTokens = ctx.headers.authorization; - const client = getClient(BASE_URL, accessTokens); - try { - const data = await client.getListTimeline(ctx.params.listId, toLimitToInt(ctx.query)); - ctx.body = toTextWithReaction(data.data, ctx.hostname); - } catch (e: any) { - console.error(e) - console.error(e.response.data) - ctx.status = (401); - ctx.body = e.response.data; - } - }); - router.get('/v1/conversations', async (ctx, reply) => { - const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; - const accessTokens = ctx.headers.authorization; - const client = getClient(BASE_URL, accessTokens); - try { - const data = await client.getConversationTimeline(toLimitToInt(ctx.query)); - ctx.body = data.data; - } catch (e: any) { - console.error(e) - console.error(e.response.data) - ctx.status = (401); - ctx.body = e.response.data; - } - }); - router.get('/v1/lists', async (ctx, reply) => { - const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; - const accessTokens = ctx.headers.authorization; - const client = getClient(BASE_URL, accessTokens); - try { - const data = await client.getLists(); - ctx.body = data.data; - } catch (e: any) { - console.error(e) - console.error(e.response.data) - ctx.status = (401); - ctx.body = e.response.data; - } - }); - router.get<{ Params: { id: string } }>('/v1/lists/:id', async (ctx, reply) => { - const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; - const accessTokens = ctx.headers.authorization; - const client = getClient(BASE_URL, accessTokens); - try { - const data = await client.getList(ctx.params.id); - ctx.body = data.data; - } catch (e: any) { - console.error(e) - console.error(e.response.data) - ctx.status = (401); - ctx.body = e.response.data; - } - }); - router.post('/v1/lists', async (ctx, reply) => { - const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; - const accessTokens = ctx.headers.authorization; - const client = getClient(BASE_URL, accessTokens); - try { - const data = await client.createList((ctx.query as any).title); - ctx.body = data.data; - } catch (e: any) { - console.error(e) - console.error(e.response.data) - ctx.status = (401); - ctx.body = e.response.data; - } - }); - router.put<{ Params: { id: string } }>('/v1/lists/:id', async (ctx, reply) => { - const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; - const accessTokens = ctx.headers.authorization; - const client = getClient(BASE_URL, accessTokens); - try { - const data = await client.updateList(ctx.params.id, ctx.query as any); - ctx.body = data.data; - } catch (e: any) { - console.error(e) - console.error(e.response.data) - ctx.status = (401); - ctx.body = e.response.data; - } - }); - router.delete<{ Params: { id: string } }>('/v1/lists/:id', async (ctx, reply) => { - const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; - const accessTokens = ctx.headers.authorization; - const client = getClient(BASE_URL, accessTokens); - try { - const data = await client.deleteList(ctx.params.id); - ctx.body = data.data; - } catch (e: any) { - console.error(e) - console.error(e.response.data) - ctx.status = (401); - ctx.body = e.response.data; - } - }); - router.get<{ Params: { id: string } }>('/v1/lists/:id/accounts', async (ctx, reply) => { - const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; - const accessTokens = ctx.headers.authorization; - const client = getClient(BASE_URL, accessTokens); - try { - const data = await client.getAccountsInList(ctx.params.id, ctx.query as any); - ctx.body = data.data; - } catch (e: any) { - console.error(e) - console.error(e.response.data) - ctx.status = (401); - ctx.body = e.response.data; - } - }); - router.post<{ Params: { id: string } }>('/v1/lists/:id/accounts', async (ctx, reply) => { - const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; - const accessTokens = ctx.headers.authorization; - const client = getClient(BASE_URL, accessTokens); - try { - const data = await client.addAccountsToList(ctx.params.id, (ctx.query as any).account_ids); - ctx.body = data.data; - } catch (e: any) { - console.error(e) - console.error(e.response.data) - ctx.status = (401); - ctx.body = e.response.data; - } - }); - router.delete<{ Params: { id: string } }>('/v1/lists/:id/accounts', async (ctx, reply) => { - const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; - const accessTokens = ctx.headers.authorization; - const client = getClient(BASE_URL, accessTokens); - try { - const data = await client.deleteAccountsFromList(ctx.params.id, (ctx.query as any).account_ids); - ctx.body = data.data; - } catch (e: any) { - console.error(e) - console.error(e.response.data) - ctx.status = (401); - ctx.body = e.response.data; - } - }); + router.get("/v1/timelines/public", async (ctx, reply) => { + const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; + const accessTokens = ctx.headers.authorization; + const client = getClient(BASE_URL, accessTokens); + try { + const query: any = ctx.query; + const data = + query.local === "true" + ? await client.getLocalTimeline( + convertTimelinesArgsId(argsToBools(limitToInt(query))), + ) + : await client.getPublicTimeline( + convertTimelinesArgsId(argsToBools(limitToInt(query))), + ); + ctx.body = data.data.map((status) => convertStatus(status)); + } catch (e: any) { + console.error(e); + console.error(e.response.data); + ctx.status = 401; + ctx.body = e.response.data; + } + }); + router.get<{ Params: { hashtag: string } }>( + "/v1/timelines/tag/:hashtag", + async (ctx, reply) => { + const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; + const accessTokens = ctx.headers.authorization; + const client = getClient(BASE_URL, accessTokens); + try { + const data = await client.getTagTimeline( + ctx.params.hashtag, + convertTimelinesArgsId(argsToBools(limitToInt(ctx.query))), + ); + ctx.body = data.data.map((status) => convertStatus(status)); + } catch (e: any) { + console.error(e); + console.error(e.response.data); + ctx.status = 401; + ctx.body = e.response.data; + } + }, + ); + router.get("/v1/timelines/home", async (ctx, reply) => { + const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; + const accessTokens = ctx.headers.authorization; + const client = getClient(BASE_URL, accessTokens); + try { + const data = await client.getHomeTimeline( + convertTimelinesArgsId(limitToInt(ctx.query)), + ); + ctx.body = data.data.map((status) => convertStatus(status)); + } catch (e: any) { + console.error(e); + console.error(e.response.data); + ctx.status = 401; + ctx.body = e.response.data; + } + }); + router.get<{ Params: { listId: string } }>( + "/v1/timelines/list/:listId", + async (ctx, reply) => { + const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; + const accessTokens = ctx.headers.authorization; + const client = getClient(BASE_URL, accessTokens); + try { + const data = await client.getListTimeline( + convertId(ctx.params.listId, IdType.FirefishId), + convertTimelinesArgsId(limitToInt(ctx.query)), + ); + ctx.body = data.data.map((status) => convertStatus(status)); + } catch (e: any) { + console.error(e); + console.error(e.response.data); + ctx.status = 401; + ctx.body = e.response.data; + } + }, + ); + router.get("/v1/conversations", async (ctx, reply) => { + const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; + const accessTokens = ctx.headers.authorization; + const client = getClient(BASE_URL, accessTokens); + try { + const data = await client.getConversationTimeline( + convertTimelinesArgsId(limitToInt(ctx.query)), + ); + ctx.body = data.data; + } catch (e: any) { + console.error(e); + console.error(e.response.data); + ctx.status = 401; + ctx.body = e.response.data; + } + }); + router.get("/v1/lists", async (ctx, reply) => { + const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; + const accessTokens = ctx.headers.authorization; + const client = getClient(BASE_URL, accessTokens); + try { + const data = await client.getLists(); + ctx.body = data.data.map((list) => convertList(list)); + } catch (e: any) { + console.error(e); + console.error(e.response.data); + ctx.status = 401; + ctx.body = e.response.data; + } + }); + router.get<{ Params: { id: string } }>( + "/v1/lists/:id", + async (ctx, reply) => { + const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; + const accessTokens = ctx.headers.authorization; + const client = getClient(BASE_URL, accessTokens); + try { + const data = await client.getList( + convertId(ctx.params.id, IdType.FirefishId), + ); + ctx.body = convertList(data.data); + } catch (e: any) { + console.error(e); + console.error(e.response.data); + ctx.status = 401; + ctx.body = e.response.data; + } + }, + ); + router.post("/v1/lists", async (ctx, reply) => { + const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; + const accessTokens = ctx.headers.authorization; + const client = getClient(BASE_URL, accessTokens); + try { + const data = await client.createList((ctx.request.body as any).title); + ctx.body = convertList(data.data); + } catch (e: any) { + console.error(e); + console.error(e.response.data); + ctx.status = 401; + ctx.body = e.response.data; + } + }); + router.put<{ Params: { id: string } }>( + "/v1/lists/:id", + async (ctx, reply) => { + const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; + const accessTokens = ctx.headers.authorization; + const client = getClient(BASE_URL, accessTokens); + try { + const data = await client.updateList( + convertId(ctx.params.id, IdType.FirefishId), + (ctx.request.body as any).title, + ); + ctx.body = convertList(data.data); + } catch (e: any) { + console.error(e); + console.error(e.response.data); + ctx.status = 401; + ctx.body = e.response.data; + } + }, + ); + router.delete<{ Params: { id: string } }>( + "/v1/lists/:id", + async (ctx, reply) => { + const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; + const accessTokens = ctx.headers.authorization; + const client = getClient(BASE_URL, accessTokens); + try { + const data = await client.deleteList( + convertId(ctx.params.id, IdType.FirefishId), + ); + ctx.body = data.data; + } catch (e: any) { + console.error(e); + console.error(e.response.data); + ctx.status = 401; + ctx.body = e.response.data; + } + }, + ); + router.get<{ Params: { id: string } }>( + "/v1/lists/:id/accounts", + async (ctx, reply) => { + const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; + const accessTokens = ctx.headers.authorization; + const client = getClient(BASE_URL, accessTokens); + try { + const data = await client.getAccountsInList( + convertId(ctx.params.id, IdType.FirefishId), + convertTimelinesArgsId(ctx.query as any), + ); + ctx.body = data.data.map((account) => convertAccount(account)); + } catch (e: any) { + console.error(e); + console.error(e.response.data); + ctx.status = 401; + ctx.body = e.response.data; + } + }, + ); + router.post<{ Params: { id: string } }>( + "/v1/lists/:id/accounts", + async (ctx, reply) => { + const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; + const accessTokens = ctx.headers.authorization; + const client = getClient(BASE_URL, accessTokens); + try { + const data = await client.addAccountsToList( + convertId(ctx.params.id, IdType.FirefishId), + (ctx.query.account_ids as string[]).map((id) => + convertId(id, IdType.FirefishId), + ), + ); + ctx.body = data.data; + } catch (e: any) { + console.error(e); + console.error(e.response.data); + ctx.status = 401; + ctx.body = e.response.data; + } + }, + ); + router.delete<{ Params: { id: string } }>( + "/v1/lists/:id/accounts", + async (ctx, reply) => { + const BASE_URL = `${ctx.protocol}://${ctx.hostname}`; + const accessTokens = ctx.headers.authorization; + const client = getClient(BASE_URL, accessTokens); + try { + const data = await client.deleteAccountsFromList( + convertId(ctx.params.id, IdType.FirefishId), + (ctx.query.account_ids as string[]).map((id) => + convertId(id, IdType.FirefishId), + ), + ); + ctx.body = data.data; + } catch (e: any) { + console.error(e); + console.error(e.response.data); + ctx.status = 401; + ctx.body = e.response.data; + } + }, + ); } function escapeHTML(str: string) { - if (!str) { - return '' - } - return str.replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"').replace(/'/g, ''') + if (!str) { + return ""; + } + return str + .replace(/&/g, "&") + .replace(//g, ">") + .replace(/"/g, """) + .replace(/'/g, "'"); } function nl2br(str: string) { - if (!str) { - return '' - } - str = str.replace(/\r\n/g, '
') - str = str.replace(/(\n|\r)/g, '
') - return str + if (!str) { + return ""; + } + str = str.replace(/\r\n/g, "
"); + str = str.replace(/(\n|\r)/g, "
"); + return str; } diff --git a/packages/backend/src/server/api/openapi/errors.ts b/packages/backend/src/server/api/openapi/errors.ts index 0fe229d88e..9e7c77c0f2 100644 --- a/packages/backend/src/server/api/openapi/errors.ts +++ b/packages/backend/src/server/api/openapi/errors.ts @@ -3,7 +3,7 @@ export const errors = { INVALID_PARAM: { value: { error: { - message: "Invalid param.", + message: "Invalid parameter.", code: "INVALID_PARAM", id: "3d81ceae-475f-4600-b2a8-2bc116157532", }, @@ -25,8 +25,7 @@ export const errors = { AUTHENTICATION_FAILED: { value: { error: { - message: - "Authentication failed. Please ensure your token is correct.", + message: "Authentication failed.", code: "AUTHENTICATION_FAILED", id: "b0a7f5f8-dc2f-4171-b91f-de88ad238e14", }, @@ -38,7 +37,7 @@ export const errors = { value: { error: { message: - "You sent a request to Calc, Calckey's resident stoner furry, instead of the server.", + "You sent a request to Calc instead of the server. How did this happen?", code: "I_AM_CALC", id: "60c46cd1-f23a-46b1-bebe-5d2b73951a84", }, diff --git a/packages/backend/src/server/api/openapi/gen-spec.ts b/packages/backend/src/server/api/openapi/gen-spec.ts index dfaacf9e50..7c9e9de8c9 100644 --- a/packages/backend/src/server/api/openapi/gen-spec.ts +++ b/packages/backend/src/server/api/openapi/gen-spec.ts @@ -9,13 +9,13 @@ export function genOpenapiSpec() { info: { version: "v1", - title: "Calckey API", + title: "Firefish API", "x-logo": { url: "/static-assets/api-doc.png" }, }, externalDocs: { description: "Repository", - url: "https://codeberg.org/calckey/calckey", + url: "https://gitlab.prometheus.systems/firefish/firefish", }, servers: [ @@ -106,7 +106,7 @@ export function genOpenapiSpec() { description: desc, externalDocs: { description: "Source code", - url: `https://codeberg.org/calckey/calckey/src/branch/develop/packages/backend/src/server/api/endpoints/${endpoint.name}.ts`, + url: `https://gitlab.prometheus.systems/firefish/firefish/src/branch/develop/packages/backend/src/server/api/endpoints/${endpoint.name}.ts`, }, tags: endpoint.meta.tags || undefined, security, @@ -182,7 +182,7 @@ export function genOpenapiSpec() { ...(endpoint.meta.limit ? { "429": { - description: "To many requests", + description: "Too many requests", content: { "application/json": { schema: { diff --git a/packages/backend/src/server/api/private/signin.ts b/packages/backend/src/server/api/private/signin.ts index b06f47ed4c..06d801a953 100644 --- a/packages/backend/src/server/api/private/signin.ts +++ b/packages/backend/src/server/api/private/signin.ts @@ -1,6 +1,5 @@ import type Koa from "koa"; -import bcrypt from "bcryptjs"; -import * as speakeasy from "speakeasy"; +import * as OTPAuth from "otpauth"; import signin from "../common/signin.js"; import config from "@/config/index.js"; import { @@ -12,6 +11,11 @@ import { } from "@/models/index.js"; import type { ILocalUser } from "@/models/entities/user.js"; import { genId } from "@/misc/gen-id.js"; +import { + comparePassword, + hashPassword, + isOldAlgorithm, +} from "@/misc/password.js"; import { verifyLogin, hash } from "../2fa.js"; import { randomBytes } from "node:crypto"; import { IsNull } from "typeorm"; @@ -88,7 +92,12 @@ export default async (ctx: Koa.Context) => { const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); // Compare password - const same = await bcrypt.compare(password, profile.password!); + const same = await comparePassword(password, profile.password!); + + if (same && isOldAlgorithm(profile.password!)) { + profile.password = await hashPassword(password); + await UserProfiles.save(profile); + } async function fail(status?: number, failure?: { id: string }) { // Append signin history @@ -127,14 +136,18 @@ export default async (ctx: Koa.Context) => { return; } - const verified = (speakeasy as any).totp.verify({ - secret: profile.twoFactorSecret, - encoding: "base32", - token: token, - window: 2, + if (profile.twoFactorSecret == null) { + throw new Error("Attempted 2FA signin without 2FA enabled."); + } + + const delta = OTPAuth.TOTP.validate({ + secret: OTPAuth.Secret.fromBase32(profile.twoFactorSecret), + digits: 6, + token, + window: 1, }); - if (verified) { + if (delta != null) { signin(ctx, user); return; } else { diff --git a/packages/backend/src/server/api/private/signup.ts b/packages/backend/src/server/api/private/signup.ts index dc5eb23168..440d0e3686 100644 --- a/packages/backend/src/server/api/private/signup.ts +++ b/packages/backend/src/server/api/private/signup.ts @@ -1,6 +1,5 @@ import type Koa from "koa"; import rndstr from "rndstr"; -import bcrypt from "bcryptjs"; import { fetchMeta } from "@/misc/fetch-meta.js"; import { verifyHcaptcha, verifyRecaptcha } from "@/misc/captcha.js"; import { Users, RegistrationTickets, UserPendings } from "@/models/index.js"; @@ -9,6 +8,7 @@ import config from "@/config/index.js"; import { sendEmail } from "@/services/send-email.js"; import { genId } from "@/misc/gen-id.js"; import { validateEmailForAccount } from "@/services/validate-email-for-account.js"; +import { hashPassword } from "@/misc/password.js"; export default async (ctx: Koa.Context) => { const body = ctx.request.body; @@ -44,13 +44,18 @@ export default async (ctx: Koa.Context) => { const invitationCode = body["invitationCode"]; const emailAddress = body["emailAddress"]; + if (config.reservedUsernames?.includes(username.toLowerCase())) { + ctx.status = 400; + return; + } + if (instance.emailRequiredForSignup) { if (emailAddress == null || typeof emailAddress !== "string") { ctx.status = 400; return; } - const available = await validateEmailForAccount(emailAddress); + const { available } = await validateEmailForAccount(emailAddress); if (!available) { ctx.status = 400; return; @@ -79,8 +84,7 @@ export default async (ctx: Koa.Context) => { const code = rndstr("a-z0-9", 16); // Generate hash of password - const salt = await bcrypt.genSalt(8); - const hash = await bcrypt.hash(password, salt); + const hash = await hashPassword(password); await UserPendings.insert({ id: genId(), diff --git a/packages/backend/src/server/api/private/verify-email.ts b/packages/backend/src/server/api/private/verify-email.ts new file mode 100644 index 0000000000..e6c8295d18 --- /dev/null +++ b/packages/backend/src/server/api/private/verify-email.ts @@ -0,0 +1,38 @@ +import type Koa from "koa"; +import { Users, UserProfiles } from "@/models/index.js"; +import { publishMainStream } from "@/services/stream.js"; + +export default async (ctx: Koa.Context) => { + const body = ctx.request.body; + + const code = body["code"]; + + const profile = await UserProfiles.findOneByOrFail({ emailVerifyCode: code }); + + if (profile != null) { + ctx.body = "Verify succeeded!"; + + await UserProfiles.update( + { userId: profile.userId }, + { + emailVerified: true, + emailVerifyCode: null, + }, + ); + + publishMainStream( + profile.userId, + "meUpdated", + await Users.pack( + profile.userId, + { id: profile.userId }, + { + detail: true, + includeSecrets: true, + }, + ), + ); + } else { + ctx.throw(404); + } +}; diff --git a/packages/backend/src/server/api/service/discord.ts b/packages/backend/src/server/api/service/discord.ts index 9906d2f7ca..848a70d4a5 100644 --- a/packages/backend/src/server/api/service/discord.ts +++ b/packages/backend/src/server/api/service/discord.ts @@ -18,7 +18,7 @@ function getUserToken(ctx: Koa.BaseContext): string | null { function compareOrigin(ctx: Koa.BaseContext): boolean { function normalizeUrl(url?: string): string { - return url ? (url.endsWith("/") ? url.substr(0, url.length - 1) : url) : ""; + return url ? (url.endsWith("/") ? url.slice(0, url.length - 1) : url) : ""; } const referer = ctx.headers["referer"]; diff --git a/packages/backend/src/server/api/service/github.ts b/packages/backend/src/server/api/service/github.ts index f77c5f795d..fd015fb8ad 100644 --- a/packages/backend/src/server/api/service/github.ts +++ b/packages/backend/src/server/api/service/github.ts @@ -18,7 +18,7 @@ function getUserToken(ctx: Koa.BaseContext): string | null { function compareOrigin(ctx: Koa.BaseContext): boolean { function normalizeUrl(url?: string): string { - return url ? (url.endsWith("/") ? url.substr(0, url.length - 1) : url) : ""; + return url ? (url.endsWith("/") ? url.slice(0, url.length - 1) : url) : ""; } const referer = ctx.headers["referer"]; diff --git a/packages/backend/src/server/api/stream/channel.ts b/packages/backend/src/server/api/stream/channel.ts index 93dbdc426c..fc8e0ce35e 100644 --- a/packages/backend/src/server/api/stream/channel.ts +++ b/packages/backend/src/server/api/stream/channel.ts @@ -30,6 +30,10 @@ export default abstract class Channel { return this.connection.muting; } + protected get renoteMuting() { + return this.connection.renoteMuting; + } + protected get blocking() { return this.connection.blocking; } diff --git a/packages/backend/src/server/api/stream/channels/antenna.ts b/packages/backend/src/server/api/stream/channels/antenna.ts index 8e0d081107..ec5a8b175f 100644 --- a/packages/backend/src/server/api/stream/channels/antenna.ts +++ b/packages/backend/src/server/api/stream/channels/antenna.ts @@ -34,6 +34,9 @@ export default class extends Channel { // 流れてきたNoteがブロックされているユーザーが関わるものだったら無視する if (isUserRelated(note, this.blocking)) return; + if (note.renote && !note.text && this.renoteMuting.has(note.userId)) + return; + this.connection.cacheNote(note); this.send("note", note); diff --git a/packages/backend/src/server/api/stream/channels/channel.ts b/packages/backend/src/server/api/stream/channels/channel.ts index 841c84af8e..2ff4e08209 100644 --- a/packages/backend/src/server/api/stream/channels/channel.ts +++ b/packages/backend/src/server/api/stream/channels/channel.ts @@ -10,12 +10,12 @@ export default class extends Channel { public static shouldShare = false; public static requireCredential = false; private channelId: string; - private typers: Record = {}; + private typers: Map = new Map(); private emitTypersIntervalId: ReturnType; constructor(id: string, connection: Channel["connection"]) { super(id, connection); - this.onNote = this.onNote.bind(this); + this.onNote = this.withPackedNote(this.onNote.bind(this)); this.emitTypers = this.emitTypers.bind(this); } @@ -29,6 +29,7 @@ export default class extends Channel { } private async onNote(note: Packed<"Note">) { + if (note.visibility === "hidden") return; if (note.channelId !== this.channelId) return; // 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する @@ -36,6 +37,8 @@ export default class extends Channel { // 流れてきたNoteがブロックされているユーザーが関わるものだったら無視する if (isUserRelated(note, this.blocking)) return; + if (note.renote && !note.text && this.renoteMuting.has(note.userId)) return; + this.connection.cacheNote(note); this.send("note", note); @@ -44,8 +47,8 @@ export default class extends Channel { private onEvent(data: StreamMessages["channel"]["payload"]) { if (data.type === "typing") { const id = data.body; - const begin = this.typers[id] == null; - this.typers[id] = new Date(); + const begin = !this.typers.has(id); + this.typers.set(id, new Date()); if (begin) { this.emitTypers(); } @@ -57,11 +60,11 @@ export default class extends Channel { // Remove not typing users for (const [userId, date] of Object.entries(this.typers)) { - if (now.getTime() - date.getTime() > 5000) - this.typers[userId] = undefined; + if (now.getTime() - date.getTime() > 5000) this.typers.delete(userId); } - const users = await Users.packMany(Object.keys(this.typers), null, { + const userIds = Array.from(this.typers.keys()); + const users = await Users.packMany(userIds, null, { detail: false, }); diff --git a/packages/backend/src/server/api/stream/channels/global-timeline.ts b/packages/backend/src/server/api/stream/channels/global-timeline.ts index bea201088f..2257be2b8c 100644 --- a/packages/backend/src/server/api/stream/channels/global-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/global-timeline.ts @@ -1,6 +1,6 @@ import Channel from "../channel.js"; import { fetchMeta } from "@/misc/fetch-meta.js"; -import { checkWordMute } from "@/misc/check-word-mute.js"; +import { getWordHardMute } from "@/misc/check-word-mute.js"; import { isInstanceMuted } from "@/misc/is-instance-muted.js"; import { isUserRelated } from "@/misc/is-user-related.js"; import type { Packed } from "@/misc/schema.js"; @@ -9,6 +9,7 @@ export default class extends Channel { public readonly chName = "globalTimeline"; public static shouldShare = true; public static requireCredential = false; + private withReplies: boolean; constructor(id: string, connection: Channel["connection"]) { super(id, connection); @@ -22,6 +23,8 @@ export default class extends Channel { return; } + this.withReplies = params.withReplies as boolean; + // Subscribe events this.subscriber.on("notesStream", this.onNote); } @@ -31,7 +34,7 @@ export default class extends Channel { if (note.channelId != null) return; // 関係ない返信は除外 - if (note.reply && !this.user!.showTimelineReplies) { + if (note.reply && !this.withReplies) { const reply = note.reply; // 「チャンネル接続主への返信」でもなければ、「チャンネル接続主が行った返信」でもなければ、「投稿者の投稿者自身への返信」でもない場合 if ( @@ -56,14 +59,16 @@ export default class extends Channel { // 流れてきたNoteがブロックされているユーザーが関わるものだったら無視する if (isUserRelated(note, this.blocking)) return; + if (note.renote && !note.text && this.renoteMuting.has(note.userId)) return; + // 流れてきたNoteがミュートすべきNoteだったら無視する // TODO: 将来的には、単にMutedNoteテーブルにレコードがあるかどうかで判定したい(以下の理由により難しそうではある) // 現状では、ワードミュートにおけるMutedNoteレコードの追加処理はストリーミングに流す処理と並列で行われるため、 // レコードが追加されるNoteでも追加されるより先にここのストリーミングの処理に到達することが起こる。 - // そのためレコードが存在するかのチェックでは不十分なので、改めてcheckWordMuteを呼んでいる + // そのためレコードが存在するかのチェックでは不十分なので、改めてgetWordHardMuteを呼んでいる if ( this.userProfile && - (await checkWordMute(note, this.user, this.userProfile.mutedWords)) + (await getWordHardMute(note, this.user, this.userProfile.mutedWords)) ) return; diff --git a/packages/backend/src/server/api/stream/channels/hashtag.ts b/packages/backend/src/server/api/stream/channels/hashtag.ts index 9d7b518d9f..011bb0889d 100644 --- a/packages/backend/src/server/api/stream/channels/hashtag.ts +++ b/packages/backend/src/server/api/stream/channels/hashtag.ts @@ -24,6 +24,7 @@ export default class extends Channel { } private async onNote(note: Packed<"Note">) { + if (note.visibility === "hidden") return; const noteTags = note.tags ? note.tags.map((t: string) => t.toLowerCase()) : []; @@ -37,6 +38,8 @@ export default class extends Channel { // 流れてきたNoteがブロックされているユーザーが関わるものだったら無視する if (isUserRelated(note, this.blocking)) return; + if (note.renote && !note.text && this.renoteMuting.has(note.userId)) return; + this.connection.cacheNote(note); this.send("note", note); diff --git a/packages/backend/src/server/api/stream/channels/home-timeline.ts b/packages/backend/src/server/api/stream/channels/home-timeline.ts index 47d7736388..47875aeda7 100644 --- a/packages/backend/src/server/api/stream/channels/home-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/home-timeline.ts @@ -1,5 +1,5 @@ import Channel from "../channel.js"; -import { checkWordMute } from "@/misc/check-word-mute.js"; +import { getWordHardMute } from "@/misc/check-word-mute.js"; import { isUserRelated } from "@/misc/is-user-related.js"; import { isInstanceMuted } from "@/misc/is-instance-muted.js"; import type { Packed } from "@/misc/schema.js"; @@ -8,6 +8,7 @@ export default class extends Channel { public readonly chName = "homeTimeline"; public static shouldShare = true; public static requireCredential = true; + private withReplies: boolean; constructor(id: string, connection: Channel["connection"]) { super(id, connection); @@ -15,11 +16,14 @@ export default class extends Channel { } public async init(params: any) { + this.withReplies = params.withReplies as boolean; + // Subscribe events this.subscriber.on("notesStream", this.onNote); } private async onNote(note: Packed<"Note">) { + if (note.visibility === "hidden") return; if (note.channelId) { if (!this.followingChannels.has(note.channelId)) return; } else { @@ -38,7 +42,7 @@ export default class extends Channel { return; // 関係ない返信は除外 - if (note.reply && !this.user!.showTimelineReplies) { + if (note.reply && !this.withReplies) { const reply = note.reply; // 「チャンネル接続主への返信」でもなければ、「チャンネル接続主が行った返信」でもなければ、「投稿者の投稿者自身への返信」でもない場合 if ( @@ -54,14 +58,16 @@ export default class extends Channel { // 流れてきたNoteがブロックされているユーザーが関わるものだったら無視する if (isUserRelated(note, this.blocking)) return; + if (note.renote && !note.text && this.renoteMuting.has(note.userId)) return; + // 流れてきたNoteがミュートすべきNoteだったら無視する // TODO: 将来的には、単にMutedNoteテーブルにレコードがあるかどうかで判定したい(以下の理由により難しそうではある) // 現状では、ワードミュートにおけるMutedNoteレコードの追加処理はストリーミングに流す処理と並列で行われるため、 // レコードが追加されるNoteでも追加されるより先にここのストリーミングの処理に到達することが起こる。 - // そのためレコードが存在するかのチェックでは不十分なので、改めてcheckWordMuteを呼んでいる + // そのためレコードが存在するかのチェックでは不十分なので、改めてgetWordHardMuteを呼んでいる if ( this.userProfile && - (await checkWordMute(note, this.user, this.userProfile.mutedWords)) + (await getWordHardMute(note, this.user, this.userProfile.mutedWords)) ) return; diff --git a/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts b/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts index 398127c402..1f1a9b831a 100644 --- a/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts @@ -1,6 +1,6 @@ import Channel from "../channel.js"; import { fetchMeta } from "@/misc/fetch-meta.js"; -import { checkWordMute } from "@/misc/check-word-mute.js"; +import { getWordHardMute } from "@/misc/check-word-mute.js"; import { isUserRelated } from "@/misc/is-user-related.js"; import { isInstanceMuted } from "@/misc/is-instance-muted.js"; import type { Packed } from "@/misc/schema.js"; @@ -9,6 +9,7 @@ export default class extends Channel { public readonly chName = "hybridTimeline"; public static shouldShare = true; public static requireCredential = true; + private withReplies: boolean; constructor(id: string, connection: Channel["connection"]) { super(id, connection); @@ -24,11 +25,14 @@ export default class extends Channel { ) return; + this.withReplies = params.withReplies as boolean; + // Subscribe events this.subscriber.on("notesStream", this.onNote); } private async onNote(note: Packed<"Note">) { + if (note.visibility === "hidden") return; // チャンネルの投稿ではなく、自分自身の投稿 または // チャンネルの投稿ではなく、その投稿のユーザーをフォローしている または // チャンネルの投稿ではなく、全体公開のローカルの投稿 または @@ -55,7 +59,7 @@ export default class extends Channel { return; // 関係ない返信は除外 - if (note.reply && !this.user!.showTimelineReplies) { + if (note.reply && !this.withReplies) { const reply = note.reply; // 「チャンネル接続主への返信」でもなければ、「チャンネル接続主が行った返信」でもなければ、「投稿者の投稿者自身への返信」でもない場合 if ( @@ -71,14 +75,16 @@ export default class extends Channel { // 流れてきたNoteがブロックされているユーザーが関わるものだったら無視する if (isUserRelated(note, this.blocking)) return; + if (note.renote && !note.text && this.renoteMuting.has(note.userId)) return; + // 流れてきたNoteがミュートすべきNoteだったら無視する // TODO: 将来的には、単にMutedNoteテーブルにレコードがあるかどうかで判定したい(以下の理由により難しそうではある) // 現状では、ワードミュートにおけるMutedNoteレコードの追加処理はストリーミングに流す処理と並列で行われるため、 // レコードが追加されるNoteでも追加されるより先にここのストリーミングの処理に到達することが起こる。 - // そのためレコードが存在するかのチェックでは不十分なので、改めてcheckWordMuteを呼んでいる + // そのためレコードが存在するかのチェックでは不十分なので、改めてgetWordHardMuteを呼んでいる if ( this.userProfile && - (await checkWordMute(note, this.user, this.userProfile.mutedWords)) + (await getWordHardMute(note, this.user, this.userProfile.mutedWords)) ) return; diff --git a/packages/backend/src/server/api/stream/channels/local-timeline.ts b/packages/backend/src/server/api/stream/channels/local-timeline.ts index 6f8075b7aa..bd488bdd7d 100644 --- a/packages/backend/src/server/api/stream/channels/local-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/local-timeline.ts @@ -1,6 +1,6 @@ import Channel from "../channel.js"; import { fetchMeta } from "@/misc/fetch-meta.js"; -import { checkWordMute } from "@/misc/check-word-mute.js"; +import { getWordHardMute } from "@/misc/check-word-mute.js"; import { isUserRelated } from "@/misc/is-user-related.js"; import type { Packed } from "@/misc/schema.js"; @@ -8,6 +8,7 @@ export default class extends Channel { public readonly chName = "localTimeline"; public static shouldShare = true; public static requireCredential = false; + private withReplies: boolean; constructor(id: string, connection: Channel["connection"]) { super(id, connection); @@ -21,6 +22,8 @@ export default class extends Channel { return; } + this.withReplies = params.withReplies as boolean; + // Subscribe events this.subscriber.on("notesStream", this.onNote); } @@ -32,7 +35,7 @@ export default class extends Channel { return; // 関係ない返信は除外 - if (note.reply && !this.user!.showTimelineReplies) { + if (note.reply && !this.withReplies) { const reply = note.reply; // 「チャンネル接続主への返信」でもなければ、「チャンネル接続主が行った返信」でもなければ、「投稿者の投稿者自身への返信」でもない場合 if ( @@ -48,14 +51,16 @@ export default class extends Channel { // 流れてきたNoteがブロックされているユーザーが関わるものだったら無視する if (isUserRelated(note, this.blocking)) return; + if (note.renote && !note.text && this.renoteMuting.has(note.userId)) return; + // 流れてきたNoteがミュートすべきNoteだったら無視する // TODO: 将来的には、単にMutedNoteテーブルにレコードがあるかどうかで判定したい(以下の理由により難しそうではある) // 現状では、ワードミュートにおけるMutedNoteレコードの追加処理はストリーミングに流す処理と並列で行われるため、 // レコードが追加されるNoteでも追加されるより先にここのストリーミングの処理に到達することが起こる。 - // そのためレコードが存在するかのチェックでは不十分なので、改めてcheckWordMuteを呼んでいる + // そのためレコードが存在するかのチェックでは不十分なので、改めてgetWordHardMuteを呼んでいる if ( this.userProfile && - (await checkWordMute(note, this.user, this.userProfile.mutedWords)) + (await getWordHardMute(note, this.user, this.userProfile.mutedWords)) ) return; diff --git a/packages/backend/src/server/api/stream/channels/messaging.ts b/packages/backend/src/server/api/stream/channels/messaging.ts index dc18250592..0622bd4649 100644 --- a/packages/backend/src/server/api/stream/channels/messaging.ts +++ b/packages/backend/src/server/api/stream/channels/messaging.ts @@ -20,7 +20,7 @@ export default class extends Channel { private subCh: | `messagingStream:${User["id"]}-${User["id"]}` | `messagingStream:${UserGroup["id"]}`; - private typers: Record = {}; + private typers: Map = new Map(); private emitTypersIntervalId: ReturnType; constructor(id: string, connection: Channel["connection"]) { @@ -66,8 +66,8 @@ export default class extends Channel { ) { if (data.type === "typing") { const id = data.body; - const begin = this.typers[id] == null; - this.typers[id] = new Date(); + const begin = !this.typers.has(id); + this.typers.set(id, new Date()); if (begin) { this.emitTypers(); } @@ -107,12 +107,12 @@ export default class extends Channel { const now = new Date(); // Remove not typing users - for (const [userId, date] of Object.entries(this.typers)) { - if (now.getTime() - date.getTime() > 5000) - this.typers[userId] = undefined; + for (const [userId, date] of this.typers.entries()) { + if (now.getTime() - date.getTime() > 5000) this.typers.delete(userId); } - const users = await Users.packMany(Object.keys(this.typers), null, { + const userIds = Array.from(this.typers.keys()); + const users = await Users.packMany(userIds, null, { detail: false, }); diff --git a/packages/backend/src/server/api/stream/channels/recommended-timeline.ts b/packages/backend/src/server/api/stream/channels/recommended-timeline.ts index a2a03fca12..0b78d8b66c 100644 --- a/packages/backend/src/server/api/stream/channels/recommended-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/recommended-timeline.ts @@ -1,6 +1,6 @@ import Channel from "../channel.js"; import { fetchMeta } from "@/misc/fetch-meta.js"; -import { checkWordMute } from "@/misc/check-word-mute.js"; +import { getWordHardMute } from "@/misc/check-word-mute.js"; import { isUserRelated } from "@/misc/is-user-related.js"; import { isInstanceMuted } from "@/misc/is-instance-muted.js"; import type { Packed } from "@/misc/schema.js"; @@ -9,6 +9,7 @@ export default class extends Channel { public readonly chName = "recommendedTimeline"; public static shouldShare = true; public static requireCredential = true; + private withReplies: boolean; constructor(id: string, connection: Channel["connection"]) { super(id, connection); @@ -24,11 +25,14 @@ export default class extends Channel { ) return; + this.withReplies = params.withReplies as boolean; + // Subscribe events this.subscriber.on("notesStream", this.onNote); } private async onNote(note: Packed<"Note">) { + if (note.visibility === "hidden") return; // チャンネルの投稿ではなく、自分自身の投稿 または // チャンネルの投稿ではなく、その投稿のユーザーをフォローしている または // チャンネルの投稿ではなく、全体公開のローカルの投稿 または @@ -53,7 +57,7 @@ export default class extends Channel { return; // 関係ない返信は除外 - if (note.reply && !this.user!.showTimelineReplies) { + if (note.reply && !this.withReplies) { const reply = note.reply; // 「チャンネル接続主への返信」でもなければ、「チャンネル接続主が行った返信」でもなければ、「投稿者の投稿者自身への返信」でもない場合 if ( @@ -69,14 +73,16 @@ export default class extends Channel { // 流れてきたNoteがブロックされているユーザーが関わるものだったら無視する if (isUserRelated(note, this.blocking)) return; + if (note.renote && !note.text && this.renoteMuting.has(note.userId)) return; + // 流れてきたNoteがミュートすべきNoteだったら無視する // TODO: 将来的には、単にMutedNoteテーブルにレコードがあるかどうかで判定したい(以下の理由により難しそうではある) // 現状では、ワードミュートにおけるMutedNoteレコードの追加処理はストリーミングに流す処理と並列で行われるため、 // レコードが追加されるNoteでも追加されるより先にここのストリーミングの処理に到達することが起こる。 - // そのためレコードが存在するかのチェックでは不十分なので、改めてcheckWordMuteを呼んでいる + // そのためレコードが存在するかのチェックでは不十分なので、改めてgetWordHardMuteを呼んでいる if ( this.userProfile && - (await checkWordMute(note, this.user, this.userProfile.mutedWords)) + (await getWordHardMute(note, this.user, this.userProfile.mutedWords)) ) return; diff --git a/packages/backend/src/server/api/stream/channels/user-list.ts b/packages/backend/src/server/api/stream/channels/user-list.ts index f63776c9e9..d140319503 100644 --- a/packages/backend/src/server/api/stream/channels/user-list.ts +++ b/packages/backend/src/server/api/stream/channels/user-list.ts @@ -22,11 +22,13 @@ export default class extends Channel { this.listId = params.listId as string; // Check existence and owner - const list = await UserLists.findOneBy({ - id: this.listId, - userId: this.user!.id, + const exist = await UserLists.exist({ + where: { + id: this.listId, + userId: this.user!.id, + }, }); - if (!list) return; + if (!exist) return; // Subscribe stream this.subscriber.on(`userListStream:${this.listId}`, this.send); @@ -49,6 +51,7 @@ export default class extends Channel { } private async onNote(note: Packed<"Note">) { + if (note.visibility === "hidden") return; if (!this.listUsers.includes(note.userId)) return; // 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する @@ -56,6 +59,8 @@ export default class extends Channel { // 流れてきたNoteがブロックされているユーザーが関わるものだったら無視する if (isUserRelated(note, this.blocking)) return; + if (note.renote && !note.text && this.renoteMuting.has(note.userId)) return; + this.send("note", note); } diff --git a/packages/backend/src/server/api/stream/index.ts b/packages/backend/src/server/api/stream/index.ts index 5be8ab109e..72dce19511 100644 --- a/packages/backend/src/server/api/stream/index.ts +++ b/packages/backend/src/server/api/stream/index.ts @@ -7,6 +7,7 @@ import { Users, Followings, Mutings, + RenoteMutings, UserProfiles, ChannelFollowings, Blockings, @@ -24,9 +25,8 @@ import { readNotification } from "../common/read-notification.js"; import channels from "./channels/index.js"; import type Channel from "./channel.js"; import type { StreamEventEmitter, StreamMessages } from "./types.js"; -import { Converter } from "@cutls/megalodon"; +import { Converter } from "megalodon"; import { getClient } from "../mastodon/ApiMastodonCompatibleService.js"; -import { toTextWithReaction } from "../mastodon/endpoints/timeline.js"; /** * Main stream connection @@ -36,15 +36,16 @@ export default class Connection { public userProfile?: UserProfile | null; public following: Set = new Set(); public muting: Set = new Set(); + public renoteMuting: Set = new Set(); public blocking: Set = new Set(); // "被"blocking public followingChannels: Set = new Set(); public token?: AccessToken; private wsConnection: websocket.connection; public subscriber: StreamEventEmitter; private channels: Channel[] = []; - private subscribingNotes: any = {}; + private subscribingNotes: Map = new Map(); private cachedNotes: Packed<"Note">[] = []; - private isMastodonCompatible: boolean = false; + private isMastodonCompatible = false; private host: string; private accessToken: string; private currentSubscribe: string[][] = []; @@ -80,6 +81,7 @@ export default class Connection { if (this.user) { this.updateFollowing(); this.updateMuting(); + this.updateRenoteMuting(); this.updateBlocking(); this.updateFollowingChannels(); this.updateUserProfile(); @@ -114,6 +116,7 @@ export default class Connection { this.muting.delete(data.body.id); break; + // TODO: renote mute events // TODO: block events case "followChannel": @@ -152,7 +155,7 @@ export default class Connection { } catch (e) { return; } - + const simpleObj = objs[0]; if (simpleObj.stream) { // is Mastodon Compatible @@ -244,7 +247,7 @@ export default class Connection { for (const obj of objs) { const { type, body } = obj; - console.log(type, body); + // console.log(type, body); switch (type) { case "readNotification": this.onReadNotification(body); @@ -339,13 +342,10 @@ export default class Connection { private onSubscribeNote(payload: any) { if (!payload.id) return; - if (this.subscribingNotes[payload.id] == null) { - this.subscribingNotes[payload.id] = 0; - } + const current = this.subscribingNotes.get(payload.id) || 0; + this.subscribingNotes.set(payload.id, current + 1); - this.subscribingNotes[payload.id]++; - - if (this.subscribingNotes[payload.id] === 1) { + if (!current) { this.subscriber.on(`noteStream:${payload.id}`, this.onNoteStreamMessage); } } @@ -356,11 +356,13 @@ export default class Connection { private onUnsubscribeNote(payload: any) { if (!payload.id) return; - this.subscribingNotes[payload.id]--; - if (this.subscribingNotes[payload.id] <= 0) { - this.subscribingNotes[payload.id] = undefined; + const current = this.subscribingNotes.get(payload.id) || 0; + if (current <= 1) { + this.subscribingNotes.delete(payload.id); this.subscriber.off(`noteStream:${payload.id}`, this.onNoteStreamMessage); + return; } + this.subscribingNotes.set(payload.id, current - 1); } private async onNoteStreamMessage(data: StreamMessages["note"]["payload"]) { @@ -391,19 +393,13 @@ export default class Connection { * クライアントにメッセージ送信 */ public sendMessageToWs(type: string, payload: any) { - console.log(payload, this.isMastodonCompatible); if (this.isMastodonCompatible) { if (payload.type === "note") { this.wsConnection.send( JSON.stringify({ stream: [payload.id], event: "update", - payload: JSON.stringify( - toTextWithReaction( - [Converter.note(payload.body, this.host)], - this.host, - )[0], - ), + payload: JSON.stringify(Converter.note(payload.body, this.host)), }), ); this.onSubscribeNote({ @@ -413,13 +409,14 @@ export default class Connection { // reaction const client = getClient(this.host, this.accessToken); client.getStatus(payload.id).then((data) => { - const newPost = toTextWithReaction([data.data], this.host); + const newPost = [data.data]; + const targetPost = newPost[0]; for (const stream of this.currentSubscribe) { this.wsConnection.send( JSON.stringify({ stream, event: "status.update", - payload: JSON.stringify(newPost[0]), + payload: JSON.stringify(targetPost), }), ); } @@ -439,10 +436,6 @@ export default class Connection { if (payload.id === "user") { const body = Converter.notification(payload.body, this.host); if (body.type === "reaction") body.type = "favourite"; - body.status = toTextWithReaction( - body.status ? [body.status] : [], - "", - )[0]; this.wsConnection.send( JSON.stringify({ stream: ["user"], @@ -564,6 +557,17 @@ export default class Connection { this.muting = new Set(mutings.map((x) => x.muteeId)); } + private async updateRenoteMuting() { + const renoteMutings = await RenoteMutings.find({ + where: { + muterId: this.user!.id, + }, + select: ["muteeId"], + }); + + this.renoteMuting = new Set(renoteMutings.map((x) => x.muteeId)); + } + private async updateBlocking() { // ここでいうBlockingは被Blockingの意 const blockings = await Blockings.find({ diff --git a/packages/backend/src/server/api/stream/types.ts b/packages/backend/src/server/api/stream/types.ts index 837f42c871..91095a46d3 100644 --- a/packages/backend/src/server/api/stream/types.ts +++ b/packages/backend/src/server/api/stream/types.ts @@ -7,7 +7,6 @@ import type { Note } from "@/models/entities/note.js"; import type { Antenna } from "@/models/entities/antenna.js"; import type { DriveFile } from "@/models/entities/drive-file.js"; import type { DriveFolder } from "@/models/entities/drive-folder.js"; -import { Emoji } from "@/models/entities/emoji.js"; import type { UserList } from "@/models/entities/user-list.js"; import type { MessagingMessage } from "@/models/entities/messaging-message.js"; import type { UserGroup } from "@/models/entities/user-group.js"; @@ -23,7 +22,10 @@ export interface InternalStreamTypes { id: User["id"]; isSuspended: User["isSuspended"]; }; - userChangeSilencedState: { id: User["id"]; isSilenced: User["isSilenced"] }; + userChangeSilencedState: { + id: User["id"]; + isSilenced: User["isSilenced"]; + }; userChangeModeratorState: { id: User["id"]; isModerator: User["isModerator"]; @@ -33,7 +35,12 @@ export interface InternalStreamTypes { oldToken: User["token"]; newToken: User["token"]; }; - remoteUserUpdated: { id: User["id"] }; + localUserUpdated: { + id: User["id"]; + }; + remoteUserUpdated: { + id: User["id"]; + }; webhookCreated: Webhook; webhookDeleted: Webhook; webhookUpdated: Webhook; @@ -135,6 +142,12 @@ export interface NoteStreamTypes { reaction: string; userId: User["id"]; }; + replied: { + id: Note["id"]; + }; + updated: { + updatedAt?: Note["updatedAt"]; + }; } type NoteStreamEventTypes = { [key in keyof NoteStreamTypes]: { diff --git a/packages/backend/src/server/api/streaming.ts b/packages/backend/src/server/api/streaming.ts index 4ccad96e81..14e07b7487 100644 --- a/packages/backend/src/server/api/streaming.ts +++ b/packages/backend/src/server/api/streaming.ts @@ -16,7 +16,7 @@ export const initializeStreamingServer = (server: http.Server) => { ws.on("request", async (request) => { const q = request.resourceURL.query as ParsedUrlQuery; - const headers = request.httpRequest.headers['sec-websocket-protocol'] || ''; + const headers = request.httpRequest.headers["sec-websocket-protocol"] || ""; const cred = q.i || q.access_token || headers; const accessToken = cred.toString(); @@ -48,9 +48,17 @@ export const initializeStreamingServer = (server: http.Server) => { redisClient.on("message", onRedisMessage); const host = `https://${request.host}`; const prepareStream = q.stream?.toString(); - console.log('start', q); + console.log("start", q); - const main = new MainStreamConnection(connection, ev, user, app, host, accessToken, prepareStream); + const main = new MainStreamConnection( + connection, + ev, + user, + app, + host, + accessToken, + prepareStream, + ); const intervalId = user ? setInterval(() => { diff --git a/packages/backend/src/server/index.ts b/packages/backend/src/server/index.ts index 6ade50d18d..efff6dd236 100644 --- a/packages/backend/src/server/index.ts +++ b/packages/backend/src/server/index.ts @@ -7,6 +7,7 @@ import * as fs from "node:fs"; import * as http from "node:http"; import Koa from "koa"; import Router from "@koa/router"; +import cors from "@koa/cors"; import mount from "koa-mount"; import koaLogger from "koa-logger"; import * as slow from "koa-slow"; @@ -15,13 +16,13 @@ import { IsNull } from "typeorm"; import config from "@/config/index.js"; import Logger from "@/services/logger.js"; import { UserProfiles, Users } from "@/models/index.js"; +import { fetchMeta } from "@/misc/fetch-meta.js"; import { genIdenticon } from "@/misc/gen-identicon.js"; import { createTemp } from "@/misc/create-temp.js"; import { publishMainStream } from "@/services/stream.js"; import * as Acct from "@/misc/acct.js"; import { envOption } from "@/env.js"; -import { koaBody } from 'koa-body'; -import megalodon, { MegalodonInterface } from '@cutls/megalodon'; +import megalodon, { MegalodonInterface } from "megalodon"; import activityPub from "./activitypub.js"; import nodeinfo from "./nodeinfo.js"; import wellKnown from "./well-known.js"; @@ -30,6 +31,9 @@ import fileServer from "./file/index.js"; import proxyServer from "./proxy/index.js"; import webServer from "./web/index.js"; import { initializeStreamingServer } from "./api/streaming.js"; +import { koaBody } from "koa-body"; +import removeTrailingSlash from "koa-remove-trailing-slashes"; +import { v4 as uuid } from "uuid"; export const serverLogger = new Logger("server", "gray", false); @@ -37,6 +41,14 @@ export const serverLogger = new Logger("server", "gray", false); const app = new Koa(); app.proxy = true; +app.use(removeTrailingSlash()); + +app.use( + cors({ + origin: "*", + }), +); + if (!["production", "test"].includes(process.env.NODE_ENV || "")) { // Logger app.use( @@ -70,6 +82,25 @@ app.use(mount("/proxy", proxyServer)); // Init router const router = new Router(); +const mastoRouter = new Router(); + +mastoRouter.use( + koaBody({ + urlencoded: true, + multipart: true, + }), +); + +mastoRouter.use(async (ctx, next) => { + if (ctx.request.query) { + if (!ctx.request.body || Object.keys(ctx.request.body).length === 0) { + ctx.request.body = ctx.request.query; + } else { + ctx.request.body = { ...ctx.request.body, ...ctx.request.query }; + } + } + await next(); +}); // Routing router.use(activityPub.routes()); @@ -95,67 +126,78 @@ router.get("/avatar/@:acct", async (ctx) => { }); router.get("/identicon/:x", async (ctx) => { - const [temp, cleanup] = await createTemp(); - await genIdenticon(ctx.params.x, fs.createWriteStream(temp)); - ctx.set("Content-Type", "image/png"); - ctx.body = fs.createReadStream(temp).on("close", () => cleanup()); -}); - -router.get("/verify-email/:code", async (ctx) => { - const profile = await UserProfiles.findOneBy({ - emailVerifyCode: ctx.params.code, - }); - - if (profile != null) { - ctx.body = "Verify succeeded!"; - ctx.status = 200; - - await UserProfiles.update( - { userId: profile.userId }, - { - emailVerified: true, - emailVerifyCode: null, - }, - ); - - publishMainStream( - profile.userId, - "meUpdated", - await Users.pack( - profile.userId, - { id: profile.userId }, - { - detail: true, - includeSecrets: true, - }, - ), - ); + const meta = await fetchMeta(); + if (meta.enableIdenticonGeneration) { + const [temp, cleanup] = await createTemp(); + await genIdenticon(ctx.params.x, fs.createWriteStream(temp)); + ctx.set("Content-Type", "image/png"); + ctx.body = fs.createReadStream(temp).on("close", () => cleanup()); } else { - ctx.status = 404; + ctx.redirect("/static-assets/avatar.png"); } }); -router.get("/oauth/authorize", async (ctx) => { - const client_id = ctx.request.query.client_id; +mastoRouter.get("/oauth/authorize", async (ctx) => { + const { client_id, state, redirect_uri } = ctx.request.query; console.log(ctx.request.req); - ctx.redirect(Buffer.from(client_id?.toString() || '', 'base64').toString()); + let param = "mastodon=true"; + if (state) param += `&state=${state}`; + if (redirect_uri) param += `&redirect_uri=${redirect_uri}`; + const client = client_id ? client_id : ""; + ctx.redirect( + `${Buffer.from(client.toString(), "base64").toString()}?${param}`, + ); }); -router.post("/oauth/token", async (ctx) => { - const body: any = ctx.request.body; +mastoRouter.post("/oauth/token", async (ctx) => { + const body: any = ctx.request.body || ctx.request.query; + console.log("token-request", body); + console.log("token-query", ctx.request.query); + if (body.grant_type === "client_credentials") { + const ret = { + access_token: uuid(), + token_type: "Bearer", + scope: "read", + created_at: Math.floor(new Date().getTime() / 1000), + }; + ctx.body = ret; + return; + } + let client_id: any = body.client_id; const BASE_URL = `${ctx.request.protocol}://${ctx.request.hostname}`; const generator = (megalodon as any).default; - const client = generator('misskey', BASE_URL, null) as MegalodonInterface; - const m = body.code.match(/^[a-zA-Z0-9-]+/); - if (!m.length) return { error: 'Invalid code' } + const client = generator(BASE_URL, null) as MegalodonInterface; + let m = null; + let token = null; + if (body.code) { + //m = body.code.match(/^([a-zA-Z0-9]{8})([a-zA-Z0-9]{4})([a-zA-Z0-9]{4})([a-zA-Z0-9]{4})([a-zA-Z0-9]{12})/); + //if (!m.length) { + // ctx.body = { error: "Invalid code" }; + // return; + //} + //token = `${m[1]}-${m[2]}-${m[3]}-${m[4]}-${m[5]}` + console.log(body.code, token); + token = body.code; + } + if (client_id instanceof Array) { + client_id = client_id.toString(); + } else if (!client_id) { + client_id = null; + } try { - const atData = await client.fetchAccessToken(null, body.client_secret, m[0]); - ctx.body = { + const atData = await client.fetchAccessToken( + client_id, + body.client_secret, + token ? token : "", + ); + const ret = { access_token: atData.accessToken, - token_type: 'Bearer', - scope: 'read write follow', - created_at: new Date().getTime() / 1000 + token_type: "Bearer", + scope: body.scope || "read write follow push", + created_at: Math.floor(new Date().getTime() / 1000), }; + console.log("token-response", ret); + ctx.body = ret; } catch (err: any) { console.error(err); ctx.status = 401; @@ -164,6 +206,7 @@ router.post("/oauth/token", async (ctx) => { }); // Register router +app.use(mastoRouter.routes()); app.use(router.routes()); app.use(mount(webServer)); diff --git a/packages/backend/src/server/nodeinfo.ts b/packages/backend/src/server/nodeinfo.ts index a7fa0de4c8..2d01446522 100644 --- a/packages/backend/src/server/nodeinfo.ts +++ b/packages/backend/src/server/nodeinfo.ts @@ -3,7 +3,7 @@ import config from "@/config/index.js"; import { fetchMeta } from "@/misc/fetch-meta.js"; import { Users, Notes } from "@/models/index.js"; import { IsNull, MoreThan } from "typeorm"; -import { MAX_NOTE_TEXT_LENGTH } from "@/const.js"; +import { MAX_NOTE_TEXT_LENGTH, MAX_CAPTION_TEXT_LENGTH } from "@/const.js"; import { Cache } from "@/misc/cache.js"; const router = new Router(); @@ -50,10 +50,10 @@ const nodeinfo2 = async () => { return { software: { - name: "calckey", + name: "firefish", version: config.version, repository: meta.repositoryUrl, - homepage: "https://calckey.cloud", + homepage: "https://joinfirefish.org/", }, protocols: ["activitypub"], services: { @@ -82,9 +82,13 @@ const nodeinfo2 = async () => { disableRecommendedTimeline: meta.disableRecommendedTimeline, disableGlobalTimeline: meta.disableGlobalTimeline, emailRequiredForSignup: meta.emailRequiredForSignup, + searchFilters: config.meilisearch ? true : false, + postEditing: true, + postImports: meta.experimentalFeatures?.postImports || false, enableHcaptcha: meta.enableHcaptcha, enableRecaptcha: meta.enableRecaptcha, maxNoteTextLength: MAX_NOTE_TEXT_LENGTH, + maxCaptionTextLength: MAX_CAPTION_TEXT_LENGTH, enableTwitterIntegration: meta.enableTwitterIntegration, enableGithubIntegration: meta.enableGithubIntegration, enableDiscordIntegration: meta.enableDiscordIntegration, @@ -96,7 +100,10 @@ const nodeinfo2 = async () => { }; }; -const cache = new Cache>>(1000 * 60 * 10); +const cache = new Cache>>( + "nodeinfo", + 60 * 10, +); router.get(nodeinfo2_1path, async (ctx) => { const base = await cache.fetch(null, () => nodeinfo2()); diff --git a/packages/backend/src/server/proxy/proxy-media.ts b/packages/backend/src/server/proxy/proxy-media.ts index a9c257bfeb..b3bb031244 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(); diff --git a/packages/backend/src/server/web/bios.css b/packages/backend/src/server/web/bios.css index e79fc0657b..d6a1285e94 100644 --- a/packages/backend/src/server/web/bios.css +++ b/packages/backend/src/server/web/bios.css @@ -45,7 +45,7 @@ main { * { font-family: BIZ UDGothic, Roboto, HelveticaNeue, Arial, sans-serif; } -#calckey_app { +#firefish_app { display: none !important; } body, diff --git a/packages/backend/src/server/web/bios.js b/packages/backend/src/server/web/bios.js index 1acdafd1d5..e715a01068 100644 --- a/packages/backend/src/server/web/bios.js +++ b/packages/backend/src/server/web/bios.js @@ -1,7 +1,7 @@ -'use strict'; +"use strict"; window.onload = async () => { - const account = JSON.parse(localStorage.getItem('account')); + const account = JSON.parse(localStorage.getItem("account")); const i = account.token; const api = (endpoint, data = {}) => { @@ -10,42 +10,44 @@ window.onload = async () => { if (i) data.i = i; // Send request - fetch(endpoint.indexOf('://') > -1 ? endpoint : `/api/${endpoint}`, { - method: 'POST', + fetch(endpoint.indexOf("://") > -1 ? endpoint : `/api/${endpoint}`, { + method: "POST", body: JSON.stringify(data), - credentials: 'omit', - cache: 'no-cache' - }).then(async (res) => { - const body = res.status === 204 ? null : await res.json(); + credentials: "omit", + cache: "no-cache", + }) + .then(async (res) => { + const body = res.status === 204 ? null : await res.json(); - if (res.status === 200) { - resolve(body); - } else if (res.status === 204) { - resolve(); - } else { - reject(body.error); - } - }).catch(reject); + if (res.status === 200) { + resolve(body); + } else if (res.status === 204) { + resolve(); + } else { + reject(body.error); + } + }) + .catch(reject); }); return promise; }; - const content = document.getElementById('content'); + const content = document.getElementById("content"); - document.getElementById('ls').addEventListener('click', () => { - content.innerHTML = ''; + document.getElementById("ls").addEventListener("click", () => { + content.innerHTML = ""; - const lsEditor = document.createElement('div'); - lsEditor.id = 'lsEditor'; + const lsEditor = document.createElement("div"); + lsEditor.id = "lsEditor"; - const adder = document.createElement('div'); - adder.classList.add('adder'); - const addKeyInput = document.createElement('input'); - const addValueTextarea = document.createElement('textarea'); - const addButton = document.createElement('button'); - addButton.textContent = 'Add'; - addButton.addEventListener('click', () => { + const adder = document.createElement("div"); + adder.classList.add("adder"); + const addKeyInput = document.createElement("input"); + const addValueTextarea = document.createElement("textarea"); + const addButton = document.createElement("button"); + addButton.textContent = "Add"; + addButton.addEventListener("click", () => { localStorage.setItem(addKeyInput.value, addValueTextarea.value); location.reload(); }); @@ -57,21 +59,21 @@ window.onload = async () => { for (let i = 0; i < localStorage.length; i++) { const k = localStorage.key(i); - const record = document.createElement('div'); - record.classList.add('record'); - const header = document.createElement('header'); + const record = document.createElement("div"); + record.classList.add("record"); + const header = document.createElement("header"); header.textContent = k; - const textarea = document.createElement('textarea'); + const textarea = document.createElement("textarea"); textarea.textContent = localStorage.getItem(k); - const saveButton = document.createElement('button'); - saveButton.textContent = 'Save'; - saveButton.addEventListener('click', () => { + const saveButton = document.createElement("button"); + saveButton.textContent = "Save"; + saveButton.addEventListener("click", () => { localStorage.setItem(k, textarea.value); location.reload(); }); - const removeButton = document.createElement('button'); - removeButton.textContent = 'Remove'; - removeButton.addEventListener('click', () => { + const removeButton = document.createElement("button"); + removeButton.textContent = "Remove"; + removeButton.addEventListener("click", () => { localStorage.removeItem(k); location.reload(); }); diff --git a/packages/backend/src/server/web/boot.js b/packages/backend/src/server/web/boot.js index f4e0707a93..c8efc4824f 100644 --- a/packages/backend/src/server/web/boot.js +++ b/packages/backend/src/server/web/boot.js @@ -9,120 +9,131 @@ * 注: webpackは介さないため、このファイルではrequireやimportは使えません。 */ -'use strict'; +"use strict"; // ブロックの中に入れないと、定義した変数がブラウザのグローバルスコープに登録されてしまい邪魔なので (async () => { window.onerror = (e) => { console.error(e); - renderError('SOMETHING_HAPPENED', e); + renderError("SOMETHING_HAPPENED", e); }; window.onunhandledrejection = (e) => { console.error(e); - renderError('SOMETHING_HAPPENED_IN_PROMISE', e); + renderError("SOMETHING_HAPPENED_IN_PROMISE", e); }; //#region Detect language & fetch translations - const v = localStorage.getItem('v') || VERSION; + const v = localStorage.getItem("v") || VERSION; const supportedLangs = LANGS; - let lang = localStorage.getItem('lang'); + let lang = localStorage.getItem("lang"); if (lang == null || !supportedLangs.includes(lang)) { if (supportedLangs.includes(navigator.language)) { lang = navigator.language; } else { - lang = supportedLangs.find(x => x.split('-')[0] === navigator.language); + lang = supportedLangs.find((x) => x.split("-")[0] === navigator.language); // Fallback - if (lang == null) lang = 'en-US'; + if (lang == null) lang = "en-US"; } } const res = await fetch(`/assets/locales/${lang}.${v}.json`); if (res.status === 200) { - localStorage.setItem('lang', lang); - localStorage.setItem('locale', await res.text()); - localStorage.setItem('localeVersion', v); + localStorage.setItem("lang", lang); + localStorage.setItem("locale", await res.text()); + localStorage.setItem("localeVersion", v); } else { await checkUpdate(); - renderError('LOCALE_FETCH'); + renderError("LOCALE_FETCH"); return; } //#endregion //#region Script function importAppScript() { - import(`/assets/${CLIENT_ENTRY}`) - .catch(async e => { - await checkUpdate(); - console.error(e); - renderError('APP_IMPORT', e); - }); + import(`/assets/${CLIENT_ENTRY}`).catch(async (e) => { + await checkUpdate(); + console.error(e); + renderError("APP_IMPORT", e); + }); } // タイミングによっては、この時点でDOMの構築が済んでいる場合とそうでない場合とがある - if (document.readyState !== 'loading') { + if (document.readyState !== "loading") { importAppScript(); } else { - window.addEventListener('DOMContentLoaded', () => { + window.addEventListener("DOMContentLoaded", () => { importAppScript(); }); } //#endregion //#region Theme - const theme = localStorage.getItem('theme'); + const theme = localStorage.getItem("theme"); if (theme) { for (const [k, v] of Object.entries(JSON.parse(theme))) { document.documentElement.style.setProperty(`--${k}`, v.toString()); // HTMLの theme-color 適用 - if (k === 'htmlThemeColor') { + if (k === "htmlThemeColor") { for (const tag of document.head.children) { - if (tag.tagName === 'META' && tag.getAttribute('name') === 'theme-color') { - tag.setAttribute('content', v); + if ( + tag.tagName === "META" && + tag.getAttribute("name") === "theme-color" + ) { + tag.setAttribute("content", v); break; } } } } } - const colorSchema = localStorage.getItem('colorSchema'); + const colorSchema = localStorage.getItem("colorSchema"); if (colorSchema) { - document.documentElement.style.setProperty('color-schema', colorSchema); + document.documentElement.style.setProperty("color-schema", colorSchema); } //#endregion - const fontSize = localStorage.getItem('fontSize'); + let fontSize = localStorage.getItem("fontSize"); if (fontSize) { - document.documentElement.classList.add('f-' + fontSize); + if (fontSize < 10) { + // need to do this for now, as values before were 1, 2, 3 depending on the option + localStorage.setItem("fontSize", null); + fontSize = localStorage.getItem("fontSize"); + } + document.documentElement.style.fontSize = `${fontSize}px`; } - const useSystemFont = localStorage.getItem('useSystemFont'); + if (["ja-JP", "ja-KS", "ko-KR", "zh-CN", "zh-TW"].includes(lang)) { + document.documentElement.classList.add("useCJKFont"); + } + + const useSystemFont = localStorage.getItem("useSystemFont"); if (useSystemFont) { - document.documentElement.classList.add('useSystemFont'); + document.documentElement.classList.add("useSystemFont"); } - const wallpaper = localStorage.getItem('wallpaper'); + const wallpaper = localStorage.getItem("wallpaper"); if (wallpaper) { document.documentElement.style.backgroundImage = `url(${wallpaper})`; } - const customCss = localStorage.getItem('customCss'); + const customCss = localStorage.getItem("customCss"); if (customCss && customCss.length > 0) { - const style = document.createElement('style'); + const style = document.createElement("style"); style.innerHTML = customCss; document.head.appendChild(style); } async function addStyle(styleText) { - let css = document.createElement('style'); + const css = document.createElement("style"); css.appendChild(document.createTextNode(styleText)); document.head.appendChild(css); } function renderError(code, details) { - let errorsElement = document.getElementById('errors'); + let errorsElement = document.getElementById("errors"); if (!errorsElement) { document.body.innerHTML = ` @@ -158,9 +169,9 @@
`; - errorsElement = document.getElementById('errors'); + errorsElement = document.getElementById("errors"); } - const detailsElement = document.createElement('details'); + const detailsElement = document.createElement("details"); detailsElement.innerHTML = `
@@ -173,7 +184,7 @@ font-family: BIZ UDGothic, Roboto, HelveticaNeue, Arial, sans-serif; } - #calckey_app, + #firefish_app, #splash { display: none !important; } @@ -278,25 +289,25 @@ details { width: 50%; } - `) + `); } async function checkUpdate() { try { - const res = await fetch('/api/meta', { - method: 'POST', - cache: 'no-cache' + const res = await fetch("/api/meta", { + method: "POST", + cache: "no-cache", }); const meta = await res.json(); if (meta.version != v) { - localStorage.setItem('v', meta.version); + localStorage.setItem("v", meta.version); refresh(); } } catch (e) { console.error(e); - renderError('UPDATE_CHECK', e); + renderError("UPDATE_CHECK", e); throw e; } } @@ -304,9 +315,9 @@ function refresh() { // Clear cache (service worker) try { - navigator.serviceWorker.controller.postMessage('clear'); - navigator.serviceWorker.getRegistrations().then(registrations => { - registrations.forEach(registration => registration.unregister()); + navigator.serviceWorker.controller.postMessage("clear"); + navigator.serviceWorker.getRegistrations().then((registrations) => { + registrations.forEach((registration) => registration.unregister()); }); } catch (e) { console.error(e); diff --git a/packages/backend/src/server/web/cli.css b/packages/backend/src/server/web/cli.css index 6903db9839..740a2aa1a2 100644 --- a/packages/backend/src/server/web/cli.css +++ b/packages/backend/src/server/web/cli.css @@ -6,27 +6,38 @@ main { border-radius: 10px; } #tl > div { - padding: 16px; - border-bottom: 1px solid #908caa; + border: 1px solid #908caa; + border-radius: 10px; + margin: 10px; + padding: 10px; + width: fit-content; } #tl > div > header { font-weight: 700; + display: inline-flex; } -* { - font-family: BIZ UDGothic, Roboto, HelveticaNeue, Arial, sans-serif; +img { + border-radius: 10px; + margin-right: 10px; } -#calckey_app { + +#form { + text-align: center; +} + +#firefish_app { display: none !important; } + body, html { + font-family: BIZ UDGothic, Roboto, HelveticaNeue, Arial, sans-serif; background-color: #191724; color: #e0def4; justify-content: center; margin: auto; padding: 10px; - text-align: center; } button { border-radius:999px; diff --git a/packages/backend/src/server/web/cli.js b/packages/backend/src/server/web/cli.js index 3dff1d4860..85a61a2446 100644 --- a/packages/backend/src/server/web/cli.js +++ b/packages/backend/src/server/web/cli.js @@ -1,54 +1,71 @@ -'use strict'; +"use strict"; window.onload = async () => { - const account = JSON.parse(localStorage.getItem('account')); + const account = JSON.parse(localStorage.getItem("account")); const i = account.token; const api = (endpoint, data = {}) => { const promise = new Promise((resolve, reject) => { // Append a credential if (i) data.i = i; - + // Send request - fetch(endpoint.indexOf('://') > -1 ? endpoint : `/api/${endpoint}`, { - method: 'POST', + fetch(endpoint.indexOf("://") > -1 ? endpoint : `/api/${endpoint}`, { + method: "POST", body: JSON.stringify(data), - credentials: 'omit', - cache: 'no-cache' - }).then(async (res) => { - const body = res.status === 204 ? null : await res.json(); - - if (res.status === 200) { - resolve(body); - } else if (res.status === 204) { - resolve(); - } else { - reject(body.error); - } - }).catch(reject); + credentials: "omit", + cache: "no-cache", + }) + .then(async (res) => { + const body = res.status === 204 ? null : await res.json(); + + if (res.status === 200) { + resolve(body); + } else if (res.status === 204) { + resolve(); + } else { + reject(body.error); + } + }) + .catch(reject); }); - + return promise; }; - document.getElementById('submit').addEventListener('click', () => { - api('notes/create', { - text: document.getElementById('text').value + document.getElementById("submit").addEventListener("click", () => { + api("notes/create", { + text: document.getElementById("text").value, }).then(() => { location.reload(); }); }); - api('notes/timeline').then(notes => { - const tl = document.getElementById('tl'); + api("notes/timeline").then((notes) => { + const tl = document.getElementById("tl"); for (const note of notes) { - const el = document.createElement('div'); - const name = document.createElement('header'); + const el = document.createElement("div"); + const header = document.createElement("header"); + const name = document.createElement("p"); + const avatar = document.createElement("img"); name.textContent = `${note.user.name} @${note.user.username}`; - const text = document.createElement('div'); + avatar.src = note.user.avatarUrl; + avatar.style = "height: 40px"; + const text = document.createElement("div"); text.textContent = `${note.text}`; - el.appendChild(name); - el.appendChild(text); + el.appendChild(header); + header.appendChild(avatar); + header.appendChild(name); + if (note.text) { + el.appendChild(text); + } + if (note.files) { + for (const file of note.files) { + const img = document.createElement("img"); + img.src = file.properties.thumbnailUrl; + el.appendChild(img); + } + } tl.appendChild(el); } }); diff --git a/packages/backend/src/server/web/feed.ts b/packages/backend/src/server/web/feed.ts index 9cbeb28ae1..50e6bfc325 100644 --- a/packages/backend/src/server/web/feed.ts +++ b/packages/backend/src/server/web/feed.ts @@ -4,29 +4,46 @@ import config from "@/config/index.js"; import type { User } from "@/models/entities/user.js"; import { Notes, DriveFiles, UserProfiles, Users } from "@/models/index.js"; -export default async function (user: User) { +export default async function ( + user: User, + threadDepth = 5, + history = 20, + noteintitle = false, + renotes = true, + replies = true, +) { const author = { link: `${config.url}/@${user.username}`, + email: `${user.username}@${config.host}`, name: user.name || user.username, }; const profile = await UserProfiles.findOneByOrFail({ userId: user.id }); + const searchCriteria = { + userId: user.id, + visibility: In(["public", "home"]), + }; + + if (!renotes) { + searchCriteria.renoteId = IsNull(); + } + + if (!replies) { + searchCriteria.replyId = IsNull(); + } + const notes = await Notes.find({ - where: { - userId: user.id, - renoteId: IsNull(), - visibility: In(["public", "home"]), - }, + where: searchCriteria, order: { createdAt: -1 }, - take: 20, + take: history, }); const feed = new Feed({ id: author.link, title: `${author.name} (@${user.username}@${config.host})`, updated: notes[0].createdAt, - generator: "Calckey", + generator: "Firefish", description: `${user.notesCount} Notes, ${ profile.ffVisibility === "public" ? user.followingCount : "?" } Following, ${ @@ -43,22 +60,105 @@ export default async function (user: User) { }); for (const note of notes) { + let contentStr = await noteToString(note, true); + let next = note.renoteId ? note.renoteId : note.replyId; + let depth = threadDepth; + while (depth > 0 && next) { + const finding = await findById(next); + contentStr += finding.text; + next = finding.next; + depth -= 1; + } + + let title = `${author.name} `; + if (note.renoteId) { + title += "renotes"; + } else if (note.replyId) { + title += "replies"; + } else { + title += "says"; + } + if (noteintitle) { + const content = note.cw ?? note.text; + if (content) { + title += `: ${content}`; + } else { + title += "something"; + } + } + + feed.addItem({ + title: title + .replace(/[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g, "") + .substring(0, 100), + link: `${config.url}/notes/${note.id}`, + date: note.createdAt, + description: note.cw + ? note.cw.replace(/[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g, "") + : undefined, + content: contentStr.replace(/[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/g, ""), + }); + } + + async function noteToString(note, isTheNote = false) { + const author = isTheNote + ? null + : await Users.findOneBy({ id: note.userId }); + let outstr = author + ? `${author.name}(@${author.username}@${ + author.host ? author.host : config.host + }) ${ + note.renoteId ? "renotes" : note.replyId ? "replies" : "says" + }:
` + : ""; const files = note.fileIds.length > 0 ? await DriveFiles.findBy({ id: In(note.fileIds), }) : []; - const file = files.find((file) => file.type.startsWith("image/")); + let fileEle = ""; + for (const file of files) { + if (file.type.startsWith("image/")) { + fileEle += `
`; + } else if (file.type.startsWith("audio/")) { + fileEle += `