diff --git a/.github/ISSUE_TEMPLATE/01_bug-report.md b/.github/ISSUE_TEMPLATE/01_bug-report.md index 019f8c739d..8734fc0c36 100644 --- a/.github/ISSUE_TEMPLATE/01_bug-report.md +++ b/.github/ISSUE_TEMPLATE/01_bug-report.md @@ -16,11 +16,11 @@ First, in order to avoid duplicate Issues, please search to see if the problem y -## 🙂 Expected Behavior +## đŸĨ° Expected Behavior -## ☚ī¸ Actual Behavior +## đŸ¤Ŧ Actual Behavior @@ -33,3 +33,7 @@ First, in order to avoid duplicate Issues, please search to see if the problem y ## 📌 Environment + +Misskey version: +Your OS: +Your browser: diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 8abca405fb..2625cf75d3 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -5,7 +5,18 @@ version: 2 updates: - - package-ecosystem: "npm" # See documentation for possible values - directory: "/" # Location of package manifests - schedule: - interval: "daily" +- package-ecosystem: npm + directory: "/" + schedule: + interval: daily + open-pull-requests-limit: 0 +- package-ecosystem: npm + directory: "/packages/backend" + schedule: + interval: daily + open-pull-requests-limit: 0 +- package-ecosystem: npm + directory: "/packages/client" + schedule: + interval: daily + open-pull-requests-limit: 0 diff --git a/.node-version b/.node-version index 971a6537e5..53a42214a4 100644 --- a/.node-version +++ b/.node-version @@ -1 +1 @@ -v16.6.2 +v16.13.2 diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b614734c9..ee59cf73ae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,21 +2,129 @@ ## 12.x.x (unreleased) ### Improvements +- ### Bugfixes +- +You should also include the user name that made the change. --> ## 12.x.x (unreleased) ### Improvements +- ### Bugfixes -- 投į¨ŋぎNSFWį”ģåƒã‚’čĄ¨į¤ēしたあとãĢãƒĒã‚ĸã‚¯ã‚ˇãƒ§ãƒŗが更新されるとį”ģåƒãŒéžčĄ¨į¤ēãĢãĒã‚‹å•éĄŒã‚’äŋŽæ­Ŗ -- 「クãƒĒップ」ペãƒŧジが開かãĒã„å•éĄŒã‚’äŋŽæ­Ŗ -- トãƒŦãƒŗドã‚Ļã‚Ŗジェットが動äŊœã—ãĒいぎをäŋŽæ­Ŗ -- ãƒĒã‚ĸã‚¯ã‚ˇãƒ§ãƒŗč¨­åŽšã§įĩĩ文字ピッã‚Ģãƒŧが開かãĒいぎをäŋŽæ­Ŗ -- DMペãƒŧã‚¸ã§ãƒĄãƒŗã‚ˇãƒ§ãƒŗがåĢãžã‚Œã‚‹å•éĄŒã‚’äŋŽæ­Ŗ +- Client: ãƒĒã‚ĸã‚¯ã‚ˇãƒ§ãƒŗピッã‚ĢãƒŧぎéĢ˜ã•ãŒäŊŽããĒãŖたぞぞæˆģらãĒいことがあるぎをäŋŽæ­Ŗ @syuilo + +## 12.107.0 (2022/02/12) + +### Improvements +- クナイã‚ĸãƒŗト: テãƒŧマをčŋŊ加 @syuilo + +### Bugfixes +- API: stats APIで内部エナãƒŧがį™ēį”Ÿã™ã‚‹å•éĄŒã‚’äŋŽæ­Ŗ @syuilo +- クナイã‚ĸãƒŗト: ã‚ŊフトミãƒĨãƒŧトですずãĻがマッチしãĻしぞう場合があるぎをäŋŽæ­Ŗ @tamaina +- クナイã‚ĸãƒŗト: デバイ゚ぎ゚クãƒĒãƒŧãƒŗぎã‚ģãƒŧフエãƒĒã‚ĸã‚’č€ƒæ…Žã™ã‚‹ã‚ˆã†ãĢ @syuilo +- クナイã‚ĸãƒŗト: 一部į’°åĸƒã§ã‚ĩイドバãƒŧぎ投į¨ŋボã‚ŋãƒŗãŒčĄ¨į¤ēされãĒã„å•éĄŒã‚’äŋŽæ­Ŗ @syuilo + +## 12.106.3 (2022/02/11) + +### Improvements +- クナイã‚ĸãƒŗト: ゚マãƒŧトフりãƒŗでぎäŊ™į™ŊをčĒŋ整 @syuilo + +### Bugfixes +- クナイã‚ĸãƒŗト: ノãƒŧトぎčŠŗį´°ãŒčĄ¨į¤ēされãĒã„å•éĄŒã‚’äŋŽæ­Ŗ @syuilo + +## 12.106.2 (2022/02/11) + +### Bugfixes +- クナイã‚ĸãƒŗト: 削除したノãƒŧトがã‚ŋイムナイãƒŗからč‡Ē動でæļˆãˆãĒã„å•éĄŒã‚’äŋŽæ­Ŗ @syuilo +- クナイã‚ĸãƒŗト: ãƒĒã‚ĸã‚¯ã‚ˇãƒ§ãƒŗ数がæ­ŖしくãĒã„ã“ã¨ãŒã‚ã‚‹å•éĄŒã‚’äŋŽæ­Ŗ @syuilo +- 一部į’°åĸƒã§ãƒžã‚¤ã‚°ãƒŦãƒŧã‚ˇãƒ§ãƒŗが動äŊœã—ãĒã„å•éĄŒã‚’äŋŽæ­Ŗ @syuilo + +## 12.106.1 (2022/02/11) + +### Bugfixes +- クナイã‚ĸãƒŗト: ワãƒŧドミãƒĨãƒŧトがäŋå­˜ã§ããĒã„å•éĄŒã‚’äŋŽæ­Ŗ @syuilo + +## 12.106.0 (2022/02/11) + +### Improvements +- Improve federation chart @syuilo +- クナイã‚ĸãƒŗト: ãƒĒã‚ĸã‚¯ã‚ˇãƒ§ãƒŗピッã‚Ģãƒŧぎã‚ĩイã‚ēã‚’č¨­åŽšã§ãã‚‹ã‚ˆã†ãĢ @syuilo +- クナイã‚ĸãƒŗト: ãƒĒã‚ĸã‚¯ã‚ˇãƒ§ãƒŗピッã‚Ģãƒŧぎ嚅、éĢ˜ã•åˆļ限をįˇŠå’Œ @syuilo +- Docker: Update to Node v16.13.2 @mei23 +- Update dependencies + +### Bugfixes +- validate regular expressions in word mutes @Johann150 + +## 12.105.0 (2022/02/09) + +### Improvements +- イãƒŗã‚šã‚ŋãƒŗ゚ぎテãƒŧマã‚Ģナãƒŧã‚’č¨­åŽšã§ãã‚‹ã‚ˆã†ãĢ @syuilo + +### Bugfixes +- 一部į’°åĸƒã§ãƒžã‚¤ã‚°ãƒŦãƒŧã‚ˇãƒ§ãƒŗãŒå¤ąæ•—ã™ã‚‹å•éĄŒã‚’äŋŽæ­Ŗ @syuilo + +## 12.104.0 (2022/02/09) + +### Note +ビãƒĢドする前ãĢ`npm run clean`ã‚’åŽŸčĄŒã—ãĻください。 + +こぎãƒĒãƒĒãƒŧ゚はマイグãƒŦãƒŧã‚ˇãƒ§ãƒŗぎčĻæ¨ĄãŒå¤§ãã„ため、イãƒŗã‚šã‚ŋãƒŗã‚šãĢよãŖãĻはマイグãƒŦãƒŧã‚ˇãƒ§ãƒŗãĢ時間がかかる可čƒŊ性がありぞす。 +マイグãƒŦãƒŧã‚ˇãƒ§ãƒŗがįĩ‚わらãĒい場合は、チãƒŖãƒŧãƒˆãŽæƒ…å ąã¯ãƒĒã‚ģットされãĻしぞいぞすが`__chart__`で始ぞるテãƒŧブãƒĢぎ**ãƒŦã‚ŗãƒŧド**を全ãĻ削除(テãƒŧブãƒĢč‡ĒäŊ“はæļˆã•ãĒいでください)しãĻから再åēĻčŠĻす斚æŗ•ã‚‚ありぞす。 + +### Improvements +- チãƒŖãƒŧトエãƒŗジãƒŗぎåŧˇåŒ– @syuilo + - テãƒŧブãƒĢã‚ĩイã‚ēぎ削減 + - notes/instance/perUserNotesチãƒŖãƒŧトãĢæˇģäģ˜ãƒ•ã‚Ąã‚¤ãƒĢäģ˜ããƒŽãƒŧトぎ数をčŋŊ加 + - activeUsersチãƒŖãƒŧトãĢ新しい項į›Žã‚’čŋŊ加 + - federationチãƒŖãƒŧトãĢ新しい項į›Žã‚’čŋŊ加 + - apRequestチãƒŖãƒŧトをčŋŊ加 + - networkチãƒŖãƒŧトåģƒæ­ĸ +- クナイã‚ĸãƒŗト: č‡Ēイãƒŗã‚šã‚ŋãƒŗã‚šæƒ…å ąãƒšãƒŧジでチãƒŖãƒŧトをčĻ‹ã‚Œã‚‹ã‚ˆã†ãĢ @syuilo +- クナイã‚ĸãƒŗト: デバイ゚ぎį¨ŽéĄžã‚’手動指厚できるようãĢ @syuilo +- クナイã‚ĸãƒŗト: UIぎã‚ĸイã‚ŗãƒŗを更新 @syuilo +- クナイã‚ĸãƒŗト: UIぎã‚ĸイã‚ŗãƒŗをã‚ģãƒĢフポテã‚ŖãƒŗグするようãĢ @syuilo +- NodeInfo ぎãƒĻãƒŧã‚ļãƒŧ数と投į¨ŋ数ぎ内厚をčĻ‹į›´ã™ @xianonn + +### Bugfixes +- Client: ã‚ŋイムナイãƒŗį¨ŽåˆĨを切りæ›ŋえると「新しいノãƒŧãƒˆãŒã‚ã‚Šãžã™ã€ãŽčĄ¨į¤ēが掋į•™ã—ãĻしぞうぎをäŋŽæ­Ŗ @tamaina +- Client: UIぎã‚ĩイã‚ēがおかしくãĒã‚‹å•éĄŒãŽäŋŽæ­Ŗ @tamaina +- Client: Setting instance information of notes to always show breaks the timeline @Johann150 +- Client: į’°åĸƒãĢ䞝ãŖãĻはčŋ”äŋĄã™ã‚‹éš›ãŽã‚Ģãƒŧã‚ŊãƒĢäŊįŊŽãŒæ­ŖしくãĒã„å•éĄŒã‚’äŋŽæ­Ŗ @syuilo +- Client: ã‚ŗãƒŗトロãƒŧãƒĢパネãƒĢぎãƒĻãƒŧã‚ļãƒŧã€ãƒ•ã‚Ąã‚¤ãƒĢãĢãĻ、イãƒŗã‚šã‚ŋãƒŗã‚šãŽčĄ¨į¤ēį¯„å›˛åˆ‡ã‚Šæ›ŋえが抟čƒŊしãĒã„å•éĄŒã‚’äŋŽæ­Ŗ @syuilo +- Client: ã‚ĸップデãƒŧトおįŸĨらせダイã‚ĸログがå‡ēãĒいぎをäŋŽæ­Ŗ @syuilo +- Client: Follows/Followers Visibility changes won't be saved unless clicking on an other checkbox @Johann150 +- API: Fix API cast @mei23 +- add instance favicon where it's missing @solfisher +- チãƒŖãƒŧトぎ厚期resyncが動äŊœã—ãĻいãĒã„å•éĄŒã‚’äŋŽæ­Ŗ @syuilo + +## 12.103.1 (2022/02/02) + +### Bugfixes +- クナイã‚ĸãƒŗト: ツãƒŧãƒĢãƒãƒƒãƒ—ãŽčĄ¨į¤ēäŊįŊŽãŒæ­ŖしくãĒã„å•éĄŒã‚’äŋŽæ­Ŗ + +## 12.103.0 (2022/02/02) + +### Improvements +- クナイã‚ĸãƒŗト: é€Ŗ合イãƒŗã‚šã‚ŋãƒŗ゚ペãƒŧジからイãƒŗã‚šã‚ŋãƒŗã‚šæƒ…å ąå†å–åž—ã‚’čĄŒãˆã‚‹ã‚ˆã†ãĢ + +### Bugfixes +- クナイã‚ĸãƒŗト: 投į¨ŋぎNSFWį”ģåƒã‚’čĄ¨į¤ēしたあとãĢãƒĒã‚ĸã‚¯ã‚ˇãƒ§ãƒŗが更新されるとį”ģåƒãŒéžčĄ¨į¤ēãĢãĒã‚‹å•éĄŒã‚’äŋŽæ­Ŗ +- クナイã‚ĸãƒŗト: 「クãƒĒップ」ペãƒŧジが開かãĒã„å•éĄŒã‚’äŋŽæ­Ŗ +- クナイã‚ĸãƒŗト: トãƒŦãƒŗドã‚Ļã‚Ŗジェットが動äŊœã—ãĒいぎをäŋŽæ­Ŗ +- クナイã‚ĸãƒŗト: フェデãƒŦãƒŧã‚ˇãƒ§ãƒŗã‚Ļã‚Ŗジェットが動äŊœã—ãĒいぎをäŋŽæ­Ŗ +- クナイã‚ĸãƒŗト: ãƒĒã‚ĸã‚¯ã‚ˇãƒ§ãƒŗč¨­åŽšã§įĩĩ文字ピッã‚Ģãƒŧが開かãĒいぎをäŋŽæ­Ŗ +- クナイã‚ĸãƒŗト: DMペãƒŧã‚¸ã§ãƒĄãƒŗã‚ˇãƒ§ãƒŗがåĢãžã‚Œã‚‹å•éĄŒã‚’äŋŽæ­Ŗ +- クナイã‚ĸãƒŗト: 投į¨ŋフりãƒŧãƒ ãŽãƒãƒƒã‚ˇãƒĨã‚ŋグäŋæŒãƒ•ã‚ŖãƒŧãƒĢドが動äŊœã—ãĒã„å•éĄŒã‚’äŋŽæ­Ŗ +- クナイã‚ĸãƒŗト: ã‚ĩイドビãƒĨãƒŧが動かãĒいぎをäŋŽæ­Ŗ +- クナイã‚ĸãƒŗト: ensure that specified users does not get duplicates +- Add `img-src` and `media-src` directives to `Content-Security-Policy` for + files and media proxy ## 12.102.1 (2022/01/27) ### Bugfixes diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 27f5598a66..6e0f500be5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -3,7 +3,7 @@ We're glad you're interested in contributing Misskey! In this document you will **ℹī¸ Important:** This project uses Japanese as its major language, **but you do not need to translate and write the Issues/PRs in Japanese.** Also, you might receive comments on your Issue/PR in Japanese, but you do not need to reply to them in Japanese as well.\ -The accuracy of translation into Japanese is not high, so it will be easier for us to understand if you write it in the original language. +The accuracy of machine translation into Japanese is not high, so it will be easier for us to understand if you write it in the original language. It will also allow the reader to use the translation tool of their preference if necessary. ## Issues @@ -16,6 +16,9 @@ Before creating an issue, please check the following: ## Before implementation When you want to add a feature or fix a bug, **first have the design and policy reviewed in an Issue** (if it is not there, please make one). Without this step, there is a high possibility that the PR will not be merged even if it is implemented. +At this point, you also need to clarify the goals of the PR you will create, and make sure that the other members of the team are aware of them. +PRs that do not have a clear set of do's and don'ts tend to be bloated and difficult to review. + Also, when you start implementation, assign yourself to the Issue (if you cannot do it yourself, ask another member to assign you). By expressing your intention to work the Issue, you can prevent conflicts in the work. ## Well-known branches @@ -39,6 +42,23 @@ Thank you for your PR! Before creating a PR, please check the following: Thanks for your cooperation 🤗 +## Reviewers guide +Be willing to comment on the good points and not just the things you want fixed đŸ’¯ + +### Review perspective +- Scope + - Are the goals of the PR clear? + - Is the granularity of the PR appropriate? +- Security + - Does merging this PR create a vulnerability? +- Performance + - Will merging this PR cause unexpected performance degradation? + - Is there a more efficient way? +- Testing + - Does the test ensure the expected behavior? + - Are there any omissions or gaps? + - Does it check for anomalies? + ## Localization (l10n) Misskey uses [Crowdin](https://crowdin.com/project/misskey) for localization management. You can improve our translations with your Crowdin account. diff --git a/Dockerfile b/Dockerfile index df86034301..9a651b6c2d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM node:16.6.2-alpine3.13 AS base +FROM node:16.13.2-alpine3.15 AS base ENV NODE_ENV=production diff --git a/cypress/integration/basic.js b/cypress/integration/basic.js index aca44ef15d..7d27b649f4 100644 --- a/cypress/integration/basic.js +++ b/cypress/integration/basic.js @@ -176,3 +176,7 @@ describe('After user singed in', () => { cy.contains('Hello, Misskey!'); }); }); + +// TODO: 投į¨ŋフりãƒŧムぎå…Ŧ開į¯„å›˛æŒ‡åŽšãŽãƒ†ã‚šãƒˆ +// TODO: 投į¨ŋフりãƒŧãƒ ãŽãƒ•ã‚Ąã‚¤ãƒĢæˇģäģ˜ãŽãƒ†ã‚šãƒˆ +// TODO: 投į¨ŋフりãƒŧãƒ ãŽãƒãƒƒã‚ˇãƒĨã‚ŋグäŋæŒãƒ•ã‚ŖãƒŧãƒĢドぎテ゚ト diff --git a/gulpfile.js b/gulpfile.js index 3bc0b23bee..b7aa4e328e 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -19,6 +19,10 @@ 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:fontawesome', () => + gulp.src('./packages/client/node_modules/@fortawesome/fontawesome-free/**/*').pipe(gulp.dest('./built/_client_dist_/fontawesome/')) +); + gulp.task('copy:client:locales', cb => { fs.mkdirSync('./built/_client_dist_/locales', { recursive: true }); @@ -50,7 +54,7 @@ gulp.task('build:backend:style', () => { }); gulp.task('build', gulp.parallel( - 'copy:client:locales', 'copy:backend:views', 'build:backend:script', 'build:backend:style', 'copy:client:fonts' + 'copy:client:locales', 'copy:backend:views', 'build:backend:script', 'build:backend:style', 'copy:client:fonts', 'copy:client:fontawesome' )); gulp.task('default', gulp.task('build')); diff --git a/locales/ar-SA.yml b/locales/ar-SA.yml index 5a053cdee9..914a16bb2f 100644 --- a/locales/ar-SA.yml +++ b/locales/ar-SA.yml @@ -1110,6 +1110,8 @@ _exportOrImport: blockingList: "اŲ„Ų…ØŗØĒ؎دŲ…ŲˆŲ† اŲ„Ų…Ø­ØŦŲˆØ¨ŲˆŲ†" userLists: "اŲ„Ų‚ŲˆØ§ØĻŲ…" _charts: + federation: "اŲ„ŲØ¯ŲŠØąØ§Ų„ŲŠØŠ" + apRequest: "اŲ„ØˇŲ„باØĒ" usersIncDec: "ا؎ØĒŲ„اŲ ؚدد اŲ„Ų…ØŗØĒ؎دŲ…ŲŠŲ†" usersTotal: "Ų…ØŦŲ…ŲˆØš ؚدد اŲ„Ų…ØŗØĒ؎دŲ…ŲŠŲ† ŲˆØ§Ų„Ų…ØŗØĒ؎دŲ…اØĒ" activeUsers: "اŲ„Ų…ØŗØĒ؎دŲ…ŲˆŲ† اŲ„Ų†Ø´ØˇŲˆŲ†" diff --git a/locales/bn-BD.yml b/locales/bn-BD.yml index ed97d539c0..5cc2bb91b6 100644 --- a/locales/bn-BD.yml +++ b/locales/bn-BD.yml @@ -1 +1,1640 @@ --- +_lang_: "āĻŦāĻžāĻ‚āĻ˛āĻž" +headlineMisskey: "āĻ¨ā§‹āĻŸ āĻŦā§āĻ¯āĻžāĻŦāĻšāĻžāĻ° āĻ•āĻ°ā§‡ āĻ¸āĻ‚āĻ¯ā§āĻ•ā§āĻ¤ āĻ¨ā§‡āĻŸāĻ“ā§ŸāĻžāĻ°ā§āĻ•" +introMisskey: "āĻ¸ā§āĻŦāĻžāĻ—āĻ¤āĻŽ! āĻŽāĻŋāĻ¸āĻ•āĻŋ āĻāĻ•āĻŸāĻŋ āĻ“āĻĒā§‡āĻ¨ āĻ¸ā§‹āĻ°ā§āĻ¸, āĻĄāĻŋāĻ¸ā§‡āĻ¨ā§āĻŸā§āĻ°āĻžāĻ˛āĻžāĻ‡āĻœāĻĄ āĻŽāĻžāĻ‡āĻ•ā§āĻ°ā§‹āĻŦā§āĻ˛āĻ—āĻŋāĻ‚ āĻĒāĻ°āĻŋāĻˇā§‡āĻŦāĻžāĨ¤ \n\"āĻ¨ā§‹āĻŸ\" āĻ¤ā§ˆāĻ°āĻŋāĻ° āĻŽāĻžāĻ§ā§āĻ¯āĻŽā§‡ āĻ¯āĻž āĻ˜āĻŸāĻ›ā§‡ āĻ¤āĻž āĻ¸āĻŦāĻžāĻ° āĻ¸āĻžāĻĨā§‡ āĻļā§‡ā§ŸāĻžāĻ° āĻ•āĻ°ā§āĻ¨ 📡\n\"āĻ°āĻŋāĻ…ā§āĻ¯āĻžāĻ•āĻļāĻ¨\" āĻ—ā§āĻ˛āĻŋāĻ° āĻŽāĻžāĻ§ā§āĻ¯āĻŽā§‡ āĻ¯ā§‡āĻ•ā§‹āĻ¨ā§‹ āĻ¨ā§‹āĻŸ āĻ¸āĻŽā§āĻĒāĻ°ā§āĻ•ā§‡ āĻ†āĻĒāĻ¨āĻžāĻ° āĻ…āĻ¨ā§āĻ­ā§‚āĻ¤āĻŋ āĻŦā§āĻ¯āĻžāĻ•ā§āĻ¤ āĻ•āĻ°āĻ¤ā§‡ āĻĒāĻžāĻ°ā§‡āĻ¨ 👍\nāĻāĻ•āĻŸāĻŋ āĻ¨āĻ¤ā§āĻ¨ āĻĻā§āĻ¨āĻŋā§ŸāĻž āĻ˜ā§āĻ°ā§‡ āĻĻā§‡āĻ–ā§āĻ¨ 🚀\n" +monthAndDay: "{day}/{month}" +search: "āĻ–ā§āĻāĻœā§āĻ¨" +notifications: "āĻŦāĻŋāĻœā§āĻžāĻĒā§āĻ¤āĻŋ" +username: "āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ°āĻ•āĻžāĻ°ā§€āĻ° āĻ¨āĻžāĻŽ" +password: "āĻĒāĻžāĻ¸āĻ“āĻ¯āĻŧāĻžāĻ°ā§āĻĄ" +forgotPassword: "āĻĒāĻžāĻ¸āĻ“āĻ¯āĻŧāĻžāĻ°ā§āĻĄ āĻ­ā§āĻ˛ā§‡ āĻ—ā§‡āĻ›ā§‡āĻ¨" +fetchingAsApObject: "āĻĢā§‡āĻĄāĻŋāĻ­āĻžāĻ°ā§āĻ¸ āĻĨā§‡āĻ•ā§‡ āĻ–āĻŦāĻ° āĻ†āĻ¨āĻž āĻšāĻšā§āĻ›ā§‡..." +ok: "āĻ āĻŋāĻ•" +gotIt: "āĻŦā§āĻā§‡āĻ›āĻŋ" +cancel: "āĻŦāĻžāĻ¤āĻŋāĻ˛" +enterUsername: "āĻ‡āĻ‰āĻœāĻžāĻ°āĻ¨ā§‡āĻŽ āĻ˛āĻŋāĻ–ā§āĻ¨" +renotedBy: "{user} āĻ°āĻŋāĻ¨ā§‹āĻŸ āĻ•āĻ°ā§‡āĻ›ā§‡āĻ¨" +noNotes: "āĻ•ā§‹āĻ¨ āĻ¨ā§‹āĻŸ āĻ¨ā§‡āĻ‡" +noNotifications: "āĻ•ā§‹āĻ¨ā§‹ āĻŦāĻŋāĻœā§āĻžāĻĒā§āĻ¤āĻŋ āĻ¨ā§‡āĻ‡" +instance: "āĻ‡āĻ¨ā§āĻ¸āĻŸā§āĻ¯āĻžāĻ¨ā§āĻ¸" +settings: "āĻ¸ā§‡āĻŸāĻŋāĻ‚āĻ¸" +basicSettings: "āĻ¸āĻžāĻ§āĻžāĻ°āĻŖ āĻ¸ā§‡āĻŸāĻŋāĻ‚āĻ¸" +otherSettings: "āĻ…āĻ¨ā§āĻ¯āĻžāĻ¨ā§āĻ¯ āĻ¸ā§‡āĻŸāĻŋāĻ‚āĻ¸" +openInWindow: "āĻ¨āĻ¤ā§āĻ¨ āĻ‰āĻ‡āĻ¨ā§āĻĄā§‹āĻ¤ā§‡ āĻ–ā§āĻ˛āĻž" +profile: "āĻĒā§āĻ°ā§‹āĻĢāĻžāĻ‡āĻ˛" +timeline: "āĻŸāĻžāĻ‡āĻŽāĻ˛āĻžāĻ‡āĻ¨" +noAccountDescription: "āĻāĻ‡ āĻŦā§āĻ¯āĻžāĻŦāĻšāĻžāĻ°āĻ•āĻžāĻ°ā§€āĻ° āĻ•ā§‹āĻ¨ āĻŦāĻžā§Ÿā§‹ āĻ¨ā§‡āĻ‡" +login: "āĻĒā§āĻ°āĻŦā§‡āĻļ āĻ•āĻ°ā§āĻ¨" +loggingIn: "āĻĒā§āĻ°āĻŦā§‡āĻļ āĻ•āĻ°āĻž āĻšāĻšā§āĻ›ā§‡..." +logout: "āĻ˛āĻ—āĻ†āĻ‰āĻŸ" +signup: "āĻ¨āĻŋāĻŦāĻ¨ā§āĻ§āĻ¨ āĻ•āĻ°ā§āĻ¨" +uploading: "āĻ†āĻĒāĻ˛ā§‹āĻĄ āĻšāĻšā§āĻ› â€Ļ" +save: "āĻ¸āĻ‚āĻ°āĻ•ā§āĻˇāĻŖ" +users: "āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ°āĻ•āĻžāĻ°ā§€āĻ—āĻŖ" +addUser: "āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ°āĻ•āĻžāĻ°ā§€ āĻ¯ā§‹āĻ— āĻ•āĻ°ā§āĻ¨" +favorite: "āĻĒāĻ›āĻ¨ā§āĻĻ" +favorites: "āĻĒāĻ›āĻ¨ā§āĻĻāĻ—ā§āĻ˛āĻŋ" +unfavorite: "āĻĒāĻ›āĻ¨ā§āĻĻ āĻ¨āĻž" +favorited: "āĻĒāĻ›āĻ¨ā§āĻĻ āĻ•āĻ°āĻž āĻšā§Ÿā§‡āĻ›ā§‡" +alreadyFavorited: "āĻ‡āĻ¤āĻŋāĻŽāĻ§ā§āĻ¯ā§‡ āĻĒāĻ›āĻ¨ā§āĻĻ āĻ•āĻ°āĻž āĻšā§Ÿā§‡āĻ›ā§‡" +cantFavorite: "āĻĒāĻ›āĻ¨ā§āĻĻ āĻ•āĻ°āĻž āĻ¯āĻžā§ŸāĻ¨āĻŋ" +pin: "āĻĒāĻŋāĻ¨ āĻ•āĻ°āĻž" +unpin: "āĻĒāĻŋāĻ¨ āĻ¸āĻ°āĻžāĻ¨" +copyContent: "āĻŦāĻŋāĻˇā§ŸāĻŦāĻ¸ā§āĻ¤ā§ āĻ•āĻĒāĻŋ āĻ•āĻ°ā§āĻ¨" +copyLink: "āĻ˛āĻŋāĻ™ā§āĻ• āĻ•āĻĒāĻŋ āĻ•āĻ°ā§āĻ¨" +delete: "āĻŽā§āĻ›ā§āĻ¨" +deleteAndEdit: "āĻŽā§āĻ›ā§āĻ¨ āĻāĻŦāĻ‚ āĻ¸āĻŽā§āĻĒāĻžāĻĻāĻ¨āĻž āĻ•āĻ°ā§āĻ¨" +deleteAndEditConfirm: "āĻ†āĻĒāĻ¨āĻŋ āĻ•āĻŋ āĻāĻ‡ āĻ¨ā§‹āĻŸāĻŸāĻŋ āĻŽā§āĻ›ā§‡ āĻāĻŸāĻŋ āĻ¸āĻŽā§āĻĒāĻžāĻĻāĻ¨āĻž āĻ•āĻ°āĻžāĻ° āĻŦāĻŋāĻˇāĻ¯āĻŧā§‡ āĻ¨āĻŋāĻļā§āĻšāĻŋāĻ¤? āĻ†āĻĒāĻ¨āĻŋ āĻāĻŸāĻŋāĻ° āĻ¸āĻŽāĻ¸ā§āĻ¤ āĻ°āĻŋāĻ…ā§āĻ¯āĻžāĻ•āĻļāĻ¨, āĻ°āĻŋāĻ¨ā§‹āĻŸ āĻāĻŦāĻ‚ āĻœāĻŦāĻžāĻŦ āĻšāĻžāĻ°āĻžāĻŦā§‡āĻ¨āĨ¤" +addToList: "āĻ˛āĻŋāĻ¸ā§āĻŸ āĻ āĻ¯ā§‹āĻ— āĻ•āĻ°ā§āĻ¨" +sendMessage: "āĻāĻ•āĻŸāĻŋ āĻŦāĻžāĻ°ā§āĻ¤āĻž āĻĒāĻžāĻ āĻžāĻ¨" +copyUsername: "āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ°āĻ•āĻžāĻ°ā§€āĻ° āĻ¨āĻžāĻŽ āĻ•āĻĒāĻŋ āĻ•āĻ°ā§āĻ¨" +searchUser: "āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ°āĻ•āĻžāĻ°ā§€ āĻ–ā§āĻāĻœā§āĻ¨..." +reply: "āĻœāĻŦāĻžāĻŦ" +loadMore: "āĻ†āĻ°āĻ“ āĻĻā§‡āĻ–ā§āĻ¨" +showMore: "āĻ†āĻ°āĻ“ āĻĻā§‡āĻ–ā§āĻ¨" +youGotNewFollower: "āĻ†āĻĒāĻ¨āĻžāĻ•ā§‡ āĻ…āĻ¨ā§āĻ¸āĻ°āĻŖ āĻ•āĻ°āĻ›ā§‡" +receiveFollowRequest: "āĻ…āĻ¨ā§āĻ¸āĻ°āĻŖ āĻ•āĻ°āĻžāĻ° āĻœāĻ¨ā§āĻ¯ āĻ…āĻ¨ā§āĻ°ā§‹āĻ§ āĻĒāĻžāĻ“ā§ŸāĻž āĻ—ā§‡āĻ›ā§‡" +followRequestAccepted: "āĻ…āĻ¨ā§āĻ¸āĻ°āĻŖ āĻ•āĻ°āĻžāĻ° āĻ…āĻ¨ā§āĻ°ā§‹āĻ§ āĻ—ā§ƒāĻšā§€āĻ¤ āĻšā§Ÿā§‡āĻ›ā§‡" +mention: "āĻ‰āĻ˛ā§āĻ˛ā§‡āĻ–" +mentions: "āĻ‰āĻ˛ā§āĻ˛ā§‡āĻ–āĻ¸āĻŽā§‚āĻš" +directNotes: "āĻĄāĻžāĻ‡āĻ°ā§‡āĻ•ā§āĻŸ āĻ¨ā§‹āĻŸāĻ—ā§āĻ˛āĻŋ" +importAndExport: "āĻ†āĻŽāĻĻāĻžāĻ¨āĻŋ āĻāĻŦāĻ‚ āĻ°āĻĒā§āĻ¤āĻžāĻ¨āĻŋ" +import: "āĻ†āĻŽāĻĻāĻžāĻ¨āĻŋ āĻ•āĻ°ā§āĻŖ" +export: "āĻ°āĻĒā§āĻ¤āĻžāĻ¨āĻŋ" +files: "āĻĢāĻžāĻ‡āĻ˛āĻ—ā§āĻ˛āĻŋ" +download: "āĻĄāĻžāĻ‰āĻ¨āĻ˛ā§‹āĻĄ" +driveFileDeleteConfirm: "āĻ†āĻĒāĻ¨āĻŋ āĻ•āĻŋ āĻ¨āĻŋāĻļā§āĻšāĻŋāĻ¤ āĻ¯ā§‡ āĻ†āĻĒāĻ¨āĻŋ \"{name}\" āĻĄāĻŋāĻ˛āĻŋāĻŸ āĻ•āĻ°āĻ¤ā§‡ āĻšāĻžāĻ¨? āĻ¯ā§‡ āĻ¸āĻ•āĻ˛ āĻ¨ā§‹āĻŸā§‡āĻ° āĻ¸āĻžāĻĨā§‡ āĻāĻ‡ āĻĢāĻžāĻ‡āĻ˛āĻŸāĻŋ āĻ¸āĻ‚āĻ¯ā§āĻ•ā§āĻ¤ āĻ¸ā§‡āĻ—ā§āĻ˛ā§‹āĻ“ āĻĄāĻŋāĻ˛āĻŋāĻŸ āĻ•āĻ°āĻž āĻšāĻŦā§‡āĨ¤" +unfollowConfirm: "{name} āĻ•ā§‡ āĻ†āĻ¨āĻĢāĻ˛ā§‹āĻ“ āĻ•āĻ°āĻžāĻ° āĻŦā§āĻ¯āĻžāĻĒāĻžāĻ°ā§‡ āĻ¨āĻŋāĻļā§āĻšāĻŋāĻ¤?" +exportRequested: "āĻ†āĻĒāĻ¨āĻžāĻ° āĻ¤āĻĨā§āĻ¯āĻ¸āĻŽā§‚āĻš āĻ°āĻĒā§āĻ¤āĻžāĻ¨āĻŋāĻ° āĻœāĻ¨ā§āĻ¯ āĻ…āĻ¨ā§āĻ°ā§‹āĻ§ āĻ•āĻ°ā§‡āĻ›ā§‡āĻ¨āĨ¤ āĻāĻ¤ā§‡ āĻ•āĻŋāĻ›ā§ āĻ¸āĻŽā§Ÿ āĻ˛āĻžāĻ—āĻ¤ā§‡ āĻĒāĻžāĻ°ā§‡āĨ¤ āĻ°āĻĒā§āĻ¤āĻžāĻ¨āĻŋ āĻ¸āĻŽā§āĻĒāĻ¨ā§āĻ¨ āĻšāĻ˛ā§‡ āĻ¤āĻž āĻ†āĻĒāĻ¨āĻžāĻ° āĻĄā§āĻ°āĻžāĻ‡āĻ­ā§‡ āĻ¸āĻ‚āĻ°āĻ•ā§āĻˇāĻŋāĻ¤ āĻšāĻŦā§‡āĨ¤" +importRequested: "āĻ†āĻĒāĻ¨āĻžāĻ° āĻ¤āĻĨā§āĻ¯āĻ¸āĻŽā§‚āĻš āĻ†āĻŽāĻĻāĻžāĻ¨āĻŋāĻ° āĻœāĻ¨ā§āĻ¯ āĻ…āĻ¨ā§āĻ°ā§‹āĻ§ āĻ•āĻ°ā§‡āĻ›ā§‡āĻ¨āĨ¤ āĻāĻ¤ā§‡ āĻ•āĻŋāĻ›ā§ āĻ¸āĻŽā§Ÿ āĻ˛āĻžāĻ—āĻ¤ā§‡ āĻĒāĻžāĻ°ā§‡āĨ¤ " +lists: "āĻ˛āĻŋāĻ¸ā§āĻŸ" +noLists: "āĻ•ā§‹āĻ¨ āĻ˛āĻŋāĻ¸ā§āĻŸ āĻ¨ā§‡āĻ‡" +note: "āĻ¨ā§‹āĻŸ" +notes: "āĻ¨ā§‹āĻŸāĻ—ā§āĻ˛āĻŋ" +following: "āĻ…āĻ¨ā§āĻ¸āĻ°āĻŖ āĻ•āĻ°āĻž āĻšāĻšā§āĻ›ā§‡" +followers: "āĻ…āĻ¨ā§āĻ¸āĻ°āĻŖāĻ•āĻžāĻ°ā§€" +followsYou: "āĻ†āĻĒāĻ¨āĻžāĻ•ā§‡ āĻ…āĻ¨ā§āĻ¸āĻ°āĻŖ āĻ•āĻ°ā§‡" +createList: "āĻ˛āĻŋāĻ¸ā§āĻŸ āĻ¤ā§ˆāĻ°āĻŋ āĻ•āĻ°ā§āĻ¨" +manageLists: "āĻ˛āĻŋāĻ¸ā§āĻŸ āĻŦā§āĻ¯āĻžāĻŦāĻ¸ā§āĻĨāĻžāĻĒāĻ¨āĻž" +error: "āĻ¸āĻŽāĻ¸ā§āĻ¯āĻž" +somethingHappened: "āĻāĻ•āĻŸāĻŋ āĻ¤ā§āĻ°ā§āĻŸāĻŋ āĻšāĻ¯āĻŧā§‡āĻ›ā§‡" +retry: "āĻ†āĻŦāĻžāĻ° āĻšā§‡āĻˇā§āĻŸāĻž āĻ•āĻ°ā§āĻ¨" +pageLoadError: "āĻĒā§‡āĻœ āĻ˛ā§‹āĻĄ āĻ•āĻ°āĻž āĻ¯āĻžā§ŸāĻ¨āĻŋ" +pageLoadErrorDescription: "āĻāĻŸāĻŋ āĻ¸āĻžāĻ§āĻžāĻ°āĻ¨āĻ¤ āĻ¨ā§‡āĻŸāĻ“ā§ŸāĻžāĻ°ā§āĻ•ā§‡āĻ° āĻ¸āĻŽāĻ¸ā§āĻ¯āĻžāĻ° āĻŦāĻž āĻŦā§āĻ°āĻžāĻ‰āĻœāĻžāĻ° āĻ•ā§āĻ¯āĻžāĻļā§‡āĻ° āĻ•āĻžāĻ°āĻŖā§‡ āĻ˜āĻŸā§‡ āĻĨāĻžāĻ•ā§‡āĨ¤ āĻŦā§āĻ°āĻžāĻ‰āĻœāĻžāĻ° āĻāĻ° āĻ•ā§āĻ¯āĻžāĻļ āĻĒāĻ°āĻŋāĻˇā§āĻ•āĻžāĻ° āĻ•āĻ°ā§āĻ¨ āĻāĻŦāĻ‚ āĻāĻ•āĻŸā§ āĻĒāĻ° āĻ†āĻŦāĻžāĻ° āĻšā§‡āĻˇā§āĻŸāĻž āĻ•āĻ°ā§āĻ¨āĨ¤ " +serverIsDead: "āĻāĻ‡ āĻ¸āĻžāĻ°ā§āĻ­āĻžāĻ° āĻŦāĻ°ā§āĻ¤āĻŽāĻžāĻ¨ā§‡ āĻ¸āĻžā§œāĻž āĻĻāĻŋāĻšā§āĻ›ā§‡ āĻ¨āĻžāĨ¤ āĻāĻ•āĻŸā§ āĻĒāĻ°ā§‡ āĻ†āĻŦāĻžāĻ° āĻšā§‡āĻˇā§āĻŸāĻž āĻ•āĻ°ā§āĻ¨āĨ¤" +youShouldUpgradeClient: "āĻāĻ‡ āĻĒā§‡āĻœ āĻĻā§‡āĻ–āĻžāĻ° āĻœāĻ¨ā§āĻ¯ āĻ†āĻĒāĻ¨āĻžāĻ° āĻŦā§āĻ°āĻžāĻ‰āĻœāĻžāĻ° āĻ°āĻŋāĻĢā§āĻ°ā§‡āĻļ āĻ•āĻ°ā§‡ āĻ•ā§āĻ˛āĻžā§Ÿā§‡āĻ¨ā§āĻŸ āĻ†āĻĒāĻĄā§‡āĻŸ āĻ•āĻ°ā§āĻ¨āĨ¤ " +enterListName: "āĻ˛āĻŋāĻ¸ā§āĻŸā§‡āĻ° āĻ¨āĻžāĻŽ āĻ˛āĻŋāĻ–ā§āĻ¨" +privacy: "āĻ—ā§‹āĻĒāĻ¨ā§€ā§ŸāĻ¤āĻž" +makeFollowManuallyApprove: "āĻ…āĻ¨ā§āĻ¸āĻ°āĻŖ āĻ•āĻ°āĻžāĻ° āĻ…āĻ¨ā§āĻ°ā§‹āĻ§āĻ—ā§āĻ˛āĻŋ āĻ—ā§ƒāĻšā§€āĻ¤ āĻšāĻ“ā§ŸāĻžāĻ° āĻœāĻ¨ā§āĻ¯ āĻ†āĻĒāĻ¨āĻžāĻ° āĻ…āĻ¨ā§āĻŽāĻ¤āĻŋ āĻ˛āĻžāĻ—āĻŦā§‡" +defaultNoteVisibility: "āĻĄāĻŋāĻĢāĻ˛ā§āĻŸ āĻĻā§ƒāĻļā§āĻ¯āĻŽāĻžāĻ¨ā§āĻ¯āĻ¤āĻž" +follow: "āĻ…āĻ¨ā§āĻ¸āĻ°āĻŖ" +followRequest: "āĻ…āĻ¨ā§āĻ¸āĻ°āĻŖ āĻ•āĻ°āĻžāĻ° āĻ…āĻ¨ā§āĻ°ā§‹āĻ§" +followRequests: "āĻ…āĻ¨ā§āĻ¸āĻ°āĻŖ āĻ•āĻ°āĻžāĻ° āĻ…āĻ¨ā§āĻ°ā§‹āĻ§āĻ¸āĻŽā§‚āĻš" +unfollow: "āĻ…āĻ¨ā§āĻ¸āĻ°āĻŖ āĻŦāĻžāĻ¤āĻŋāĻ˛" +followRequestPending: "āĻ…āĻ¨ā§āĻ¸āĻ°āĻŖ āĻ•āĻ°āĻžāĻ° āĻ…āĻ¨ā§āĻ°ā§‹āĻ§ āĻŦāĻŋāĻšāĻžāĻ°āĻžāĻ§ā§€āĻ¨" +enterEmoji: "āĻ‡āĻŽā§‹āĻœāĻŋ āĻĒā§āĻ°āĻŦā§‡āĻļ āĻ•āĻ°āĻžāĻ¨" +renote: "āĻ°āĻŋāĻ¨ā§‹āĻŸ" +unrenote: "āĻ°āĻŋāĻ¨ā§‹āĻŸ āĻ¸āĻ°āĻžāĻ¨ " +renoted: "āĻ°āĻŋāĻ¨ā§‹āĻŸ āĻ•āĻ°āĻž āĻšā§Ÿā§‡āĻ›ā§‡" +cantRenote: "āĻāĻ‡ āĻ¨ā§‹āĻŸāĻŸāĻŋ āĻ°āĻŋāĻ¨ā§‹āĻŸ āĻ•āĻ°āĻž āĻ¯āĻžāĻŦā§‡ āĻ¨āĻžāĨ¤" +cantReRenote: "āĻ°āĻŋāĻ¨ā§‹āĻŸāĻ•ā§‡ āĻ°āĻŋāĻ¨ā§‹āĻŸ āĻ•āĻ°āĻž āĻ¯āĻžāĻŦā§‡ āĻ¨āĻžāĨ¤" +quote: "āĻ‰āĻĻā§āĻ§ā§ƒāĻ¤āĻŋ" +pinnedNote: "āĻĒāĻŋāĻ¨ āĻ•āĻ°āĻž āĻ¨ā§‹āĻŸ" +pinned: "āĻĒāĻŋāĻ¨ āĻ•āĻ°āĻž" +you: "āĻ†āĻĒāĻ¨āĻŋ" +clickToShow: "āĻĻā§‡āĻ–āĻžāĻ° āĻœāĻ¨ā§āĻ¯ āĻ•ā§āĻ˛āĻŋāĻ• āĻ•āĻ°ā§āĻ¨" +sensitive: "āĻ¸āĻ‚āĻŦā§‡āĻĻāĻ¨āĻļā§€āĻ˛ āĻŦāĻŋāĻˇā§ŸāĻŦāĻ¸ā§āĻ¤ā§" +add: "āĻ¯ā§āĻ•ā§āĻ¤ āĻ•āĻ°ā§āĻ¨" +reaction: "āĻĒā§āĻ°āĻ¤āĻŋāĻ•ā§āĻ°āĻŋāĻ¯āĻŧāĻž" +reactionSetting: "āĻ°āĻŋāĻ…ā§āĻ¯āĻžāĻ•āĻļāĻ¨ āĻĒāĻŋāĻ•āĻžāĻ°ā§‡ āĻ¯ā§‡āĻ¸āĻ•āĻ˛ āĻĒā§āĻ°āĻ¤āĻŋāĻ•ā§āĻ°āĻŋā§ŸāĻž āĻĻā§‡āĻ–āĻžāĻ¨ā§‹ āĻšāĻŦā§‡" +reactionSettingDescription2: "āĻĒā§āĻ¨āĻ°āĻžāĻ¯āĻŧ āĻ¸āĻžāĻœāĻžāĻ¤ā§‡ āĻŸā§‡āĻ¨ā§‡ āĻ†āĻ¨ā§āĻ¨, āĻŽā§āĻ›āĻ¤ā§‡ āĻ•ā§āĻ˛āĻŋāĻ• āĻ•āĻ°ā§āĻ¨, āĻ¯ā§‹āĻ— āĻ•āĻ°āĻ¤ā§‡ + āĻŸāĻŋāĻĒā§āĻ¨āĨ¤" +rememberNoteVisibility: "āĻ¨ā§‹āĻŸā§‡āĻ° āĻĻā§ƒāĻļā§āĻ¯āĻŽāĻžāĻ¨ā§āĻ¯āĻ¤āĻžāĻ° āĻ¸ā§‡āĻŸāĻŋāĻ‚āĻ¸ āĻŽāĻ¨ā§‡ āĻ°āĻžāĻ–ā§āĻ¨" +attachCancel: "āĻ…ā§āĻ¯āĻžāĻŸāĻžāĻšāĻŽā§‡āĻ¨ā§āĻŸ āĻ¸āĻ°āĻžāĻ¨ " +markAsSensitive: "āĻ¸āĻ‚āĻŦā§‡āĻĻāĻ¨āĻļā§€āĻ˛ āĻšāĻŋāĻ¸āĻžāĻŦā§‡ āĻšāĻŋāĻšā§āĻ¨āĻŋāĻ¤ āĻ•āĻ°ā§āĻ¨" +unmarkAsSensitive: "āĻ¸āĻ‚āĻŦā§‡āĻĻāĻ¨āĻļā§€āĻ˛ āĻšāĻŋāĻšā§āĻ¨ āĻ¸āĻ°āĻžāĻ¨" +enterFileName: "āĻĢāĻžāĻ‡āĻ˛ā§‡āĻ° āĻ¨āĻžāĻŽ āĻ˛āĻŋāĻ–ā§āĻ¨" +mute: "āĻŽāĻŋāĻ‰āĻŸ" +unmute: "āĻ†āĻ¨āĻŽāĻŋāĻ‰āĻŸ" +block: "āĻŦā§āĻ˛āĻ•" +unblock: "āĻŦā§āĻ˛āĻ• āĻ¸āĻ°āĻžāĻ¨" +suspend: "āĻ¸ā§āĻĨāĻ—āĻŋāĻ¤ āĻ•āĻ°āĻž" +unsuspend: "āĻ…āĻ¸ā§āĻĨāĻ—āĻŋāĻ¤ āĻ•āĻ°āĻž" +blockConfirm: "āĻŦā§āĻ˛āĻ• āĻ•āĻ°āĻ¤ā§‡ āĻšāĻžāĻ¨?" +unblockConfirm: "āĻŦā§āĻ˛āĻ• āĻ¸āĻ°āĻžāĻ¤ā§‡ āĻšāĻžāĻ¨?" +suspendConfirm: "āĻ¸ā§āĻĨāĻ—āĻŋāĻ¤ āĻ•āĻ°āĻ¤ā§‡ āĻšāĻžāĻ¨?" +unsuspendConfirm: "āĻ…āĻ¸ā§āĻĨāĻ—āĻŋāĻ¤ āĻ•āĻ°āĻ¤ā§‡ āĻšāĻžāĻ¨?" +selectList: "āĻ˛āĻŋāĻ¸ā§āĻŸ āĻ¨āĻŋāĻ°ā§āĻŦāĻžāĻšāĻ¨ āĻ•āĻ°ā§āĻ¨" +selectAntenna: "āĻ…ā§āĻ¯āĻžāĻ¨ā§āĻŸā§‡āĻ¨āĻž āĻ¨āĻŋāĻ°ā§āĻŦāĻžāĻšāĻ¨ āĻ•āĻ°ā§āĻ¨" +selectWidget: "āĻ‰āĻ‡āĻœā§‡āĻŸ āĻ¨āĻŋāĻ°ā§āĻŦāĻžāĻšāĻ¨ āĻ•āĻ°ā§āĻ¨" +editWidgets: "āĻ‰āĻ‡āĻœā§‡āĻŸ āĻ¸āĻŽā§āĻĒāĻžāĻĻāĻ¨āĻž āĻ•āĻ°ā§āĻ¨" +editWidgetsExit: "āĻ¸āĻŽā§āĻĒāĻžāĻĻāĻ¨āĻž āĻļā§‡āĻˇ āĻ•āĻ°ā§āĻ¨" +customEmojis: "āĻ¸ā§āĻŦāĻ¨āĻŋāĻ°ā§āĻ§āĻžāĻ°āĻŋāĻ¤ āĻ‡āĻŽā§‹āĻœāĻŋāĻ—ā§āĻ˛āĻŋ" +emoji: "āĻ‡āĻŽā§‹āĻœāĻŋ" +emojis: "āĻ‡āĻŽā§‹āĻœāĻŋāĻ—ā§āĻ˛āĻŋ" +emojiName: "āĻ‡āĻŽā§‹āĻœāĻŋāĻ° āĻ¨āĻžāĻŽ" +emojiUrl: "āĻ‡āĻŽā§‹āĻœāĻŋāĻ° URL" +addEmoji: "āĻ‡āĻŽā§‹āĻœāĻŋ āĻ¯ā§āĻ•ā§āĻ¤ āĻ•āĻ°ā§āĻ¨" +settingGuide: "āĻ¸ā§āĻĒāĻžāĻ°āĻŋāĻļāĻ•ā§ƒāĻ¤ āĻ¸ā§‡āĻŸāĻŋāĻ‚āĻ¸" +cacheRemoteFiles: "āĻ°āĻŋāĻŽā§‹āĻŸ āĻĢāĻžāĻ‡āĻ˛āĻ¸āĻŽā§āĻš āĻ•ā§āĻ¯āĻžāĻļ āĻ•āĻ°ā§āĻ¨" +cacheRemoteFilesDescription: "āĻ¯āĻ–āĻ¨ āĻāĻ‡ āĻ…āĻĒāĻļāĻ¨āĻŸāĻŋ āĻŦāĻ¨ā§āĻ§ āĻĨāĻžāĻ•ā§‡ āĻ¤āĻ–āĻ¨ āĻ°āĻŋāĻŽā§‹āĻŸ āĻĢāĻžāĻ‡āĻ˛ āĻ¸āĻŽā§‚āĻš āĻ¸āĻ°āĻžāĻ¸āĻ°āĻŋ āĻ°āĻŋāĻŽā§‹āĻŸ āĻ‡āĻ¨ā§āĻ¸āĻŸā§āĻ¯āĻžāĻ¨ā§āĻ¸ āĻĨā§‡āĻ•ā§‡ āĻ˛ā§‹āĻĄ āĻ•āĻ°āĻž āĻšā§ŸāĨ¤ āĻāĻ‡ āĻ…āĻĒāĻļāĻ¨āĻŸāĻŋ āĻŦāĻ¨ā§āĻ§ āĻ•āĻ°āĻ˛ā§‡ āĻ¸ā§āĻŸā§‹āĻ°ā§‡āĻœ āĻāĻ° āĻŦā§āĻ¯āĻžāĻŦāĻšāĻžāĻ° āĻ•āĻŽāĻŦā§‡ āĻ¤āĻŦā§‡ āĻĨāĻžāĻŽā§āĻŦāĻ¨ā§‡āĻ‡āĻ˛ āĻ¤ā§ˆāĻ°āĻŋ āĻ¨āĻž āĻ•āĻ°āĻžāĻ° āĻ•āĻžāĻ°āĻŖā§‡ āĻ¨ā§‡āĻŸāĻ“ā§ŸāĻžāĻ°ā§āĻ• āĻŦā§āĻ¯āĻžāĻ¨ā§āĻĄāĻ‰āĻ‡āĻĨ āĻŦā§‡āĻļā§€ āĻ˛āĻžāĻ—āĻŦā§‡āĨ¤ " +flagAsBot: "āĻŦāĻŸ āĻšāĻŋāĻ¸āĻžāĻŦā§‡ āĻšāĻŋāĻšā§āĻ¨āĻŋāĻ¤ āĻ•āĻ°ā§āĻ¨" +flagAsBotDescription: "āĻāĻ‡ āĻ…ā§āĻ¯āĻžāĻ•āĻžāĻ‰āĻ¨ā§āĻŸāĻŸāĻŋ āĻ¯āĻĻāĻŋ āĻāĻ•āĻŸāĻŋ āĻĒā§āĻ°ā§‹āĻ—ā§āĻ°āĻžāĻŽ āĻĻā§āĻŦāĻžāĻ°āĻž āĻĒāĻ°āĻŋāĻšāĻžāĻ˛āĻŋāĻ¤ āĻšāĻ¯āĻŧ, āĻ¤āĻžāĻšāĻ˛ā§‡ āĻāĻ‡ āĻ…āĻĒāĻļāĻ¨āĻŸāĻŋ āĻšāĻžāĻ˛ā§ āĻ•āĻ°ā§āĻ¨āĨ¤ āĻ‡āĻ¨ā§āĻŸāĻžāĻ°āĻ…ā§āĻ¯āĻžāĻ•āĻļāĻžāĻ¨ āĻšā§‡āĻ‡āĻ¨āĻŋāĻ‚ āĻ°ā§‹āĻ§ āĻ•āĻ°āĻ¤ā§‡, āĻŽāĻŋāĻ¸ā§āĻ•āĻŋāĻ° āĻ¸āĻŋāĻ¸ā§āĻŸā§‡āĻŽ āĻĒāĻ°āĻŋāĻšāĻžāĻ˛āĻ¨āĻžāĻ•ā§‡ āĻŦāĻŸ-āĻŦāĻžāĻ¨ā§āĻ§āĻŦ āĻ•āĻ°āĻ¤ā§‡ āĻāĻŦāĻ‚ āĻ…āĻ¨ā§āĻ¯āĻžāĻ¨ā§āĻ¯ āĻĄā§‡āĻ­ā§‡āĻ˛āĻĒāĻžāĻ°āĻĻā§‡āĻ° āĻ¸āĻžāĻšāĻžāĻ¯ā§āĻ¯ āĻ•āĻ°āĻ¤ā§‡ āĻ†āĻĒāĻ¨āĻžāĻ° āĻŦāĻŸ āĻ āĻāĻ‡ āĻ…āĻĒāĻļāĻ¨āĻŸāĻŋ āĻšāĻžāĻ˛ā§ āĻ•āĻ°ā§āĻ¨ā§ˇ" +flagAsCat: "āĻŦāĻŋā§œāĻžāĻ˛ āĻšāĻŋāĻ¸āĻžāĻŦā§‡ āĻšāĻŋāĻšā§āĻ¨āĻŋāĻ¤ āĻ•āĻ°ā§āĻ¨" +flagAsCatDescription: "āĻ…ā§āĻ¯āĻžāĻ•āĻžāĻ‰āĻ¨ā§āĻŸāĻŸāĻŋāĻ•ā§‡ āĻŦāĻŋā§œāĻžāĻ˛ āĻšāĻŋāĻ¸āĻžāĻŦā§‡ āĻšāĻŋāĻšā§āĻ¨āĻŋāĻ¤ āĻ•āĻ°āĻžāĻ° āĻœāĻ¨ā§āĻ¯ āĻ…āĻĒāĻļāĻ¨āĻŸāĻŋ āĻšāĻžāĻ˛ā§ āĻ•āĻ°ā§āĻ¨āĨ¤" +flagShowTimelineReplies: "āĻŸāĻžāĻ‡āĻŽāĻ˛āĻžāĻ‡āĻ¨ā§‡ āĻ¨ā§‹āĻŸāĻ—ā§āĻ˛āĻŋāĻ° āĻ°āĻŋāĻĒā§āĻ˛āĻžāĻ‡ āĻĻā§‡āĻ–āĻžāĻ¨" +flagShowTimelineRepliesDescription: "āĻšāĻžāĻ˛ā§ āĻ•āĻ°āĻ˛ā§‡, āĻŸāĻžāĻ‡āĻŽāĻ˛āĻžāĻ‡āĻ¨ āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ°āĻ•āĻžāĻ°ā§€āĻ° āĻ¨ā§‹āĻŸ āĻ›āĻžāĻĄāĻŧāĻžāĻ“ āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ°āĻ•āĻžāĻ°ā§€āĻ° āĻ…āĻ¨ā§āĻ¯āĻžāĻ¨ā§āĻ¯ āĻ¨ā§‹āĻŸā§‡āĻ° āĻœāĻŦāĻžāĻŦāĻ—ā§āĻ˛ā§‹ āĻĻā§‡āĻ–āĻžāĻ¯āĻŧāĨ¤" +autoAcceptFollowed: "āĻ†āĻĒāĻ¨āĻŋ āĻ¯ā§‡āĻ¸āĻŦ āĻ…ā§āĻ¯āĻžāĻ•āĻžāĻ‰āĻ¨ā§āĻŸ āĻ…āĻ¨ā§āĻ¸āĻ°āĻŖ āĻ•āĻ°ā§‡āĻ¨, āĻ¸ā§āĻŦā§ŸāĻ‚āĻ•ā§āĻ°āĻŋā§ŸāĻ­āĻžāĻŦā§‡ āĻ¤āĻžāĻĻā§‡āĻ° āĻ…āĻ¨ā§āĻ¸āĻ°āĻŖā§‡āĻ° āĻ…āĻ¨ā§āĻ°āĻ§ āĻ¸ā§āĻŦā§€āĻ•āĻžāĻ° āĻ•āĻ°ā§āĻ¨" +addAccount: "āĻ…ā§āĻ¯āĻžāĻ•āĻžāĻ‰āĻ¨ā§āĻŸ āĻ¯ā§‹āĻ— āĻ•āĻ°ā§āĻ¨" +loginFailed: "āĻĒā§āĻ°āĻŦā§‡āĻļ āĻ•āĻ°āĻž āĻ¯āĻžā§ŸāĻ¨āĻŋ" +showOnRemote: "āĻ°āĻŋāĻŽā§‹āĻŸ āĻ¸āĻžāĻ°ā§āĻ­āĻžāĻ°ā§‡ āĻĻā§‡āĻ–ā§āĻ¨" +general: "āĻ¸āĻžāĻ§āĻžāĻ°āĻŖ" +wallpaper: "āĻ“āĻ¯āĻŧāĻžāĻ˛āĻĒā§‡āĻĒāĻžāĻ°" +setWallpaper: "āĻ“āĻ¯āĻŧāĻžāĻ˛āĻĒā§‡āĻĒāĻžāĻ° āĻ¸ā§‡āĻŸ āĻ•āĻ°ā§āĻ¨" +removeWallpaper: "āĻ“ā§ŸāĻžāĻ˛āĻĒā§‡āĻĒāĻžāĻ° āĻ¸āĻ°āĻžāĻ¨" +searchWith: "āĻ–ā§āĻāĻœā§āĻ¨: {q}" +youHaveNoLists: "āĻ†āĻĒāĻ¨āĻžāĻ° āĻ•ā§‹āĻ¨ āĻ˛āĻŋāĻ¸ā§āĻŸ āĻ¨ā§‡āĻ‡" +followConfirm: "{name} āĻ•ā§‡ āĻĢāĻ˛ā§‹āĻ“ āĻ•āĻ°āĻžāĻ° āĻŦā§āĻ¯āĻžāĻĒāĻžāĻ°ā§‡ āĻ¨āĻŋāĻļā§āĻšāĻŋāĻ¤?" +proxyAccount: "āĻĒā§āĻ°āĻ•ā§āĻ¸āĻŋ āĻ…ā§āĻ¯āĻžāĻ•āĻžāĻ‰āĻ¨ā§āĻŸ" +proxyAccountDescription: "āĻāĻ•āĻŸāĻŋ āĻĒā§āĻ°āĻ•ā§āĻ¸āĻŋ āĻ…ā§āĻ¯āĻžāĻ•āĻžāĻ‰āĻ¨ā§āĻŸ āĻāĻŽāĻ¨ āĻāĻ•āĻŸāĻŋ āĻ…ā§āĻ¯āĻžāĻ•āĻžāĻ‰āĻ¨ā§āĻŸ āĻ¯āĻž āĻ¨āĻŋāĻ°ā§āĻĻāĻŋāĻˇā§āĻŸ āĻļāĻ°ā§āĻ¤ā§‡ āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ°āĻ•āĻžāĻ°ā§€āĻĻā§‡āĻ° āĻœāĻ¨ā§āĻ¯ āĻ°āĻŋāĻŽā§‹āĻŸ āĻ…āĻ¨ā§āĻ¸āĻ°āĻŖāĻ•āĻžāĻ°ā§€ āĻšāĻŋāĻ¸āĻžāĻŦā§‡ āĻ•āĻžāĻœ āĻ•āĻ°ā§‡āĨ¤ āĻ‰āĻĻāĻžāĻšāĻ°āĻŖāĻ¸ā§āĻŦāĻ°ā§‚āĻĒ, āĻ¯āĻ–āĻ¨ āĻāĻ•āĻœāĻ¨ āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ°āĻ•āĻžāĻ°ā§€ āĻāĻ•āĻŸāĻŋ āĻ°āĻŋāĻŽā§‹āĻŸ āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ°āĻ•āĻžāĻ°ā§€āĻ•ā§‡ āĻ¤āĻžāĻ˛āĻŋāĻ•āĻžāĻ­ā§āĻ•ā§āĻ¤ āĻ•āĻ°ā§‡, āĻ¤āĻ–āĻ¨ āĻ•ā§āĻ°āĻŋāĻ¯āĻŧāĻžāĻ•āĻ˛āĻžāĻĒā§‡āĻ° āĻĻā§ƒāĻˇā§āĻŸāĻžāĻ¨ā§āĻ¤ā§‡ āĻŦāĻŋāĻ¤āĻ°āĻŖ āĻ•āĻ°āĻž āĻšāĻŦā§‡ āĻ¨āĻž āĻ¯āĻĻāĻŋ āĻ¨āĻž āĻ•ā§‡āĻ‰ āĻ¤āĻžāĻ˛āĻŋāĻ•āĻžāĻ­ā§āĻ•ā§āĻ¤ āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ°āĻ•āĻžāĻ°ā§€āĻ•ā§‡ āĻ…āĻ¨ā§āĻ¸āĻ°āĻŖ āĻ•āĻ°ā§‡, āĻ¤āĻžāĻ‡ āĻĒā§āĻ°āĻ•ā§āĻ¸āĻŋ āĻ…ā§āĻ¯āĻžāĻ•āĻžāĻ‰āĻ¨ā§āĻŸ āĻĻā§āĻŦāĻžāĻ°āĻž āĻ¤āĻžāĻ•ā§‡ āĻ…āĻ¨ā§āĻ¸āĻ°āĻŖ āĻ•āĻ°āĻž āĻšāĻŦā§‡āĨ¤" +host: "āĻšā§‹āĻ¸ā§āĻŸ" +selectUser: "āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ°āĻ•āĻžāĻ°ā§€ āĻ¨āĻŋāĻ°ā§āĻŦāĻžāĻšāĻ¨ āĻ•āĻ°ā§āĻ¨" +recipient: "āĻĒā§āĻ°āĻ¤āĻŋ" +annotation: "āĻŽāĻ¨ā§āĻ¤āĻŦā§āĻ¯" +federation: "āĻĢā§‡āĻĄāĻŋāĻ­āĻžāĻ°ā§āĻ¸" +instances: "āĻ‡āĻ¨ā§āĻ¸āĻŸā§āĻ¯āĻžāĻ¨ā§āĻ¸" +registeredAt: "āĻ¯ā§‹āĻ— āĻĻāĻŋā§Ÿā§‡āĻ›ā§‡āĻ¨" +latestRequestSentAt: "āĻļā§‡āĻˇ āĻ°āĻŋāĻ•ā§ā§Ÿā§‡āĻ¸ā§āĻŸ āĻĒāĻžāĻ āĻžāĻ¨ā§‹ āĻšā§Ÿā§‡āĻ›ā§‡" +latestRequestReceivedAt: "āĻļā§‡āĻˇ āĻ°āĻŋāĻ•ā§ā§Ÿā§‡āĻ¸ā§āĻŸ āĻ—ā§ƒāĻšā§€āĻ¤ āĻšā§Ÿā§‡āĻ›ā§‡" +latestStatus: "āĻ¸āĻ°ā§āĻŦāĻļā§‡āĻˇ āĻ…āĻŦāĻ¸ā§āĻĨāĻž" +storageUsage: "āĻ¸ā§āĻŸā§‹āĻ°ā§‡āĻœā§‡āĻ° āĻŦā§āĻ¯āĻžāĻŦāĻšāĻžāĻ°" +charts: "āĻšāĻžāĻ°ā§āĻŸ" +perHour: "āĻ˜āĻ¨ā§āĻŸāĻž āĻĒā§āĻ°āĻ¤āĻŋ" +perDay: "āĻĻā§ˆāĻ¨āĻŋāĻ•" +stopActivityDelivery: "āĻ…ā§āĻ¯āĻžāĻ•ā§āĻŸāĻŋāĻ­āĻŋāĻŸāĻŋ āĻĒāĻžāĻ āĻžāĻ¨ā§‹ āĻŦāĻ¨ā§āĻ§ āĻ•āĻ°ā§āĻ¨" +blockThisInstance: "āĻ‡āĻ¨ā§āĻ¸āĻŸā§āĻ¯āĻžāĻ¨ā§āĻ¸ āĻŦā§āĻ˛āĻ• āĻ•āĻ°ā§āĻ¨" +operations: "āĻ•ā§āĻ°āĻŋāĻ¯āĻŧāĻžāĻ•āĻ˛āĻžāĻĒ" +software: "āĻ¸āĻĢāĻŸāĻ“ā§Ÿā§āĻ¯āĻžāĻ°" +version: "āĻ¸āĻ‚āĻ¸ā§āĻ•āĻ°āĻŖ" +metadata: "āĻŽā§‡āĻŸāĻžāĻĄāĻžāĻŸāĻž" +withNFiles: "{n} āĻŸāĻŋ āĻĢāĻžāĻ‡āĻ˛" +monitor: "āĻŽāĻ¨āĻŋāĻŸāĻ°" +jobQueue: "āĻœāĻŦ āĻ•āĻŋāĻ‰" +cpuAndMemory: "āĻ¸āĻŋāĻĒāĻŋāĻ‰ āĻāĻŦāĻ‚ āĻŽā§‡āĻŽāĻ°āĻŋ" +network: "āĻ¨ā§‡āĻŸāĻ“ā§ŸāĻžāĻ°ā§āĻ•" +disk: "āĻĄāĻŋāĻ¸ā§āĻ•" +instanceInfo: "āĻ‡āĻ¨ā§āĻ¸āĻŸā§āĻ¯āĻžāĻ¨ā§āĻ¸ā§‡āĻ° āĻ¤āĻĨā§āĻ¯" +statistics: "āĻĒāĻ°āĻŋāĻ¸āĻ‚āĻ–ā§āĻ¯āĻžāĻ¨" +clearQueue: "āĻ•āĻŋāĻ‰ āĻĒāĻ°āĻŋāĻˇā§āĻ•āĻžāĻ° āĻ•āĻ°ā§āĻ¨" +clearQueueConfirmTitle: "āĻ†āĻĒāĻ¨āĻŋ āĻ•āĻŋ āĻ•āĻŋāĻ‰ āĻĒāĻ°āĻŋāĻˇā§āĻ•āĻžāĻ° āĻ•āĻ°āĻžāĻ° āĻŦā§āĻ¯āĻžāĻĒāĻžāĻ°ā§‡ āĻ¨āĻŋāĻļā§āĻšāĻŋāĻ¤?" +clearQueueConfirmText: "āĻŦāĻŋāĻ¤āĻ°āĻŖ āĻ¨āĻž āĻ•āĻ°āĻž āĻ¨ā§‹āĻŸ āĻ†āĻ° āĻŦāĻŋāĻ¤āĻ°āĻŖ āĻ•āĻ°āĻž āĻšāĻŦā§‡ āĻ¨āĻžāĨ¤ āĻ¸āĻžāĻ§āĻžāĻ°āĻŖāĻ¤ āĻ†āĻĒāĻ¨āĻžāĻ° āĻāĻŸāĻŋ āĻ•āĻ°āĻžāĻ° āĻĻāĻ°āĻ•āĻžāĻ° āĻ¨ā§‡āĻ‡āĨ¤" +clearCachedFiles: "āĻ•ā§āĻ¯āĻžāĻļ āĻĒāĻ°āĻŋāĻˇā§āĻ•āĻžāĻ° āĻ•āĻ°ā§āĻ¨" +clearCachedFilesConfirm: "āĻ†āĻĒāĻ¨āĻŋ āĻ•āĻŋ āĻ•ā§āĻ¯āĻžāĻļ āĻĒāĻ°āĻŋāĻˇā§āĻ•āĻžāĻ° āĻ•āĻ°āĻžāĻ° āĻŦā§āĻ¯āĻžāĻĒāĻžāĻ°ā§‡ āĻ¨āĻŋāĻļā§āĻšāĻŋāĻ¤?" +blockedInstances: "āĻŦā§āĻ˛āĻ•āĻ•ā§ƒāĻ¤ āĻ‡āĻ¨ā§āĻ¸āĻŸā§āĻ¯āĻžāĻ¨ā§āĻ¸āĻ¸āĻŽā§āĻš" +blockedInstancesDescription: "āĻ†āĻĒāĻ¨āĻŋ āĻ¯ā§‡ āĻ‡āĻ¨ā§āĻ¸āĻŸā§āĻ¯āĻžāĻ¨ā§āĻ¸āĻ—ā§āĻ˛āĻŋ āĻŦā§āĻ˛āĻ• āĻ•āĻ°āĻ¤ā§‡ āĻšāĻžāĻ¨ āĻ¤āĻžāĻ° āĻšā§‹āĻ¸ā§āĻŸāĻ¨ā§‡āĻŽāĻ—ā§āĻ˛āĻŋ āĻĒā§āĻ°āĻ¤ā§āĻ¯ā§‡āĻ•āĻŸāĻŋ āĻ†āĻ˛āĻžāĻĻāĻž āĻ˛āĻžāĻ‡āĻ¨ā§‡ āĻ˛āĻŋāĻ–ā§āĻ¨āĨ¤ āĻŦā§āĻ˛āĻ•āĻ•ā§ƒāĻ¤ āĻ‡āĻ¨ā§āĻ¸āĻŸā§āĻ¯āĻžāĻ¨ā§āĻ¸āĻ—ā§āĻ˛āĻŋ āĻāĻ‡ āĻ‡āĻ¨ā§āĻ¸āĻŸā§āĻ¯āĻžāĻ¨ā§āĻ¸ā§‡āĻ° āĻ¸āĻžāĻĨā§‡ āĻ¯ā§‹āĻ—āĻžāĻ¯ā§‹āĻ— āĻ•āĻ°āĻ¤ā§‡ āĻĒāĻžāĻ°āĻŦā§‡āĻ¨āĻžā§ˇ" +muteAndBlock: "āĻŽāĻŋāĻ‰āĻŸ āĻāĻŦāĻ‚ āĻŦā§āĻ˛āĻ•āĻ—ā§āĻ˛āĻŋ" +mutedUsers: "āĻ¨āĻŋāĻƒāĻļāĻŦā§āĻĻāĻ•ā§ƒāĻ¤ āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ°āĻ•āĻžāĻ°ā§€" +blockedUsers: "āĻ¯āĻžāĻĻā§‡āĻ° āĻŦā§āĻ˛āĻ• āĻ•āĻ°āĻž āĻšā§Ÿā§‡āĻ›ā§‡" +noUsers: "āĻ•ā§‹āĻ¨ āĻŦā§āĻ¯āĻžāĻŦāĻšāĻžāĻ°āĻ•āĻžāĻ°ā§€ āĻ¨ā§‡āĻ‡" +editProfile: "āĻĒā§āĻ°ā§‹āĻĢāĻžāĻ‡āĻ˛ āĻ¸āĻŽā§āĻĒāĻžāĻĻāĻ¨āĻž āĻ•āĻ°ā§āĻ¨" +noteDeleteConfirm: "āĻ†āĻĒāĻ¨āĻŋ āĻ•āĻŋ āĻ¨ā§‹āĻŸ āĻĄāĻŋāĻ˛āĻŋāĻŸ āĻ•āĻ°āĻžāĻ° āĻŦā§āĻ¯āĻžāĻĒāĻžāĻ°ā§‡ āĻ¨āĻŋāĻļā§āĻšāĻŋāĻ¤?" +pinLimitExceeded: "āĻ†āĻĒāĻ¨āĻŋ āĻ†āĻ° āĻ•ā§‹āĻ¨ āĻ¨ā§‹āĻŸ āĻĒāĻŋāĻ¨ āĻ•āĻ°āĻ¤ā§‡ āĻĒāĻžāĻ°āĻŦā§‡āĻ¨ āĻ¨āĻž" +intro: "Misskey āĻāĻ° āĻ‡āĻ¨ā§āĻ¸āĻŸāĻ˛ā§‡āĻļāĻ¨ āĻ¸āĻŽā§āĻĒāĻ¨ā§āĻ¨ āĻšā§Ÿā§‡āĻ›ā§‡īŧāĻĻā§ŸāĻž āĻ•āĻ°ā§‡ āĻ…ā§āĻ¯āĻžāĻĄāĻŽāĻŋāĻ¨ āĻ‡āĻ‰āĻœāĻžāĻ° āĻ¤ā§ˆāĻ°āĻŋ āĻ•āĻ°ā§āĻ¨āĨ¤" +done: "āĻ¸āĻŽā§āĻĒāĻ¨ā§āĻ¨" +processing: "āĻĒā§āĻ°āĻ•ā§āĻ°āĻŋā§ŸāĻžāĻ§ā§€āĻ¨..." +preview: "āĻĒā§‚āĻ°ā§āĻŦāĻ°ā§‚āĻĒ āĻĻā§‡āĻ–ā§āĻ¨" +default: "āĻĒā§‚āĻ°ā§āĻŦāĻ¨āĻŋāĻ°ā§āĻ§āĻžāĻ°āĻŋāĻ¤" +noCustomEmojis: "āĻ•ā§‹āĻ¨ āĻ‡āĻŽā§‹āĻœāĻŋ āĻ¨āĻžāĻ‡" +noJobs: "āĻ•ā§‹āĻ¨ āĻœāĻŦ āĻ¨āĻžāĻ‡" +federating: "āĻĢā§‡āĻĄāĻžāĻ°ā§‡āĻŸ āĻ•āĻ°āĻž āĻšāĻšā§āĻ›ā§‡" +blocked: "āĻŦā§āĻ˛āĻ• āĻ•āĻ°āĻž āĻšā§Ÿā§‡āĻ›ā§‡" +suspended: "āĻ¸ā§āĻĨāĻ—āĻŋāĻ¤ āĻ•āĻ°āĻž āĻšā§Ÿā§‡āĻ›ā§‡" +all: "āĻ¸āĻŦāĻ—ā§āĻ˛ā§‹" +subscribing: "āĻ¸āĻĻāĻ¸ā§āĻ¯āĻ¤āĻž āĻ¨ā§‡ā§ŸāĻž āĻšāĻšā§āĻ›ā§‡" +publishing: "āĻĒā§āĻ°āĻ•āĻžāĻļ āĻ•āĻ°āĻž āĻšāĻšā§āĻ›ā§‡" +notResponding: "āĻ¸āĻžā§œāĻž āĻ¨ā§‡āĻ‡" +instanceFollowing: "āĻ‡āĻ¨ā§āĻ¸āĻŸā§āĻ¯āĻžāĻ¨ā§āĻ¸ āĻ…āĻ¨ā§āĻ¸āĻ°āĻŖ āĻ•āĻ°āĻž āĻšāĻšā§āĻ›ā§‡" +instanceFollowers: "āĻ‡āĻ¨ā§āĻ¸āĻŸā§āĻ¯āĻžāĻ¨ā§āĻ¸ āĻ…āĻ¨ā§āĻ¸āĻ°āĻŖāĻ•āĻžāĻ°ā§€" +instanceUsers: "āĻ‡āĻ¨ā§āĻ¸āĻŸā§āĻ¯āĻžāĻ¨ā§āĻ¸ āĻŦā§āĻ¯āĻžāĻŦāĻšāĻžāĻ°āĻ•āĻžāĻ°ā§€" +changePassword: "āĻĒāĻžāĻ¸āĻ“āĻ¯āĻŧāĻžāĻ°ā§āĻĄ āĻĒāĻ°āĻŋāĻŦāĻ°ā§āĻ¤āĻ¨ āĻ•āĻ°ā§āĻ¨" +security: "āĻ¨āĻŋāĻ°āĻžāĻĒāĻ¤ā§āĻ¤āĻž" +retypedNotMatch: "āĻ‡āĻ¨āĻĒā§āĻŸ āĻŽā§‡āĻ˛ā§‡ āĻ¨āĻžāĨ¤" +currentPassword: "āĻŦāĻ°ā§āĻ¤āĻŽāĻžāĻ¨ āĻĒāĻžāĻ¸āĻ“ā§ŸāĻžāĻ°ā§āĻĄ" +newPassword: "āĻ¨āĻ¤ā§āĻ¨ āĻĒāĻžāĻ¸āĻ“ā§ŸāĻžāĻ°ā§āĻĄ" +newPasswordRetype: "āĻ¨āĻ¤ā§āĻ¨ āĻĒāĻžāĻ¸āĻ“āĻ¯āĻŧāĻžāĻ°ā§āĻĄ (āĻĒā§āĻ¨āĻ°āĻžāĻ¯āĻŧ āĻ˛āĻŋāĻ–ā§āĻ¨)" +attachFile: "āĻĢāĻžāĻ‡āĻ˛ āĻ¸āĻ‚āĻ¯ā§āĻ•ā§āĻ¤ āĻ•āĻ°ā§āĻ¨" +more: "āĻ†āĻ°āĻ“!" +featured: "āĻšāĻžāĻ‡āĻ˛āĻžāĻ‡āĻŸ" +usernameOrUserId: "āĻŦā§āĻ¯āĻžāĻŦāĻšāĻžāĻ°āĻ•āĻžāĻ°ā§€āĻ° āĻ¨āĻžāĻŽ āĻŦāĻž āĻŦā§āĻ¯āĻžāĻŦāĻšāĻžāĻ°āĻ•āĻžāĻ°ā§€ ID" +noSuchUser: "āĻ•ā§‹āĻ¨ āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ°āĻ•āĻžāĻ°ā§€ āĻ–ā§āĻāĻœā§‡ āĻĒāĻžāĻ“āĻ¯āĻŧāĻž āĻ¯āĻžāĻ¯āĻŧāĻ¨āĻŋ" +lookup: "āĻ–ā§āĻāĻœā§‡ āĻĻā§‡āĻ–ā§‹" +announcements: "āĻ˜ā§‹āĻˇāĻŖāĻž" +imageUrl: "āĻšāĻŋāĻ¤ā§āĻ°ā§‡āĻ° URL" +remove: "āĻŽā§āĻ›ā§āĻ¨" +removed: "āĻ¸āĻ°āĻžāĻ¨ā§‹ āĻšāĻ¯āĻŧā§‡āĻ›ā§‡" +removeAreYouSure: "āĻ†āĻĒāĻ¨āĻŋ āĻ•āĻŋ \"{x}\" āĻ¸āĻ°āĻžāĻ¨ā§‹āĻ° āĻŦā§āĻ¯āĻžāĻĒāĻžāĻ°ā§‡ āĻ¨āĻŋāĻļā§āĻšāĻŋāĻ¤?" +deleteAreYouSure: "āĻ†āĻĒāĻ¨āĻŋ āĻ•āĻŋ \"{x}\" āĻ¸āĻ°āĻžāĻ¨ā§‹āĻ° āĻŦā§āĻ¯āĻžāĻĒāĻžāĻ°ā§‡ āĻ¨āĻŋāĻļā§āĻšāĻŋāĻ¤?" +resetAreYouSure: "āĻ°āĻŋāĻ¸ā§‡āĻŸ āĻ•āĻ°āĻžāĻ° āĻŦā§āĻ¯āĻžāĻĒāĻžāĻ°ā§‡ āĻ¨āĻŋāĻļā§āĻšāĻŋāĻ¤?" +saved: "āĻ¸āĻ‚āĻ°āĻ•ā§āĻˇāĻŋāĻ¤ āĻšā§Ÿā§‡āĻ›ā§‡" +messaging: "āĻšā§āĻ¯āĻžāĻŸ" +upload: "āĻ†āĻĒāĻ˛ā§‹āĻĄ" +keepOriginalUploading: "āĻ†āĻ¸āĻ˛ āĻ›āĻŦāĻŋ āĻ°āĻžāĻ–ā§āĻ¨" +keepOriginalUploadingDescription: "āĻ›āĻŦāĻŋāĻŸāĻŋ āĻ†āĻĒāĻ˛ā§‹āĻĄ āĻ•āĻ°āĻžāĻ° āĻ¸āĻŽāĻ¯āĻŧ āĻ†āĻ¸āĻ˛ āĻ¸āĻ‚āĻ¸ā§āĻ•āĻ°āĻŖāĻŸāĻŋ āĻ°āĻžāĻ–ā§āĻ¨āĨ¤ āĻ…āĻĒāĻļāĻ¨āĻŸāĻŋ āĻŦāĻ¨ā§āĻ§ āĻĨāĻžāĻ•āĻ˛ā§‡, āĻ†āĻĒāĻ˛ā§‹āĻĄā§‡āĻ° āĻ¸āĻŽāĻ¯āĻŧ āĻ“āĻ¯āĻŧā§‡āĻŦ āĻĒā§āĻ°āĻ•āĻžāĻļāĻ¨āĻžāĻ° āĻœāĻ¨ā§āĻ¯ āĻ›āĻŦāĻŋ āĻŦā§āĻ°āĻžāĻ‰āĻœāĻžāĻ°ā§‡ āĻ¤ā§ˆāĻ°āĻŋ āĻ•āĻ°āĻž āĻšāĻŦā§‡āĨ¤" +fromDrive: "āĻĄā§āĻ°āĻžāĻ‡āĻ­ āĻšāĻ¤ā§‡" +fromUrl: "URL āĻšāĻ¤ā§‡" +uploadFromUrl: "URL āĻšāĻ¤ā§‡ āĻ†āĻĒāĻ˛ā§‹āĻĄ" +uploadFromUrlDescription: "āĻ¯ā§‡ āĻĢāĻžāĻ‡āĻ˛āĻŸāĻŋ āĻ†āĻĒāĻ˛ā§‹āĻĄ āĻ•āĻ°āĻ¤ā§‡ āĻšāĻžāĻ¨, āĻ¸ā§‡āĻŸāĻŋāĻ° URL" +uploadFromUrlRequested: "āĻ†āĻĒāĻ˛ā§‹āĻĄ āĻ…āĻ¨ā§āĻ°ā§‹āĻ§ āĻ•āĻ°āĻž āĻšā§Ÿā§‡āĻ›ā§‡" +uploadFromUrlMayTakeTime: "URL āĻšāĻ¤ā§‡ āĻ†āĻĒāĻ˛ā§‹āĻĄ āĻšāĻ¤ā§‡ āĻ•āĻŋāĻ›ā§ āĻ¸āĻŽā§Ÿ āĻ˛āĻžāĻ—āĻ¤ā§‡ āĻĒāĻžāĻ°ā§‡āĨ¤" +explore: "āĻ˜ā§āĻ°ā§‡ āĻĻā§‡āĻ–ā§āĻ¨" +messageRead: "āĻĒāĻĄāĻŧāĻž" +noMoreHistory: "āĻ†āĻ° āĻ•ā§‹āĻ¨ āĻ‡āĻ¤āĻŋāĻšāĻžāĻ¸ āĻ¨ā§‡āĻ‡" +startMessaging: "āĻšā§āĻ¯āĻžāĻŸ āĻļā§āĻ°ā§ āĻ•āĻ°ā§āĻ¨" +nUsersRead: "{n} āĻœāĻ¨ āĻĒā§œā§‡āĻ›ā§‡āĻ¨" +agreeTo: "{0} āĻāĻ° āĻĒā§āĻ°āĻ¤āĻŋ āĻ†āĻŽāĻŋ āĻ¸āĻŽā§āĻŽāĻ¤" +tos: "āĻĒāĻ°āĻŋāĻˇā§‡āĻŦāĻžāĻ° āĻļāĻ°ā§āĻ¤āĻžāĻĻāĻŋ" +start: "āĻļā§āĻ°ā§ āĻ•āĻ°ā§āĻ¨" +home: "āĻŽā§‚āĻ˛ āĻĒāĻžāĻ¤āĻž" +remoteUserCaution: "āĻāĻ‡ āĻŦā§āĻ¯āĻžāĻŦāĻšāĻžāĻ°āĻ•āĻžāĻ°ā§€ āĻ°āĻŋāĻŽā§‹āĻŸ āĻ‡āĻ¨ā§āĻ¸āĻŸā§āĻ¯āĻžāĻ¨ā§āĻ¸ā§‡āĻ°, āĻ¨āĻŋāĻŽā§āĻ¨āĻ•ā§āĻ¤ āĻ¤āĻĨā§āĻ¯ āĻ…āĻ¸āĻŽā§āĻĒā§‚āĻ°ā§āĻŖ āĻšāĻ¤ā§‡ āĻĒāĻžāĻ°ā§‡āĨ¤" +activity: "āĻ•āĻžāĻ°ā§āĻ¯āĻ•āĻ˛āĻžāĻĒ" +images: "āĻ›āĻŦāĻŋ" +birthday: "āĻœāĻ¨ā§āĻŽāĻĻāĻŋāĻ¨" +yearsOld: "{age} āĻŦāĻ›āĻ°" +registeredDate: "āĻ¯ā§‹āĻ—āĻĻāĻžāĻ¨ā§‡āĻ° āĻ¤āĻžāĻ°āĻŋāĻ–" +location: "āĻ…āĻŦāĻ¸ā§āĻĨāĻžāĻ¨" +theme: "āĻĨāĻŋāĻŽ" +themeForLightMode: "āĻ˛āĻžāĻ‡āĻŸ āĻŽā§‹āĻĄā§‡āĻ° āĻĨāĻŋāĻŽ" +themeForDarkMode: "āĻĄāĻžāĻ°ā§āĻ• āĻŽā§‹āĻĄā§‡āĻ° āĻĨāĻŋāĻŽ" +light: "āĻ†āĻ˛ā§‹āĻ•āĻŋāĻ¤" +dark: "āĻ…āĻ¨ā§āĻ§āĻ•āĻžāĻ°" +lightThemes: "āĻ†āĻ˛ā§‹āĻ•āĻŋāĻ¤ āĻĨāĻŋāĻŽ" +darkThemes: "āĻ…āĻ¨ā§āĻ§āĻ•āĻžāĻ° āĻĨāĻŋāĻŽ" +syncDeviceDarkMode: "āĻĄāĻŋāĻ­āĻžāĻ‡āĻ¸ā§‡āĻ° āĻ¸ā§‡āĻŸāĻŋāĻ‚ āĻ…āĻ¨ā§āĻ¯āĻžā§Ÿā§€ āĻĄāĻžāĻ°ā§āĻ• āĻŽā§‹āĻĄ āĻ¸ā§‡āĻŸ āĻ•āĻ°ā§āĻ¨" +drive: "āĻĄā§āĻ°āĻžāĻ‡āĻ­" +fileName: "āĻĢāĻžāĻ‡āĻ˛ā§‡āĻ° āĻ¨āĻžāĻŽ" +selectFile: "āĻĢāĻžāĻ‡āĻ˛ āĻ¨āĻŋāĻ°ā§āĻŦāĻžāĻšāĻ¨ āĻ•āĻ°ā§āĻ¨" +selectFiles: "āĻĢāĻžāĻ‡āĻ˛ āĻ¨āĻŋāĻ°ā§āĻŦāĻžāĻšāĻ¨ āĻ•āĻ°ā§āĻ¨" +selectFolder: "āĻĢā§‹āĻ˛ā§āĻĄāĻžāĻ° āĻ¨āĻŋāĻ°ā§āĻŦāĻžāĻšāĻ¨ āĻ•āĻ°ā§āĻ¨" +selectFolders: "āĻĢā§‹āĻ˛ā§āĻĄāĻžāĻ° āĻ¨āĻŋāĻ°ā§āĻŦāĻžāĻšāĻ¨ āĻ•āĻ°ā§āĻ¨" +renameFile: "āĻĢāĻžāĻ‡āĻ˛ āĻĒā§āĻ¨āĻƒāĻ¨āĻžāĻŽāĻ•āĻ°āĻ¨" +folderName: "āĻĢā§‹āĻ˛ā§āĻĄāĻžāĻ°ā§‡āĻ° āĻ¨āĻžāĻŽ" +createFolder: "āĻĢā§‹āĻ˛ā§āĻĄāĻžāĻ° āĻ¤ā§ˆāĻ°āĻŋ āĻ•āĻ°ā§āĻ¨" +renameFolder: "āĻĢā§‹āĻ˛ā§āĻĄāĻžāĻ° āĻĒā§āĻ¨āĻƒāĻ¨āĻžāĻŽāĻ•āĻ°āĻ¨" +deleteFolder: "āĻĢā§‹āĻ˛ā§āĻĄāĻžāĻ° āĻŽā§āĻ›ā§āĻ¨" +addFile: "āĻĢāĻžāĻ‡āĻ˛ āĻ¯ā§‹āĻ— āĻ•āĻ°ā§āĻ¨" +emptyDrive: "āĻ†āĻĒāĻ¨āĻžāĻ° āĻĄā§āĻ°āĻžāĻ‡āĻ­ āĻ–āĻžāĻ˛āĻŋ" +emptyFolder: "āĻāĻ‡ āĻĢā§‹āĻ˛ā§āĻĄāĻžāĻ° āĻ–āĻžāĻ˛āĻŋ" +unableToDelete: "āĻŽā§āĻ›ā§‡ āĻĢā§‡āĻ˛āĻž āĻ¯āĻžā§ŸāĻ¨āĻŋ" +inputNewFileName: "āĻĢāĻžāĻ‡āĻ˛ā§‡āĻ° āĻ¨āĻ¤ā§āĻ¨ āĻ¨āĻžāĻŽ āĻ˛āĻŋāĻ–ā§āĻ¨" +inputNewDescription: "āĻ¨āĻ¤ā§āĻ¨ āĻ•ā§āĻ¯āĻžāĻĒāĻļāĻ¨ āĻ˛āĻŋāĻ–ā§āĻ¨" +inputNewFolderName: "āĻĢā§‹āĻ˛ā§āĻĄāĻžāĻ°ā§‡āĻ° āĻ¨āĻ¤ā§āĻ¨ āĻ¨āĻžāĻŽ āĻ˛āĻŋāĻ–ā§āĻ¨" +circularReferenceFolder: "āĻ—āĻ¨ā§āĻ¤āĻŦā§āĻ¯ āĻĢā§‹āĻ˛ā§āĻĄāĻžāĻ°āĻŸāĻŋ āĻ†āĻĒāĻ¨āĻŋ āĻ¯ā§‡ āĻĢā§‹āĻ˛ā§āĻĄāĻžāĻ°āĻŸāĻŋ āĻ¸āĻ°āĻžāĻ¤ā§‡ āĻšāĻžāĻ¨ āĻ¤āĻžāĻ° āĻāĻ•āĻŸāĻŋ āĻ¸āĻžāĻŦāĻĢā§‹āĻ˛ā§āĻĄāĻžāĻ°āĨ¤" +hasChildFilesOrFolders: "āĻāĻ‡ āĻĢā§‹āĻ˛ā§āĻĄāĻžāĻ°āĻŸāĻŋ āĻ–āĻžāĻ˛āĻŋ āĻ¨āĻž āĻšāĻ“ā§ŸāĻžā§Ÿ āĻĄāĻŋāĻ˛āĻŋāĻŸ āĻ•āĻ°āĻž āĻ¯āĻžā§ŸāĻ¨āĻŋāĨ¤" +copyUrl: "URL āĻ•āĻĒāĻŋ āĻ•āĻ°ā§āĻ¨" +rename: "āĻĒā§āĻ¨āĻƒāĻ¨āĻžāĻŽāĻ•āĻ°āĻŖ" +avatar: "āĻĒā§āĻ°ā§‹āĻĢāĻžāĻ‡āĻ˛ āĻ›āĻŦāĻŋ" +banner: "āĻŦā§āĻ¯āĻžāĻ¨āĻžāĻ°" +nsfw: "āĻ¸āĻ‚āĻŦā§‡āĻĻāĻ¨āĻļā§€āĻ˛ āĻŦāĻŋāĻˇā§ŸāĻŦāĻ¸ā§āĻ¤ā§" +whenServerDisconnected: "āĻ¸āĻžāĻ°ā§āĻ­āĻžāĻ°ā§‡āĻ° āĻ¸āĻžāĻĨā§‡ āĻ¸āĻ‚āĻ¯ā§‹āĻ— āĻŦāĻŋāĻšā§āĻ›āĻŋāĻ¨ā§āĻ¨ āĻšāĻ¯āĻŧā§‡ āĻ—ā§‡āĻ˛ā§‡" +disconnectedFromServer: "āĻ¸āĻžāĻ°ā§āĻ­āĻžāĻ° āĻĨā§‡āĻ•ā§‡ āĻ¸āĻ‚āĻ¯ā§‹āĻ— āĻŦāĻŋāĻšā§āĻ›āĻŋāĻ¨ā§āĻ¨ āĻšāĻ¯āĻŧā§‡āĻ›ā§‡" +reload: "āĻ†āĻŦāĻžāĻ° āĻ˛ā§‹āĻĄ āĻ•āĻ°ā§āĻ¨" +doNothing: "āĻ•āĻŋāĻ›ā§ āĻ•āĻ°āĻŦā§‡āĻ¨ āĻ¨āĻž" +reloadConfirm: "āĻ†āĻĒāĻ¨āĻŋ āĻ•āĻŋ āĻ°āĻŋāĻ˛ā§‹āĻĄ āĻ•āĻ°āĻ¤ā§‡ āĻšāĻžāĻ¨?" +watch: "āĻŦāĻŋāĻœā§āĻžāĻĒā§āĻ¤āĻŋ āĻĒāĻžāĻ¨" +unwatch: "āĻŦāĻŋāĻœā§āĻžāĻĒā§āĻ¤āĻŋ āĻĒāĻžāĻ“ā§ŸāĻž āĻŦāĻ¨ā§āĻ§ āĻ•āĻ°ā§āĻ¨ " +accept: "āĻ…āĻ¨ā§āĻŽā§‹āĻĻāĻ¨" +reject: "āĻĒā§āĻ°āĻ¤ā§āĻ¯āĻžāĻ–ā§āĻ¯āĻžāĻ¨" +normal: "āĻ¸ā§āĻŦāĻžāĻ­āĻžāĻŦāĻŋāĻ•" +instanceName: "āĻ‡āĻ¨ā§āĻ¸āĻŸā§āĻ¯āĻžāĻ¨ā§āĻ¸ā§‡āĻ° āĻ¨āĻžāĻŽ" +instanceDescription: "āĻ‡āĻ¨ā§āĻ¸āĻŸā§āĻ¯āĻžāĻ¨ā§āĻ¸ā§‡āĻ° āĻŦāĻ°ā§āĻŖāĻ¨āĻž" +maintainerName: "āĻŽā§‡āĻ‡āĻ¨āĻŸā§‡āĻ‡āĻ¨āĻžāĻ°" +maintainerEmail: "āĻŽā§‡āĻ‡āĻ¨āĻŸā§‡āĻ‡āĻ¨āĻžāĻ°ā§‡āĻ° āĻ‡āĻŽā§‡āĻ‡āĻ˛" +tosUrl: "āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ°ā§‡āĻ° āĻļāĻ°ā§āĻ¤āĻžāĻŦāĻ˛ā§€āĻ° URL" +thisYear: "āĻŦāĻ›āĻ°" +thisMonth: "āĻŽāĻžāĻ¸" +today: "āĻ†āĻœ" +dayX: "{day}" +monthX: "{month}" +yearX: "{year}" +pages: "āĻĒā§ƒāĻˇā§āĻ āĻž" +integration: "āĻ‡āĻ¨ā§āĻŸāĻŋāĻ—ā§āĻ°ā§‡āĻļāĻ¨" +connectService: "āĻ¸āĻ‚āĻ¯ā§āĻ•ā§āĻ¤ āĻ•āĻ°ā§āĻ¨" +disconnectService: "āĻ¸āĻ‚āĻ¯ā§‹āĻ— āĻŦāĻŋāĻšā§āĻ›āĻŋāĻ¨ā§āĻ¨ āĻ•āĻ°ā§āĻ¨" +enableLocalTimeline: "āĻ¸ā§āĻĨāĻžāĻ¨ā§€ā§Ÿ āĻŸāĻžāĻ‡āĻŽāĻ˛āĻžāĻ‡āĻ¨ āĻšāĻžāĻ˛ā§ āĻ•āĻ°ā§āĻ¨" +enableGlobalTimeline: "āĻ—ā§āĻ˛ā§‹āĻŦāĻžāĻ˛ āĻŸāĻžāĻ‡āĻŽāĻ˛āĻžāĻ‡āĻ¨ āĻšāĻžāĻ˛ā§ āĻ•āĻ°ā§āĻ¨" +disablingTimelinesInfo: "āĻ†āĻĒāĻ¨āĻŋ āĻāĻ‡ āĻŸāĻžāĻ‡āĻŽāĻ˛āĻžāĻ‡āĻ¨āĻ—ā§āĻ˛āĻŋ āĻŦāĻ¨ā§āĻ§ āĻ•āĻ°āĻ˛ā§‡āĻ“ āĻĒā§āĻ°āĻļāĻžāĻ¸āĻ• āĻāĻŦāĻ‚ āĻŽāĻĄāĻžāĻ°ā§‡āĻŸāĻ°āĻ°āĻž āĻāĻ‡ āĻŸāĻžāĻ‡āĻŽāĻ˛āĻžāĻ‡āĻ¨āĻ—ā§āĻ˛āĻŋ āĻŦā§āĻ¯āĻžāĻŦāĻšāĻžāĻ° āĻ•āĻ°āĻ¤ā§‡ āĻĒāĻžāĻ°āĻŦā§‡" +registration: "āĻ¨āĻŋāĻŦāĻ¨ā§āĻ§āĻ¨" +enableRegistration: "āĻ¨āĻ¤ā§āĻ¨ āĻŦā§āĻ¯āĻžāĻŦāĻšāĻžāĻ°āĻ•āĻžāĻ°ā§€ āĻ¨āĻŋāĻŦāĻ¨ā§āĻ§āĻ¨ āĻšāĻžāĻ˛ā§ āĻ•āĻ°ā§āĻ¨" +invite: "āĻ†āĻŽāĻ¨ā§āĻ¤ā§āĻ°āĻŖ" +proxyRemoteFiles: "āĻ°āĻŋāĻŽā§‹āĻŸ āĻĢāĻžāĻ‡āĻ˛āĻ¸āĻŽā§āĻš āĻĒā§āĻ°āĻ•ā§āĻ¸āĻŋ āĻ•āĻ°ā§āĻ¨" +proxyRemoteFilesDescription: "āĻ¯āĻ–āĻ¨ āĻāĻ‡ āĻ¸ā§‡āĻŸāĻŋāĻ‚āĻŸāĻŋ āĻšāĻžāĻ˛ā§ āĻĨāĻžāĻ•ā§‡, āĻ¤āĻ–āĻ¨ āĻ…āĻ¸āĻ‚āĻ°āĻ•ā§āĻˇāĻŋāĻ¤ āĻŦāĻž āĻ…āĻ¤āĻŋāĻ°āĻŋāĻ•ā§āĻ¤ āĻ•ā§āĻˇāĻŽāĻ¤āĻžāĻ° āĻ•āĻžāĻ°āĻŖā§‡ āĻĻā§‚āĻ°āĻŦāĻ°ā§āĻ¤ā§€ āĻĢāĻžāĻ‡āĻ˛āĻ—ā§āĻ˛āĻŋāĻ•ā§‡ āĻ¸ā§āĻĨāĻžāĻ¨ā§€āĻ¯āĻŧāĻ­āĻžāĻŦā§‡ āĻĒā§āĻ°āĻ•ā§āĻ¸āĻŋ āĻ•āĻ°āĻž āĻšāĻŦā§‡ āĻāĻŦāĻ‚ āĻĨāĻžāĻŽā§āĻŦāĻ¨ā§‡āĻ˛āĻ—ā§āĻ˛āĻŋāĻ“ āĻ¤ā§ˆāĻ°āĻŋ āĻ•āĻ°āĻž āĻšāĻŦā§‡ā§ˇ āĻ¸āĻžāĻ°ā§āĻ­āĻžāĻ° āĻ¸ā§āĻŸā§‹āĻ°ā§‡āĻœ āĻŦā§āĻ¯āĻžāĻŦāĻšāĻžāĻ° āĻ•āĻ°ā§‡ āĻ¨āĻž," +driveCapacityPerLocalAccount: "āĻĒā§āĻ°āĻ¤ā§āĻ¯ā§‡āĻ• āĻ¸ā§āĻĨāĻžāĻ¨ā§€ā§Ÿ āĻŦā§āĻ¯āĻžāĻŦāĻšāĻžāĻ°āĻ•āĻžāĻ°ā§€āĻ° āĻœāĻ¨ā§āĻ¯ āĻĄā§āĻ°āĻžāĻ‡āĻ­ā§‡āĻ° āĻœāĻžā§ŸāĻ—āĻž" +driveCapacityPerRemoteAccount: "āĻĒā§āĻ°āĻ¤ā§āĻ¯ā§‡āĻ• āĻ°āĻŋāĻŽā§‹āĻŸ āĻŦā§āĻ¯āĻžāĻŦāĻšāĻžāĻ°āĻ•āĻžāĻ°ā§€āĻ° āĻœāĻ¨ā§āĻ¯ āĻĄā§āĻ°āĻžāĻ‡āĻ­ā§‡āĻ° āĻœāĻžā§ŸāĻ—āĻž" +inMb: "āĻŽā§‡āĻ—āĻžāĻŦāĻžāĻ‡āĻŸā§‡ āĻ˛āĻŋāĻ–ā§āĻ¨" +iconUrl: "āĻ†āĻ‡āĻ•āĻ¨ā§‡āĻ° URL (āĻĢā§āĻ¯āĻžāĻ­āĻŋāĻ•āĻ¨, āĻ‡āĻ¤ā§āĻ¯āĻžāĻĻāĻŋ)" +bannerUrl: "āĻŦā§āĻ¯āĻžāĻ¨āĻžāĻ° āĻ›āĻŦāĻŋāĻ° URL" +backgroundImageUrl: "āĻĒāĻŸāĻ­ā§‚āĻŽāĻŋāĻ° āĻšāĻŋāĻ¤ā§āĻ°ā§‡āĻ° URL" +basicInfo: "āĻ†āĻĒāĻ¨āĻžāĻ° āĻŦā§āĻ¯āĻ•ā§āĻ¤āĻŋāĻ—āĻ¤ āĻ¤āĻĨā§āĻ¯" +pinnedUsers: "āĻĒāĻŋāĻ¨ āĻ•āĻ°āĻž āĻŦā§āĻ¯āĻžāĻŦāĻšāĻžāĻ°āĻ•āĻžāĻ°ā§€āĻ—āĻŖ" +pinnedUsersDescription: "āĻ†āĻĒāĻ¨āĻŋ āĻ¯ā§‡āĻ¸āĻŦ āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ°āĻ•āĻžāĻ°ā§€āĻĻā§‡āĻ° \"āĻ˜ā§āĻ°ā§‡ āĻĻā§‡āĻ–ā§āĻ¨\" āĻĒā§ƒāĻˇā§āĻ āĻžāĻ¯āĻŧ āĻĒāĻŋāĻ¨ āĻ•āĻ°āĻ¤ā§‡ āĻšāĻžāĻ¨ āĻ¤āĻžāĻĻā§‡āĻ° āĻŦāĻ°ā§āĻŖāĻ¨āĻž āĻ•āĻ°ā§āĻ¨, āĻĒā§āĻ°āĻ¤ā§āĻ¯ā§‡āĻ•ā§‡āĻ° āĻŦāĻ°ā§āĻŖāĻ¨āĻž āĻ†āĻ˛āĻžāĻĻāĻž āĻ˛āĻžāĻ‡āĻ¨ā§‡ āĻ˛āĻŋāĻ–ā§āĻ¨" +pinnedPages: "āĻĒāĻŋāĻ¨ āĻ•āĻ°āĻž āĻĒā§ƒāĻˇā§āĻ āĻžāĻ¸ā§āĻŽāĻš" +pinnedPagesDescription: "āĻ†āĻĒāĻ¨āĻŋ āĻ¯ā§‡āĻ¸āĻ•āĻ˛ āĻĒā§ƒāĻˇā§āĻ āĻžāĻ¸āĻŽā§‚āĻšāĻ•ā§‡ \"āĻ˜ā§āĻ°ā§‡ āĻĻā§‡āĻ–ā§āĻ¨\" āĻĒā§ƒāĻˇā§āĻ āĻžāĻ¯āĻŧ āĻĒāĻŋāĻ¨ āĻ•āĻ°āĻ¤ā§‡ āĻšāĻžāĻ¨ āĻ¤āĻžāĻĻā§‡āĻ° āĻŦāĻ°ā§āĻŖāĻ¨āĻž āĻ•āĻ°ā§āĻ¨, āĻĒā§āĻ°āĻ¤ā§āĻ¯ā§‡āĻ•ā§‡āĻ° āĻŦāĻ°ā§āĻŖāĻ¨āĻž āĻ†āĻ˛āĻžāĻĻāĻž āĻ˛āĻžāĻ‡āĻ¨ā§‡ āĻ˛āĻŋāĻ–ā§āĻ¨" +pinnedClipId: "āĻĒāĻŋāĻ¨āĻ•ā§ƒāĻ¤ āĻ•ā§āĻ˛āĻŋāĻĒā§‡āĻ° ID" +pinnedNotes: "āĻĒāĻŋāĻ¨ āĻ•āĻ°āĻž āĻ¨ā§‹āĻŸ" +hcaptcha: "hCaptcha" +enableHcaptcha: "hCaptcha āĻšāĻžāĻ˛ā§ āĻ•āĻ°ā§āĻ¨" +hcaptchaSiteKey: "āĻ¸āĻžāĻ‡āĻŸ āĻ•ā§€" +hcaptchaSecretKey: "āĻ¸āĻŋāĻ•ā§āĻ°ā§‡āĻŸ āĻ•ā§€" +recaptcha: "reCAPTCHA" +enableRecaptcha: "reCAPTCHA āĻšāĻžāĻ˛ā§ āĻ•āĻ°ā§āĻ¨" +recaptchaSiteKey: "āĻ¸āĻžāĻ‡āĻŸ āĻ•ā§€" +recaptchaSecretKey: "āĻ¸āĻŋāĻ•ā§āĻ°ā§‡āĻŸ āĻ•ā§€" +avoidMultiCaptchaConfirm: "āĻāĻ•āĻžāĻ§āĻŋāĻ• Captcha āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ° āĻ•āĻ°āĻ˛ā§‡ āĻ¤āĻžāĻ°āĻž āĻĒāĻ°āĻ¸ā§āĻĒāĻ°ā§‡āĻ° āĻ•āĻžāĻœā§‡ āĻŦāĻžāĻ§āĻž āĻĻāĻŋāĻ¤ā§‡ āĻĒāĻžāĻ°ā§‡āĨ¤ āĻ†āĻĒāĻ¨āĻŋ āĻ•āĻŋ āĻ…āĻ¨ā§āĻ¯āĻžāĻ¨ā§āĻ¯ Captcha āĻ¨āĻŋāĻˇā§āĻ•ā§āĻ°āĻŋāĻ¯āĻŧ āĻ•āĻ°āĻ¤ā§‡ āĻšāĻžāĻ¨? āĻ†āĻĒāĻ¨āĻŋ 'āĻŦāĻžāĻ¤āĻŋāĻ˛' āĻ•ā§āĻ˛āĻŋāĻ• āĻ•āĻ°āĻžāĻ° āĻŽāĻžāĻ§ā§āĻ¯āĻŽā§‡ āĻāĻ•āĻžāĻ§āĻŋāĻ• Captcha āĻšāĻžāĻ˛ā§ āĻ°āĻžāĻ–āĻ¤ā§‡ āĻĒāĻžāĻ°ā§‡āĻ¨āĨ¤" +antennas: "āĻ…ā§āĻ¯āĻžāĻ¨ā§āĻŸā§‡āĻ¨āĻž" +manageAntennas: "āĻ…ā§āĻ¯āĻžāĻ¨ā§āĻŸā§‡āĻ¨āĻž āĻŦā§āĻ¯āĻŦāĻ¸ā§āĻĨāĻžāĻĒāĻ¨āĻž" +name: "āĻ¨āĻžāĻŽ" +antennaSource: "āĻ…ā§āĻ¯āĻžāĻ¨ā§āĻŸā§‡āĻ¨āĻžāĻ° āĻ‰ā§ŽāĻ¸" +antennaKeywords: "āĻ¯ā§‡āĻ¸āĻŦ āĻ•ā§€āĻ“āĻ¯āĻŧāĻžāĻ°ā§āĻĄ āĻĻā§‡āĻ–āĻž āĻšāĻŦā§‡" +antennaExcludeKeywords: "āĻ¯ā§‡āĻ¸āĻŦ āĻ•ā§€āĻ“āĻ¯āĻŧāĻžāĻ°ā§āĻĄ āĻĻā§‡āĻ–āĻž āĻšāĻŦā§‡ āĻ¨āĻž" +antennaKeywordsDescription: "āĻ¸ā§āĻĒā§‡āĻ¸ āĻĻāĻŋāĻ¯āĻŧā§‡ āĻ†āĻ˛āĻžāĻĻāĻž āĻ•āĻ°āĻ˛ā§‡ AND āĻļāĻ°ā§āĻ¤ āĻ¤ā§ˆāĻ°āĻŋ āĻšāĻŦā§‡ āĻāĻŦāĻ‚ āĻ†āĻ˛āĻžāĻĻāĻž āĻ˛āĻžāĻ‡āĻ¨ā§‡ āĻ˛āĻŋāĻ–āĻ˛ā§‡ OR āĻļāĻ°ā§āĻ¤ āĻ¤ā§ˆāĻ°āĻŋ āĻšāĻŦā§‡āĨ¤" +notifyAntenna: "āĻ¨āĻ¤ā§āĻ¨ āĻ¨ā§‹āĻŸ āĻ¸āĻŽā§āĻĒāĻ°ā§āĻ•ā§‡ āĻ…āĻŦāĻšāĻŋāĻ¤ āĻ•āĻ°ā§āĻ¨" +withFileAntenna: "āĻļā§āĻ§ā§āĻŽāĻžāĻ¤ā§āĻ° āĻĢāĻžāĻ‡āĻ˛āĻ¯ā§āĻ•ā§āĻ¤ āĻ¨ā§‹āĻŸ" +enableServiceworker: "ServiceWorker āĻšāĻžāĻ˛ā§ āĻ•āĻ°ā§āĻ¨" +antennaUsersDescription: "āĻĒā§āĻ°āĻ¤ā§āĻ¯ā§‡āĻ• āĻ˛āĻžāĻ‡āĻ¨ā§‡ āĻāĻ•āĻœāĻ¨ āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ°āĻ•āĻžāĻ°ā§€āĻ° āĻ¨āĻžāĻŽ āĻ˛āĻŋāĻ–ā§āĻ¨" +caseSensitive: "āĻ›ā§‹āĻŸ āĻšāĻžāĻ¤ā§‡āĻ° āĻāĻŦāĻ‚ āĻŦā§œ āĻšāĻžāĻ¤ā§‡āĻ° āĻ…āĻ•ā§āĻˇāĻ° āĻ¨āĻŋāĻ°ā§āĻĻāĻŋāĻˇā§āĻŸ āĻ•āĻ°ā§āĻ¨" +withReplies: "āĻœāĻŦāĻžāĻŦāĻ¸āĻŽā§āĻš āĻ¯ā§āĻ•ā§āĻ¤ āĻ•āĻ°ā§āĻ¨" +connectedTo: "āĻ†āĻĒāĻ¨āĻŋ āĻ¨āĻŋāĻŽā§āĻ¨āĻ˛āĻŋāĻ–āĻŋāĻ¤ āĻ…ā§āĻ¯āĻžāĻ•āĻžāĻ‰āĻ¨ā§āĻŸā§‡āĻ° āĻ¸āĻžāĻĨā§‡ āĻ¸āĻ‚āĻ¯ā§āĻ•ā§āĻ¤" +notesAndReplies: "āĻ¨ā§‹āĻŸāĻ¸āĻŽā§‚āĻš āĻāĻŦāĻ‚ āĻœāĻŦāĻžāĻŦāĻ—ā§āĻ˛āĻŋ" +withFiles: "āĻĢāĻžāĻ‡āĻ˛āĻ—ā§āĻ˛āĻŋ āĻ¯ā§āĻ•ā§āĻ¤ āĻ•āĻ°ā§āĻ¨" +silence: "āĻ¨ā§€āĻ°āĻŦ" +silenceConfirm: "āĻ†āĻĒāĻ¨āĻŋ āĻ•āĻŋ āĻāĻ‡ āĻŦā§āĻ¯āĻžāĻŦāĻšāĻžāĻ°āĻ•āĻžāĻ°ā§€āĻ•ā§‡āĻ° āĻ¨ā§€āĻ°āĻŦ āĻ•āĻ°āĻ¤ā§‡ āĻšāĻžāĻ¨?" +unsilence: "āĻ¸āĻ°āĻŦ" +unsilenceConfirm: "āĻ†āĻĒāĻ¨āĻŋ āĻ•āĻŋ āĻāĻ‡ āĻŦā§āĻ¯āĻžāĻŦāĻšāĻžāĻ°āĻ•āĻžāĻ°ā§€āĻ•ā§‡āĻ° āĻ¸āĻ°āĻŦ āĻ•āĻ°āĻ¤ā§‡ āĻšāĻžāĻ¨?" +popularUsers: "āĻœāĻ¨āĻĒā§āĻ°āĻŋāĻ¯āĻŧ āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ°āĻ•āĻžāĻ°ā§€āĻ—āĻ¨" +recentlyUpdatedUsers: "āĻ¸āĻŽā§āĻĒā§āĻ°āĻ¤āĻŋ āĻĒā§‹āĻ¸ā§āĻŸ āĻ•āĻ°āĻž āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ°āĻ•āĻžāĻ°ā§€āĻ—āĻ¨" +recentlyRegisteredUsers: "āĻ¨āĻ¤ā§āĻ¨ āĻ¯ā§‹āĻ— āĻĻā§‡āĻ“ā§ŸāĻž āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ°āĻ•āĻžāĻ°ā§€āĻ—āĻ¨" +recentlyDiscoveredUsers: "āĻ¨āĻ¤ā§āĻ¨ āĻ–ā§āĻāĻœā§‡ āĻĒāĻžāĻ“ā§ŸāĻž āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ°āĻ•āĻžāĻ°ā§€āĻ—āĻ¨" +exploreUsersCount: "{count} āĻœāĻ¨ āĻŦā§āĻ¯āĻžāĻŦāĻšāĻžāĻ°āĻ•āĻžāĻ°ā§€" +exploreFediverse: "Fediverse āĻ˜ā§āĻ°ā§‡ āĻĻā§‡āĻ–ā§āĻ¨" +popularTags: "āĻœāĻ¨āĻĒā§āĻ°āĻŋā§Ÿ āĻŸā§āĻ¯āĻžāĻ—āĻ—ā§āĻ˛āĻŋ" +userList: "āĻ˛āĻŋāĻ¸ā§āĻŸ" +about: "āĻ†āĻĒāĻ¨āĻžāĻ° āĻ¸āĻŽā§āĻĒāĻ°ā§āĻ•ā§‡" +aboutMisskey: "Misskey āĻ¸āĻŽā§āĻĒāĻ°ā§āĻ•ā§‡" +administrator: "āĻĒā§āĻ°āĻļāĻžāĻ¸āĻ•" +token: "āĻŸā§‹āĻ•ā§‡āĻ¨" +twoStepAuthentication: "ā§¨-āĻ§āĻžāĻĒ āĻĒā§āĻ°āĻŽāĻžāĻŖā§€āĻ•āĻ°āĻŖ" +moderator: "āĻŽāĻĄāĻžāĻ°ā§‡āĻŸāĻ°" +nUsersMentioned: "{n} āĻœāĻ¨āĻ•ā§‡ āĻ‰āĻ˛ā§āĻ˛ā§‡āĻ– āĻ•āĻ°āĻž āĻšā§Ÿā§‡āĻ›ā§‡" +securityKey: "āĻ¸āĻŋāĻ•āĻŋāĻ‰āĻ°āĻŋāĻŸāĻŋ āĻ•ā§€" +securityKeyName: "āĻ•ā§€'āĻ° āĻ¨āĻžāĻŽ" +registerSecurityKey: "āĻ¸āĻŋāĻ•āĻŋāĻ‰āĻ°āĻŋāĻŸāĻŋ āĻ•ā§€ āĻ¨āĻŋāĻŦāĻ¨ā§āĻ§āĻ¨ āĻ•āĻ°ā§āĻ¨" +lastUsed: "āĻļā§‡āĻˇ āĻŦā§āĻ¯āĻžāĻŦāĻšāĻžāĻ° āĻ•āĻ°āĻž āĻšā§Ÿā§‡āĻ›ā§‡" +unregister: "āĻ¨āĻŋāĻŦāĻ¨ā§āĻ§āĻ¨āĻŽā§āĻ•ā§āĻ¤ āĻšāĻ¨" +passwordLessLogin: "āĻĒāĻžāĻ¸āĻ“āĻ¯āĻŧāĻžāĻ°ā§āĻĄ-āĻŦāĻŋāĻšā§€āĻ¨ āĻ˛āĻ—āĻ‡āĻ¨ āĻ¸ā§‡āĻŸ āĻ†āĻĒ āĻ•āĻ°ā§āĻ¨" +resetPassword: "āĻĒāĻžāĻ¸āĻ“āĻ¯āĻŧāĻžāĻ°ā§āĻĄ āĻ°āĻŋāĻ¸ā§‡āĻŸ āĻ•āĻ°ā§āĻ¨" +newPasswordIs: "āĻ¨āĻ¤ā§āĻ¨ āĻĒāĻžāĻ¸āĻ“ā§ŸāĻžāĻ°ā§āĻĄ āĻšāĻšā§āĻ›ā§‡ \"{password}\"" +reduceUiAnimation: "UI āĻ…ā§āĻ¯āĻžāĻ¨āĻŋāĻŽā§‡āĻļāĻ¨ āĻ•āĻŽāĻžāĻ¨" +share: "āĻļā§‡ā§ŸāĻžāĻ°" +notFound: "āĻĒāĻžāĻ“ā§ŸāĻž āĻ¯āĻžā§ŸāĻ¨āĻŋ" +notFoundDescription: "āĻāĻ‡ URL-āĻāĻ° āĻ¸āĻžāĻĨā§‡ āĻ¸āĻŽā§āĻĒāĻ°ā§āĻ•āĻŋāĻ¤ āĻ•ā§‹āĻ¨ā§‹ āĻĒā§ƒāĻˇā§āĻ āĻž āĻ¨ā§‡āĻ‡āĨ¤" +uploadFolder: "āĻ†āĻĒāĻ˛ā§‹āĻĄā§‡āĻ° āĻœāĻ¨ā§āĻ¯ āĻĄāĻŋāĻĢāĻ˛ā§āĻŸ āĻĢā§‹āĻ˛ā§āĻĄāĻžāĻ°" +cacheClear: "āĻ•ā§āĻ¯āĻžāĻļ āĻĒāĻ°āĻŋāĻˇā§āĻ•āĻžāĻ° āĻ•āĻ°ā§āĻ¨" +markAsReadAllNotifications: "āĻ¸āĻŽāĻ¸ā§āĻ¤ āĻŦāĻŋāĻœā§āĻžāĻĒā§āĻ¤āĻŋāĻ—ā§āĻ˛āĻŋ āĻĒāĻ āĻŋāĻ¤ āĻšāĻŋāĻ¸āĻžāĻŦā§‡ āĻšāĻŋāĻšā§āĻ¨āĻŋāĻ¤ āĻ•āĻ°ā§āĻ¨" +markAsReadAllUnreadNotes: "āĻ¸āĻŽāĻ¸ā§āĻ¤ āĻ¨ā§‹āĻŸāĻ—ā§āĻ˛āĻŋ āĻĒāĻ āĻŋāĻ¤ āĻšāĻŋāĻ¸āĻžāĻŦā§‡ āĻšāĻŋāĻšā§āĻ¨āĻŋāĻ¤ āĻ•āĻ°ā§āĻ¨" +markAsReadAllTalkMessages: "āĻ¸āĻŽāĻ¸ā§āĻ¤ āĻŽā§‡āĻ¸ā§‡āĻœ āĻĒāĻ āĻŋāĻ¤ āĻšāĻŋāĻ¸āĻžāĻŦā§‡ āĻšāĻŋāĻšā§āĻ¨āĻŋāĻ¤ āĻ•āĻ°ā§āĻ¨" +help: "āĻ¸āĻšāĻžāĻ¯āĻŧāĻ¤āĻž" +inputMessageHere: "āĻāĻ–āĻžāĻ¨ā§‡ āĻŽā§‡āĻ¸ā§‡āĻœ āĻ˛āĻŋāĻ–ā§āĻ¨" +close: "āĻŦāĻ¨ā§āĻ§" +group: "āĻ—ā§āĻ°ā§āĻĒ" +groups: "āĻ—ā§āĻ°ā§āĻĒāĻ¸āĻŽā§‚āĻš" +createGroup: "āĻ—ā§āĻ°ā§āĻĒ āĻ¤ā§ˆāĻ°ā§€ āĻ•āĻ°ā§āĻ¨" +ownedGroups: "āĻ†āĻĒāĻ¨āĻžāĻ° āĻ—ā§āĻ°ā§āĻĒāĻ—ā§āĻ˛āĻŋ" +joinedGroups: "āĻ¯ā§‡āĻ¸āĻŦ āĻ—ā§āĻ°ā§āĻĒā§‡ āĻ†āĻĒāĻ¨āĻŋ āĻ†āĻ›ā§‡āĻ¨" +invites: "āĻ†āĻŽāĻ¨ā§āĻ¤ā§āĻ°āĻŖ" +groupName: "āĻ—ā§āĻ°ā§āĻĒā§‡āĻ° āĻ¨āĻžāĻŽ" +members: "āĻ¸āĻĻāĻ¸ā§āĻ¯āĻŦā§ƒāĻ¨ā§āĻĻ" +transfer: "āĻšāĻ¸ā§āĻ¤āĻžāĻ¨ā§āĻ¤āĻ°" +messagingWithUser: "āĻĒā§āĻ°āĻžāĻ‡āĻ­ā§‡āĻŸ āĻšā§āĻ¯āĻžāĻŸ" +messagingWithGroup: "āĻ—ā§āĻ°ā§āĻĒ āĻšā§āĻ¯āĻžāĻŸ" +title: "āĻļāĻŋāĻ°ā§‹āĻ¨āĻžāĻŽ" +text: "āĻĒāĻžāĻ ā§āĻ¯" +enable: "āĻ¸āĻ•ā§āĻ°āĻŋā§Ÿ" +next: "āĻĒāĻ°āĻŦāĻ°ā§āĻ¤ā§€" +retype: "āĻĒā§āĻ¨āĻƒ āĻĒā§āĻ°āĻŦā§‡āĻļ" +noteOf: "{user} āĻāĻ° āĻ¨ā§‹āĻŸ" +inviteToGroup: "āĻ—ā§āĻ°ā§āĻĒā§‡ āĻ†āĻŽāĻ¨ā§āĻ¤ā§āĻ°āĻŖ āĻœāĻžāĻ¨āĻžāĻ¨" +maxNoteTextLength: "āĻ¨ā§‹āĻŸ āĻāĻ° āĻ¸āĻ°ā§āĻŦā§‹āĻšā§āĻš āĻĻā§ˆāĻ°ā§āĻ˜ā§āĻ¯" +quoteAttached: "āĻ‰āĻĻā§āĻ§ā§ƒāĻ¤" +quoteQuestion: "āĻ‰āĻĻā§āĻ§ā§ƒāĻ¤āĻŋ āĻšāĻŋāĻ¸āĻžāĻŦā§‡ āĻ¸āĻ‚āĻ¯ā§āĻ•ā§āĻ¤ āĻ•āĻ°āĻŦā§‡āĻ¨?" +noMessagesYet: "āĻ•ā§‹āĻ¨ āĻŽā§‡āĻ¸ā§‡āĻœ āĻ¨ā§‡āĻ‡" +newMessageExists: "āĻ¨āĻ¤ā§āĻ¨ āĻŽā§‡āĻ¸ā§‡āĻœ āĻĒā§‡ā§Ÿā§‡āĻ›ā§‡āĻ¨" +onlyOneFileCanBeAttached: "āĻ†āĻĒāĻ¨āĻŋ āĻŽā§‡āĻ¸ā§‡āĻœā§‡āĻ° āĻ¸āĻžāĻĨā§‡ āĻ¸āĻ°ā§āĻŦā§‹āĻšā§āĻš āĻāĻ•āĻŸāĻŋ āĻĢāĻžāĻ‡āĻ˛ āĻ¯ā§āĻ•ā§āĻ¤ āĻ•āĻ°āĻ¤ā§‡ āĻĒāĻžāĻ°āĻŦā§‡āĻ¨" +signinRequired: "āĻĻāĻ¯āĻŧāĻž āĻ•āĻ°ā§‡ āĻ˛āĻ— āĻ‡āĻ¨ āĻ•āĻ°ā§āĻ¨" +invitations: "āĻ†āĻŽāĻ¨ā§āĻ¤ā§āĻ°āĻŖ" +invitationCode: "āĻ‡āĻ¨āĻ­āĻžāĻ‡āĻŸ āĻ•ā§‹āĻĄ" +checking: "āĻĒāĻ°ā§€āĻ•ā§āĻˇāĻž āĻ•āĻ°āĻž āĻšāĻšā§āĻ›ā§‡..." +available: "āĻ‰āĻĒāĻ˛āĻŦā§āĻ§" +unavailable: "āĻ…āĻ¨ā§āĻĒāĻ˛āĻŦā§āĻ§" +usernameInvalidFormat: "āĻ†āĻĒāĻ¨āĻŋ āĻ•ā§‡āĻŦāĻ˛āĻŽāĻžāĻ¤ā§āĻ° a-z, A-Z, 0-9, _ āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ° āĻ•āĻ°āĻ¤ā§‡ āĻĒāĻžāĻ°ā§‡āĻ¨" +tooShort: "āĻ–ā§āĻŦ āĻ›ā§‹āĻŸ" +tooLong: "āĻ–ā§āĻŦ āĻŦā§œ" +weakPassword: "āĻĻā§āĻ°ā§āĻŦāĻ˛ āĻĒāĻžāĻ¸āĻ“ā§ŸāĻžāĻ°ā§āĻĄ" +normalPassword: "āĻ¸āĻžāĻ§āĻžāĻ°āĻŖ āĻĒāĻžāĻ¸āĻ“ā§ŸāĻžāĻ°ā§āĻĄ" +strongPassword: "āĻļāĻ•ā§āĻ¤āĻŋāĻļāĻžāĻ˛ā§€ āĻĒāĻžāĻ¸āĻ“ā§ŸāĻžāĻ°ā§āĻĄ" +passwordMatched: "āĻŽāĻŋāĻ˛ā§‡āĻ›ā§‡" +passwordNotMatched: "āĻŽāĻŋāĻ˛ā§‡āĻ¨āĻŋ" +signinWith: "{x} āĻāĻ° āĻ¸āĻžāĻšāĻžāĻ¯ā§āĻ¯ā§‡ āĻ¸āĻžāĻ‡āĻ¨ āĻ‡āĻ¨ āĻ•āĻ°ā§āĻ¨" +signinFailed: "āĻ˛āĻ— āĻ‡āĻ¨ āĻ•āĻ°āĻž āĻ¯āĻžāĻ¯āĻŧāĻ¨āĻŋāĨ¤ āĻ†āĻĒāĻ¨āĻžāĻ° āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ°āĻ•āĻžāĻ°ā§€āĻ° āĻ¨āĻžāĻŽ āĻāĻŦāĻ‚ āĻĒāĻžāĻ¸āĻ“āĻ¯āĻŧāĻžāĻ°ā§āĻĄ āĻšā§‡āĻ• āĻ•āĻ°ā§āĻ¨." +tapSecurityKey: "āĻ¸āĻŋāĻ•āĻŋāĻ‰āĻ°āĻŋāĻŸāĻŋ āĻ•ā§€ āĻ¸ā§āĻĒāĻ°ā§āĻļ āĻ•āĻ°ā§āĻ¨" +or: "āĻ…āĻĨāĻŦāĻž" +language: "āĻ­āĻžāĻˇāĻž" +uiLanguage: "UI āĻāĻ° āĻ­āĻžāĻˇāĻž" +groupInvited: "āĻ†āĻĒāĻ¨āĻŋ āĻāĻ•āĻŸāĻŋ āĻ—ā§āĻ°ā§āĻĒā§‡ āĻ†āĻŽāĻ¨ā§āĻ¤ā§āĻ°āĻŋāĻ¤ āĻšā§Ÿā§‡āĻ›ā§‡āĻ¨" +aboutX: "{x} āĻ¸āĻŽā§āĻĒāĻ°ā§āĻ•ā§‡" +useOsNativeEmojis: "āĻ…āĻĒāĻžāĻ°ā§‡āĻŸāĻŋāĻ‚ āĻ¸āĻŋāĻ¸ā§āĻŸā§‡āĻŽā§‡āĻ° āĻ¨ā§‡āĻŸāĻŋāĻ­ āĻ‡āĻŽā§‹āĻœāĻŋ āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ° āĻ•āĻ°ā§āĻ¨" +disableDrawer: "āĻĄā§āĻ°āĻ¯āĻŧāĻžāĻ° āĻŽā§‡āĻ¨ā§ āĻĒā§āĻ°āĻĻāĻ°ā§āĻļāĻ¨ āĻ•āĻ°āĻŦā§‡āĻ¨ āĻ¨āĻž" +youHaveNoGroups: "āĻ†āĻĒāĻ¨āĻžāĻ° āĻ•ā§‹āĻ¨ āĻ—ā§āĻ°ā§āĻĒ āĻ¨ā§‡āĻ‡ " +joinOrCreateGroup: "āĻāĻ•āĻŸāĻŋ āĻŦāĻŋāĻĻā§āĻ¯āĻŽāĻžāĻ¨ āĻ—ā§āĻ°ā§āĻĒā§‡āĻ° āĻ†āĻŽāĻ¨ā§āĻ¤ā§āĻ°āĻŖ āĻĒāĻžāĻ¨ āĻŦāĻž āĻāĻ•āĻŸāĻŋ āĻ¨āĻ¤ā§āĻ¨ āĻ—ā§āĻ°ā§āĻĒ āĻ¤ā§ˆāĻ°āĻŋ āĻ•āĻ°ā§āĻ¨ā§ˇ" +noHistory: "āĻ•ā§‹āĻ¨ā§‹ āĻ‡āĻ¤āĻŋāĻšāĻžāĻ¸ āĻ¨ā§‡āĻ‡" +signinHistory: "āĻĒā§āĻ°āĻŦā§‡āĻļ āĻ•āĻ°āĻžāĻ° āĻ‡āĻ¤āĻŋāĻšāĻžāĻ¸" +disableAnimatedMfm: "āĻ…ā§āĻ¯āĻžāĻ¨āĻŋāĻŽā§‡āĻŸā§‡āĻĄ MFM āĻ…āĻ•ā§āĻˇāĻŽ āĻ•āĻ°ā§āĻ¨" +doing: "āĻĒā§āĻ°āĻ•ā§āĻ°āĻŋā§ŸāĻž āĻ•āĻ°āĻ›ā§‡..." +category: "āĻŦāĻŋāĻ­āĻžāĻ—" +tags: "āĻŸâ€ā§āĻ¯āĻžāĻ—āĻ¸āĻŽā§‚āĻš" +docSource: "āĻĄāĻ•ā§āĻŽā§‡āĻ¨ā§āĻŸā§‡āĻ° āĻ‰ā§ŽāĻ¸" +createAccount: "āĻ…ā§āĻ¯āĻžāĻ•āĻžāĻ‰āĻ¨ā§āĻŸ āĻ¤ā§ˆāĻ°āĻŋ āĻ•āĻ°ā§āĻ¨" +existingAccount: "āĻŦāĻŋāĻĻā§āĻ¯āĻŽāĻžāĻ¨ āĻ…ā§āĻ¯āĻžāĻ•āĻžāĻ‰āĻ¨ā§āĻŸ" +regenerate: "āĻ†āĻŦāĻžāĻ°āĻ“ āĻ¤ā§ˆāĻ°āĻŋ āĻ•āĻ°ā§āĻ¨" +fontSize: "āĻĢāĻ¨ā§āĻŸā§‡āĻ° āĻ†āĻ•āĻžāĻ°" +noFollowRequests: "āĻ†āĻĒāĻ¨āĻžāĻ° āĻ•ā§‹āĻ¨ āĻĢāĻ˛ā§‹āĻ“ āĻ°āĻŋāĻ•ā§ā§Ÿā§‡āĻ¸ā§āĻŸ āĻ¨ā§‡āĻ‡" +openImageInNewTab: "āĻ›āĻŦāĻŋ āĻ¨āĻ¤ā§āĻ¨ āĻŸā§āĻ¯āĻžāĻŦā§‡ āĻ–ā§āĻ˛ā§āĻ¨" +dashboard: "āĻĄā§āĻ¯āĻžāĻļāĻŦā§‹āĻ°ā§āĻĄ" +local: "āĻ¸ā§āĻĨāĻžāĻ¨ā§€āĻ¯āĻŧ" +remote: "āĻ°āĻŋāĻŽā§‹āĻŸ" +total: "āĻŽā§‹āĻŸ" +weekOverWeekChanges: "āĻ—āĻ¤ āĻ¸āĻĒā§āĻ¤āĻžāĻšā§‡" +dayOverDayChanges: "āĻ—āĻ¤āĻ•āĻžāĻ˛" +appearance: "āĻ…āĻŦā§ŸāĻŦ" +clientSettings: "āĻ•ā§āĻ˛āĻžā§Ÿā§‡āĻ¨ā§āĻŸ āĻ¸ā§‡āĻŸāĻŋāĻ‚āĻ¸" +accountSettings: "āĻ…ā§āĻ¯āĻžāĻ•āĻžāĻ‰āĻ¨ā§āĻŸ āĻ¸ā§‡āĻŸāĻŋāĻ‚āĻ¸" +promotion: "āĻĒā§āĻ°āĻŽā§‹āĻļāĻ¨" +promote: "āĻĒā§āĻ°āĻšāĻžāĻ° āĻ•āĻ°ā§āĻ¨" +numberOfDays: "āĻĻāĻŋāĻ¨ā§‡āĻ° āĻ¸āĻ‚āĻ–ā§āĻ¯āĻž" +hideThisNote: "āĻ¨ā§‹āĻŸāĻŸāĻŋ āĻ˛ā§āĻ•āĻžāĻ¨" +showFeaturedNotesInTimeline: "āĻŸāĻžāĻ‡āĻŽāĻ˛āĻžāĻ‡āĻ¨ā§‡ āĻ¸ā§āĻĒāĻžāĻ°āĻŋāĻļāĻ•ā§ƒāĻ¤ āĻ¨ā§‹āĻŸāĻ—ā§āĻ˛āĻŋ āĻĻā§‡āĻ–āĻžāĻ¨" +objectStorage: "āĻ…āĻŦāĻœā§‡āĻ•ā§āĻŸ āĻ¸ā§āĻŸā§‹āĻ°ā§‡āĻœ" +useObjectStorage: "āĻ…āĻŦāĻœā§‡āĻ•ā§āĻŸ āĻ¸ā§āĻŸā§‹āĻ°ā§‡āĻœ āĻŦā§āĻ¯āĻžāĻŦāĻšāĻžāĻ° āĻ•āĻ°ā§āĻ¨" +objectStorageBaseUrl: "Base URL" +objectStorageBaseUrlDesc: "āĻ°ā§‡āĻĢāĻžāĻ°ā§‡āĻ¨ā§āĻ¸ āĻšāĻŋāĻ¸āĻžāĻŦā§‡ āĻŦā§āĻ¯āĻŦāĻšā§ƒāĻ¤ URLāĨ¤ āĻ†āĻĒāĻ¨āĻŋ āĻāĻ•āĻŸāĻŋ CDN āĻŦāĻž āĻĒā§āĻ°āĻ•ā§āĻ¸āĻŋ āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ° āĻ•āĻ°āĻ˛ā§‡ URL, S3: 'https://.s3.amazonaws.com', GCS: 'https://storage.googleapis.com/'āĨ¤" +objectStorageBucket: "Bucket" +objectStorageBucketDesc: "āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ° āĻ•āĻ°āĻž āĻĒāĻ°āĻŋāĻˇā§‡āĻŦāĻžāĻ° bucket āĻāĻ° āĻ¨āĻžāĻŽ āĻ˛āĻŋāĻ–ā§āĻ¨āĨ¤ " +objectStoragePrefix: "Prefix" +objectStoragePrefixDesc: "āĻĢāĻžāĻ‡āĻ˛āĻ¸āĻŽā§‚āĻš āĻāĻ‡ prefix āĻ¯ā§āĻ•ā§āĻ¤ āĻĢā§‹āĻ˛ā§āĻĄāĻžāĻ°ā§‡āĻ° āĻ…āĻ§ā§€āĻ¨ā§‡ āĻ¸āĻ‚āĻ°āĻ•ā§āĻˇāĻŖ āĻ•āĻ°āĻž āĻšāĻŦā§‡āĨ¤" +objectStorageEndpoint: "āĻāĻ¨ā§āĻĄāĻĒā§Ÿā§‡āĻ¨ā§āĻŸ" +objectStorageEndpointDesc: "S3 āĻāĻ° āĻœāĻ¨ā§āĻ¯ āĻĢāĻžāĻāĻ•āĻž āĻ°āĻžāĻ–ā§āĻ¨, āĻ…āĻ¨ā§āĻ¯āĻĨāĻžāĻ¯āĻŧ āĻĒā§āĻ°āĻ¤āĻŋāĻŸāĻŋ āĻĒāĻ°āĻŋāĻˇā§‡āĻŦāĻžāĻ° āĻāĻ¨ā§āĻĄāĻĒā§Ÿā§‡āĻ¨ā§āĻŸ āĻ¨āĻŋāĻ°ā§āĻĻāĻŋāĻˇā§āĻŸ āĻ•āĻ°ā§āĻ¨āĨ¤ ''āĻŦāĻž': ' āĻšāĻŋāĻ¸ā§‡āĻŦā§‡ āĻ˛āĻŋāĻ–ā§āĻ¨āĨ¤" +objectStorageRegion: "Region" +objectStorageRegionDesc: "'xx-east-1'-āĻāĻ° āĻŽāĻ¤ā§‹ āĻāĻ•āĻŸāĻŋ region āĻ¨āĻŋāĻ°ā§āĻĻāĻŋāĻˇā§āĻŸ āĻ•āĻ°ā§āĻ¨āĨ¤ āĻ¯āĻĻāĻŋ āĻ†āĻĒāĻ¨āĻžāĻ° āĻĒāĻ°āĻŋāĻˇā§‡āĻŦāĻžāĻ¤ā§‡ region āĻāĻ° āĻ§āĻžāĻ°āĻŖāĻž āĻ¨āĻž āĻĨāĻžāĻ•ā§‡, āĻ¤āĻžāĻšāĻ˛ā§‡ āĻāĻŸāĻŋ āĻ–āĻžāĻ˛āĻŋ āĻŦāĻž 'us-east-1' āĻšāĻ“āĻ¯āĻŧāĻž āĻ‰āĻšāĻŋāĻ¤āĨ¤" +objectStorageUseSSL: "SSL āĻŦā§āĻ¯āĻžāĻŦāĻšāĻžāĻ° āĻ•āĻ°ā§āĻ¨" +objectStorageUseSSLDesc: "API āĻ•āĻžāĻ¨ā§‡āĻ•āĻļāĻ¨āĻ—ā§āĻ˛āĻŋāĻ° āĻœāĻ¨ā§āĻ¯ āĻ¯āĻĻāĻŋ https āĻŦā§āĻ¯āĻžāĻŦāĻšāĻžāĻ° āĻ¨āĻž āĻ•āĻ°ā§‡āĻ¨, āĻ¤āĻžāĻšāĻ˛ā§‡ āĻāĻ‡ āĻ…āĻĒāĻļāĻ¨āĻŸāĻŋ āĻŦāĻ¨ā§āĻ§ āĻ•āĻ°ā§āĻ¨" +objectStorageUseProxy: "Proxy āĻŦā§āĻ¯āĻžāĻŦāĻšāĻžāĻ° āĻ•āĻ°ā§āĻ¨" +objectStorageUseProxyDesc: "āĻ†āĻĒāĻ¨āĻŋ API āĻ¸āĻ‚āĻ¯ā§‹āĻ—ā§‡āĻ° āĻœāĻ¨ā§āĻ¯ proxy āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ° āĻ¨āĻž āĻ•āĻ°āĻ˛ā§‡, āĻāĻŸāĻŋ āĻŦāĻ¨ā§āĻ§ āĻ•āĻ°ā§āĻ¨āĨ¤" +objectStorageSetPublicRead: "āĻ†āĻĒāĻ˛ā§‹āĻĄā§‡āĻ° āĻ‰āĻĒāĻ° ''public-read' āĻ¸ā§‡āĻŸ āĻ•āĻ°ā§āĻ¨" +serverLogs: "āĻ¸āĻžāĻ°ā§āĻ­āĻžāĻ° āĻ˛āĻ—" +deleteAll: "āĻ¸āĻŦ āĻŽā§āĻ›ā§āĻ¨" +showFixedPostForm: "āĻŸāĻžāĻ‡āĻŽāĻ˛āĻžāĻ‡āĻ¨ā§‡āĻ° āĻļā§€āĻ°ā§āĻˇā§‡ āĻĒā§‹āĻ¸ā§āĻŸ āĻ•āĻ°āĻžāĻ° āĻĢāĻ°ā§āĻŽāĻŸāĻŋ āĻĻā§‡āĻ–āĻžāĻ¨" +newNoteRecived: "āĻ¨āĻ¤ā§āĻ¨ āĻ¨ā§‹āĻŸ āĻ†āĻ›ā§‡" +sounds: "āĻļāĻŦā§āĻĻ" +listen: "āĻļā§āĻ¨ā§āĻ¨" +none: "āĻ•āĻŋāĻ›ā§āĻ‡ āĻ¨āĻž" +showInPage: "āĻĒā§‡āĻœā§‡ āĻĻā§‡āĻ–āĻžāĻ¨" +popout: "āĻĒāĻĒ-āĻ†āĻ‰āĻŸ" +volume: "āĻ†āĻ“ā§ŸāĻžāĻœā§‡āĻ° āĻŽāĻžāĻ¤ā§āĻ°āĻž" +masterVolume: "āĻŽāĻžāĻ¸ā§āĻŸāĻžāĻ° āĻ†āĻ“ā§ŸāĻžāĻœā§‡āĻ° āĻŽāĻžāĻ¤ā§āĻ°āĻž" +details: "āĻ†āĻ°āĻ“ āĻœāĻžāĻ¨ā§āĻ¨" +chooseEmoji: "āĻ‡āĻŽā§‹āĻœāĻŋ āĻ¨āĻŋāĻ°ā§āĻŦāĻžāĻšāĻ¨ āĻ•āĻ°ā§āĻ¨" +unableToProcess: "āĻ•āĻžāĻœāĻŸāĻŋ āĻ¸āĻŽā§āĻĒāĻ¨ā§āĻ¨ āĻ•āĻ°āĻž āĻ¯āĻžā§ŸāĻ¨āĻŋ" +recentUsed: "āĻ¸āĻŽā§āĻĒā§āĻ°āĻ¤āĻŋ āĻŦā§āĻ¯āĻŦāĻšā§ƒāĻ¤" +install: "āĻ‡āĻ¨ā§āĻ¸āĻŸāĻ˛" +uninstall: "āĻ†āĻ¨āĻ‡āĻ¨ā§āĻ¸āĻŸāĻ˛" +installedApps: "āĻ‡āĻ¨ā§āĻ¸āĻŸāĻ˛ āĻ•āĻ°āĻž āĻ…ā§āĻ¯āĻžāĻĒāĻ¸āĻŽā§‚āĻš" +nothing: "āĻāĻ–āĻžāĻ¨ā§‡ āĻ•āĻŋāĻ›ā§āĻ‡ āĻ¨āĻžāĻ‡" +installedDate: "āĻ‡āĻ¨ā§āĻ¸āĻŸāĻ˛ āĻ•āĻ°āĻžāĻ° āĻ¤āĻžāĻ°āĻŋāĻ–" +lastUsedDate: "āĻ¸āĻ°ā§āĻŦāĻļā§‡āĻˇ āĻŦā§āĻ¯āĻžāĻŦāĻšā§ƒāĻ¤" +state: "āĻ…āĻŦāĻ¸ā§āĻĨāĻž" +sort: "āĻ¸āĻžāĻœāĻžāĻ¨" +ascendingOrder: "āĻŠāĻ°ā§āĻ§ā§āĻŦāĻ•ā§āĻ°āĻŽā§‡" +descendingOrder: "āĻ¨āĻŋāĻŽā§āĻ¨āĻ•ā§āĻ°āĻŽā§‡" +scratchpad: "āĻ¸ā§āĻ•ā§āĻ°ā§āĻ¯āĻžāĻšāĻĒā§āĻ¯āĻžāĻĄ" +scratchpadDescription: "āĻ¸ā§āĻ•ā§āĻ°ā§āĻ¯āĻžāĻšāĻĒā§āĻ¯āĻžāĻĄ AiScript-āĻāĻ° āĻœāĻ¨ā§āĻ¯ āĻāĻ•āĻŸāĻŋ āĻĒāĻ°ā§€āĻ•ā§āĻˇāĻžāĻŽā§‚āĻ˛āĻ• āĻĒāĻ°āĻŋāĻŦā§‡āĻļ āĻĒā§āĻ°āĻĻāĻžāĻ¨ āĻ•āĻ°ā§‡āĨ¤ āĻ†āĻĒāĻ¨āĻŋ āĻŽāĻŋāĻ¸ā§āĻ•āĻŋāĻ° āĻ¸āĻžāĻĨā§‡ āĻ‡āĻ¨ā§āĻŸāĻžāĻ°āĻ…ā§āĻ¯āĻžāĻ•ā§āĻŸ āĻ•āĻ°ā§‡ āĻāĻŽāĻ¨ āĻ•ā§‹āĻĄ āĻ˛āĻŋāĻ–āĻ¤ā§‡, āĻšāĻžāĻ˛āĻžāĻ¤ā§‡ āĻāĻŦāĻ‚ āĻ¤āĻžāĻ° āĻĢāĻ˛āĻžāĻĢāĻ˛ āĻĻā§‡āĻ–āĻ¤ā§‡ āĻĒāĻžāĻ°ā§‡āĻ¨āĨ¤" +output: "āĻ†āĻ‰āĻŸāĻĒā§āĻŸ" +script: "āĻ¸ā§āĻ•ā§āĻ°āĻŋāĻĒā§āĻŸ" +disablePagesScript: "āĻĒā§‡āĻœāĻ—ā§āĻ˛ā§‹āĻ¤ā§‡ AiScript āĻ…āĻ•ā§āĻˇāĻŽ āĻ•āĻ°ā§āĻ¨" +updateRemoteUser: "āĻ°āĻŋāĻŽā§‹āĻŸ āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ°āĻ•āĻžāĻ°ā§€āĻ° āĻ¤āĻĨā§āĻ¯ āĻ†āĻĒāĻĄā§‡āĻŸ āĻ•āĻ°ā§āĻ¨" +deleteAllFiles: "āĻ¸āĻ•āĻ˛ āĻĢāĻžāĻ‡āĻ˛ āĻĄāĻŋāĻ˛āĻŋāĻŸ āĻ•āĻ°ā§āĻ¨" +deleteAllFilesConfirm: "āĻ¸āĻ•āĻ˛ āĻĢāĻžāĻ‡āĻ˛ āĻĄāĻŋāĻ˛āĻŋāĻŸ āĻ•āĻ°āĻ¤ā§‡ āĻšāĻžāĻ¨?" +removeAllFollowing: "āĻ¸āĻ•āĻ˛ āĻ…āĻ¨ā§āĻ¸āĻ°āĻŖ āĻŦāĻžāĻ¤āĻŋāĻ˛ āĻ•āĻ°ā§āĻ¨" +removeAllFollowingDescription: "{host} āĻāĻ° āĻ¸āĻ•āĻ˛ āĻŦā§āĻ¯āĻžāĻŦāĻšāĻžāĻ°āĻ•āĻžāĻ°ā§€āĻ•ā§‡ āĻ†āĻ° āĻĢāĻ˛ā§‹āĻ“ āĻ•āĻ°āĻŦā§‡āĻ¨ āĻ¨āĻž āĨ¤ āĻ¯āĻĻāĻŋ āĻ‡āĻ¨ā§āĻ¸āĻ¤ā§āĻ¯āĻžāĻ¨ā§āĻ¸āĻŸāĻŋāĻ° āĻ•ā§‹āĻ¨ āĻ¸āĻŽāĻ¸ā§āĻ¯āĻž (āĻ¯ā§‡āĻŽāĻ¨āĻƒ āĻ‡āĻ¨ā§āĻ¸āĻ¤ā§āĻ¯āĻžāĻ¨ā§āĻ¸āĻŸāĻŋ āĻ†āĻ° āĻ¨ā§‡āĻ‡) āĻšā§Ÿā§‡ āĻĨāĻžāĻ•ā§‡ āĻ¤āĻŦā§‡ āĻāĻŸāĻŋ āĻŦā§āĻ¯āĻžāĻŦāĻšāĻžāĻ° āĻ•āĻ°ā§āĻ¨ āĨ¤ " +userSuspended: "āĻāĻ‡ āĻŦā§āĻ¯āĻžāĻŦāĻšāĻžāĻ°āĻ•āĻžāĻ°āĻŋāĻ° āĻ…ā§āĻ¯āĻžāĻ•āĻžāĻ‰āĻ¨ā§āĻŸ āĻ¸ā§āĻĨāĻ—āĻŋāĻ¤ āĻ•āĻ°āĻž āĻšā§Ÿā§‡āĻ›ā§‡" +userSilenced: "āĻāĻ‡ āĻŦā§āĻ¯āĻžāĻŦāĻšāĻžāĻ°āĻ•āĻžāĻ°āĻŋāĻ•ā§‡ āĻŽāĻŋāĻ‰āĻŸ āĻ•āĻ°āĻž āĻšā§Ÿā§‡āĻ›ā§‡" +yourAccountSuspendedTitle: "āĻāĻ‡ āĻ…ā§āĻ¯āĻžāĻ•āĻžāĻ‰āĻ¨ā§āĻŸāĻŸāĻŋ āĻ¸ā§āĻĨāĻ—āĻŋāĻ¤ āĻ•āĻ°āĻž āĻšā§Ÿā§‡āĻ›ā§‡" +yourAccountSuspendedDescription: "āĻ¸āĻžāĻ°ā§āĻ­āĻžāĻ°ā§‡āĻ° āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ°ā§‡āĻ° āĻļāĻ°ā§āĻ¤āĻžāĻŦāĻ˛ā§€ āĻ˛āĻ™ā§āĻ˜āĻ¨ā§‡āĻ° āĻŽāĻ¤ā§‹ āĻ•āĻžāĻ°āĻŖā§‡ āĻāĻ‡ āĻ…ā§āĻ¯āĻžāĻ•āĻžāĻ‰āĻ¨ā§āĻŸāĻŸāĻŋ āĻ¸ā§āĻĨāĻ—āĻŋāĻ¤ āĻ•āĻ°āĻž āĻšāĻ¯āĻŧā§‡āĻ›ā§‡ā§ˇ āĻŦāĻŋāĻ¸ā§āĻ¤āĻžāĻ°āĻŋāĻ¤ āĻœāĻžāĻ¨āĻžāĻ° āĻœāĻ¨ā§āĻ¯ āĻĒā§āĻ°āĻļāĻžāĻ¸āĻ•ā§‡āĻ° āĻ¸āĻžāĻĨā§‡ āĻ¯ā§‹āĻ—āĻžāĻ¯ā§‹āĻ— āĻ•āĻ°ā§āĻ¨ āĨ¤ āĻāĻ•āĻŸāĻŋ āĻ¨āĻ¤ā§āĻ¨ āĻ…ā§āĻ¯āĻžāĻ•āĻžāĻ‰āĻ¨ā§āĻŸ āĻ¤ā§ˆāĻ°āĻŋ āĻ•āĻ°āĻŦā§‡āĻ¨ āĻ¨āĻž āĻĻāĻ¯āĻŧāĻž āĻ•āĻ°ā§‡ āĨ¤" +menu: "āĻŽā§‡āĻ¨ā§" +divider: "āĻ–āĻ¨ā§āĻĄāĻ•" +addItem: "āĻ†āĻ‡āĻŸā§‡āĻŽ āĻ¯ā§‹āĻ— āĻ•āĻ°ā§āĻ¨" +relays: "āĻ°āĻŋāĻ˛ā§‡āĻ—ā§āĻ˛āĻŋ" +addRelay: "āĻ°āĻŋāĻ˛ā§‡ āĻ¯ā§‹āĻ— āĻ•āĻ°ā§āĻ¨" +inboxUrl: "inbox āĻāĻ° URL" +addedRelays: "āĻ¯ā§‹āĻ—āĻ•ā§ƒāĻ¤ āĻ°āĻŋāĻ˛ā§‡āĻ—ā§āĻ˛āĻŋ" +serviceworkerInfo: "āĻĒā§āĻļ āĻŦāĻŋāĻœā§āĻžāĻĒā§āĻ¤āĻŋāĻ° āĻœāĻ¨ā§āĻ¯ āĻšāĻžāĻ˛ā§ āĻ•āĻ°āĻž āĻ˛āĻžāĻ—āĻŦā§‡āĨ¤" +deletedNote: "āĻĄāĻŋāĻ˛āĻŋāĻŸ āĻ•āĻ°āĻž āĻ¨ā§‹āĻŸ" +invisibleNote: "āĻ…āĻĻā§ƒāĻļā§āĻ¯ āĻ¨ā§‹āĻŸ" +enableInfiniteScroll: "āĻ‡āĻ¨āĻĢāĻŋāĻ¨āĻŋāĻŸ āĻ¸ā§āĻ•ā§āĻ°āĻ˛ āĻšāĻžāĻ˛ā§ āĻ•āĻ°ā§āĻ¨" +visibility: "āĻĻā§ƒāĻļā§āĻ¯āĻŽāĻžāĻ¨āĻ¤āĻž" +poll: "āĻœāĻ°āĻŋāĻĒ" +useCw: "āĻ•āĻ¨ā§āĻŸā§‡āĻ¨ā§āĻŸ āĻ˛ā§āĻ•āĻžāĻ¨" +enablePlayer: "āĻ­āĻŋāĻĄāĻŋāĻ“ āĻĒā§āĻ˛ā§‡ā§ŸāĻžāĻ° āĻ–ā§āĻ˛ā§āĻ¨" +disablePlayer: "āĻ­āĻŋāĻĄāĻŋāĻ“ āĻĒā§āĻ˛ā§‡ā§ŸāĻžāĻ° āĻŦāĻ¨ā§āĻ§ āĻ•āĻ°ā§āĻ¨" +expandTweet: "āĻŸā§āĻ‡āĻŸ āĻŦāĻŋāĻ¸ā§āĻ¤āĻžāĻ°āĻŋāĻ¤ āĻ•āĻ°ā§āĻ¨" +themeEditor: "āĻĨāĻŋāĻŽ āĻ¸āĻŽā§āĻĒāĻžāĻĻāĻ•" +description: "āĻŦāĻ°ā§āĻŖāĻ¨āĻž" +describeFile: "āĻ•ā§āĻ¯āĻžāĻĒāĻļāĻ¨ āĻ¯ā§‹āĻ— āĻ•āĻ°ā§āĻ¨" +enterFileDescription: "āĻ•ā§āĻ¯āĻžāĻĒāĻļāĻ¨ āĻ¯ā§‹āĻ— āĻ•āĻ°ā§āĻ¨" +author: "āĻ˛ā§‡āĻ–āĻ•" +leaveConfirm: "āĻ•āĻŋāĻ›ā§ āĻĒāĻ°āĻŋāĻŦāĻ°ā§āĻ¤āĻ¨ āĻ¸ā§‡āĻ­ āĻ•āĻ°āĻž āĻšā§ŸāĻ¨āĻŋāĨ¤ āĻ†āĻĒāĻ¨āĻŋ āĻ•āĻŋ āĻšāĻ˛ā§‡ āĻ¯ā§‡āĻ¤ā§‡ āĻšāĻžāĻ¨?" +manage: "āĻĒāĻ°āĻŋāĻšāĻžāĻ˛āĻ¨āĻž" +plugins: "āĻĒā§āĻ˛āĻžāĻ—āĻ‡āĻ¨āĻ¸āĻŽā§‚āĻš" +deck: "āĻĄā§‡āĻ•" +undeck: "āĻĄā§‡āĻ•āĻŽā§āĻ•ā§āĻ¤ āĻ•āĻ°ā§āĻ¨" +useBlurEffectForModal: "āĻŽā§‹āĻĄāĻžāĻ˛ā§‡āĻ° āĻœāĻ¨ā§āĻ¯ āĻŦā§āĻ˛āĻžāĻ° āĻ‡āĻĢā§‡āĻ•ā§āĻŸ āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ° āĻ•āĻ°ā§āĻ¨" +useFullReactionPicker: "āĻ¸āĻŽā§āĻĒā§‚āĻ°ā§āĻŖ āĻŦā§ˆāĻļāĻŋāĻˇā§āĻŸā§āĻ¯āĻ¯ā§āĻ•ā§āĻ¤ āĻ°āĻŋāĻ…ā§āĻ¯āĻžāĻ•āĻļāĻ¨ āĻĒāĻŋāĻ•āĻžāĻ° āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ° āĻ•āĻ°ā§āĻ¨" +width: "āĻĒā§āĻ°āĻ¸ā§āĻĨ" +height: "āĻ‰āĻšā§āĻšāĻ¤āĻž" +large: "āĻŦā§œ" +medium: "āĻŽāĻžāĻāĻžāĻ°āĻŋ" +small: "āĻ›ā§‹āĻŸ" +generateAccessToken: "āĻ…ā§āĻ¯āĻžāĻ•ā§āĻ¸ā§‡āĻ¸ āĻŸā§‹āĻ•ā§‡āĻ¨ āĻ¤ā§ˆāĻ°āĻŋ āĻ•āĻ°ā§āĻ¨" +permission: "āĻ…āĻ¨ā§āĻŽāĻ¤āĻŋ" +enableAll: "āĻ¸āĻŦāĻ—ā§āĻ˛āĻŋ āĻ¸āĻ•ā§āĻ°āĻŋā§Ÿ āĻ•āĻ°ā§āĻ¨" +disableAll: "āĻ¸āĻŦāĻ—ā§āĻ˛āĻŋ āĻ¨āĻŋāĻˇā§āĻ•ā§āĻ°āĻŋā§Ÿ āĻ•āĻ°ā§āĻ¨" +tokenRequested: "āĻ…ā§āĻ¯āĻžāĻ•āĻžāĻ‰āĻ¨ā§āĻŸā§‡ āĻ…ā§āĻ¯āĻžāĻ•ā§āĻ¸ā§‡āĻ¸ āĻĒā§āĻ°āĻĻāĻžāĻ¨ āĻ•āĻ°āĻŦā§‡āĻ¨" +pluginTokenRequestedDescription: "āĻāĻ‡ āĻĒā§āĻ˛āĻžāĻ—āĻ‡āĻ¨āĻŸāĻŋ āĻāĻ–āĻžāĻ¨ā§‡ āĻĻā§‡āĻ“ā§ŸāĻž āĻ…āĻ¨ā§āĻŽā§āĻ¤āĻŋāĻ¸āĻŽā§‚āĻš āĻŦā§āĻ¯āĻžāĻŦāĻšāĻžāĻ° āĻ•āĻ°āĻŦā§‡" +notificationType: "āĻŦāĻŋāĻœā§āĻžāĻĒā§āĻ¤āĻŋāĻ° āĻ§āĻ°āĻ¨" +edit: "āĻ¸āĻŽā§āĻĒāĻžāĻĻāĻ¨āĻž" +useStarForReactionFallback: "āĻ°āĻŋāĻ…ā§āĻ¯āĻžāĻ•āĻļāĻ¨ā§‡āĻ° āĻ‡āĻŽā§‹āĻœāĻŋ āĻ¨āĻž āĻœāĻžāĻ¨āĻ˛ā§‡ ★ āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ° āĻ•āĻ°ā§āĻ¨" +emailServer: "āĻ‡āĻŽā§‡āĻ‡āĻ˛ āĻ¸āĻžāĻ°ā§āĻ­āĻžāĻ°" +enableEmail: "āĻ‡āĻŽā§‡āĻ‡āĻ˛ āĻŦāĻŋāĻ¤āĻ°āĻŖ āĻšāĻžāĻ˛ā§ āĻ•āĻ°ā§āĻ¨" +emailConfigInfo: "āĻ†āĻĒāĻ¨āĻžāĻ° āĻ‡āĻŽā§‡āĻ˛ āĻ āĻŋāĻ•āĻžāĻ¨āĻž āĻ¨āĻŋāĻļā§āĻšāĻŋāĻ¤ āĻ•āĻ°āĻ¤ā§‡ āĻāĻŦāĻ‚ āĻ†āĻĒāĻ¨āĻžāĻ° āĻĒāĻžāĻ¸āĻ“āĻ¯āĻŧāĻžāĻ°ā§āĻĄ āĻĒā§āĻ¨āĻ°āĻžāĻ¯āĻŧ āĻ¸ā§‡āĻŸ āĻ•āĻ°āĻ¤ā§‡ āĻŦā§āĻ¯āĻŦāĻšā§ƒāĻ¤ āĻšāĻ¯āĻŧ" +email: "āĻ‡āĻŽā§‡āĻ‡āĻ˛" +emailAddress: "āĻ‡āĻŽā§‡āĻ‡āĻ˛ āĻ āĻŋāĻ•āĻžāĻ¨āĻž" +smtpConfig: "SMTP āĻ¸āĻžāĻ°ā§āĻ­āĻžāĻ° āĻ•āĻ¨āĻĢāĻŋāĻ—āĻžāĻ°ā§‡āĻļāĻ¨" +smtpHost: "āĻšā§‹āĻ¸ā§āĻŸ" +smtpPort: "āĻĒā§‹āĻ°ā§āĻŸ" +smtpUser: "āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ°āĻ•āĻžāĻ°ā§€āĻ° āĻ¨āĻžāĻŽ" +smtpPass: "āĻĒāĻžāĻ¸āĻ“āĻ¯āĻŧāĻžāĻ°ā§āĻĄ" +emptyToDisableSmtpAuth: "āĻ†āĻĒāĻ¨āĻŋ āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ°āĻ•āĻžāĻ°ā§€āĻ° āĻ¨āĻžāĻŽ āĻāĻŦāĻ‚ āĻĒāĻžāĻ¸āĻ“āĻ¯āĻŧāĻžāĻ°ā§āĻĄ āĻĢāĻžāĻāĻ•āĻž āĻ°ā§‡āĻ–ā§‡ SMTP āĻĒā§āĻ°āĻŽāĻžāĻŖā§€āĻ•āĻ°āĻŖ āĻ¨āĻŋāĻˇā§āĻ•ā§āĻ°āĻŋāĻ¯āĻŧ āĻ•āĻ°āĻ¤ā§‡ āĻĒāĻžāĻ°ā§‡āĻ¨āĨ¤" +smtpSecure: "SMTP āĻ¸āĻ‚āĻ¯ā§‹āĻ—ā§‡āĻ° āĻœāĻ¨ā§āĻ¯ SSL/TLS āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ° āĻ•āĻ°ā§āĻ¨" +smtpSecureInfo: "STARTTLS āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ° āĻ•āĻ°āĻžāĻ° āĻ¸āĻŽāĻ¯āĻŧ āĻāĻŸāĻŋ āĻŦāĻ¨ā§āĻ§ āĻ•āĻ°ā§āĻ¨āĨ¤" +testEmail: "āĻ‡āĻŽā§‡āĻ˛ āĻŦāĻŋāĻ¤āĻ°āĻŖ āĻĒāĻ°ā§€āĻ•ā§āĻˇāĻž āĻ•āĻ°ā§āĻ¨" +wordMute: "āĻŦāĻŋāĻļā§‡āĻˇ āĻ•ā§‹āĻ¨ āĻļāĻŦā§āĻĻāĻ•ā§‡ āĻŽāĻŋāĻ‰āĻŸ āĻ•āĻ°ā§āĻ¨" +regexpError: "āĻ°ā§‡āĻ—ā§āĻ˛āĻžāĻ° āĻāĻ•ā§āĻ¸āĻĒā§āĻ°ā§‡āĻļāĻ¨ āĻ¤ā§āĻ°ā§āĻŸāĻŋ" +regexpErrorDescription: "{tab} āĻ“ā§ŸāĻžāĻ°ā§āĻĄ āĻŽāĻŋāĻ‰āĻŸā§‡āĻ° {line} āĻ˛āĻžāĻ‡āĻ¨ā§‡ āĻ°ā§‡āĻ—ā§āĻ˛āĻžāĻ° āĻāĻ•ā§āĻ¸āĻĒā§āĻ°ā§‡āĻļāĻ¨ā§‡ āĻāĻ•āĻŸāĻŋ āĻ¤ā§āĻ°ā§āĻŸāĻŋ āĻ›āĻŋāĻ˛:" +instanceMute: "āĻŽāĻŋāĻ‰āĻŸ āĻ•āĻ°āĻž āĻ‡āĻ¨ā§āĻ¸āĻ¤ā§āĻ¯āĻžāĻ¨ā§āĻ¸āĻ—ā§āĻ˛āĻŋ" +userSaysSomething: "{name} āĻ•āĻŋāĻ›ā§ āĻŦāĻ˛ā§‡āĻ›ā§‡" +makeActive: "āĻ¸āĻ•ā§āĻ°āĻŋāĻ¯āĻŧ āĻ•āĻ°āĻž" +display: "āĻĒā§āĻ°āĻĻāĻ°ā§āĻļāĻ¨" +copy: "āĻ…āĻ¨ā§āĻ˛āĻŋāĻĒāĻŋ" +metrics: "āĻŽā§‡āĻŸā§āĻ°āĻŋāĻ•ā§āĻ¸" +overview: "āĻ¸āĻžāĻ°āĻžāĻ‚āĻļ" +logs: "āĻ˛āĻ—" +delayed: "āĻĻā§‡āĻ°āĻŋ āĻ•āĻ°ā§āĻ¨" +database: "āĻĄā§‡āĻŸāĻžāĻŦā§‡āĻœ" +channel: "āĻšā§āĻ¯āĻžāĻ¨ā§‡āĻ˛āĻ—ā§āĻ˛āĻŋ" +create: "āĻ¤ā§ˆāĻ°āĻŋ āĻ•āĻ°ā§āĻ¨" +notificationSetting: "āĻŦāĻŋāĻœā§āĻžāĻĒā§āĻ¤āĻŋāĻ° āĻ¸ā§‡āĻŸāĻŋāĻ‚āĻ¸" +notificationSettingDesc: "āĻ•āĻŋ āĻ§āĻ°āĻ¨ā§‡āĻ° āĻŦāĻŋāĻœā§āĻžāĻĒā§āĻ¤āĻŋ āĻĒāĻžāĻŦā§‡āĻ¨ āĻ¤āĻž āĻ¨āĻŋāĻ°ā§āĻ§āĻžāĻ°āĻŖ āĻ•āĻ°ā§āĻ¨" +useGlobalSetting: "āĻ—ā§āĻ˛ā§‹āĻŦāĻžāĻ˛ āĻ¸ā§‡āĻŸāĻŋāĻ‚āĻ¸ āĻŦā§āĻ¯āĻžāĻŦāĻšāĻžāĻ° āĻ•āĻ°ā§āĻ¨" +useGlobalSettingDesc: "āĻšāĻžāĻ˛ā§ āĻ•āĻ°āĻ˛ā§‡, āĻ†āĻĒāĻ¨āĻžāĻ° āĻ…ā§āĻ¯āĻžāĻ•āĻžāĻ‰āĻ¨ā§āĻŸā§‡āĻ° āĻŦāĻŋāĻœā§āĻžāĻĒā§āĻ¤āĻŋ āĻ¸ā§‡āĻŸāĻŋāĻ‚āĻ¸ āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ° āĻ•āĻ°āĻž āĻšāĻŦā§‡āĨ¤ āĻŦāĻ¨ā§āĻ§ āĻ•āĻ°āĻ˛ā§‡, āĻāĻŸāĻŋ āĻĒā§ƒāĻĨāĻ•āĻ­āĻžāĻŦā§‡ āĻ¸ā§‡āĻŸ āĻ•āĻ°āĻž āĻ¯ā§‡āĻ¤ā§‡ āĻĒāĻžāĻ°ā§‡āĨ¤" +other: "āĻ…āĻ¨ā§āĻ¯āĻžāĻ¨ā§āĻ¯" +regenerateLoginToken: "āĻ˛āĻ—āĻ‡āĻ¨ āĻŸā§‹āĻ•ā§‡āĻ¨ āĻ†āĻŦāĻžāĻ° āĻŦāĻžāĻ¨āĻžāĻ¨" +regenerateLoginTokenDescription: "āĻ˛āĻ— āĻ‡āĻ¨ āĻ•āĻ°āĻžāĻ° āĻœāĻ¨ā§āĻ¯ āĻŦā§āĻ¯āĻŦāĻšā§ƒāĻ¤ āĻ…āĻ­ā§āĻ¯āĻ¨ā§āĻ¤āĻ°ā§€āĻŖ āĻŸā§‹āĻ•ā§‡āĻ¨ āĻĒā§āĻ¨āĻ°āĻžāĻ¯āĻŧ āĻ¤ā§ˆāĻ°āĻŋ āĻ•āĻ°ā§‡āĨ¤ āĻ¸āĻžāĻ§āĻžāĻ°āĻŖāĻ¤ āĻ†āĻĒāĻ¨āĻžāĻ° āĻāĻŸāĻŋ āĻ•āĻ°āĻžāĻ° āĻĻāĻ°āĻ•āĻžāĻ° āĻ¨ā§‡āĻ‡āĨ¤ āĻāĻŸāĻŋ āĻ•āĻ°āĻ˛ā§‡, āĻ†āĻĒāĻ¨āĻŋ āĻ¸āĻŽāĻ¸ā§āĻ¤ āĻĄāĻŋāĻ­āĻžāĻ‡āĻ¸ā§‡ āĻ˛āĻ— āĻ†āĻ‰āĻŸ āĻšāĻ¯āĻŧā§‡ āĻ¯āĻžāĻŦā§‡āĻ¨ā§ˇ" +setMultipleBySeparatingWithSpace: "āĻ†āĻĒāĻ¨āĻŋ āĻāĻ•āĻŸāĻŋ āĻ¸ā§āĻĒā§‡āĻ¸ āĻĻāĻŋāĻ¯āĻŧā§‡ āĻ†āĻ˛āĻžāĻĻāĻž āĻ•āĻ°ā§‡ āĻāĻ•āĻžāĻ§āĻŋāĻ• āĻāĻ¨ā§āĻŸā§āĻ°āĻŋ āĻĻāĻŋāĻ¤ā§‡ āĻĒāĻžāĻ°ā§‡āĻ¨āĨ¤" +fileIdOrUrl: "āĻĢāĻžāĻ‡āĻ˛ ID āĻ…āĻĨāĻŦāĻž URL" +behavior: "āĻ†āĻšāĻ°āĻŖ" +sample: "āĻ‰āĻĻāĻžāĻšāĻ°āĻŖ" +abuseReports: "āĻ…āĻ­āĻŋāĻ¯ā§‹āĻ—" +reportAbuse: "āĻ…āĻ­āĻŋāĻ¯ā§‹āĻ—" +reportAbuseOf: "{name} āĻ āĻ…āĻ­āĻŋāĻ¯ā§‹āĻ— āĻ•āĻ°ā§āĻ¨" +fillAbuseReportDescription: "āĻ°āĻŋāĻĒā§‹āĻ°ā§āĻŸā§‡āĻ° āĻ•āĻžāĻ°āĻŖ āĻŦāĻ°ā§āĻŖāĻ¨āĻž āĻ•āĻ°ā§āĻ¨. āĻāĻ•āĻŸāĻŋ āĻŦāĻŋāĻļā§‡āĻˇ āĻ¨ā§‹āĻŸ āĻāĻ° āĻœāĻ¨ā§āĻ¯ āĻ°āĻŋāĻĒā§‹āĻ°ā§āĻŸāĻŸāĻŋ āĻšā§Ÿā§‡ āĻĨāĻžāĻ•ā§‡ āĻ¤āĻŦā§‡ āĻ¤āĻžāĻ° URL āĻŸāĻŋ āĻ…āĻ¨ā§āĻ¤āĻ°ā§āĻ­ā§āĻ•ā§āĻ¤ āĻ•āĻ°ā§āĻ¨āĨ¤ " +abuseReported: "āĻ†āĻĒāĻ¨āĻžāĻ° āĻ…āĻ­āĻŋāĻ¯ā§‹āĻ—āĻŸāĻŋ āĻĻāĻžāĻ–āĻŋāĻ˛ āĻ•āĻ°āĻž āĻšā§Ÿā§‡āĻ›ā§‡āĨ¤ āĻ†āĻĒāĻ¨āĻžāĻ•ā§‡ āĻ§āĻ¨ā§āĻ¯āĻŦāĻžāĻĻāĨ¤" +reporter: "āĻ…āĻ­āĻŋāĻ¯ā§‹āĻ—āĻ•āĻžāĻ°ā§€" +reporteeOrigin: "āĻ…āĻ­āĻŋāĻ¯ā§‹āĻ—āĻŸāĻŋāĻ° āĻ‰ā§ŽāĻ¸" +reporterOrigin: "āĻ…āĻ­āĻŋāĻ¯ā§‹āĻ—āĻ•āĻžāĻ°ā§€āĻ° āĻ‰ā§ŽāĻ¸" +forwardReport: "āĻ°āĻŋāĻŽā§‹āĻŸ āĻ‡āĻ¨ā§āĻ¸āĻ¤ā§āĻ¯āĻžāĻ¨ā§āĻ¸ā§‡ āĻ…āĻ­āĻŋāĻ¯ā§‹āĻ—āĻŸāĻŋ āĻĒāĻžāĻ āĻžāĻ¨" +forwardReportIsAnonymous: "āĻ†āĻĒāĻ¨āĻžāĻ° āĻ¤āĻĨā§āĻ¯ āĻ°āĻŋāĻŽā§‹āĻŸ āĻ‡āĻ¨ā§āĻ¸āĻ¤ā§āĻ¯āĻžāĻ¨ā§āĻ¸ā§‡ āĻĒāĻžāĻ āĻžāĻ¨ā§‹ āĻšāĻŦā§‡ āĻ¨āĻž āĻāĻŦāĻ‚ āĻāĻ•āĻŸāĻŋ āĻŦā§‡āĻ¨āĻžāĻŽā§€ āĻ¸āĻŋāĻ¸ā§āĻŸā§‡āĻŽ āĻ…ā§āĻ¯āĻžāĻ•āĻžāĻ‰āĻ¨ā§āĻŸ āĻšāĻŋāĻ¸āĻžāĻŦā§‡ āĻĒā§āĻ°āĻĻāĻ°ā§āĻļāĻŋāĻ¤ āĻšāĻŦā§‡āĨ¤" +send: "āĻĒāĻžāĻ āĻžāĻ¨" +abuseMarkAsResolved: "āĻ…āĻ­āĻŋāĻ¯ā§‹āĻ—āĻŸāĻŋāĻ•ā§‡ āĻ¸āĻŽāĻžāĻ§āĻžāĻ•ā§ƒāĻ¤ āĻšāĻŋāĻ¸āĻžāĻŦā§‡ āĻšāĻŋāĻšā§āĻ¨āĻŋāĻ¤ āĻ•āĻ°ā§āĻ¨" +openInNewTab: "āĻ¨āĻ¤ā§āĻ¨ āĻŸā§āĻ¯āĻžāĻŦā§‡ āĻ–ā§āĻ˛ā§āĻ¨" +openInSideView: "āĻ¸āĻžāĻ‡āĻĄ āĻ­āĻŋāĻ‰āĻ¤ā§‡ āĻ–ā§āĻ˛ā§āĻ¨" +defaultNavigationBehaviour: "āĻĄāĻŋāĻĢāĻ˛ā§āĻŸ āĻ¨ā§‡āĻ­āĻŋāĻ—ā§‡āĻļāĻ¨" +editTheseSettingsMayBreakAccount: "āĻāĻ¸āĻŦ āĻ¸ā§‡āĻŸāĻŋāĻ‚āĻ¸ āĻ¸āĻŽā§āĻĒāĻžāĻĻāĻ¨āĻž āĻ•āĻ°āĻ˛ā§‡ āĻ†āĻĒāĻ¨āĻžāĻ° āĻ…ā§āĻ¯āĻžāĻ•āĻžāĻ‰āĻ¨ā§āĻŸā§‡āĻ° āĻ•ā§āĻˇāĻ¤āĻŋ āĻšāĻ¤ā§‡ āĻĒāĻžāĻ°ā§‡āĨ¤ " +instanceTicker: "āĻ‡āĻ¨ā§āĻ¸āĻ¤ā§āĻ¯āĻžāĻ¨ā§āĻ¸ā§‡ āĻ¨ā§‹āĻŸā§‡āĻ° āĻ¤āĻĨā§āĻ¯" +waitingFor: "{x} āĻāĻ° āĻœāĻ¨ā§āĻ¯ āĻ…āĻĒā§‡āĻ•ā§āĻˇāĻž āĻ•āĻ°āĻž āĻšāĻšā§āĻ›ā§‡" +random: "āĻ°â€ā§āĻ¯āĻžāĻ¨ā§āĻĄāĻŽ" +system: "āĻ¸āĻŋāĻ¸ā§āĻŸā§‡āĻŽ" +switchUi: "UI āĻĒāĻ°āĻŋāĻŦāĻ°ā§āĻ¤āĻ¨ āĻ•āĻ°ā§āĻ¨" +desktop: "āĻĄā§‡āĻ¸ā§āĻ•āĻŸāĻĒ" +clip: "āĻ•ā§āĻ˛āĻŋāĻĒ" +createNew: "āĻ¨āĻ¤ā§āĻ¨" +optional: "āĻĒā§āĻ°āĻ¯āĻŧā§‹āĻœāĻ¨ā§€ā§Ÿ āĻ¨ā§Ÿ" +createNewClip: "āĻ¨āĻ¤ā§āĻ¨ āĻ•ā§āĻ˛āĻŋāĻĒ āĻ¤ā§ˆāĻ°āĻŋ āĻ•āĻ°ā§āĻ¨" +public: "āĻ¸āĻ°ā§āĻŦāĻœāĻ¨ā§€āĻ¨" +i18nInfo: "Misskey āĻ¸ā§āĻŦā§‡āĻšā§āĻ›āĻžāĻ¸ā§‡āĻŦāĻ•āĻĻā§‡āĻ° āĻĻā§āĻŦāĻžāĻ°āĻž āĻŦāĻŋāĻ­āĻŋāĻ¨ā§āĻ¨ āĻ­āĻžāĻˇāĻžāĻ¯āĻŧ āĻ…āĻ¨ā§āĻŦāĻžāĻĻ āĻ•āĻ°āĻž āĻšāĻšā§āĻ›ā§‡āĨ¤ āĻ†āĻĒāĻ¨āĻŋ {link} āĻ āĻ—āĻŋā§Ÿā§‡ āĻ…āĻ¨ā§āĻŦāĻžāĻĻā§‡ āĻ¸āĻšāĻ¯ā§‹āĻ—āĻŋāĻ¤āĻž āĻ•āĻ°āĻ¤ā§‡ āĻĒāĻžāĻ°ā§‡āĻ¨āĨ¤" +manageAccessTokens: "āĻ…ā§āĻ¯āĻžāĻ•ā§āĻ¸ā§‡āĻ¸ āĻŸā§‹āĻ•ā§‡āĻ¨ āĻĒāĻ°āĻŋāĻšāĻžāĻ˛āĻ¨āĻž āĻ•āĻ°ā§āĻ¨" +accountInfo: "āĻ…ā§āĻ¯āĻžāĻ•āĻžāĻ‰āĻ¨ā§āĻŸā§‡āĻ° āĻ¤āĻĨā§āĻ¯" +notesCount: "āĻ¨ā§‹āĻŸā§‡āĻ° āĻ¸āĻ‚āĻ–ā§āĻ¯āĻž" +repliesCount: "āĻœāĻŦāĻžāĻŦā§‡āĻ° āĻ¸āĻ‚āĻ–ā§āĻ¯āĻž" +renotesCount: "āĻ°āĻŋāĻ¨ā§‹āĻŸā§‡āĻ° āĻ¸āĻ‚āĻ–ā§āĻ¯āĻž" +repliedCount: "āĻœāĻŦāĻžāĻŦ āĻ—ā§āĻ°āĻšāĻ¨ āĻ•āĻ°āĻž āĻšā§Ÿā§‡āĻ›ā§‡" +renotedCount: "āĻ°āĻŋāĻ¨ā§‹āĻŸ āĻĒā§‡ā§Ÿā§‡āĻ›ā§‡āĻ¨" +followingCount: "āĻ¯āĻžāĻĻā§‡āĻ°āĻ•ā§‡ āĻ…āĻ¨ā§āĻ¸āĻ°āĻŖ āĻ•āĻ°ā§‡āĻ¨, āĻ¤āĻžāĻĻā§‡āĻ° āĻ¸āĻ‚āĻ–ā§āĻ¯āĻž" +followersCount: "āĻ…āĻ¨ā§āĻ¸āĻ°āĻŖāĻ•āĻžāĻ°ā§€āĻĻā§‡āĻ° āĻ¸āĻ‚āĻ–ā§āĻ¯āĻž" +sentReactionsCount: "āĻ°āĻŋāĻ…ā§āĻ¯āĻžāĻ•āĻļāĻ¨ āĻĒāĻžāĻ āĻžāĻ¨ā§‹ āĻšā§Ÿā§‡āĻ›ā§‡" +receivedReactionsCount: "āĻ°āĻŋāĻ…ā§āĻ¯āĻžāĻ•āĻļāĻ¨ āĻĒā§‡ā§Ÿā§‡āĻ›ā§‡āĻ¨" +pollVotesCount: "āĻĒā§‹āĻ˛ āĻ­ā§‹āĻŸ āĻĻāĻŋā§Ÿā§‡āĻ›ā§‡āĻ¨" +pollVotedCount: "āĻĒā§‹āĻ˛ āĻ­ā§‹āĻŸ āĻĒā§‡ā§Ÿā§‡āĻ›ā§‡āĻ¨" +yes: "āĻšā§āĻ¯āĻžāĻ" +no: "āĻ¨āĻž" +driveFilesCount: "āĻĄā§āĻ°āĻžāĻ‡āĻ­ā§‡ āĻĢāĻžāĻ‡āĻ˛ āĻāĻ° āĻ¸āĻ‚āĻ–ā§āĻ¯āĻž" +driveUsage: "āĻĄā§āĻ°āĻžāĻ‡āĻ­ āĻāĻ° āĻŦā§āĻ¯āĻžāĻŦāĻšāĻžāĻ°" +noCrawle: "āĻ•ā§āĻ°āĻ˛āĻžāĻ° āĻ‡āĻ¨ā§āĻĄā§‡āĻ•ā§āĻ¸āĻŋāĻ‚ āĻŦāĻ¨ā§āĻ§ āĻ•āĻ°ā§āĻ¨" +noCrawleDescription: "āĻ¸āĻžāĻ°ā§āĻš āĻ‡āĻžā§āĻœāĻŋāĻ¨āĻ—ā§āĻ˛āĻŋāĻ•ā§‡ āĻ†āĻĒāĻ¨āĻžāĻ° āĻĒā§āĻ°ā§‹āĻĢāĻžāĻ‡āĻ˛, āĻ¨ā§‹āĻŸ, āĻĒā§‡āĻœ āĻ‡āĻ¤ā§āĻ¯āĻžāĻĻāĻŋ āĻ‡āĻ¨āĻĄā§‡āĻ•ā§āĻ¸ āĻ•āĻ°āĻ¤ā§‡ āĻ¨āĻŋāĻˇā§‡āĻ§ āĻ•āĻ°ā§āĻ¨āĨ¤ " +lockedAccountInfo: "āĻāĻŽāĻ¨āĻ•āĻŋ āĻ†āĻĒāĻ¨āĻŋ āĻ†āĻĒāĻ¨āĻžāĻ° āĻ…āĻ¨ā§āĻ¸āĻ°āĻŖāĻ•āĻžāĻ°ā§€āĻĻā§‡āĻ° āĻŦā§‡āĻ›ā§‡ āĻŦā§‡āĻ›ā§‡ āĻ…āĻ¨ā§āĻŽā§‹āĻĻāĻ¨ āĻ•āĻ°āĻ˛ā§‡āĻ“, āĻ¯ā§‡ āĻ•ā§‡āĻ‰ āĻ†āĻĒāĻ¨āĻžāĻ° āĻ¨ā§‹āĻŸāĻ—ā§āĻ˛āĻŋ āĻĻā§‡āĻ–āĻ¤ā§‡ āĻĒāĻžāĻŦā§‡, āĻ¯āĻ¤āĻ•ā§āĻˇāĻŖ āĻ¨āĻž āĻ†āĻĒāĻ¨āĻŋ āĻ†āĻĒāĻ¨āĻžāĻ° āĻ¨ā§‹āĻŸāĻ—ā§āĻ˛āĻŋāĻ•ā§‡ \"āĻ…āĻ¨ā§āĻ¸āĻžāĻ°ā§€āĻĻā§‡āĻ° āĻœāĻ¨ā§āĻ¯\" āĻšāĻŋāĻ¸āĻžāĻŦā§‡ āĻ¸ā§‡āĻŸ āĻ¨āĻž āĻ•āĻ°ā§‡āĻ¨ā§ˇ" +alwaysMarkSensitive: "āĻ¸āĻ°ā§āĻŦāĻĻāĻž āĻ¸ā§āĻĒāĻ°ā§āĻļāĻ•āĻžāĻ¤āĻ° āĻšāĻŋāĻ¸āĻžāĻŦā§‡ āĻšāĻŋāĻšā§āĻ¨āĻŋāĻ¤ āĻ•āĻ°ā§āĻ¨" +loadRawImages: "āĻ¸āĻ‚āĻ¯ā§āĻ•ā§āĻ¤ āĻ›āĻŦāĻŋāĻ° āĻĨāĻžāĻŽā§āĻŦāĻ¨ā§‡āĻ‡āĻ˛āĻŸāĻŋ āĻĻā§‡āĻ–āĻžāĻ¨āĻ° āĻĒāĻ°āĻŋāĻŦāĻ°ā§āĻ¤ā§‡ āĻ†āĻ¸āĻ˛ āĻ›āĻŦāĻŋ āĻĻā§‡āĻ–āĻžāĻ¨" +disableShowingAnimatedImages: "āĻ…ā§āĻ¯āĻžāĻ¨āĻŋāĻŽā§‡āĻŸā§‡āĻĄ āĻšāĻŋāĻ¤ā§āĻ° āĻĻā§‡āĻ–āĻžāĻ¨ā§‹ āĻŦāĻ¨ā§āĻ§ āĻ•āĻ°ā§āĻ¨" +verificationEmailSent: "āĻ¨āĻŋāĻļā§āĻšāĻŋāĻ¤āĻ•āĻ°āĻŖ āĻ‡āĻŽā§‡āĻ˛ āĻĒāĻžāĻ āĻžāĻ¨ā§‹ āĻšā§Ÿā§‡āĻ›ā§‡āĨ¤ āĻ¸ā§‡āĻŸāĻ†āĻĒ āĻ¸āĻŽā§āĻĒā§‚āĻ°ā§āĻŖ āĻ•āĻ°āĻ¤ā§‡ āĻ‡āĻŽā§‡āĻ˛ āĻāĻ° āĻ˛āĻŋāĻ™ā§āĻ• āĻ…āĻ¨ā§āĻ¸āĻ°āĻŖ āĻ•āĻ°ā§āĻ¨āĨ¤" +notSet: "āĻ¸ā§‡āĻŸ āĻ•āĻ°āĻž āĻšā§ŸāĻ¨āĻŋ" +emailVerified: "āĻ‡āĻŽā§‡āĻ‡āĻ˛ āĻ¨āĻŋāĻļā§āĻšāĻŋāĻ¤ āĻ•āĻ°āĻž āĻšā§Ÿā§‡āĻ›ā§‡" +noteFavoritesCount: "āĻĒāĻ›āĻ¨ā§āĻĻ āĻ•āĻ°āĻž āĻ¨ā§‹āĻŸā§‡āĻ° āĻ¸āĻ‚āĻ–ā§āĻ¯āĻž" +pageLikesCount: "āĻĒā§‡āĻœ āĻ˛āĻžāĻ‡āĻ• āĻ•āĻ°ā§‡āĻ›ā§‡āĻ¨" +pageLikedCount: "āĻĒā§‡āĻœ āĻ˛āĻžāĻ‡āĻ• āĻĒā§‡ā§Ÿā§‡āĻ›ā§‡āĻ¨" +contact: "āĻĒāĻ°āĻŋāĻšāĻŋāĻ¤āĻŋ āĻ¸āĻŽā§‚āĻš" +useSystemFont: "āĻ¸āĻŋāĻ¸ā§āĻŸā§‡āĻŽ āĻĢāĻ¨ā§āĻŸ āĻŦā§āĻ¯āĻžāĻŦāĻšāĻžāĻ° āĻ•āĻ°ā§āĻ¨" +clips: "āĻ•ā§āĻ˛āĻŋāĻĒāĻ—ā§āĻ˛āĻŋ " +experimentalFeatures: "āĻĒāĻ°ā§€āĻ•ā§āĻˇāĻžāĻŽā§‚āĻ˛āĻ• āĻŦā§ˆāĻļāĻŋāĻˇā§āĻŸā§āĻ¯āĻ—ā§āĻ˛āĻŋ" +developer: "āĻĄā§‡āĻ­ā§‡āĻ˛āĻĒāĻžāĻ°" +makeExplorable: "āĻ…ā§āĻ¯āĻžāĻ•āĻžāĻ‰āĻ¨ā§āĻŸ \"āĻ˜ā§āĻ°ā§‡ āĻĻā§‡āĻ–ā§āĻ¨\" āĻĒā§ƒāĻˇā§āĻ āĻžā§Ÿ āĻĻā§‡āĻ–āĻžāĻ¨" +makeExplorableDescription: "āĻ†āĻĒāĻ¨āĻŋ āĻāĻŸāĻŋ āĻŦāĻ¨ā§āĻ§ āĻ•āĻ°āĻ˛ā§‡, āĻ†āĻĒāĻ¨āĻžāĻ° āĻ…ā§āĻ¯āĻžāĻ•āĻžāĻ‰āĻ¨ā§āĻŸ \"āĻ˜ā§āĻ°ā§‡ āĻĻā§‡āĻ–ā§āĻ¨\" āĻĒā§ƒāĻˇā§āĻ āĻžā§Ÿ āĻĒā§āĻ°āĻĻāĻ°ā§āĻļāĻŋāĻ¤ āĻšāĻŦā§‡ āĻ¨āĻžāĨ¤" +showGapBetweenNotesInTimeline: "āĻŸāĻžāĻ‡āĻŽāĻ˛āĻžāĻ‡āĻ¨ āĻāĻŦāĻ‚ āĻ¨ā§‹āĻŸā§‡āĻ° āĻŽāĻžāĻā§‡ āĻĢāĻžāĻ•āĻž āĻœāĻžā§ŸāĻ—āĻž āĻ°āĻžāĻ–ā§āĻ¨" +duplicate: "āĻĒā§āĻ°āĻ¤āĻŋāĻ°ā§‚āĻĒ" +left: "āĻŦāĻžāĻŽ" +center: "āĻŽāĻžāĻāĻ–āĻžāĻ¨" +wide: "āĻšāĻ“ā§œāĻž" +narrow: "āĻ¸āĻ‚āĻ•ā§€āĻ°ā§āĻŖ" +reloadToApplySetting: "āĻĒā§ƒāĻˇā§āĻ āĻžāĻŸāĻŋ āĻ°āĻŋāĻ˛ā§‹āĻĄ āĻ•āĻ°āĻžāĻ° āĻĒāĻ° āĻ¸ā§‡āĻŸāĻŋāĻ‚āĻ¸āĻŸāĻŋ āĻĒā§āĻ°āĻ¯āĻŧā§‹āĻ— āĻ•āĻ°āĻž āĻšāĻŦā§‡āĨ¤ āĻ†āĻĒāĻ¨āĻŋ āĻ•āĻŋ āĻāĻ–āĻ¨ āĻ°āĻŋāĻ˛ā§‹āĻĄ āĻ•āĻ°āĻ¤ā§‡ āĻšāĻžāĻ¨?" +needReloadToApply: "āĻĒā§ƒāĻˇā§āĻ āĻžāĻŸāĻŋ āĻ°āĻŋāĻ˛ā§‹āĻĄ āĻ•āĻ°āĻžāĻ° āĻĒāĻ° āĻ¸ā§‡āĻŸāĻŋāĻ‚āĻ¸āĻŸāĻŋ āĻĒā§āĻ°āĻ¯āĻŧā§‹āĻ— āĻ•āĻ°āĻž āĻšāĻŦā§‡āĨ¤" +showTitlebar: "āĻŸāĻžāĻ‡āĻŸā§‡āĻ˛ āĻŦāĻžāĻ° āĻĻā§‡āĻ–āĻžāĻ¨" +clearCache: "āĻ•ā§āĻ¯āĻžāĻļ āĻĒāĻ°āĻŋāĻˇā§āĻ•āĻžāĻ° āĻ•āĻ°ā§āĻ¨" +onlineUsersCount: "{n} āĻœāĻ¨ āĻŦā§āĻ¯āĻžāĻŦāĻšāĻžāĻ°āĻ•āĻžāĻ°ā§€ āĻ…āĻ¨āĻ˛āĻžāĻ‡āĻ¨" +nUsers: "{n} āĻœāĻ¨ āĻŦā§āĻ¯āĻžāĻŦāĻšāĻžāĻ°āĻ•āĻžāĻ°ā§€" +nNotes: "{n} āĻŸāĻŋ āĻ¨ā§‹āĻŸ" +sendErrorReports: "āĻ•ā§āĻ°ā§āĻŸāĻŋ āĻĒā§āĻ°āĻ¤āĻŋāĻŦā§‡āĻĻāĻ¨ āĻĒāĻžāĻ āĻžāĻ¨" +sendErrorReportsDescription: "āĻšāĻžāĻ˛ā§ āĻĨāĻžāĻ•āĻ˛ā§‡, āĻŦāĻŋāĻ¸ā§āĻ¤āĻžāĻ°āĻŋāĻ¤ āĻ¤ā§āĻ°ā§āĻŸāĻŋāĻ° āĻ¤āĻĨā§āĻ¯ Misskey-āĻāĻ° āĻ¸āĻžāĻĨā§‡ āĻļā§‡āĻ¯āĻŧāĻžāĻ° āĻ•āĻ°āĻž āĻšāĻ¯āĻŧāĨ¤ āĻ¯āĻž āĻ¸āĻĢā§āĻŸāĻ“āĻ¯āĻŧā§āĻ¯āĻžāĻ°āĻŸāĻŋāĻ° āĻ—ā§āĻŖāĻŽāĻžāĻ¨ āĻ‰āĻ¨ā§āĻ¨āĻ¤ āĻ•āĻ°āĻ¤ā§‡ āĻ¸āĻžāĻšāĻžāĻ¯ā§āĻ¯ āĻ•āĻ°ā§‡āĨ¤ āĻ¤ā§āĻ°ā§āĻŸāĻŋāĻ° āĻ¤āĻĨā§āĻ¯ā§‡āĻ° āĻŽāĻ§ā§āĻ¯ā§‡ āĻ°āĻ¯āĻŧā§‡āĻ›ā§‡ OS āĻ¸āĻ‚āĻ¸ā§āĻ•āĻ°āĻŖ, āĻŦā§āĻ°āĻžāĻ‰āĻœāĻžāĻ°ā§‡āĻ° āĻ§āĻ°āĻ¨, āĻ•āĻ°ā§āĻŽā§‡āĻ° āĻ‡āĻ¤āĻŋāĻšāĻžāĻ¸ āĻ‡āĻ¤ā§āĻ¯āĻžāĻĻāĻŋāĨ¤" +myTheme: "āĻ†āĻŽāĻžāĻ° āĻĨāĻŋāĻŽ" +backgroundColor: "āĻĒāĻŸāĻ­ā§‚āĻŽāĻŋāĻ° āĻ°āĻ‚" +accentColor: "āĻāĻ•ā§āĻ¸ā§‡āĻ¨ā§āĻŸā§‡āĻ° āĻ°āĻ‚" +textColor: "āĻ˛ā§‡āĻ–āĻžāĻ° āĻ°āĻ‚" +saveAs: "āĻāĻ‡āĻ°ā§‚āĻĒā§‡ āĻ¸āĻ‚āĻ°āĻ•ā§āĻˇāĻŖ āĻ•āĻ°ā§āĻ¨" +advanced: "āĻ‰āĻ¨ā§āĻ¨āĻ¤" +value: "āĻŽāĻžāĻ¨" +createdAt: "āĻ¤ā§ˆāĻ°āĻŋ āĻšā§Ÿā§‡āĻ›ā§‡" +updatedAt: "āĻļā§‡āĻˇ āĻšāĻžāĻ˛āĻ¨āĻžāĻ—āĻžāĻĻ āĻšāĻ¯āĻŧā§‡āĻ›ā§‡" +saveConfirm: "āĻĒāĻ°āĻŋāĻŦāĻ°ā§āĻ¤āĻ¨āĻ—ā§āĻ˛āĻŋ āĻ¸āĻ‚āĻ°āĻ•ā§āĻˇāĻŖ āĻ•āĻ°āĻ¤ā§‡ āĻšāĻžāĻ¨?" +deleteConfirm: "āĻ†āĻ¸āĻ˛ā§‡āĻ‡ āĻŽā§āĻ›ā§‡ āĻĢā§‡āĻ˛āĻ¤ā§‡ āĻšāĻžāĻ¨?" +invalidValue: "āĻ…āĻ—ā§āĻ°āĻšāĻŖāĻ¯ā§‹āĻ—ā§āĻ¯ āĻŽāĻžāĻ¨" +registry: "āĻ°ā§‡āĻœāĻŋāĻ¸ā§āĻŸā§āĻ°āĻŋ" +closeAccount: "āĻ…ā§āĻ¯āĻžāĻ•āĻžāĻ‰āĻ¨ā§āĻŸ āĻŦāĻ¨ā§āĻ§ āĻ•āĻ°ā§āĻ¨" +currentVersion: "āĻŦāĻ°ā§āĻ¤āĻŽāĻžāĻ¨ āĻ¸āĻ‚āĻ¸ā§āĻ•āĻ°āĻŖ" +latestVersion: "āĻ¸āĻ°ā§āĻŦāĻļā§‡āĻˇ āĻ¸āĻ‚āĻ¸ā§āĻ•āĻ°āĻŖ" +youAreRunningUpToDateClient: "āĻ†āĻĒāĻ¨āĻŋ āĻ¸āĻŦāĻšā§‡ā§Ÿā§‡ āĻ¨āĻ¤ā§āĻ¨ āĻ•ā§āĻ˛āĻžā§Ÿā§‡āĻ¨ā§āĻŸ āĻŦā§āĻ¯āĻžāĻŦāĻšāĻžāĻ° āĻ•āĻ°āĻ›ā§‡āĻ¨" +newVersionOfClientAvailable: "āĻ†āĻĒāĻ¨āĻžāĻ° āĻ•ā§āĻ˛āĻžā§Ÿā§‡āĻ¨ā§āĻŸā§‡āĻ° āĻāĻ•āĻŸāĻŋ āĻ¨āĻ¤ā§āĻ¨ āĻ­āĻžāĻ°ā§āĻ¸āĻ¨ āĻšāĻ˛ā§‡ āĻāĻ¸ā§‡āĻ›ā§‡" +usageAmount: "āĻŦā§āĻ¯āĻžāĻŦāĻšāĻžāĻ°" +capacity: "āĻ§āĻžāĻ°āĻŖāĻ•ā§āĻˇāĻŽāĻ¤āĻž" +inUse: "āĻŦā§āĻ¯āĻŦāĻšā§ƒāĻ¤" +editCode: "āĻ•ā§‹āĻĄ āĻ¸āĻŽā§āĻĒāĻžāĻĻāĻ¨āĻž āĻ•āĻ°ā§āĻ¨" +apply: "āĻĒā§āĻ°āĻ¯āĻŧā§‹āĻ— āĻ•āĻ°ā§āĻ¨" +receiveAnnouncementFromInstance: "āĻāĻ‡ āĻ‡āĻ¨ā§āĻ¸āĻŸā§āĻ¯āĻžāĻ¨ā§āĻ¸ āĻĨā§‡āĻ•ā§‡ āĻŦāĻŋāĻœā§āĻžāĻĒā§āĻ¤āĻŋ āĻĒāĻžāĻ¨" +emailNotification: "āĻ‡āĻŽā§‡āĻ‡āĻ˛ āĻŦāĻŋāĻœā§āĻžāĻĒā§āĻ¤āĻŋ" +publish: "āĻĒā§āĻ°āĻ•āĻžāĻļ" +inChannelSearch: "āĻšā§āĻ¯āĻžāĻ¨ā§‡āĻ˛ā§‡ āĻ–ā§āĻāĻœā§āĻ¨" +useReactionPickerForContextMenu: "āĻ°āĻžāĻ‡āĻŸ āĻ•ā§āĻ˛āĻŋāĻ•ā§‡āĻ° āĻŽāĻžāĻ§ā§āĻ¯āĻŽā§‡ āĻ°āĻŋāĻ…ā§āĻ¯āĻžāĻ•āĻļāĻ¨ āĻĒāĻŋāĻ•āĻžāĻ° āĻ–ā§āĻ˛ā§āĻ¨" +typingUsers: "{users} āĻ˛ā§‡āĻ–āĻ›ā§‡" +jumpToSpecifiedDate: "āĻāĻ•āĻŸāĻŋ āĻ¨āĻŋāĻ°ā§āĻĻāĻŋāĻˇā§āĻŸ āĻ¤āĻžāĻ°āĻŋāĻ–ā§‡ āĻ¯āĻžāĻ¨" +showingPastTimeline: "āĻ…āĻ¤ā§€āĻ¤ā§‡āĻ° āĻŸāĻžāĻ‡āĻŽāĻ˛āĻžāĻ‡āĻ¨ āĻĻā§‡āĻ–āĻžāĻ¨ā§‹ āĻšāĻšā§āĻ›ā§‡" +clear: "āĻĒāĻ°āĻŋāĻˇā§āĻ•āĻžāĻ°" +markAllAsRead: "āĻ¸āĻŦ āĻĒāĻ āĻŋāĻ¤ āĻšāĻŋāĻ¸ā§‡āĻŦā§‡ āĻšāĻŋāĻšā§āĻ¨āĻŋāĻ¤ āĻ•āĻ°ā§āĻ¨" +goBack: "āĻĒāĻŋāĻ›āĻ¨ā§‡" +unlikeConfirm: "āĻ†āĻ¸āĻ˛ā§‡āĻ‡ āĻ˛āĻžāĻ‡āĻ• āĻ¸āĻ°āĻŋā§Ÿā§‡ āĻ¨āĻŋāĻŦā§‡āĻ¨?" +fullView: "āĻĢā§āĻ˛ āĻ­āĻŋāĻ‰" +quitFullView: "āĻĢā§āĻ˛ āĻ­āĻŋāĻ‰ āĻŦāĻ¨ā§āĻ§ āĻ•āĻ°ā§āĻ¨" +addDescription: "āĻŦāĻ°ā§āĻŖāĻ¨āĻž āĻ¯ā§‹āĻ— āĻ•āĻ°ā§āĻ¨" +userPagePinTip: "āĻ†āĻĒāĻ¨āĻŋ āĻĒā§āĻ°āĻ¤āĻŋāĻŸāĻŋ āĻ¨ā§‹āĻŸā§‡āĻ° āĻœāĻ¨ā§āĻ¯ āĻŽā§‡āĻ¨ā§ āĻĨā§‡āĻ•ā§‡ \"āĻĒā§āĻ°ā§‹āĻĢāĻžāĻ‡āĻ˛ā§‡ āĻĒāĻŋāĻ¨ āĻ•āĻ°ā§āĻ¨\" āĻ¨āĻŋāĻ°ā§āĻŦāĻžāĻšāĻ¨ āĻ•āĻ°ā§‡ āĻ†āĻĒāĻ¨āĻžāĻ° āĻ¨ā§‹āĻŸāĻ—ā§āĻ˛āĻŋ āĻāĻ–āĻžāĻ¨ā§‡ āĻĒā§āĻ°āĻĻāĻ°ā§āĻļāĻ¨ āĻ•āĻ°āĻ¤ā§‡ āĻĒāĻžāĻ°ā§‡āĻ¨āĨ¤" +notSpecifiedMentionWarning: "āĻĒā§āĻ°āĻžāĻĒāĻ• āĻ›āĻžā§œāĻžāĻ“ āĻāĻ‡ āĻ¨ā§‹āĻŸā§‡ āĻ…āĻ¨ā§āĻ¯ āĻŦā§āĻ¯āĻžāĻŦāĻšāĻžāĻ°āĻ•āĻžāĻ°ā§€āĻĻā§‡āĻ° āĻ‰āĻ˛ā§āĻ˛ā§‡āĻ–ā§āĻ¯ āĻ•āĻ°āĻž āĻšā§Ÿā§‡āĻ›ā§‡" +info: "āĻ†āĻĒāĻ¨āĻžāĻ° āĻ¸āĻŽā§āĻĒāĻ°ā§āĻ•ā§‡" +userInfo: "āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ°āĻ•āĻžāĻ°ā§€āĻ° āĻ¤āĻĨā§āĻ¯" +unknown: "āĻ…āĻœāĻžāĻ¨āĻž" +onlineStatus: "āĻ…āĻ¨āĻ˛āĻžāĻ‡āĻ¨ āĻ¸ā§āĻŸā§āĻ¯āĻžāĻŸāĻžāĻ¸" +hideOnlineStatus: "āĻ…āĻ¨āĻ˛āĻžāĻ‡āĻ¨ āĻ¸ā§āĻŸā§āĻ¯āĻžāĻŸāĻžāĻ¸ āĻ˛ā§āĻ•āĻžāĻ¨" +hideOnlineStatusDescription: "āĻ…āĻ¨āĻ˛āĻžāĻ‡āĻ¨ āĻ¸ā§āĻŸā§āĻ¯āĻžāĻŸāĻžāĻ¸ āĻ˛ā§āĻ•āĻŋāĻ¯āĻŧā§‡ āĻ°āĻžāĻ–āĻ˛ā§‡ āĻ¸āĻžāĻ°ā§āĻšā§‡āĻ° āĻŽāĻ¤ā§‹ āĻ•āĻŋāĻ›ā§ āĻĢāĻžāĻ‚āĻļāĻ¨ā§‡āĻ° āĻ¸ā§āĻŦāĻŋāĻ§āĻž āĻ•āĻŽā§‡ āĻ¯āĻžā§ŸāĨ¤" +online: "āĻ…āĻ¨āĻ˛āĻžāĻ‡āĻ¨" +active: "āĻ…ā§āĻ¯āĻžāĻ•āĻŸāĻŋāĻ­" +offline: "āĻ…āĻĢāĻ˛āĻžāĻ‡āĻ¨" +notRecommended: "āĻ¸ā§āĻĒāĻžāĻ°āĻŋāĻļ āĻ•āĻ°āĻž āĻšāĻ¯āĻŧ āĻ¨āĻž" +botProtection: "āĻŦāĻŸ āĻĒā§āĻ°ā§‹āĻŸā§‡āĻ•āĻļāĻ¨" +instanceBlocking: "āĻŦā§āĻ˛āĻ• āĻ•āĻ°āĻž āĻ‡āĻ¨ā§āĻ¸āĻŸā§āĻ¯āĻžāĻ¨ā§āĻ¸āĻ—ā§āĻ˛āĻŋ" +selectAccount: "āĻ…ā§āĻ¯āĻžāĻ•āĻžāĻ‰āĻ¨ā§āĻŸ āĻ¨āĻŋāĻ°ā§āĻŦāĻžāĻšāĻ¨" +switchAccount: "āĻ…ā§āĻ¯āĻžāĻ•āĻžāĻ‰āĻ¨ā§āĻŸ āĻĒāĻžāĻ˛ā§āĻŸāĻžāĻ¨" +enabled: "āĻšāĻžāĻ˛ā§" +disabled: "āĻŦāĻ¨ā§āĻ§" +quickAction: "āĻ•ā§āĻ‡āĻ• āĻ…ā§āĻ¯āĻžāĻ•āĻļāĻ¨" +user: "āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ°āĻ•āĻžāĻ°ā§€āĻ—āĻŖ" +administration: "āĻĒāĻ°āĻŋāĻšāĻžāĻ˛āĻ¨āĻž" +accounts: "āĻ…ā§āĻ¯āĻžāĻ•āĻžāĻ‰āĻ¨ā§āĻŸāĻ—ā§āĻ˛āĻŋ" +switch: "āĻĒāĻžāĻ˛ā§āĻŸāĻžāĻ¨" +noMaintainerInformationWarning: "āĻĒā§āĻ°āĻļāĻžāĻ¸āĻ•ā§‡āĻ° āĻ¤āĻĨā§āĻ¯ āĻ¸ā§‡āĻŸ āĻ•āĻ°āĻž āĻšāĻ¯āĻŧāĻ¨āĻŋāĨ¤" +noBotProtectionWarning: "āĻŦāĻŸ āĻĒā§āĻ°ā§‹āĻŸā§‡āĻ•āĻļāĻ¨ āĻ¸ā§‡āĻŸ āĻ•āĻ°āĻž āĻšāĻ¯āĻŧāĻ¨āĻŋāĨ¤" +configure: "āĻ•āĻ¨āĻĢāĻŋāĻ—āĻžāĻ° āĻ•āĻ°ā§āĻ¨" +postToGallery: "āĻ—ā§āĻ¯āĻžāĻ˛āĻžāĻ°ā§€ āĻĒā§‹āĻ¸ā§āĻŸ āĻ¤ā§ˆāĻ°āĻŋ āĻ•āĻ°ā§āĻ¨" +gallery: "āĻ—ā§āĻ¯āĻžāĻ˛āĻžāĻ°ā§€" +recentPosts: "āĻ¨āĻ¤ā§āĻ¨ āĻĒā§‹āĻ¸ā§āĻŸ" +popularPosts: "āĻœāĻ¨āĻĒā§āĻ°āĻŋā§Ÿ āĻĒā§‹āĻ¸ā§āĻŸ" +shareWithNote: "āĻ¨ā§‹āĻŸā§‡āĻ° āĻŽāĻžāĻ§ā§āĻ¯āĻŽā§‡ āĻļā§‡ā§ŸāĻžāĻ° āĻ•āĻ°ā§āĻ¨" +ads: "āĻŦāĻŋāĻœā§āĻžāĻžāĻĒāĻ¨" +expiration: "āĻ¨āĻŋāĻ°ā§āĻĻāĻŋāĻˇā§āĻŸ āĻ¸āĻŽāĻ¯āĻŧāĻ¸ā§€āĻŽāĻž" +memo: "āĻŽā§‡āĻŽā§‹" +priority: "āĻ…āĻ—ā§āĻ°āĻžāĻ§āĻŋāĻ•āĻžāĻ°" +high: "āĻ‰āĻšā§āĻš" +middle: "āĻŽāĻžāĻāĻžāĻ°āĻŋ" +low: "āĻ¨āĻŋāĻŽā§āĻ¨" +emailNotConfiguredWarning: "āĻ‡āĻŽā§‡āĻ‡āĻ˛ āĻ…ā§āĻ¯āĻžāĻĄā§āĻ°ā§‡āĻ¸ āĻ¸ā§‡āĻŸ āĻ•āĻ°āĻž āĻšā§ŸāĻ¨āĻŋāĨ¤" +ratio: "āĻ…āĻ¨ā§āĻĒāĻžāĻ¤" +previewNoteText: "āĻĒā§āĻ°āĻŋāĻ­āĻŋāĻ‰ āĻĻā§‡āĻ–āĻžāĻ¨" +customCss: "āĻ•āĻžāĻ¸ā§āĻŸāĻŽ CSS" +customCssWarn: "āĻāĻ‡ āĻŦā§āĻ¯āĻžāĻĒāĻžāĻ°ā§‡ āĻ…āĻ­āĻŋāĻœā§āĻžāĻ¤āĻž āĻ¨āĻž āĻĨāĻžāĻ•āĻ˛ā§‡ āĻāĻ‡ āĻ¸ā§‡āĻŸāĻŋāĻ‚āĻŸāĻŋ āĻŦā§āĻ¯āĻžāĻŦāĻšāĻžāĻ° āĻ•āĻ°āĻŦā§‡āĻ¨ āĻ¨āĻžāĨ¤ āĻ…āĻ¨ā§āĻĒāĻ¯ā§āĻ•ā§āĻ¤ āĻ¸ā§‡āĻŸāĻŋāĻ‚āĻ¸ āĻ•ā§āĻ˛āĻžāĻ¯āĻŧā§‡āĻ¨ā§āĻŸāĻ•ā§‡ āĻ¸ā§āĻŦāĻžāĻ­āĻžāĻŦāĻŋāĻ•āĻ­āĻžāĻŦā§‡ āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ° āĻ•āĻ°āĻ¤ā§‡ āĻŦāĻžāĻ§āĻž āĻĻāĻŋāĻ¤ā§‡ āĻĒāĻžāĻ°ā§‡āĨ¤" +global: "āĻ—ā§āĻ˛ā§‹āĻŦāĻžāĻ˛" +squareAvatars: "āĻšāĻžāĻ°āĻ•ā§‹āĻ¨āĻž āĻĒā§āĻ°ā§‹āĻĢāĻžāĻ‡āĻ˛ āĻĒāĻŋāĻ•āĻšāĻžāĻ° āĻĻā§‡āĻ–āĻžāĻ¨ " +sent: "āĻĒāĻžāĻ āĻžāĻ¨" +received: "āĻĒā§āĻ°āĻžāĻĒā§āĻ¤" +searchResult: "āĻ…āĻ¨ā§āĻ¸āĻ¨ā§āĻ§āĻžāĻ¨ā§‡āĻ° āĻĢāĻ˛āĻžāĻĢāĻ˛" +hashtags: "āĻšā§āĻ¯āĻžāĻļāĻŸā§āĻ¯āĻžāĻ—" +troubleshooting: "āĻŸā§āĻ°āĻžāĻŦāĻ˛āĻļā§āĻŸāĻŋāĻ‚" +useBlurEffect: "UI āĻ¤ā§‡ āĻŦā§āĻ˛āĻžāĻ° āĻ‡āĻĢā§‡āĻ•ā§āĻŸ āĻŦā§āĻ¯āĻžāĻŦāĻšāĻžāĻ° āĻ•āĻ°ā§āĻ¨" +learnMore: "āĻ†āĻ°āĻ“ āĻœāĻžāĻ¨ā§āĻ¨" +misskeyUpdated: "Misskey āĻ†āĻĒāĻĄā§‡āĻŸ āĻ•āĻ°āĻž āĻšā§Ÿā§‡āĻ›ā§‡īŧ" +whatIsNew: "āĻĒāĻ°āĻŋāĻŦāĻ°ā§āĻ¤āĻ¨āĻ—ā§āĻ˛āĻŋ āĻĻā§‡āĻ–āĻžāĻ¨" +translate: "āĻ…āĻ¨ā§āĻŦāĻžāĻĻ" +translatedFrom: "{x} āĻšāĻ¤ā§‡ āĻ…āĻ¨ā§āĻŦāĻžāĻĻ āĻ•āĻ°āĻž" +accountDeletionInProgress: "āĻ…ā§āĻ¯āĻžāĻ•āĻžāĻ‰āĻ¨ā§āĻŸ āĻŽā§āĻ›ā§‡ āĻĢā§‡āĻ˛āĻž āĻšāĻšā§āĻ›ā§‡" +usernameInfo: "āĻāĻ•āĻŸāĻŋ āĻ¨āĻžāĻŽ āĻ¯āĻž āĻ¸āĻžāĻ°ā§āĻ­āĻžāĻ°ā§‡ āĻ†āĻĒāĻ¨āĻžāĻ° āĻ…ā§āĻ¯āĻžāĻ•āĻžāĻ‰āĻ¨ā§āĻŸāĻŸāĻŋāĻ•ā§‡ āĻ…āĻ¨āĻ¨ā§āĻ¯āĻ­āĻžāĻŦā§‡ āĻ¸āĻ¨āĻžāĻ•ā§āĻ¤ āĻ•āĻ°ā§‡āĨ¤ āĻ†āĻĒāĻ¨āĻŋ āĻŦāĻ°ā§āĻŖāĻŽāĻžāĻ˛āĻž (a ~ z, A ~ Z), āĻ¸āĻ‚āĻ–ā§āĻ¯āĻž (0 ~ 9), āĻāĻŦāĻ‚ āĻ†āĻ¨ā§āĻĄāĻžāĻ°āĻ¸ā§āĻ•ā§‹āĻ° (_) āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ° āĻ•āĻ°āĻ¤ā§‡ āĻĒāĻžāĻ°ā§‡āĻ¨āĨ¤ āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ°āĻ•āĻžāĻ°ā§€āĻ° āĻ¨āĻžāĻŽ āĻĒāĻ°ā§‡ āĻĒāĻ°āĻŋāĻŦāĻ°ā§āĻ¤āĻ¨ āĻ•āĻ°āĻž āĻ¯āĻžāĻŦā§‡ āĻ¨āĻžāĨ¤" +aiChanMode: "Ai āĻŽā§‹āĻĄ" +keepCw: "CW āĻ°āĻžāĻ–ā§āĻ¨" +pubSub: "Pub/Sub āĻ…ā§āĻ¯āĻžāĻ•āĻžāĻ‰āĻ¨ā§āĻŸāĻ—ā§āĻ˛ā§‹" +lastCommunication: "āĻļā§‡āĻˇ āĻ¯ā§‹āĻ—āĻžāĻ¯ā§‹āĻ—" +resolved: "āĻ¸āĻŽāĻžāĻ§āĻžāĻ¨ āĻšāĻ¯āĻŧā§‡āĻ›ā§‡" +unresolved: "āĻ¸āĻŽāĻžāĻ§āĻžāĻ¨ āĻšāĻ¯āĻŧāĻ¨āĻŋ" +breakFollow: "āĻ…āĻ¨ā§āĻ¸āĻ°āĻŖ āĻ•āĻ°āĻž āĻŦāĻ¨ā§āĻ§" +itsOn: "āĻšāĻžāĻ˛ā§" +itsOff: "āĻŦāĻ¨ā§āĻ§" +emailRequiredForSignup: "āĻ…ā§āĻ¯āĻžāĻ•āĻžāĻ‰āĻ¨ā§āĻŸ āĻ¤ā§ˆāĻ°āĻŋāĻ° āĻœāĻ¨ā§āĻ¯ āĻ‡āĻŽā§‡āĻ‡āĻ˛ āĻāĻ° āĻĻāĻ°āĻ•āĻžāĻ° āĻĒā§œāĻŦā§‡" +unread: "āĻ…āĻĒāĻ āĻŋāĻ¤" +filter: "āĻĢāĻŋāĻ˛ā§āĻŸāĻžāĻ°" +controlPanel: "āĻ¨āĻŋā§ŸāĻ¨ā§āĻ¤ā§āĻ°āĻ¨ āĻ•ā§‡āĻ¨ā§āĻĻā§āĻ°" +manageAccounts: "āĻ…ā§āĻ¯āĻžāĻ•āĻžāĻ‰āĻ¨ā§āĻŸāĻ—ā§āĻ˛āĻŋ āĻĒāĻ°āĻŋāĻšāĻžāĻ˛āĻ¨āĻž āĻ•āĻ°ā§āĻ¨" +makeReactionsPublic: "āĻ°āĻŋāĻ…ā§āĻ¯āĻžāĻ•āĻļāĻ¨ā§‡āĻ° āĻ‡āĻ¤āĻŋāĻšāĻžāĻ¸ āĻ‰āĻ¨ā§āĻŽā§āĻ•ā§āĻ¤ āĻ•āĻ°ā§āĻ¨" +makeReactionsPublicDescription: "āĻ†āĻĒāĻ¨āĻžāĻ° āĻĒā§‚āĻ°ā§āĻŦāĻŦāĻ°ā§āĻ¤ā§€ āĻ°āĻŋāĻ…ā§āĻ¯āĻžāĻ•āĻļāĻ¨āĻ—ā§āĻ˛āĻŋāĻ° āĻ¤āĻžāĻ˛āĻŋāĻ•āĻž āĻ¯ā§‡ āĻ•āĻžāĻ°āĻ“ āĻ•āĻžāĻ›ā§‡ āĻĻā§ƒāĻļā§āĻ¯āĻŽāĻžāĻ¨ āĻšāĻŦā§‡āĨ¤" +classic: "āĻ•ā§āĻ˛āĻžāĻ¸āĻŋāĻ•" +muteThread: "āĻĨā§āĻ°ā§‡āĻĄ āĻŽāĻŋāĻ‰āĻŸ āĻ•āĻ°ā§āĻ¨" +unmuteThread: "āĻĨā§āĻ°ā§‡āĻĄ āĻ†āĻ¨āĻŽāĻŋāĻ‰āĻŸ āĻ•āĻ°ā§āĻ¨" +ffVisibility: "āĻ…āĻ¨ā§āĻ¸āĻ°āĻŖ/āĻ…āĻ¨ā§āĻ¸āĻ°āĻŖāĻ•āĻžāĻ°ā§€āĻĻā§‡āĻ° āĻĻā§ƒāĻļā§āĻ¯āĻŽāĻžāĻ¨ā§āĻ¯āĻ¤āĻž" +ffVisibilityDescription: "āĻ†āĻĒāĻ¨āĻŋ āĻ•āĻžāĻ•ā§‡ āĻ…āĻ¨ā§āĻ¸āĻ°āĻŖ āĻ•āĻ°ā§‡āĻ¨ āĻāĻŦāĻ‚ āĻ•ā§‡ āĻ†āĻĒāĻ¨āĻžāĻ•ā§‡ āĻ…āĻ¨ā§āĻ¸āĻ°āĻŖ āĻ•āĻ°ā§‡, āĻ¸ā§‡āĻŸāĻž āĻ•āĻžāĻ°āĻž āĻĻā§‡āĻ–āĻ¤ā§‡ āĻĒāĻžāĻŦā§‡ āĻ¤āĻž āĻ¨āĻŋāĻ°ā§āĻ§āĻžāĻ°āĻŖ āĻ•āĻ°ā§‡āĨ¤" +continueThread: "āĻ†āĻ°ā§‹ āĻĨā§āĻ°ā§‡āĻĄ āĻĻā§‡āĻ–ā§āĻ¨" +deleteAccountConfirm: "āĻ†āĻĒāĻ¨āĻžāĻ° āĻ…ā§āĻ¯āĻžāĻ•āĻžāĻ‰āĻ¨ā§āĻŸ āĻŽā§āĻ›ā§‡ āĻĢā§‡āĻ˛āĻž āĻšāĻŦā§‡āĨ¤ āĻ āĻŋāĻ• āĻ†āĻ›ā§‡?" +incorrectPassword: "āĻ†āĻĒāĻ¨āĻžāĻ° āĻĻā§‡āĻ“ā§ŸāĻž āĻĒāĻžāĻ¸āĻ“āĻ¯āĻŧāĻžāĻ°ā§āĻĄāĻŸāĻŋ āĻ­ā§āĻ˛āĨ¤" +voteConfirm: "\"{choice}\" āĻ āĻ­ā§‹āĻŸ āĻĻāĻŋāĻ¤ā§‡ āĻšāĻžāĻ¨īŧŸ" +hide: "āĻ˛ā§āĻ•āĻžāĻ¨" +leaveGroup: "āĻ—ā§āĻ°ā§āĻĒ āĻ›ā§‡ā§œā§‡ āĻšāĻ˛ā§‡ āĻ¯āĻžāĻ¨" +leaveGroupConfirm: "\"{name}\" āĻ—ā§āĻ°ā§āĻĒ āĻ›ā§‡ā§œā§‡ āĻšāĻ˛ā§‡ āĻ¯ā§‡āĻ¤ā§‡ āĻšāĻžāĻ¨?" +useDrawerReactionPickerForMobile: "āĻŽā§‹āĻŦāĻžāĻ‡āĻ˛ā§‡ āĻ°āĻŋāĻ…ā§āĻ¯āĻžāĻ•āĻļāĻ¨ āĻĒāĻŋāĻ•āĻžāĻ°āĻ•ā§‡ āĻĄā§āĻ°āĻ¯āĻŧāĻžāĻ°ā§‡ āĻĒā§āĻ°āĻĻāĻ°ā§āĻļāĻ¨ āĻ•āĻ°ā§āĻ¨" +welcomeBackWithName: "āĻ†āĻŦāĻžāĻ° āĻ¸ā§āĻŦāĻžāĻ—āĻ¤āĻŽ, {name}" +clickToFinishEmailVerification: " [{ok}] āĻ•ā§āĻ˛āĻŋāĻ• āĻ•āĻ°āĻžāĻ° āĻŽāĻžāĻ§ā§āĻ¯āĻŽā§‡ āĻ†āĻĒāĻ¨āĻžāĻ° āĻ‡āĻŽā§‡āĻ˛ āĻ āĻŋāĻ•āĻžāĻ¨āĻž āĻ¨āĻŋāĻļā§āĻšāĻŋāĻ¤ āĻ•āĻ°ā§āĻ¨āĨ¤" +overridedDeviceKind: "āĻĄāĻŋāĻ­āĻžāĻ‡āĻ¸ā§‡āĻ° āĻ§āĻ°āĻ¨" +smartphone: "āĻ¸ā§āĻŽāĻžāĻ°ā§āĻŸāĻĢā§‹āĻ¨" +tablet: "āĻŸā§āĻ¯āĻžāĻŦāĻ˛ā§‡āĻŸ" +auto: "āĻ¸ā§āĻŦāĻ¯āĻŧāĻ‚āĻ•ā§āĻ°āĻŋāĻ¯āĻŧ" +themeColor: "āĻĨāĻŋāĻŽā§‡āĻ° āĻ°āĻ‚" +_emailUnavailable: + used: "āĻāĻ‡ āĻ‡āĻŽā§‡āĻ‡āĻ˛ āĻ āĻŋāĻ•āĻžāĻ¨āĻžāĻŸāĻŋ āĻ‡āĻ¤ā§‹āĻŽāĻ§ā§āĻ¯ā§‡ āĻŦā§āĻ¯āĻŦāĻšā§ƒāĻ¤ āĻšā§Ÿā§‡āĻ›ā§‡" + format: "āĻāĻ‡ āĻ‡āĻŽā§‡āĻ˛ āĻ āĻŋāĻ•āĻžāĻ¨āĻžāĻŸāĻŋ āĻ¸āĻ āĻŋāĻ•āĻ­āĻžāĻŦā§‡ āĻ˛āĻŋāĻ–āĻž āĻšā§ŸāĻ¨āĻŋ" + disposable: "āĻ…āĻ¸ā§āĻĨāĻžā§Ÿā§€ āĻ‡āĻŽā§‡āĻ‡āĻ˛ āĻ āĻŋāĻ•āĻžāĻ¨āĻž āĻŦā§āĻ¯āĻžāĻŦāĻšāĻžāĻ° āĻ•āĻ°āĻž āĻ¯āĻžāĻŦā§‡ āĻ¨āĻž" + mx: "āĻ‡āĻŽā§‡āĻ‡āĻ˛ ​​āĻ¸āĻžāĻ°ā§āĻ­āĻžāĻ°āĻŸāĻŋ āĻ āĻŋāĻ• āĻ¨āĻžāĻ‡" + smtp: "āĻ‡āĻŽā§‡āĻ‡āĻ˛ āĻ¸āĻžāĻ°ā§āĻ­āĻžāĻ°āĻŸāĻŋ āĻ¸āĻžā§œāĻž āĻĻāĻŋāĻšā§āĻ›ā§‡ āĻ¨āĻž" +_ffVisibility: + public: "āĻĒā§āĻ°āĻ•āĻžāĻļ" + followers: "āĻļā§āĻ§ā§āĻŽāĻžāĻ¤ā§āĻ° āĻ…āĻ¨ā§āĻ¸āĻ°āĻŖāĻ•āĻžāĻ°ā§€āĻĻā§‡āĻ° āĻ•āĻžāĻ›ā§‡ āĻĻā§ƒāĻļā§āĻ¯āĻŽāĻžāĻ¨" + private: "āĻŦā§āĻ¯āĻžāĻ•ā§āĻ¤āĻŋāĻ—āĻ¤" +_signup: + almostThere: "āĻĒā§āĻ°āĻžāĻ¯āĻŧ āĻļā§‡āĻˇ" + emailAddressInfo: "āĻ†āĻĒāĻ¨āĻŋ āĻ¯ā§‡ āĻ‡āĻŽā§‡āĻ˛ āĻ āĻŋāĻ•āĻžāĻ¨āĻžāĻŸāĻŋ āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ° āĻ•āĻ°āĻŦā§‡āĻ¨ āĻ¸ā§‡āĻŸāĻŋ āĻ˛āĻŋāĻ–ā§āĻ¨āĨ¤ āĻ†āĻĒāĻ¨āĻžāĻ° āĻ‡āĻŽā§‡āĻ‡āĻ˛ āĻ āĻŋāĻ•āĻžāĻ¨āĻž āĻĒā§āĻ°āĻ•āĻžāĻļ āĻ•āĻ°āĻž āĻšāĻŦā§‡ āĻ¨āĻžāĨ¤" + emailSent: "āĻ†āĻĒāĻ¨āĻžāĻ° āĻĻā§‡āĻ“āĻ¯āĻŧāĻž āĻ‡āĻŽā§‡āĻ˛ āĻ āĻŋāĻ•āĻžāĻ¨āĻžāĻ¯āĻŧ ({email}) āĻāĻ•āĻŸāĻŋ āĻ¨āĻŋāĻļā§āĻšāĻŋāĻ¤āĻ•āĻ°āĻŖ āĻ‡āĻŽā§‡āĻ˛ āĻĒāĻžāĻ āĻžāĻ¨ā§‹ āĻšāĻ¯āĻŧā§‡āĻ›ā§‡āĨ¤ āĻ…ā§āĻ¯āĻžāĻ•āĻžāĻ‰āĻ¨ā§āĻŸ āĻ¤ā§ˆāĻ°āĻŋ āĻ¸āĻŽā§āĻĒā§‚āĻ°ā§āĻŖ āĻ•āĻ°āĻ¤ā§‡ āĻ‡āĻŽā§‡āĻ˛ā§‡āĻ° āĻ˛āĻŋāĻ™ā§āĻ•āĻŸāĻŋ āĻ…ā§āĻ¯āĻžāĻ•ā§āĻ¸ā§‡āĻ¸ āĻ•āĻ°ā§āĻ¨āĨ¤" +_accountDelete: + accountDelete: "āĻ…ā§āĻ¯āĻžāĻ•āĻžāĻ‰āĻ¨ā§āĻŸ āĻŽā§āĻ›ā§‡ āĻĢā§‡āĻ˛ā§āĻ¨" + mayTakeTime: "āĻāĻ•āĻŸāĻŋ āĻ…ā§āĻ¯āĻžāĻ•āĻžāĻ‰āĻ¨ā§āĻŸ āĻŽā§āĻ›ā§‡ āĻĢā§‡āĻ˛āĻž āĻāĻ•āĻŸāĻŋ āĻĻā§€āĻ°ā§āĻ˜ āĻĒā§āĻ°āĻ•ā§āĻ°āĻŋāĻ¯āĻŧāĻž āĻāĻŦāĻ‚ āĻ†āĻĒāĻ¨āĻŋ āĻ¯āĻĻāĻŋ āĻĒā§āĻ°āĻšā§āĻ° āĻĒāĻ°āĻŋāĻŽāĻžāĻŖā§‡ āĻ¸āĻžāĻŽāĻ—ā§āĻ°ā§€ āĻ¤ā§ˆāĻ°āĻŋ āĻ•āĻ°ā§‡ āĻĨāĻžāĻ•ā§‡āĻ¨ āĻŦāĻž āĻĢāĻžāĻ‡āĻ˛ āĻ†āĻĒāĻ˛ā§‹āĻĄ āĻ•āĻ°ā§‡āĻ¨ āĻ¤āĻŦā§‡ āĻāĻŸāĻŋ āĻ¸āĻŽā§āĻĒā§‚āĻ°ā§āĻŖ āĻšāĻ¤ā§‡ āĻĻā§€āĻ°ā§āĻ˜ āĻ¸āĻŽāĻ¯āĻŧ āĻ¨āĻŋāĻ¤ā§‡ āĻĒāĻžāĻ°ā§‡āĨ¤" + sendEmail: "āĻ…ā§āĻ¯āĻžāĻ•āĻžāĻ‰āĻ¨ā§āĻŸ āĻŽā§āĻ›ā§‡ āĻĢā§‡āĻ˛āĻž āĻ¸āĻŽā§āĻĒā§‚āĻ°ā§āĻŖ āĻšāĻ˛ā§‡, āĻ¨āĻŋāĻŦāĻ¨ā§āĻ§āĻŋāĻ¤ āĻ‡āĻŽā§‡āĻ˛ āĻ āĻŋāĻ•āĻžāĻ¨āĻžāĻ¯āĻŧ āĻāĻ•āĻŸāĻŋ āĻŦāĻŋāĻœā§āĻžāĻĒā§āĻ¤āĻŋ āĻĒāĻžāĻ āĻžāĻ¨ā§‹ āĻšāĻŦā§‡āĨ¤" + requestAccountDelete: "āĻ…ā§āĻ¯āĻžāĻ•āĻžāĻ‰āĻ¨ā§āĻŸ āĻŽā§āĻ›ā§‡ āĻĢā§‡āĻ˛āĻžāĻ° āĻ…āĻ¨ā§āĻ°ā§‹āĻ§ āĻ•āĻ°ā§āĻ¨" + started: "āĻŽā§āĻ›ā§‡ āĻĢā§‡āĻ˛āĻžāĻ° āĻĒā§āĻ°āĻ•ā§āĻ°āĻŋāĻ¯āĻŧāĻž āĻļā§āĻ°ā§ āĻšāĻ¯āĻŧā§‡āĻ›ā§‡āĨ¤" + inProgress: "āĻŽā§āĻ›ā§‡ āĻĢā§‡āĻ˛āĻžāĻ° āĻ•āĻžāĻœ āĻšāĻ˛āĻ›ā§‡" +_ad: + back: "āĻĒāĻŋāĻ›āĻ¨ā§‡" + reduceFrequencyOfThisAd: "āĻāĻ‡ āĻŦāĻŋāĻœā§āĻžāĻžāĻĒāĻ¨āĻŸāĻŋ āĻ•āĻŽ āĻĻā§‡āĻ–āĻžāĻ¨" +_forgotPassword: + enterEmail: "āĻ†āĻĒāĻ¨āĻŋ āĻ†āĻĒāĻ¨āĻžāĻ° āĻ…ā§āĻ¯āĻžāĻ•āĻžāĻ‰āĻ¨ā§āĻŸā§‡āĻ° āĻœāĻ¨ā§āĻ¯ āĻ¨āĻŋāĻŦāĻ¨ā§āĻ§āĻŋāĻ¤ āĻ‡āĻŽā§‡āĻ˛ āĻ āĻŋāĻ•āĻžāĻ¨āĻž āĻ˛āĻŋāĻ–ā§āĻ¨. āĻ¸ā§‡āĻ‡ āĻ āĻŋāĻ•āĻžāĻ¨āĻžāĻ¯āĻŧ āĻāĻ•āĻŸāĻŋ āĻĒāĻžāĻ¸āĻ“āĻ¯āĻŧāĻžāĻ°ā§āĻĄ āĻ°āĻŋāĻ¸ā§‡āĻŸ āĻ˛āĻŋāĻ™ā§āĻ• āĻĒāĻžāĻ āĻžāĻ¨ā§‹ āĻšāĻŦā§‡āĨ¤" + ifNoEmail: "āĻ†āĻĒāĻ¨āĻŋ āĻ¯āĻĻāĻŋ āĻ¨āĻŋāĻŦāĻ¨ā§āĻ§āĻ¨ā§‡āĻ° āĻ¸āĻŽā§Ÿ āĻ‡-āĻŽā§‡āĻ‡āĻ˛ āĻ āĻŋāĻ•āĻžāĻ¨āĻž āĻ¨āĻž āĻĻāĻŋā§Ÿā§‡ āĻĨāĻžāĻ•ā§‡āĻ¨, āĻ¤āĻžāĻšāĻ˛ā§‡ āĻ…āĻ¨ā§āĻ—ā§āĻ°āĻš āĻ•āĻ°ā§‡ āĻĒā§āĻ°āĻļāĻžāĻ¸āĻ•ā§‡āĻ° āĻ¸āĻžāĻĨā§‡ āĻ¯ā§‹āĻ—āĻžāĻ¯ā§‹āĻ— āĻ•āĻ°ā§āĻ¨āĨ¤" + contactAdmin: "āĻāĻ‡ āĻ‡āĻ¨ā§āĻ¸āĻŸā§āĻ¯āĻžāĻ¨ā§āĻ¸āĻŸāĻŋ āĻ‡āĻŽā§‡āĻ‡āĻ˛ āĻŦā§āĻ¯āĻžāĻŦāĻšāĻžāĻ° āĻ•āĻ°ā§‡ āĻ¨āĻž, āĻ¤āĻžāĻ‡ āĻ†āĻĒāĻ¨āĻžāĻ° āĻĒāĻžāĻ¸āĻ“āĻ¯āĻŧāĻžāĻ°ā§āĻĄ āĻĒā§āĻ¨āĻ°āĻžāĻ¯āĻŧ āĻ¸ā§‡āĻŸ āĻ•āĻ°āĻ¤ā§‡ āĻĒā§āĻ°āĻļāĻžāĻ¸āĻ•ā§‡āĻ° āĻ¸āĻžāĻĨā§‡ āĻ¯ā§‹āĻ—āĻžāĻ¯ā§‹āĻ— āĻ•āĻ°ā§āĻ¨ā§ˇ" +_gallery: + my: "āĻ†āĻŽāĻžāĻ° āĻ—ā§āĻ¯āĻžāĻ˛āĻžāĻ°ā§€" + liked: "āĻĒāĻ›āĻ¨ā§āĻĻ āĻ•āĻ°āĻž āĻĒā§‹āĻ¸ā§āĻŸ" + like: "āĻĒāĻ›āĻ¨ā§āĻĻ āĻ•āĻ°āĻž" + unlike: "āĻĒāĻ›āĻ¨ā§āĻĻ āĻ¸āĻ°āĻžāĻ¨" +_email: + _follow: + title: "āĻ†āĻĒāĻ¨āĻžāĻ•ā§‡ āĻ…āĻ¨ā§āĻ¸āĻ°āĻŖ āĻ•āĻ°āĻ›ā§‡" + _receiveFollowRequest: + title: "āĻ…āĻ¨ā§āĻ¸āĻ°āĻŖ āĻ•āĻ°āĻžāĻ° āĻ…āĻ¨ā§āĻ°ā§‹āĻ§ āĻĒā§‡ā§Ÿā§‡āĻ›ā§‡āĻ¨" +_plugin: + install: "āĻĒā§āĻ˛āĻžāĻ—āĻ‡āĻ¨ āĻ‡āĻ¨ā§āĻ¸āĻŸāĻ˛ āĻ•āĻ°ā§āĻ¨" + installWarn: "āĻ…āĻŦāĻŋāĻļā§āĻŦāĻ¸ā§āĻ¤ āĻĒā§āĻ˛āĻžāĻ—āĻ‡āĻ¨ āĻ‡āĻ¨āĻ¸ā§āĻŸāĻ˛ āĻ•āĻ°āĻŦā§‡āĻ¨ āĻ¨āĻžāĨ¤" + manage: "āĻĒā§āĻ˛āĻžāĻ—āĻ‡āĻ¨ āĻŽā§āĻ¯āĻžāĻ¨ā§‡āĻœ āĻ•āĻ°ā§āĻ¨" +_registry: + scope: "āĻ¸ā§āĻ•ā§‹āĻĒ" + key: "āĻ•ā§€" + keys: "āĻ•ā§€ - āĻ¸āĻŽā§‚āĻš" + domain: "āĻĄā§‹āĻŽā§‡āĻ¨" + createKey: "āĻ•ā§€ āĻŦāĻžāĻ¨āĻžāĻ¨" +_aboutMisskey: + about: "Misskey, āĻāĻ•āĻŸāĻŋ āĻ“āĻĒā§‡āĻ¨ āĻ¸ā§‹āĻ°ā§āĻ¸ āĻ¸āĻĢā§āĻŸāĻ“āĻ¯āĻŧā§āĻ¯āĻžāĻ° āĻ¯āĻž 2014 āĻ¸āĻžāĻ˛ āĻĨā§‡āĻ•ā§‡ syuilo āĻ¤ā§ˆāĻ°āĻŋ āĻ•āĻ°āĻ›ā§‡āĻ¨āĨ¤" + contributors: "āĻĒā§āĻ°āĻ§āĻžāĻ¨ āĻ•āĻ¨ā§āĻŸā§āĻ°āĻŋāĻŦāĻŋāĻ‰āĻŸāĻžāĻ°āĻ—āĻŖ" + allContributors: "āĻ¸āĻ•āĻ˛ āĻ•āĻ¨ā§āĻŸā§āĻ°āĻŋāĻŦāĻŋāĻ‰āĻŸāĻžāĻ°āĻ—āĻŖ" + source: "āĻ¸ā§‹āĻ°ā§āĻ¸ āĻ•ā§‹āĻĄ" + translation: "Misskey āĻ…āĻ¨ā§āĻŦāĻžāĻĻ āĻ•āĻ°ā§āĻ¨" + donate: "Misskey āĻ¤ā§‡ āĻĻāĻžāĻ¨ āĻ•āĻ°ā§āĻ¨" + morePatrons: "āĻ†āĻ°āĻ“ āĻ…āĻ¨ā§‡āĻ•ā§‡ āĻ†āĻŽāĻžāĻĻā§‡āĻ° āĻ¸āĻžāĻšāĻžāĻ¯ā§āĻ¯ āĻ•āĻ°āĻ›ā§‡āĻ¨āĨ¤ āĻ¤āĻžāĻĻā§‡āĻ° āĻ¸āĻŦāĻžāĻ‡āĻ•ā§‡ āĻ§āĻ¨ā§āĻ¯āĻŦāĻžāĻĻ đŸĨ°" + patrons: "āĻ¸āĻŽāĻ°ā§āĻĨāĻ¨āĻ•āĻžāĻ°ā§€" +_nsfw: + respect: "āĻ¸ā§āĻĒāĻ°ā§āĻļāĻ•āĻžāĻ¤āĻ° āĻŽāĻŋāĻĄāĻŋā§ŸāĻž āĻ˛ā§āĻ•āĻžāĻ¨" + ignore: "āĻ¸ā§āĻĒāĻ°ā§āĻļāĻ•āĻžāĻ¤āĻ° āĻŽāĻŋāĻĄāĻŋā§ŸāĻž āĻ˛ā§āĻ•āĻžāĻŦā§‡āĻ¨ āĻ¨āĻž" + force: "āĻ¸āĻ•āĻ˛ āĻŽāĻŋāĻĄāĻŋā§ŸāĻž āĻ˛ā§āĻ•āĻžāĻ¨" +_mfm: + cheatSheet: "MFM āĻšāĻŋāĻŸāĻļāĻŋāĻŸ" + intro: "MFM āĻāĻ•āĻŸāĻŋ āĻŽāĻžāĻ°ā§āĻ•āĻ†āĻĒ āĻ­āĻžāĻˇāĻž āĻ¯āĻž Misskey-āĻāĻ° āĻŽāĻ§ā§āĻ¯ā§‡ āĻŦāĻŋāĻ­āĻŋāĻ¨ā§āĻ¨ āĻœāĻžāĻ¯āĻŧāĻ—āĻžāĻ¯āĻŧ āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ° āĻ•āĻ°āĻž āĻ¯ā§‡āĻ¤ā§‡ āĻĒāĻžāĻ°ā§‡āĨ¤ āĻāĻ–āĻžāĻ¨ā§‡ āĻ†āĻĒāĻ¨āĻŋ MFM-āĻāĻ° āĻ¸āĻŋāĻ¨āĻŸā§āĻ¯āĻžāĻ•ā§āĻ¸āĻ—ā§āĻ˛āĻŋāĻ° āĻāĻ•āĻŸāĻŋ āĻ¤āĻžāĻ˛āĻŋāĻ•āĻž āĻĻā§‡āĻ–āĻ¤ā§‡ āĻĒāĻžāĻ°āĻŦā§‡āĻ¨āĨ¤" + dummy: "āĻŽāĻŋāĻ¸āĻ•āĻŋ āĻĢā§‡āĻĄāĻŋāĻ­āĻžāĻ°ā§āĻ¸ā§‡āĻ° āĻŦāĻŋāĻļā§āĻŦāĻ•ā§‡ āĻĒā§āĻ°āĻ¸āĻžāĻ°āĻŋāĻ¤ āĻ•āĻ°ā§‡" + mention: "āĻ‰āĻ˛ā§āĻ˛ā§‡āĻ–" + mentionDescription: "@ āĻšāĻŋāĻšā§āĻ¨ + āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ°āĻ•āĻžāĻ°ā§€āĻ° āĻ¨āĻžāĻŽ āĻāĻ•āĻŸāĻŋ āĻ¨āĻŋāĻ°ā§āĻĻāĻŋāĻˇā§āĻŸ āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ°āĻ•āĻžāĻ°ā§€āĻ•ā§‡ āĻ¨āĻŋāĻ°ā§āĻĻā§‡āĻļ āĻ•āĻ°āĻ¤ā§‡ āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ° āĻ•āĻ°āĻž āĻ¯āĻžā§ŸāĨ¤" + hashtag: "āĻšā§āĻ¯āĻžāĻļāĻŸā§āĻ¯āĻžāĻ—" + hashtagDescription: "āĻ†āĻĒāĻ¨āĻŋ āĻāĻ•āĻŸāĻŋ # āĻšāĻŋāĻšā§āĻ¨ + āĻŸā§āĻ¯āĻžāĻ— āĻ¸āĻš āĻāĻ•āĻŸāĻŋ āĻšā§āĻ¯āĻžāĻļāĻŸā§āĻ¯āĻžāĻ— āĻ¨āĻŋāĻ°ā§āĻĻā§‡āĻļ āĻ•āĻ°āĻ¤ā§‡ āĻĒāĻžāĻ°ā§‡āĻ¨āĨ¤" + url: "URL" + urlDescription: "URL āĻĻā§‡āĻ–āĻžāĻ¨ā§‹ āĻ¸āĻŽā§āĻ­āĻŦāĨ¤" + link: "āĻ˛āĻŋāĻ‚āĻ•" + linkDescription: "āĻ†āĻĒāĻ¨āĻŋ āĻĒāĻžāĻ ā§āĻ¯ā§‡āĻ° āĻāĻ•āĻŸāĻŋ āĻ¨āĻŋāĻ°ā§āĻĻāĻŋāĻˇā§āĻŸ āĻ…āĻ‚āĻļāĻ•ā§‡ URL āĻšāĻŋāĻ¸āĻžāĻŦā§‡ āĻĻā§‡āĻ–āĻžāĻ¤ā§‡ āĻĒāĻžāĻ°ā§‡āĻ¨ā§ˇ" + bold: "āĻ—āĻžā§" + boldDescription: "āĻ…āĻ•ā§āĻˇāĻ°āĻ—ā§āĻ˛āĻŋāĻ•ā§‡ āĻŽā§‹āĻŸāĻžāĻ•āĻ°ā§‡ āĻĒā§āĻ°āĻĻāĻ°ā§āĻļāĻ¨ āĻ•āĻ°āĻž āĻšāĻŦā§‡āĨ¤" + small: "āĻ›ā§‹āĻŸ" + smallDescription: "āĻ˛ā§‡āĻ–āĻž āĻ›ā§‹āĻŸ āĻāĻŦāĻ‚ āĻĒāĻžāĻ¤āĻ˛āĻž āĻ•āĻ°ā§‡ āĻĻā§‡āĻ–āĻžāĻ¨ā§‹ āĻšāĻŦā§‡āĨ¤" + center: "āĻ¸ā§‡āĻ¨ā§āĻŸāĻžāĻ°" + centerDescription: "āĻ˛ā§‡āĻ–āĻž āĻŽāĻžāĻ āĻŦāĻ°āĻžāĻŦāĻ° āĻĻā§‡āĻ–āĻžāĻ¨ā§‹ āĻšāĻŦā§‡" + inlineCode: "āĻ•ā§‹āĻĄ (āĻ‡āĻ¨āĻ˛āĻžāĻ‡āĻ¨)" + inlineCodeDescription: " āĻĒā§āĻ°ā§‹āĻ—ā§āĻ°āĻžāĻŽā§‡āĻ° āĻ•ā§‹āĻĄā§‡āĻ° āĻœāĻ¨ā§āĻ¯ āĻ‡āĻ¨āĻ˛āĻžāĻ‡āĻ¨ āĻ¸āĻŋāĻ¨āĻŸā§āĻ¯āĻžāĻ•ā§āĻ¸ āĻšāĻžāĻ‡āĻ˛āĻžāĻ‡āĻŸāĻŋāĻ‚ āĻ•āĻ°āĻž āĻšāĻŦā§‡" + blockCode: "āĻ•ā§‹āĻĄ (āĻŦā§āĻ˛āĻ•)" + blockCodeDescription: "āĻŽāĻžāĻ˛ā§āĻŸāĻŋ-āĻ˛āĻžāĻ‡āĻ¨ āĻĒā§āĻ°ā§‹āĻ—ā§āĻ°āĻžāĻŽā§‡āĻ° āĻ•ā§‹āĻĄā§‡āĻ° āĻœāĻ¨ā§āĻ¯ āĻ¸āĻŋāĻ¨āĻŸā§āĻ¯āĻžāĻ•ā§āĻ¸ āĻšāĻžāĻ‡āĻ˛āĻžāĻ‡āĻŸ āĻ•āĻ°ā§‡āĨ¤" + inlineMath: "āĻ—āĻžāĻŖāĻŋāĻ¤āĻŋāĻ• āĻ¸ā§‚āĻ¤ā§āĻ° (āĻ‡āĻ¨āĻ˛āĻžāĻ‡āĻ¨)" + inlineMathDescription: "āĻ—āĻžāĻŖāĻŋāĻ¤āĻŋāĻ• āĻ¸ā§‚āĻ¤ā§āĻ° āĻĒā§āĻ°āĻĻāĻ°ā§āĻļāĻ¨ āĻ•āĻ°ā§āĻ¨ (KaTeX) āĻ‡āĻ¨āĻ˛āĻžāĻ‡āĻ¨āĨ¤" + blockMath: "āĻ—āĻžāĻŖāĻŋāĻ¤āĻŋāĻ• āĻ¸ā§‚āĻ¤ā§āĻ° (āĻŦā§āĻ˛āĻ•)" + blockMathDescription: "āĻāĻ•āĻŸāĻŋ āĻŦā§āĻ˛āĻ•ā§‡ āĻāĻ•āĻžāĻ§āĻŋāĻ• āĻ˛āĻžāĻ‡āĻ¨ā§‡āĻ° āĻ—āĻžāĻŖāĻŋāĻ¤āĻŋāĻ• āĻ¸ā§‚āĻ¤ā§āĻ° āĻĒā§āĻ°āĻĻāĻ°ā§āĻļāĻ¨ āĻ•āĻ°ā§āĻ¨ (KaTeX)āĨ¤" + quote: "āĻ‰āĻĻā§āĻ§ā§ƒāĻ¤āĻŋ" + quoteDescription: "āĻŦāĻŋāĻˇāĻ¯āĻŧāĻŦāĻ¸ā§āĻ¤ā§āĻ•ā§‡ āĻāĻ•āĻŸāĻŋ āĻ‰āĻĻā§āĻ§ā§ƒāĻ¤āĻŋ āĻšāĻŋāĻ¸āĻžāĻŦā§‡ āĻĻā§‡āĻ–āĻžāĻ¨ā§‹ āĻšāĻŦā§‡āĨ¤" + emoji: "āĻ¸ā§āĻŦāĻ¨āĻŋāĻ°ā§āĻ§āĻžāĻ°āĻŋāĻ¤ āĻ‡āĻŽā§‹āĻœāĻŋāĻ—ā§āĻ˛āĻŋ" + emojiDescription: "āĻ†āĻĒāĻ¨āĻŋ āĻāĻ•āĻŸāĻŋ āĻ•āĻžāĻ¸ā§āĻŸāĻŽ āĻ‡āĻŽā§‹āĻœāĻŋāĻ° āĻ¨āĻžāĻŽ āĻ•ā§‹āĻ˛āĻ¨ā§‡ āĻ†āĻŦāĻĻā§āĻ§ āĻ•āĻ°ā§‡ āĻ•āĻžāĻ¸ā§āĻŸāĻŽ āĻ‡āĻŽā§‹āĻœāĻŋāĻŸāĻŋ āĻĻā§‡āĻ–āĻžāĻ¤ā§‡ āĻĒāĻžāĻ°ā§‡āĻ¨ā§ˇ" + search: "āĻ–ā§āĻāĻœā§āĻ¨" + searchDescription: "āĻĒā§‚āĻ°ā§āĻŦ-āĻŸāĻžāĻ‡āĻĒ āĻ•āĻ°āĻž āĻĒāĻžāĻ ā§āĻ¯ āĻ¸āĻš āĻāĻ•āĻŸāĻŋ āĻ…āĻ¨ā§āĻ¸āĻ¨ā§āĻ§āĻžāĻ¨ āĻŦāĻžāĻ•ā§āĻ¸ āĻĒā§āĻ°āĻĻāĻ°ā§āĻļāĻ¨ āĻ•āĻ°ā§‡āĨ¤" + flip: "āĻ‰āĻ˛ā§āĻŸāĻžāĻ¨" + flipDescription: "āĻŦāĻŋāĻˇāĻ¯āĻŧāĻŦāĻ¸ā§āĻ¤ā§ āĻ‰āĻĒāĻ°ā§‡/āĻ¨ā§€āĻšā§‡ āĻŦāĻž āĻŦāĻžāĻŽ/āĻĄāĻžāĻ¨ā§‡ āĻ‰āĻ˛ā§āĻŸāĻžāĻ¨āĨ¤" + jelly: "āĻ…ā§āĻ¯āĻžāĻ¨āĻŋāĻŽā§‡āĻļāĻ¨ (āĻœā§‡āĻ˛āĻŋ)" + jellyDescription: "āĻœā§‡āĻ˛āĻŋāĻ° āĻŽāĻ¤ āĻ…ā§āĻ¯āĻžāĻ¨āĻŋāĻŽā§‡āĻļāĻ¨ āĻĻā§‡āĻ–āĻžā§ŸāĨ¤" + tada: "āĻ…ā§āĻ¯āĻžāĻ¨āĻŋāĻŽā§‡āĻļāĻ¨ (āĻŸāĻžāĻĄāĻž)" + tadaDescription: "\"āĻŸāĻžāĻĄāĻž!\" āĻāĻ° āĻŽāĻ¤ āĻ…ā§āĻ¯āĻžāĻ¨āĻŋāĻŽā§‡āĻļāĻ¨ āĻĻā§‡āĻ–āĻžā§ŸāĨ¤" + jump: "āĻ…ā§āĻ¯āĻžāĻ¨āĻŋāĻŽā§‡āĻļāĻ¨ (āĻ˛āĻžāĻĢ)" + jumpDescription: "āĻŦāĻŋāĻˇā§ŸāĻŦāĻ¸ā§āĻ¤ā§āĻ¤ā§‡ āĻ˛āĻžāĻĢ āĻŽāĻžāĻ°āĻžāĻ° āĻŽāĻ¤ āĻ…ā§āĻ¯āĻžāĻ¨āĻŋāĻŽā§‡āĻļāĻ¨ āĻĻā§‡āĻ–āĻžā§ŸāĨ¤" + bounce: "āĻ…ā§āĻ¯āĻžāĻ¨āĻŋāĻŽā§‡āĻļāĻ¨ (āĻ¤āĻŋā§œāĻŋāĻ‚ āĻŦāĻŋā§œāĻŋāĻ‚)" + bounceDescription: "āĻ¤āĻŋā§œāĻŋāĻ‚ āĻŦāĻŋā§œāĻŋāĻ‚ āĻ•āĻ°āĻžāĻ° āĻŽāĻ¤ āĻ…ā§āĻ¯āĻžāĻ¨āĻŋāĻŽā§‡āĻļāĻ¨ āĻĻā§‡āĻ–āĻžā§ŸāĨ¤" + shake: "āĻ…ā§āĻ¯āĻžāĻ¨āĻŋāĻŽā§‡āĻļāĻ¨ (āĻāĻžāĻāĻ•āĻŋ)" + shakeDescription: "āĻāĻžāĻāĻ•āĻŋāĻ° āĻŽāĻ¤ āĻ…ā§āĻ¯āĻžāĻ¨āĻŋāĻŽā§‡āĻļāĻ¨ āĻĻā§‡āĻ–āĻžā§ŸāĨ¤" + twitch: "āĻ…ā§āĻ¯āĻžāĻ¨āĻŋāĻŽā§‡āĻļāĻ¨ (āĻŽā§‹āĻšāĻĄāĻŧāĻžāĻ¨ā§‹)" + twitchDescription: "āĻŽā§‹āĻšāĻĄāĻŧāĻžāĻ¨ā§‹āĻ° āĻŽāĻ¤ āĻ…ā§āĻ¯āĻžāĻ¨āĻŋāĻŽā§‡āĻļāĻ¨ āĻĻā§‡āĻ–āĻžā§ŸāĨ¤" + spin: "āĻ…ā§āĻ¯āĻžāĻ¨āĻŋāĻŽā§‡āĻļāĻ¨ (āĻ˜ā§āĻ°āĻž)" + spinDescription: "āĻ˜ā§āĻ°āĻžāĻ° āĻŽāĻ¤ āĻ…ā§āĻ¯āĻžāĻ¨āĻŋāĻŽā§‡āĻļāĻ¨ āĻĻā§‡āĻ–āĻžā§ŸāĨ¤" + x2: "āĻŦā§œ" + x2Description: "āĻŦāĻŋāĻˇā§ŸāĻŦāĻ¸ā§āĻ¤ā§ āĻŦā§œ āĻ•āĻ°ā§‡ āĻĻā§‡āĻ–āĻžā§ŸāĨ¤" + x3: "āĻ…āĻ¨ā§‡āĻ• āĻŦā§œ" + x3Description: "āĻŦāĻŋāĻˇā§ŸāĻŦāĻ¸ā§āĻ¤ā§ āĻ†āĻ°āĻ“ āĻŦā§œ āĻ•āĻ°ā§‡ āĻĻā§‡āĻ–āĻžā§ŸāĨ¤" + x4: "āĻ…āĻ¸ā§āĻŦāĻžāĻ­āĻžāĻŦāĻŋāĻ• āĻŦā§œ" + x4Description: "āĻŦāĻŋāĻˇā§ŸāĻŦāĻ¸ā§āĻ¤ā§āĻ•ā§‡ āĻ†āĻ—ā§‡āĻ° āĻĨā§‡āĻ•ā§‡āĻ“ āĻ†āĻ°āĻ“ āĻŦā§œ āĻ•āĻ°ā§‡ āĻĻā§‡āĻ–āĻžā§ŸāĨ¤" + blur: "āĻŦā§āĻ˛āĻžāĻ°" + blurDescription: "āĻŦāĻŋāĻˇāĻ¯āĻŧāĻŦāĻ¸ā§āĻ¤ā§āĻ•ā§‡ āĻŦā§āĻ˛āĻžāĻ° āĻ•āĻ°āĻ¤ā§‡ āĻĒāĻžāĻ°ā§‡āĻ¨āĨ¤ āĻ†āĻĒāĻ¨āĻŋ āĻāĻ° āĻ‰āĻĒāĻ° āĻŽāĻžāĻ‰āĻ¸ āĻ•āĻžāĻ°ā§āĻ¸āĻžāĻ° āĻ°āĻžāĻ–āĻ˛ā§‡, āĻāĻŸāĻŋ āĻĒāĻ°āĻŋāĻˇā§āĻ•āĻžāĻ°āĻ­āĻžāĻŦā§‡ āĻĻā§‡āĻ–āĻ¤ā§‡ āĻĒāĻžāĻŦā§‡āĻ¨āĨ¤" + font: "āĻĢāĻ¨ā§āĻŸ" + fontDescription: "āĻŦāĻŋāĻˇā§ŸāĻŦāĻ¸ā§āĻ¤ā§āĻ•ā§‡ āĻ•ā§‹āĻ¨ āĻĢāĻ¨ā§āĻŸā§‡ āĻĻā§‡āĻ–āĻžāĻ¨ā§‹ āĻšāĻŦā§‡ āĻ¤āĻž āĻ¨āĻŋāĻ°ā§āĻ§āĻžāĻ°āĻŖ āĻ•āĻ°ā§‡āĨ¤" + rainbow: "āĻ°ā§‡āĻ‡āĻ¨āĻŦā§‹" + rainbowDescription: "āĻŦāĻŋāĻˇā§ŸāĻŦāĻ¸ā§āĻ¤ā§āĻ•ā§‡ āĻ°āĻ‚āĻ§āĻ¨ā§āĻ° āĻ°āĻ‚ āĻ—ā§āĻ˛āĻŋāĻ¤ā§‡ āĻĒā§āĻ°āĻĻāĻ°ā§āĻļāĻ¨ āĻ•āĻ°ā§‡āĨ¤" + sparkle: "āĻšāĻŋāĻ• āĻšāĻŋāĻ•" + sparkleDescription: "āĻŦāĻŋāĻˇāĻ¯āĻŧāĻŦāĻ¸ā§āĻ¤ā§āĻ•ā§‡ āĻāĻ•āĻŸāĻŋ āĻšāĻŋāĻ•āĻšāĻŋāĻ•ā§‡ āĻ•āĻŖāĻž āĻĒā§āĻ°āĻ­āĻžāĻŦ āĻĻā§‡āĻ¯āĻŧāĨ¤" + rotate: "āĻ˜ā§āĻ°āĻžāĻ¨" + rotateDescription: "āĻŦāĻŋāĻˇā§ŸāĻŦāĻ¸ā§āĻ¤ā§āĻ•ā§‡ āĻāĻ•āĻŸāĻŋ āĻ¨āĻŋāĻ°ā§āĻĻāĻŋāĻˇā§āĻŸ āĻ•ā§‹āĻ¨ā§‡ āĻ˜ā§āĻ°āĻžā§ŸāĨ¤" +_instanceTicker: + none: "āĻĻā§‡āĻ–āĻžāĻŦā§‡āĻ¨ āĻ¨āĻž" + remote: "āĻ°āĻŋāĻŽā§‹āĻŸ āĻŦā§āĻ¯āĻžāĻŦāĻšāĻžāĻ°āĻ•āĻžāĻ°ā§€āĻĻā§‡āĻ° āĻœāĻ¨ā§āĻ¯ āĻĻā§‡āĻ–āĻžāĻ¨" + always: "āĻ¸āĻ°ā§āĻŦāĻĻāĻž āĻĻā§‡āĻ–āĻžāĻ¨" +_serverDisconnectedBehavior: + reload: "āĻ¸ā§āĻŦā§ŸāĻ‚āĻ•ā§āĻ°āĻŋā§ŸāĻ­āĻžāĻŦā§‡ āĻ°āĻŋāĻ˛ā§‹āĻĄ" + dialog: "āĻ¸āĻ¤āĻ°ā§āĻ•āĻ¤āĻž āĻĄāĻžā§ŸāĻžāĻ˛āĻ— āĻĻā§‡āĻ–āĻžāĻ¨" + quiet: "āĻ…āĻ—āĻšāĻ°ā§€ āĻ¸āĻ¤āĻ°ā§āĻ•āĻ¤āĻž āĻĻā§‡āĻ–āĻžāĻ¨" +_channel: + create: "āĻšā§āĻ¯āĻžāĻ¨ā§‡āĻ˛ āĻŦāĻžāĻ¨āĻžāĻ¨" + edit: "āĻšā§āĻ¯āĻžāĻ¨ā§‡āĻ˛ āĻ¸āĻŽā§āĻĒāĻžāĻĻāĻ¨āĻž āĻ•āĻ°ā§āĻ¨" + setBanner: "āĻŦā§āĻ¯āĻžāĻ¨āĻžāĻ° āĻ¸ā§‡āĻŸ āĻ•āĻ°ā§āĻ¨" + removeBanner: "āĻŦā§āĻ¯āĻžāĻ¨āĻžāĻ° āĻ¸āĻ°āĻžāĻ¨" + featured: "āĻŦāĻ°ā§āĻ¤āĻŽāĻžāĻ¨ā§‡ āĻœāĻ¨āĻĒā§āĻ°āĻŋā§Ÿ" + owned: "āĻ¨āĻŋāĻœā§‡āĻ°" + following: "āĻ…āĻ¨ā§āĻ¸āĻ°āĻŖ āĻ•āĻ°āĻž āĻšāĻšā§āĻ›ā§‡" + usersCount: "{n} āĻœāĻ¨ āĻ…āĻ‚āĻļāĻ—ā§āĻ°āĻšāĻŖāĻ•āĻžāĻ°ā§€" + notesCount: "{n} āĻŸāĻŋ āĻ¨ā§‹āĻŸ" +_menuDisplay: + sideFull: "āĻĒāĻžāĻļā§‡" + sideIcon: "āĻĒāĻžāĻļā§‡ (āĻ†āĻ‡āĻ•āĻ¨)" + top: "āĻļā§€āĻ°ā§āĻˇā§‡" + hide: "āĻ˛ā§āĻ•āĻžāĻ¨" +_wordMute: + muteWords: "āĻ¨āĻŋāĻƒāĻļāĻŦā§āĻĻ āĻ•āĻ°āĻž āĻļāĻŦā§āĻĻāĻ—ā§āĻ˛āĻŋ" + muteWordsDescription: "āĻ¸ā§āĻĒā§‡āĻ¸ āĻĻāĻŋāĻ¯āĻŧā§‡ āĻ†āĻ˛āĻžāĻĻāĻž āĻ•āĻ°āĻ˛ā§‡ AND āĻļāĻ°ā§āĻ¤ āĻ¤ā§ˆāĻ°āĻŋ āĻšāĻŦā§‡ āĻāĻŦāĻ‚ āĻ†āĻ˛āĻžāĻĻāĻž āĻ˛āĻžāĻ‡āĻ¨ā§‡ āĻ˛āĻŋāĻ–āĻ˛ā§‡ OR āĻļāĻ°ā§āĻ¤ āĻ¤ā§ˆāĻ°āĻŋ āĻšāĻŦā§‡āĨ¤" + muteWordsDescription2: "āĻ°ā§‡āĻ—ā§āĻ˛āĻžāĻ° āĻāĻ•ā§āĻ¸āĻĒā§āĻ°ā§‡āĻļāĻ¨ āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ° āĻ•āĻ°āĻ¤ā§‡ āĻ¸ā§āĻ˛ā§āĻ¯āĻžāĻļ āĻĻāĻŋāĻ¯āĻŧā§‡ āĻ•ā§€āĻ“āĻ¯āĻŧāĻžāĻ°ā§āĻĄāĻ•ā§‡ āĻ˜āĻŋāĻ°ā§‡ āĻ°āĻžāĻ–ā§āĻ¨āĨ¤" + softDescription: "āĻŸāĻžāĻ‡āĻŽāĻ˛āĻžāĻ‡āĻ¨ āĻĨā§‡āĻ•ā§‡ āĻ¨āĻŋāĻ°ā§āĻĻāĻŋāĻˇā§āĻŸ āĻļāĻ°ā§āĻ¤āĻžāĻ¨ā§āĻ¯āĻžā§Ÿā§€ āĻ¨ā§‹āĻŸ āĻ˛ā§āĻ•āĻŋāĻ¯āĻŧā§‡ āĻ°āĻžāĻ–ā§‡āĨ¤" + hardDescription: "āĻ¨āĻŋāĻ°ā§āĻĻāĻŋāĻˇā§āĻŸ āĻļāĻ°ā§āĻ¤āĻžāĻ¨ā§āĻ¯āĻžā§Ÿā§€ āĻ¨ā§‹āĻŸāĻ—ā§āĻ˛āĻŋāĻ•ā§‡ āĻŸāĻžāĻ‡āĻŽāĻ˛āĻžāĻ‡āĻ¨ āĻĨā§‡āĻ•ā§‡ āĻŦāĻžāĻĻ āĻĻā§‡ā§ŸāĨ¤ āĻ†āĻĒāĻ¨āĻŋ āĻļāĻ°ā§āĻ¤ āĻĒāĻ°āĻŋāĻŦāĻ°ā§āĻ¤āĻ¨ āĻ•āĻ°āĻ˛ā§‡āĻ“ āĻ¯ā§‡ āĻ¨ā§‹āĻŸāĻ—ā§āĻ˛āĻŋ āĻ¯ā§‹āĻ— āĻ•āĻ°āĻž āĻšāĻ¯āĻŧāĻ¨āĻŋ āĻ¸ā§‡āĻ—ā§āĻ˛āĻŋ āĻŦāĻžāĻĻ āĻĻā§‡āĻ“āĻ¯āĻŧāĻž āĻšāĻŦā§‡āĨ¤" + soft: "āĻ¨āĻŽāĻ¨ā§€ā§Ÿ" + hard: "āĻ•āĻ ā§‹āĻ°" + mutedNotes: "āĻŽāĻŋāĻ‰āĻŸ āĻ•āĻ°āĻž āĻ¨ā§‹āĻŸāĻ—ā§āĻ˛āĻŋ" +_instanceMute: + instanceMuteDescription: "āĻ•āĻ¨āĻĢāĻŋāĻ—āĻžāĻ° āĻ•āĻ°āĻž āĻ‡āĻ¨ā§āĻ¸āĻŸā§āĻ¯āĻžāĻ¨ā§āĻ¸ā§‡āĻ° āĻ¸āĻŦ āĻ¨ā§‹āĻŸ āĻāĻŦāĻ‚ āĻ°āĻŋāĻ¨ā§‹āĻŸ āĻŽāĻŋāĻ‰āĻŸ āĻ•āĻ°ā§āĻ¨, āĻŽāĻŋāĻ‰āĻŸ āĻ•āĻ°āĻž āĻ‡āĻ¨ā§āĻ¸āĻŸā§āĻ¯āĻžāĻ¨ā§āĻ¸ā§‡āĻ° āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ°āĻ•āĻžāĻ°ā§€āĻĻā§‡āĻ° āĻ‰āĻ¤ā§āĻ¤āĻ° āĻ¸āĻšāĨ¤" + instanceMuteDescription2: "āĻĒā§āĻ°āĻ¤āĻŋāĻŸāĻŋāĻ•ā§‡ āĻ†āĻ˛āĻžāĻĻāĻž āĻ˛āĻžāĻ‡āĻ¨ā§‡ āĻ˛āĻŋāĻ–ā§āĻ¨" + title: "āĻ•āĻ¨āĻĢāĻŋāĻ—āĻžāĻ° āĻ•āĻ°āĻž āĻ‡āĻ¨ā§āĻ¸āĻŸā§āĻ¯āĻžāĻ¨ā§āĻ¸ā§‡āĻ° āĻ¨ā§‹āĻŸāĻ—ā§āĻ˛āĻŋāĻ•ā§‡ āĻ˛ā§āĻ•āĻŋāĻ¯āĻŧā§‡ āĻ°āĻžāĻ–ā§‡āĨ¤" + heading: "āĻŽāĻŋāĻ‰āĻŸ āĻ•āĻ°āĻž āĻ‡āĻ¨ā§āĻ¸āĻ¤ā§āĻ¯āĻžāĻ¨ā§āĻ¸ā§‡āĻ° āĻ¤āĻžāĻ˛āĻŋāĻ•āĻž" +_theme: + explore: "āĻĨāĻŋāĻŽāĻ—ā§āĻ˛āĻŋ āĻ˜ā§āĻ°ā§‡ āĻĻā§‡āĻ–ā§āĻ¨" + install: "āĻĨāĻŋāĻŽ āĻ‡āĻ¨āĻ¸ā§āĻŸāĻ˛ āĻ•āĻ°ā§āĻ¨" + manage: "āĻĨāĻŋāĻŽ āĻŦā§āĻ¯āĻžāĻŦāĻ¸ā§āĻĨāĻžāĻĒāĻ¨āĻž" + code: "āĻĨāĻŋāĻŽ āĻ•ā§‹āĻĄ" + description: "āĻŦāĻ°ā§āĻŖāĻ¨āĻž" + installed: "{name} āĻ‡āĻ¨ā§āĻ¸āĻŸāĻ˛ āĻ•āĻ°āĻž āĻšā§Ÿā§‡āĻ›ā§‡" + installedThemes: "āĻ‡āĻ¨ā§āĻ¸āĻŸāĻ˛ āĻ•āĻ°āĻž āĻĨāĻŋāĻŽāĻ¸āĻŽā§‚āĻš" + builtinThemes: "āĻŦāĻŋāĻ˛ā§āĻŸ-āĻ‡āĻ¨ āĻĨāĻŋāĻŽāĻ¸āĻŽā§‚āĻš" + alreadyInstalled: "āĻāĻ‡ āĻĨāĻŋāĻŽāĻŸāĻŋ āĻ‡āĻ¤āĻŋāĻŽāĻ§ā§āĻ¯ā§‡ āĻ‡āĻ¨ā§āĻ¸āĻŸāĻ˛ āĻ•āĻ°āĻž āĻšā§Ÿā§‡āĻ›ā§‡" + invalid: "āĻĨāĻŋāĻŽāĻŸāĻŋāĻ° āĻĢāĻ°āĻŽā§āĻ¯āĻžāĻŸ āĻ¸āĻ āĻŋāĻ• āĻ¨ā§Ÿ" + make: "āĻĨāĻŋāĻŽ āĻŦāĻžāĻ¨āĻžāĻ¨" + base: "āĻŦā§‡āĻ¸" + addConstant: "āĻ§ā§āĻ°ā§āĻŦāĻ• āĻ¯ā§‹āĻ— āĻ•āĻ°ā§āĻ¨" + constant: "āĻ§ā§āĻ°ā§āĻŦāĻ•" + defaultValue: "āĻĄāĻŋāĻĢāĻ˛ā§āĻŸ āĻŽāĻžāĻ¨" + color: "āĻ°āĻ‚" + refProp: "āĻĒā§āĻ°ā§‹āĻĒāĻžāĻ°ā§āĻŸāĻŋ āĻ°ā§‡āĻĢāĻžāĻ°ā§‡āĻ¨ā§āĻ¸ āĻ•āĻ°ā§āĻ¨" + refConst: "āĻ§ā§āĻ°ā§āĻŦāĻ• āĻ°ā§‡āĻĢāĻžāĻ°ā§‡āĻ¨ā§āĻ¸ āĻ•āĻ°ā§āĻ¨" + key: "āĻ•ā§€" + func: "āĻĢāĻžāĻ‚āĻļāĻ¨" + funcKind: "āĻĢāĻžāĻ‚āĻļāĻ¨ā§‡āĻ° āĻ§āĻ°āĻ¨" + argument: "āĻ†āĻ°ā§āĻ—ā§āĻŽā§‡āĻ¨ā§āĻŸ" + basedProp: "āĻ°ā§‡āĻĢāĻžāĻ°ā§‡āĻ¨ā§āĻ¸ āĻ•āĻ°āĻž āĻĒā§āĻ°ā§‹āĻĒāĻžāĻ°ā§āĻŸāĻŋ" + alpha: "āĻ…āĻ¸ā§āĻŦāĻšā§āĻ›āĻ¤āĻž" + darken: "āĻ…āĻ¨ā§āĻ§āĻ•āĻžāĻ° āĻ•āĻ°ā§āĻ¨" + lighten: "āĻ‰āĻœā§āĻœā§āĻŦāĻ˛ āĻ•āĻ°ā§āĻ¨" + inputConstantName: "āĻ§ā§āĻ°ā§āĻŦāĻ•āĻŸāĻŋāĻ° āĻ¨āĻžāĻŽ āĻ˛āĻŋāĻ–ā§āĻ¨" + importInfo: "āĻ†āĻĒāĻ¨āĻŋ āĻāĻ–āĻžāĻ¨ā§‡ āĻĨāĻŋāĻŽ āĻ•ā§‹āĻĄ āĻĒā§‡āĻ¸ā§āĻŸ āĻ•āĻ°āĻ¤ā§‡ āĻĒāĻžāĻ°ā§‡āĻ¨ āĻāĻŦāĻ‚ āĻ¸ā§‡āĻŸāĻŋāĻ•ā§‡ āĻāĻĄāĻŋāĻŸāĻ°ā§‡ āĻ‡āĻŽā§āĻĒā§‹āĻ°ā§āĻŸ āĻ•āĻ°āĻ¤ā§‡ āĻĒāĻžāĻ°ā§‡āĻ¨" + deleteConstantConfirm: "āĻ†āĻĒāĻ¨āĻŋ āĻ•āĻŋ āĻ§ā§āĻ°ā§āĻŦāĻ• {const} āĻŽā§āĻ›ā§‡ āĻĢā§‡āĻ˛āĻ¤ā§‡ āĻšāĻžāĻ¨īŧŸ" + keys: + accent: "āĻ…ā§āĻ¯āĻžāĻ•āĻ¸ā§‡āĻ¨ā§āĻŸ" + bg: "āĻĒāĻŸāĻ­ā§‚āĻŽāĻŋ" + fg: "āĻ˛ā§‡āĻ–āĻž" + focus: "āĻĢā§‹āĻ•āĻžāĻ¸" + indicator: "āĻ‡āĻ¨āĻĄāĻŋāĻ•ā§‡āĻŸāĻ°" + panel: "āĻĒā§āĻ¯āĻžāĻ¨ā§‡āĻ˛" + shadow: "āĻ›āĻžā§ŸāĻž" + header: "āĻšā§‡āĻĄāĻžāĻ°" + navBg: "āĻ¸āĻžāĻ‡āĻĄāĻŦāĻžāĻ°ā§‡āĻ° āĻĒāĻŸāĻ­ā§‚āĻŽāĻŋ" + navFg: "āĻ¸āĻžāĻ‡āĻĄāĻŦāĻžāĻ°ā§‡āĻ° āĻĒāĻžāĻ ā§āĻ¯" + navHoverFg: "āĻ¸āĻžāĻ‡āĻĄāĻŦāĻžāĻ°ā§‡āĻ° āĻĒāĻžāĻ ā§āĻ¯ (āĻšāĻ­āĻžāĻ°)" + navActive: "āĻ¸āĻžāĻ‡āĻĄāĻŦāĻžāĻ°ā§‡āĻ° āĻĒāĻžāĻ ā§āĻ¯ (āĻ…ā§āĻ¯āĻžāĻ•āĻŸāĻŋāĻ­)" + navIndicator: "āĻ¸āĻžāĻ‡āĻĄāĻŦāĻžāĻ°ā§‡āĻ° āĻ‡āĻ¨āĻĄāĻŋāĻ•ā§‡āĻŸāĻ°" + link: "āĻ˛āĻŋāĻ‚āĻ•" + hashtag: "āĻšā§āĻ¯āĻžāĻļāĻŸā§āĻ¯āĻžāĻ—" + mention: "āĻ‰āĻ˛ā§āĻ˛ā§‡āĻ–" + mentionMe: "āĻ†āĻĒāĻ¨āĻžāĻ•ā§‡ āĻ‰āĻ˛ā§āĻ˛ā§‡āĻ–ā§āĻ¯ āĻ•āĻ°āĻž" + renote: "āĻ°āĻŋāĻ¨ā§‹āĻŸ" + modalBg: "āĻŽā§‹āĻĄāĻžāĻ˛ā§‡āĻ° āĻĒāĻŸāĻ­ā§‚āĻŽāĻŋ" + divider: "āĻ–āĻ¨ā§āĻĄāĻ•" + scrollbarHandle: "āĻ¸ā§āĻ•ā§āĻ°āĻ˛āĻŦāĻžāĻ° āĻšā§āĻ¯āĻžāĻ¨ā§āĻĄā§‡āĻ˛" + scrollbarHandleHover: "āĻ¸ā§āĻ•ā§āĻ°āĻ˛āĻŦāĻžāĻ° āĻšā§āĻ¯āĻžāĻ¨ā§āĻĄā§‡āĻ˛ (āĻšāĻ­āĻžāĻ°)" + dateLabelFg: "āĻ¤āĻžāĻ°āĻŋāĻ– āĻ˛ā§‡āĻŦā§‡āĻ˛ā§‡āĻ° āĻĒāĻžāĻ ā§āĻ¯" + infoBg: "āĻ¤āĻĨā§āĻ¯ā§‡āĻ° āĻĒāĻŸāĻ­ā§‚āĻŽāĻŋ" + infoFg: "āĻ¤āĻĨā§āĻ¯ā§‡āĻ° āĻĒāĻžāĻ ā§āĻ¯" + infoWarnBg: "āĻ“ā§ŸāĻžāĻ°ā§āĻ¨āĻŋāĻ‚ āĻāĻ° āĻĒāĻŸāĻ­ā§‚āĻŽāĻŋ" + infoWarnFg: "āĻ“ā§ŸāĻžāĻ°ā§āĻ¨āĻŋāĻ‚ āĻāĻ° āĻĒāĻžāĻ ā§āĻ¯" + cwBg: "CW āĻŦāĻžāĻŸāĻ¨ā§‡āĻ° āĻĒāĻŸāĻ­ā§‚āĻŽāĻŋ" + cwFg: "CW āĻŦāĻžāĻŸāĻ¨ā§‡āĻ° āĻĒāĻžāĻ ā§āĻ¯" + cwHoverBg: "CW āĻŦāĻžāĻŸāĻ¨ā§‡āĻ° āĻĒāĻŸāĻ­ā§‚āĻŽāĻŋ (āĻšāĻ­āĻžāĻ°)" + toastBg: "āĻŦāĻŋāĻœā§āĻžāĻĒā§āĻ¤āĻŋāĻ° āĻĒāĻŸāĻ­ā§‚āĻŽāĻŋ" + toastFg: "āĻŦāĻŋāĻœā§āĻžāĻĒā§āĻ¤āĻŋāĻ° āĻĒāĻžāĻ ā§āĻ¯" + buttonBg: "āĻŦāĻžāĻŸāĻ¨ā§‡āĻ° āĻĒāĻŸāĻ­ā§‚āĻŽāĻŋ" + buttonHoverBg: "āĻŦāĻžāĻŸāĻ¨ā§‡āĻ° āĻĒāĻŸāĻ­ā§‚āĻŽāĻŋ (āĻšāĻ­āĻžāĻ°)" + inputBorder: "āĻ‡āĻ¨āĻĒā§āĻŸ āĻĢāĻŋāĻ˛ā§āĻĄā§‡āĻ° āĻŦāĻ°ā§āĻĄāĻžāĻ°" + listItemHoverBg: "āĻ˛āĻŋāĻ¸ā§āĻŸ āĻ†āĻ‡āĻŸā§‡āĻŽā§‡āĻ° āĻĒāĻŸāĻ­ā§‚āĻŽāĻŋ (āĻšā§‹āĻ­āĻžāĻ°)" + driveFolderBg: "āĻĄā§āĻ°āĻžāĻ‡āĻ­ āĻĢā§‹āĻ˛ā§āĻĄāĻžāĻ°ā§‡āĻ° āĻĒāĻŸāĻ­ā§‚āĻŽāĻŋ" + wallpaperOverlay: "āĻ“āĻ¯āĻŧāĻžāĻ˛āĻĒā§‡āĻĒāĻžāĻ° āĻ“āĻ­āĻžāĻ°āĻ˛ā§‡" + badge: "āĻŦā§āĻ¯āĻžāĻœ" + messageBg: "āĻšā§āĻ¯āĻžāĻŸā§‡āĻ° āĻĒāĻŸāĻ­ā§‚āĻŽāĻŋ" + accentDarken: "āĻ…ā§āĻ¯āĻžāĻ•āĻ¸ā§‡āĻ¨ā§āĻŸ (āĻ—āĻžā§)" + accentLighten: "āĻ…ā§āĻ¯āĻžāĻ•āĻ¸ā§‡āĻ¨ā§āĻŸ (āĻšāĻžāĻ˛ā§āĻ•āĻž)" + fgHighlighted: "āĻšāĻžāĻ‡āĻ˛āĻžāĻ‡āĻŸ āĻ•āĻ°āĻž āĻĒāĻžāĻ ā§āĻ¯" +_sfx: + note: "āĻ¨ā§‹āĻŸāĻ—ā§āĻ˛āĻŋ" + noteMy: "āĻ¨ā§‹āĻŸ (āĻ†āĻĒāĻ¨āĻžāĻ°)" + notification: "āĻŦāĻŋāĻœā§āĻžāĻĒā§āĻ¤āĻŋ" + chat: "āĻšā§āĻ¯āĻžāĻŸ" + chatBg: "āĻšā§āĻ¯āĻžāĻŸ (āĻŦā§āĻ¯āĻžāĻ•āĻ—ā§āĻ°āĻžāĻ‰āĻ¨ā§āĻĄ)" + antenna: "āĻ…ā§āĻ¯āĻžāĻ¨ā§āĻŸā§‡āĻ¨āĻžāĻ—ā§āĻ˛āĻŋ" + channel: "āĻšā§āĻ¯āĻžāĻ¨ā§‡āĻ˛ā§‡āĻ° āĻŦāĻŋāĻœā§āĻžāĻĒā§āĻ¤āĻŋ" +_ago: + unknown: "āĻ…āĻœāĻžāĻ¨āĻž" + future: "āĻ­āĻŦāĻŋāĻˇā§āĻ¯ā§Ž" + justNow: "āĻāĻ‡āĻŽāĻžāĻ¤ā§āĻ°" + secondsAgo: "{n} āĻ¸ā§‡āĻ•ā§‡āĻ¨ā§āĻĄ āĻ†āĻ—ā§‡" + minutesAgo: "{n} āĻŽāĻŋāĻ¨āĻŋāĻŸ āĻ†āĻ—ā§‡" + hoursAgo: "{n} āĻ˜āĻŖā§āĻŸāĻž āĻ†āĻ—ā§‡" + daysAgo: "{n} āĻĻāĻŋāĻ¨ āĻ†āĻ—ā§‡" + weeksAgo: "{n} āĻ¸āĻĒā§āĻ¤āĻžāĻš āĻ†āĻ—ā§‡" + monthsAgo: "{n} āĻŽāĻžāĻ¸ āĻ†āĻ—ā§‡" + yearsAgo: "{n} āĻŦāĻ›āĻ° āĻ†āĻ—ā§‡" +_time: + second: "āĻ¸ā§‡āĻ•ā§‡āĻ¨ā§āĻĄ" + minute: "āĻŽāĻŋāĻ¨āĻŋāĻŸ" + hour: "āĻ˜āĻŖā§āĻŸāĻž" + day: "āĻĻāĻŋāĻ¨" +_tutorial: + title: "Misskey āĻ•āĻŋāĻ­āĻžāĻŦā§‡ āĻŦā§āĻ¯āĻžāĻŦāĻšāĻžāĻ° āĻ•āĻ°āĻŦā§‡āĻ¨" + step1_1: "āĻ¸ā§āĻŦāĻžāĻ—āĻ¤āĻŽ!" + step1_2: "āĻāĻ‡ āĻ¸ā§āĻ•ā§āĻ°ā§€āĻ¨āĻŸāĻŋāĻ•ā§‡ \"āĻŸāĻžāĻ‡āĻŽāĻ˛āĻžāĻ‡āĻ¨\" āĻŦāĻ˛āĻž āĻšāĻ¯āĻŧ āĻāĻŦāĻ‚ āĻ•āĻžāĻ˛āĻžāĻ¨ā§āĻ•ā§āĻ°āĻŽāĻŋāĻ• āĻ•ā§āĻ°āĻŽā§‡ āĻ†āĻĒāĻ¨āĻžāĻ° āĻāĻŦāĻ‚ āĻ†āĻĒāĻ¨āĻŋ āĻ¯āĻžāĻĻā§‡āĻ° \"āĻ…āĻ¨ā§āĻ¸āĻ°āĻŖ āĻ•āĻ°ā§‡āĻ¨\" āĻ¤āĻžāĻĻā§‡āĻ° \"āĻ¨ā§‹āĻŸāĻ—ā§āĻ˛āĻŋ\" āĻĻā§‡āĻ–āĻžāĻ¯āĻŧā§ˇ" + step1_3: "āĻ†āĻĒāĻ¨āĻŋ āĻ†āĻĒāĻ¨āĻžāĻ° āĻŸāĻžāĻ‡āĻŽāĻ˛āĻžāĻ‡āĻ¨ā§‡ āĻ•āĻŋāĻ›ā§ āĻĻā§‡āĻ–āĻ¤ā§‡ āĻĒāĻžāĻŦā§‡āĻ¨ āĻ¨āĻž āĻ•āĻžāĻ°āĻŖ āĻ†āĻĒāĻ¨āĻŋ āĻāĻ–āĻ¨āĻ“ āĻ•ā§‹āĻ¨ā§‹ āĻ¨ā§‹āĻŸ āĻĒā§‹āĻ¸ā§āĻŸ āĻ•āĻ°ā§‡āĻ¨āĻ¨āĻŋ āĻāĻŦāĻ‚ āĻ†āĻĒāĻ¨āĻŋ āĻ•āĻžāĻ‰āĻ•ā§‡ āĻ…āĻ¨ā§āĻ¸āĻ°āĻŖ āĻ•āĻ°āĻ›ā§‡āĻ¨ āĻ¨āĻžā§ˇ" + step2_1: "āĻ¨ā§‹āĻŸ āĻ¤ā§ˆāĻ°āĻŋ āĻ•āĻ°āĻžāĻ° āĻ†āĻ—ā§‡ āĻŦāĻž āĻ•āĻžāĻ‰āĻ•ā§‡ āĻ…āĻ¨ā§āĻ¸āĻ°āĻŖ āĻ•āĻ°āĻžāĻ° āĻ†āĻ—ā§‡ āĻĒā§āĻ°āĻĨāĻŽā§‡ āĻ†āĻĒāĻ¨āĻžāĻ° āĻĒā§āĻ°ā§‹āĻĢāĻžāĻ‡āĻ˛āĻŸāĻŋ āĻ¸āĻŽā§āĻĒā§‚āĻ°ā§āĻŖ āĻ•āĻ°ā§āĻ¨āĨ¤" + step2_2: "āĻ†āĻĒāĻ¨āĻŋ āĻ•ā§‡ āĻ¤āĻž āĻœāĻžāĻ¨āĻž āĻ…āĻ¨ā§‡āĻ• āĻ˛ā§‹āĻ•ā§‡āĻ° āĻœāĻ¨ā§āĻ¯ āĻ†āĻĒāĻ¨āĻžāĻ° āĻ¨ā§‹āĻŸāĻ—ā§āĻ˛āĻŋ āĻĻā§‡āĻ–āĻž āĻāĻŦāĻ‚ āĻ…āĻ¨ā§āĻ¸āĻ°āĻŖ āĻ•āĻ°āĻžāĻ•ā§‡ āĻ¸āĻšāĻœ āĻ•āĻ°ā§‡ āĻ¤ā§‹āĻ˛ā§‡ā§ˇ" + step3_1: "āĻ†āĻĒāĻ¨āĻŋ āĻ•āĻŋ āĻ¸āĻĢāĻ˛āĻ­āĻžāĻŦā§‡ āĻ†āĻĒāĻ¨āĻžāĻ° āĻĒā§āĻ°ā§‹āĻĢāĻžāĻ‡āĻ˛ āĻ¸ā§‡āĻŸ āĻ†āĻĒ āĻ•āĻ°ā§‡āĻ›ā§‡āĻ¨?" + step3_2: "āĻāĻ–āĻ¨, āĻ•āĻŋāĻ›ā§ āĻ¨ā§‹āĻŸ āĻĒā§‹āĻ¸ā§āĻŸ āĻ•āĻ°āĻžāĻ° āĻšā§‡āĻˇā§āĻŸāĻž āĻ•āĻ°ā§āĻ¨āĨ¤ āĻĒā§‹āĻ¸ā§āĻŸ āĻĢāĻ°ā§āĻŽ āĻ–ā§āĻ˛āĻ¤ā§‡ āĻĒā§‡āĻ¨ā§āĻ¸āĻŋāĻ˛ āĻšāĻŋāĻšā§āĻ¨āĻ¯ā§āĻ•ā§āĻ¤ āĻŦāĻžāĻŸāĻ¨ā§‡ āĻ•ā§āĻ˛āĻŋāĻ• āĻ•āĻ°ā§āĻ¨āĨ¤" + step3_3: "āĻŦāĻŋāĻˇāĻ¯āĻŧāĻŦāĻ¸ā§āĻ¤ā§ āĻ˛ā§‡āĻ–āĻžāĻ° āĻĒāĻ°ā§‡, āĻ†āĻĒāĻ¨āĻŋ āĻĢāĻ°ā§āĻŽā§‡āĻ° āĻ‰āĻĒāĻ°ā§‡āĻ° āĻĄāĻžāĻ¨āĻĻāĻŋāĻ•ā§‡āĻ° āĻŦāĻžāĻŸāĻ¨ā§‡ āĻ•ā§āĻ˛āĻŋāĻ• āĻ•āĻ°ā§‡ āĻĒā§‹āĻ¸ā§āĻŸ āĻ•āĻ°āĻ¤ā§‡ āĻĒāĻžāĻ°ā§‡āĻ¨āĨ¤" + step3_4: "āĻĒā§‹āĻ¸ā§āĻŸ āĻ•āĻ°āĻžāĻ° āĻŽāĻ¤ āĻ•āĻŋāĻ›ā§ āĻŽāĻ¨ā§‡ āĻĒāĻ°āĻ›ā§‡ āĻ¨āĻž? \"āĻ†āĻŽāĻŋ āĻŽāĻŋāĻ¸āĻ•āĻŋ āĻ¸ā§‡āĻŸ āĻ†āĻĒ āĻ•āĻ°āĻ›āĻŋ\" āĻŦāĻ˛āĻ˛ā§‡ āĻ•ā§‡āĻŽāĻ¨ āĻšāĻ¯āĻŧ?" + step4_1: "āĻĒā§‹āĻ¸ā§āĻŸ āĻ•āĻ°ā§‡āĻ›ā§‡āĻ¨?" + step4_2: "āĻ¸āĻžāĻŦāĻžāĻļ! āĻāĻ–āĻ¨ āĻ†āĻĒāĻ¨āĻžāĻ° āĻ¨ā§‹āĻŸ āĻŸāĻžāĻ‡āĻŽāĻ˛āĻžāĻ‡āĻ¨ā§‡ āĻĻā§‡āĻ–āĻž āĻ¯āĻžāĻŦā§‡āĨ¤" + step5_1: "āĻāĻ–āĻ¨ āĻ…āĻ¨ā§āĻ¯āĻĻā§‡āĻ°āĻ•ā§‡ āĻ…āĻ¨ā§āĻ¸āĻ°āĻŖ āĻ•āĻ°ā§‡ āĻ†āĻĒāĻ¨āĻžāĻ° āĻŸāĻžāĻ‡āĻŽāĻ˛āĻžāĻ‡āĻ¨āĻ•ā§‡ āĻĒā§āĻ°āĻžāĻŖāĻŦāĻ¨ā§āĻ¤ āĻ•āĻ°ā§‡ āĻ¤ā§āĻ˛ā§āĻ¨āĨ¤" + step5_2: "āĻ†āĻĒāĻ¨āĻŋ {featured}-āĻ āĻœāĻ¨āĻĒā§āĻ°āĻŋāĻ¯āĻŧ āĻ¨ā§‹āĻŸāĻ—ā§āĻ˛āĻŋ āĻĻā§‡āĻ–āĻ¤ā§‡ āĻĒāĻžāĻ°ā§‡āĻ¨, āĻ¯āĻžāĻ¤ā§‡ āĻ†āĻĒāĻ¨āĻŋ āĻ¯ā§‡ āĻŦā§āĻ¯āĻ•ā§āĻ¤āĻŋāĻ•ā§‡ āĻĒāĻ›āĻ¨ā§āĻĻ āĻ•āĻ°ā§‡āĻ¨ āĻ¤āĻžāĻ•ā§‡ āĻŦā§‡āĻ›ā§‡ āĻ¨āĻŋāĻ¤ā§‡ āĻāĻŦāĻ‚ āĻ…āĻ¨ā§āĻ¸āĻ°āĻŖ āĻ•āĻ°āĻ¤ā§‡ āĻĒāĻžāĻ°ā§‡āĻ¨, āĻ…āĻĨāĻŦāĻž {explore}-āĻ āĻœāĻ¨āĻĒā§āĻ°āĻŋāĻ¯āĻŧ āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ°āĻ•āĻžāĻ°ā§€āĻĻā§‡āĻ° āĻĻā§‡āĻ–āĻ¤ā§‡ āĻĒāĻžāĻ°ā§‡āĻ¨ā§ˇ" + step5_3: "āĻāĻ•āĻœāĻ¨ āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ°āĻ•āĻžāĻ°ā§€āĻ•ā§‡ āĻ…āĻ¨ā§āĻ¸āĻ°āĻŖ āĻ•āĻ°āĻ¤ā§‡, āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ°āĻ•āĻžāĻ°ā§€āĻ° āĻ†āĻ‡āĻ•āĻ¨ā§‡ āĻ•ā§āĻ˛āĻŋāĻ• āĻ•āĻ°ā§āĻ¨ āĻāĻŦāĻ‚ āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ°āĻ•āĻžāĻ°ā§€āĻ° āĻĒā§ƒāĻˇā§āĻ āĻžāĻ¤ā§‡ \"āĻ…āĻ¨ā§āĻ¸āĻ°āĻŖ āĻ•āĻ°ā§āĻ¨\" āĻŦāĻžāĻŸāĻ¨ā§‡ āĻ•ā§āĻ˛āĻŋāĻ• āĻ•āĻ°ā§āĻ¨āĨ¤" + step5_4: "āĻ¯āĻĻāĻŋ āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ°āĻ•āĻžāĻ°ā§€āĻ° āĻ¨āĻžāĻŽā§‡āĻ° āĻĒāĻžāĻļā§‡ āĻāĻ•āĻŸāĻŋ āĻ˛āĻ• āĻ†āĻ‡āĻ•āĻ¨ āĻĨāĻžāĻ•ā§‡ āĻ¤āĻžāĻšāĻ˛ā§‡ āĻ†āĻĒāĻ¨āĻžāĻ° āĻ…āĻ¨ā§āĻ¸āĻ°āĻŖā§‡āĻ° āĻ…āĻ¨ā§āĻ°ā§‹āĻ§ āĻ—ā§āĻ°āĻšāĻŖ āĻ•āĻ°āĻžāĻ° āĻœāĻ¨ā§āĻ¯ āĻ¤āĻžāĻ°āĻž āĻ•āĻŋāĻ›ā§ āĻ¸āĻŽāĻ¯āĻŧ āĻ¨āĻŋāĻ¤ā§‡ āĻĒāĻžāĻ°ā§‡āĨ¤" + step6_1: "āĻ¸āĻŦāĻ•āĻŋāĻ›ā§ āĻ āĻŋāĻ• āĻĨāĻžāĻ•āĻ˛ā§‡ āĻ†āĻĒāĻ¨āĻŋ āĻŸāĻžāĻ‡āĻŽāĻ˛āĻžāĻ‡āĻ¨ā§‡ āĻ…āĻ¨ā§āĻ¯ āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ°āĻ•āĻžāĻ°ā§€āĻĻā§‡āĻ° āĻ¨ā§‹āĻŸ āĻĻā§‡āĻ–āĻ¤ā§‡ āĻĒāĻžāĻŦā§‡āĻ¨āĨ¤" + step6_2: "āĻ†āĻĒāĻ¨āĻŋ āĻ¸āĻšāĻœā§‡āĻ‡ āĻ†āĻĒāĻ¨āĻžāĻ° āĻĒā§āĻ°āĻ¤āĻŋāĻ•ā§āĻ°āĻŋāĻ¯āĻŧāĻž āĻœāĻžāĻ¨āĻžāĻ¤ā§‡ āĻ…āĻ¨ā§āĻ¯ āĻŦā§āĻ¯āĻ•ā§āĻ¤āĻŋāĻ° āĻ¨ā§‹āĻŸā§‡ \"āĻ°āĻŋāĻ…ā§āĻ¯āĻžāĻ•āĻļāĻ¨\" āĻ¯ā§‹āĻ— āĻ•āĻ°āĻ¤ā§‡ āĻĒāĻžāĻ°ā§‡āĻ¨āĨ¤" + step6_3: "āĻāĻ•āĻŸāĻŋ āĻ°āĻŋāĻ…ā§āĻ¯āĻžāĻ•āĻļāĻ¨ āĻ¯ā§‹āĻ— āĻ•āĻ°āĻ¤ā§‡, āĻ¨ā§‹āĻŸā§‡ \"+\" āĻšāĻŋāĻšā§āĻ¨ā§‡ āĻ•ā§āĻ˛āĻŋāĻ• āĻ•āĻ°ā§āĻ¨ āĻāĻŦāĻ‚ āĻ†āĻĒāĻ¨āĻžāĻ° āĻĒāĻ›āĻ¨ā§āĻĻā§‡āĻ° āĻ°āĻŋāĻ…ā§āĻ¯āĻžāĻ•āĻļāĻ¨ āĻ¨āĻŋāĻ°ā§āĻŦāĻžāĻšāĻ¨ āĻ•āĻ°ā§āĻ¨āĨ¤" + step7_1: "āĻ…āĻ­āĻŋāĻ¨āĻ¨ā§āĻĻāĻ¨! āĻ†āĻĒāĻ¨āĻŋ āĻāĻ–āĻ¨ Misskey-āĻ° āĻĒā§āĻ°āĻžāĻĨāĻŽāĻŋāĻ• āĻŸāĻŋāĻ‰āĻŸā§‹āĻ°āĻŋāĻ¯āĻŧāĻžāĻ˛āĻŸāĻŋ āĻļā§‡āĻˇ āĻ•āĻ°ā§‡āĻ›ā§‡āĻ¨āĨ¤" + step7_2: "āĻ†āĻĒāĻ¨āĻŋ āĻ¯āĻĻāĻŋ Misskey āĻ¸āĻŽā§āĻĒāĻ°ā§āĻ•ā§‡ āĻ†āĻ°āĻ“ āĻœāĻžāĻ¨āĻ¤ā§‡ āĻšāĻžāĻ¨, āĻ¤āĻžāĻšāĻ˛ā§‡ {help} āĻ āĻĻā§‡āĻ–ā§āĻ¨āĨ¤" + step7_3: "āĻāĻ–āĻ¨ Misskey āĻ‰āĻĒāĻ­ā§‹āĻ— āĻ•āĻ°ā§āĻ¨ 🚀" +_2fa: + alreadyRegistered: "āĻ†āĻĒāĻ¨āĻŋ āĻ‡āĻ¤āĻŋāĻŽāĻ§ā§āĻ¯ā§‡ āĻāĻ•āĻŸāĻŋ 2-āĻĢā§āĻ¯āĻžāĻ•ā§āĻŸāĻ° āĻ…āĻĨā§‡āĻ¨āĻŸāĻŋāĻ•ā§‡āĻļāĻ¨ āĻĄāĻŋāĻ­āĻžāĻ‡āĻ¸ āĻ¨āĻŋāĻŦāĻ¨ā§āĻ§āĻ¨ āĻ•āĻ°ā§‡āĻ›ā§‡āĻ¨ā§ˇ" + registerDevice: "āĻ¨āĻ¤ā§āĻ¨ āĻĄāĻŋāĻ­āĻžāĻ‡āĻ¸ āĻ¨āĻŋāĻŦāĻ¨ā§āĻ§āĻ¨ āĻ•āĻ°ā§āĻ¨" + registerKey: "āĻ¸āĻŋāĻ•āĻŋāĻ‰āĻ°āĻŋāĻŸāĻŋ āĻ•ā§€ āĻ¨āĻŋāĻŦāĻ¨ā§āĻ§āĻ¨ āĻ•āĻ°ā§āĻ¨" + step1: "āĻĒā§āĻ°āĻĨāĻŽā§‡, āĻ†āĻĒāĻ¨āĻžāĻ° āĻĄāĻŋāĻ­āĻžāĻ‡āĻ¸ā§‡ {a} āĻŦāĻž {b} āĻāĻ° āĻŽāĻ¤ā§‹ āĻāĻ•āĻŸāĻŋ āĻ…āĻĨā§‡āĻ¨āĻŸāĻŋāĻ•ā§‡āĻļāĻ¨ āĻ…ā§āĻ¯āĻžāĻĒ āĻ‡āĻ¨āĻ¸ā§āĻŸāĻ˛ āĻ•āĻ°ā§āĻ¨ā§ˇ" + step2: "āĻāĻ°āĻĒāĻ°ā§‡, āĻ…ā§āĻ¯āĻžāĻĒā§‡āĻ° āĻ¸āĻžāĻšāĻžāĻ¯ā§āĻ¯ā§‡ āĻĒā§āĻ°āĻĻāĻ°ā§āĻļāĻŋāĻ¤ QR āĻ•ā§‹āĻĄāĻŸāĻŋ āĻ¸ā§āĻ•ā§āĻ¯āĻžāĻ¨ āĻ•āĻ°ā§āĻ¨āĨ¤" + step3: "āĻ…ā§āĻ¯āĻžāĻĒā§‡ āĻĒā§āĻ°āĻĻāĻ°ā§āĻļāĻŋāĻ¤ āĻŸā§‹āĻ•ā§‡āĻ¨āĻŸāĻŋ āĻ˛āĻŋāĻ–ā§āĻ¨ āĻāĻŦāĻ‚ āĻ†āĻĒāĻ¨āĻžāĻ° āĻ•āĻžāĻœ āĻļā§‡āĻˇāĨ¤" + step4: "āĻ†āĻĒāĻ¨āĻžāĻ•ā§‡ āĻāĻ–āĻ¨ āĻĨā§‡āĻ•ā§‡ āĻ˛āĻ— āĻ‡āĻ¨ āĻ•āĻ°āĻžāĻ° āĻ¸āĻŽāĻ¯āĻŧ, āĻāĻ‡āĻ­āĻžāĻŦā§‡ āĻŸā§‹āĻ•ā§‡āĻ¨ āĻ˛āĻŋāĻ–āĻ¤ā§‡ āĻšāĻŦā§‡āĨ¤" + securityKeyInfo: "āĻ†āĻĒāĻ¨āĻŋ āĻāĻ•āĻŸāĻŋ āĻšāĻžāĻ°ā§āĻĄāĻ“āĻ¯āĻŧā§āĻ¯āĻžāĻ° āĻ¸āĻŋāĻ•āĻŋāĻ‰āĻ°āĻŋāĻŸāĻŋ āĻ•ā§€ āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ° āĻ•āĻ°ā§‡ āĻ˛āĻ— āĻ‡āĻ¨ āĻ•āĻ°āĻ¤ā§‡ āĻĒāĻžāĻ°ā§‡āĻ¨ āĻ¯āĻž FIDO2 āĻŦāĻž āĻĄāĻŋāĻ­āĻžāĻ‡āĻ¸ā§‡āĻ° āĻĢāĻŋāĻ™ā§āĻ—āĻžāĻ°āĻĒā§āĻ°āĻŋāĻ¨ā§āĻŸ āĻ¸ā§‡āĻ¨ā§āĻ¸āĻ° āĻŦāĻž āĻĒāĻŋāĻ¨ āĻ¸āĻŽāĻ°ā§āĻĨāĻ¨ āĻ•āĻ°ā§‡ā§ˇ" +_permissions: + "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:notes": "āĻ¨ā§‹āĻŸ āĻ˛āĻŋāĻ–āĻž" + "read:notifications": "āĻŦāĻŋāĻœā§āĻžāĻĒā§āĻ¤āĻŋāĻ—ā§āĻ˛āĻŋ āĻĻā§‡āĻ–ā§āĻ¨" + "write:notifications": "āĻŦāĻŋāĻœā§āĻžāĻĒā§āĻ¤āĻŋ āĻ¨āĻŋā§Ÿā§‡ āĻ•āĻžāĻœ āĻ•āĻ°ā§‡" + "read:reactions": "āĻ°āĻŋāĻ…ā§āĻ¯āĻžāĻ•āĻļāĻ¨āĻ—ā§āĻ˛āĻŋ āĻĻā§‡āĻ–ā§āĻ¨" + "write:reactions": "āĻ°āĻŋāĻ…ā§āĻ¯āĻžāĻ•āĻļāĻ¨āĻ—ā§āĻ˛āĻŋ āĻ¸āĻŽā§āĻĒāĻžāĻĻāĻ¨āĻž āĻ•āĻ°ā§āĻ¨" + "write:votes": "āĻ­ā§‹āĻŸ āĻĻāĻŋāĻ¨" + "read:pages": "āĻ†āĻĒāĻ¨āĻžāĻ° āĻĒā§‡āĻœāĻ—ā§āĻ˛āĻŋ āĻĻā§‡āĻ–ā§āĻ¨" + "write:pages": "āĻĒā§‡āĻœāĻ—ā§āĻ˛āĻŋ āĻ¸āĻŽā§āĻĒāĻžāĻĻāĻ¨āĻž āĻŦāĻž āĻĄāĻŋāĻ˛āĻŋāĻŸ āĻ•āĻ°ā§āĻ¨" + "read:page-likes": "āĻĒā§ƒāĻˇā§āĻ āĻžā§Ÿ āĻĻā§‡ā§ŸāĻž āĻĒāĻ›āĻ¨ā§āĻĻāĻ—ā§āĻ˛āĻŋ āĻĻā§‡āĻ–ā§āĻ¨" + "write:page-likes": "āĻĒā§ƒāĻˇā§āĻ āĻžā§Ÿ āĻĻā§‡ā§ŸāĻž āĻĒāĻ›āĻ¨ā§āĻĻāĻ—ā§āĻ˛āĻŋ āĻ¸āĻŽā§āĻĒāĻžāĻĻāĻ¨āĻž āĻ•āĻ°ā§āĻ¨" + "read:user-groups": "āĻŦā§āĻ¯āĻžāĻŦāĻšāĻžāĻ°āĻ•āĻžāĻ°ā§€ āĻ—ā§āĻ°ā§āĻĒāĻ—ā§āĻ˛āĻŋ āĻĻā§‡āĻ–ā§āĻ¨" + "write:user-groups": "āĻŦā§āĻ¯āĻžāĻŦāĻšāĻžāĻ°āĻ•āĻžāĻ°ā§€ āĻ—ā§āĻ°ā§āĻĒāĻ—ā§āĻ˛āĻŋ āĻ¸āĻŽā§āĻĒāĻžāĻĻāĻ¨āĻž āĻ•āĻ°ā§āĻ¨" + "read:channels": "āĻšā§āĻ¯āĻžāĻ¨ā§‡āĻ˛āĻ—ā§āĻ˛āĻŋ āĻĻā§‡āĻ–ā§āĻ¨" + "write:channels": "āĻšā§āĻ¯āĻžāĻ¨ā§‡āĻ˛āĻ—ā§āĻ˛āĻŋ āĻ¸āĻŽā§āĻĒāĻžāĻĻāĻ¨āĻž āĻ•āĻ°ā§āĻ¨" + "read:gallery": "āĻ—ā§āĻ¯āĻžāĻ˛āĻžāĻ°ā§€ āĻĻā§‡āĻ–ā§āĻ¨" + "write:gallery": "āĻ—ā§āĻ¯āĻžāĻ˛āĻžāĻ°ā§€ āĻ¸āĻŽā§āĻĒāĻžāĻĻāĻ¨āĻž āĻ•āĻ°ā§āĻ¨" + "read:gallery-likes": "āĻ—ā§āĻ¯āĻžāĻ˛āĻžāĻ°ā§€āĻ° āĻĒāĻ›āĻ¨ā§āĻĻāĻ—ā§āĻ˛āĻŋ āĻĻā§‡āĻ–ā§āĻ¨" + "write:gallery-likes": "āĻ—ā§āĻ¯āĻžāĻ˛āĻžāĻ°ā§€āĻ° āĻĒāĻ›āĻ¨ā§āĻĻāĻ—ā§āĻ˛āĻŋ āĻ¸āĻŽā§āĻĒāĻžāĻĻāĻ¨āĻž āĻ•āĻ°ā§āĻ¨" +_auth: + shareAccess: "\"{name}\" āĻ•ā§‡ āĻ…ā§āĻ¯āĻžāĻ•āĻžāĻ‰āĻ¨ā§āĻŸā§‡āĻ° āĻ…ā§āĻ¯āĻžāĻ•ā§āĻ¸ā§‡āĻ¸ āĻĻāĻŋāĻŦā§‡āĻ¨?" + shareAccessAsk: "āĻ…ā§āĻ¯āĻžāĻĒā§āĻ˛āĻŋāĻ•ā§‡āĻļāĻ¨āĻŸāĻŋāĻ•ā§‡ āĻ…ā§āĻ¯āĻžāĻ•āĻžāĻ‰āĻ¨ā§āĻŸā§‡āĻ° āĻ…ā§āĻ¯āĻžāĻ•ā§āĻ¸ā§‡āĻ¸ āĻĻāĻŋāĻŦā§‡āĻ¨?" + permissionAsk: "āĻāĻ‡ āĻ…ā§āĻ¯āĻžāĻĒā§āĻ˛āĻŋāĻ•ā§‡āĻļāĻ¨āĻŸāĻŋ āĻ¨āĻŋāĻŽā§āĻ¨āĻ˛āĻŋāĻ–āĻŋāĻ¤ āĻ…āĻ¨ā§āĻŽāĻ¤āĻŋ āĻšāĻžāĻ‡" + pleaseGoBack: "āĻĻā§ŸāĻž āĻ•āĻ°ā§‡ āĻ…ā§āĻ¯āĻžāĻĒā§āĻ˛āĻŋāĻ•ā§‡āĻļāĻ¨ā§‡ āĻĢāĻŋāĻ°ā§‡ āĻ¯āĻžāĻ¨" + callback: "āĻ…ā§āĻ¯āĻžāĻĒā§āĻ˛āĻŋāĻ•ā§‡āĻļāĻ¨ā§‡ āĻĢāĻŋāĻ°ā§‡ āĻ¯āĻžāĻšā§āĻ›āĻŋ" + denied: "āĻĒā§āĻ°āĻŦā§‡āĻļ āĻ¨āĻŋāĻˇā§‡āĻ§" +_antennaSources: + all: "āĻ¸āĻ•āĻ˛ āĻ¨ā§‹āĻŸ" + homeTimeline: "āĻ†āĻĒāĻ¨āĻŋ āĻ…āĻ¨ā§āĻ¸āĻ°āĻŖ āĻ•āĻ°āĻ›ā§‡āĻ¨, āĻāĻŽāĻ¨ āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ°āĻ•āĻžāĻ°ā§€āĻĻā§‡āĻ° āĻ¨ā§‹āĻŸ" + users: "āĻāĻ• āĻŦāĻž āĻāĻ•āĻžāĻ§āĻŋāĻ• āĻ¨āĻŋāĻ°ā§āĻĻāĻŋāĻˇā§āĻŸ āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ°āĻ•āĻžāĻ°ā§€āĻ° āĻ¨ā§‹āĻŸ" + userList: "āĻ¨āĻŋāĻ°ā§āĻĻāĻŋāĻˇā§āĻŸ āĻ¤āĻžāĻ˛āĻŋāĻ•āĻžāĻ¯āĻŧ āĻ¨āĻžāĻŽ āĻĨāĻžāĻ•āĻž āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ°āĻ•āĻžāĻ°ā§€āĻĻā§‡āĻ° āĻ¨ā§‹āĻŸ" + userGroup: "āĻ¨āĻŋāĻ°ā§āĻĻāĻŋāĻˇā§āĻŸ āĻ—ā§āĻ°ā§āĻĒā§‡ āĻĨāĻžāĻ•āĻž āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ°āĻ•āĻžāĻ°ā§€āĻĻā§‡āĻ° āĻ¨ā§‹āĻŸ" +_weekday: + sunday: "āĻ°āĻŦāĻŋāĻŦāĻžāĻ°" + monday: "āĻ¸ā§‹āĻŽāĻŦāĻžāĻ°" + tuesday: "āĻŽāĻ™ā§āĻ—āĻ˛āĻŦāĻžāĻ°" + wednesday: "āĻŦā§āĻ§āĻŦāĻžāĻ°" + thursday: "āĻŦā§ƒāĻšāĻ¸ā§āĻĒāĻ¤āĻŋāĻŦāĻžāĻ°" + friday: "āĻļā§āĻ•ā§āĻ°āĻŦāĻžāĻ°" + saturday: "āĻļāĻ¨āĻŋāĻŦāĻžāĻ°" +_widgets: + memo: "āĻ¸ā§āĻŸāĻŋāĻ•āĻŋ āĻ¨ā§‹āĻŸ" + notifications: "āĻŦāĻŋāĻœā§āĻžāĻĒā§āĻ¤āĻŋ" + timeline: "āĻŸāĻžāĻ‡āĻŽāĻ˛āĻžāĻ‡āĻ¨" + calendar: "āĻ•ā§āĻ¯āĻžāĻ˛ā§‡āĻ¨ā§āĻĄāĻžāĻ°" + trends: "āĻŦāĻ°ā§āĻ¤āĻŽāĻžāĻ¨ā§‡ āĻœāĻ¨āĻĒā§āĻ°āĻŋā§Ÿ" + clock: "āĻ˜āĻĄāĻŧāĻŋ" + rss: "RSS āĻ°āĻŋāĻĄāĻžāĻ°" + activity: "āĻ•āĻžāĻ°ā§āĻ¯āĻ•āĻ˛āĻžāĻĒ" + photos: "āĻĢāĻŸā§‹āĻ—ā§āĻ˛āĻŋ" + digitalClock: "āĻĄāĻŋāĻœāĻŋāĻŸāĻžāĻ˛ āĻ˜ā§œāĻŋ" + federation: "āĻĢā§‡āĻĄāĻŋāĻ­āĻžāĻ°ā§āĻ¸" + postForm: "āĻ¨ā§‹āĻŸ āĻ˛āĻŋāĻ–ā§āĻ¨" + slideshow: "āĻ¸ā§āĻ˛āĻžāĻ‡āĻĄāĻļā§‹" + button: "āĻŦāĻžāĻŸāĻ¨" + onlineUsers: "āĻ…āĻ¨āĻ˛āĻžāĻ‡āĻ¨ā§‡ āĻĨāĻžāĻ•āĻž āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ°āĻ•āĻžāĻ°ā§€āĻ—āĻŖ" + jobQueue: "āĻœāĻŦ āĻ•āĻŋāĻ‰" + serverMetric: "āĻ¸āĻžāĻ°ā§āĻ­āĻžāĻ° āĻŽā§‡āĻŸā§āĻ°āĻŋāĻ•ā§āĻ¸" + aiscript: "AiScript āĻ•āĻ¨āĻ¸ā§‹āĻ˛" + aichan: "āĻ†āĻ‡ āĻšāĻžāĻ¨" +_cw: + hide: "āĻ˛ā§āĻ•āĻžāĻ¨" + show: "āĻ†āĻ°āĻ“ āĻĻā§‡āĻ–ā§āĻ¨" + chars: "{count} āĻŸāĻŋ āĻ…āĻ•ā§āĻˇāĻ°" + files: "{count} āĻŸāĻŋ āĻĢāĻžāĻ‡āĻ˛" +_poll: + noOnlyOneChoice: "āĻ¸āĻ°ā§āĻŦāĻ¨āĻŋāĻŽā§āĻ¨ 2 āĻŸāĻŋ āĻ…āĻĒāĻļāĻ¨ āĻŦā§‡āĻ›ā§‡ āĻ¨āĻŋāĻ¤ā§‡ āĻšāĻŦā§‡" + choiceN: "āĻŦāĻŋāĻ•āĻ˛ā§āĻĒāĻ—ā§āĻ˛āĻŋ {n}" + noMore: "āĻ†āĻĒāĻ¨āĻŋ āĻ†āĻ° āĻ•ā§‹āĻ¨ āĻŦāĻŋāĻ•āĻ˛ā§āĻĒ āĻ¯ā§‹āĻ— āĻ•āĻ°āĻ¤ā§‡ āĻĒāĻžāĻ°āĻŦā§‡āĻ¨ āĻ¨āĻž" + canMultipleVote: "āĻāĻ•āĻžāĻ§āĻŋāĻ• āĻŦāĻŋāĻ•āĻ˛ā§āĻĒ āĻŦāĻžāĻ›āĻžāĻ‡ āĻ•āĻ°āĻž āĻ¯āĻžāĻŦā§‡" + expiration: "āĻĒā§‹āĻ˛ā§‡āĻ° āĻ¸āĻŽāĻ¯āĻŧāĻ¸ā§€āĻŽāĻž" + infinite: "āĻ…āĻ¨āĻŋāĻ°ā§āĻĻāĻŋāĻˇā§āĻŸ" + at: "āĻļā§‡āĻˇ āĻšāĻŦā§‡" + after: "āĻļā§‡āĻˇ āĻšāĻŦā§‡" + deadlineDate: "āĻļā§‡āĻˇ āĻšāĻ“ā§ŸāĻžāĻ° āĻ¤āĻžāĻ°āĻŋāĻ–" + deadlineTime: "āĻ˜āĻŖā§āĻŸāĻž" + duration: "āĻŦā§āĻ¯āĻžāĻĒā§āĻ¤āĻŋāĻ•āĻžāĻ˛" + votesCount: "{n} āĻŸāĻŋ āĻ­ā§‹āĻŸ" + totalVotes: "āĻ¸āĻ°ā§āĻŦāĻŽā§‹āĻŸ {n} āĻŸāĻŋ āĻ­ā§‹āĻŸ" + vote: "āĻ­ā§‹āĻŸ āĻĻāĻŋāĻ¨" + showResult: "āĻ°ā§‡āĻœāĻžāĻ˛ā§āĻŸ āĻĻā§‡āĻ–āĻžāĻ¨" + voted: "āĻ­ā§‹āĻŸ āĻĻāĻŋāĻ¯āĻŧā§‡āĻ›ā§‡āĻ¨" + closed: "āĻļā§‡āĻˇ āĻšā§Ÿā§‡ āĻ—ā§‡āĻ›ā§‡" + remainingDays: "āĻ†āĻ° {d} āĻĻāĻŋāĻ¨ {h} āĻ˜āĻŖā§āĻŸāĻž āĻŦāĻžāĻ•āĻŋ āĻ†āĻ›ā§‡" + remainingHours: "āĻ†āĻ° {h} āĻ˜āĻŖā§āĻŸāĻž {m} āĻŽāĻŋāĻ¨āĻŋāĻŸ āĻŦāĻžāĻ•āĻŋ āĻ†āĻ›ā§‡" + remainingMinutes: "āĻ†āĻ° āĻŦāĻžāĻ•āĻŋ āĻ†āĻ›ā§‡ {m} āĻŽāĻŋāĻ¨āĻŋāĻŸ {s} āĻ¸ā§‡āĻ•ā§‡āĻ¨ā§āĻĄ" + remainingSeconds: "āĻ†āĻ° āĻŦāĻžāĻ•āĻŋ āĻ†āĻ›ā§‡ {s} āĻ¸ā§‡āĻ•ā§‡āĻ¨ā§āĻĄ" +_visibility: + public: "āĻ¸āĻ°ā§āĻŦāĻœāĻ¨ā§€āĻ¨" + publicDescription: "āĻ¸āĻŦāĻžāĻ‡ āĻ†āĻĒāĻ¨āĻžāĻ° āĻ¨ā§‹āĻŸāĻ—ā§āĻ˛āĻŋ āĻĻā§‡āĻ–āĻ¤ā§‡ āĻĒāĻžāĻŦā§‡" + home: "āĻŽā§‚āĻ˛ āĻĒāĻžāĻ¤āĻž" + homeDescription: "āĻļā§āĻ§ā§āĻŽāĻžāĻ¤ā§āĻ° āĻšā§‹āĻŽ āĻŸāĻžāĻ‡āĻŽāĻ˛āĻžāĻ‡āĻ¨ā§‡ āĻ†āĻĒāĻ¨āĻžāĻ° āĻ¨ā§‹āĻŸāĻ—ā§āĻ˛āĻŋ āĻĒā§‹āĻ¸ā§āĻŸ āĻ•āĻ°ā§āĻ¨" + followers: "āĻ…āĻ¨ā§āĻ¸āĻ°āĻŖāĻ•āĻžāĻ°ā§€" + followersDescription: "āĻļā§āĻ§ā§āĻŽāĻžāĻ¤ā§āĻ° āĻ†āĻĒāĻ¨āĻžāĻ° āĻ…āĻ¨ā§āĻ¸āĻ°āĻŖāĻ•āĻžāĻ°ā§€āĻĻā§‡āĻ° āĻ¨āĻŋāĻ•āĻŸ āĻĒā§‹āĻ¸ā§āĻŸ āĻ•āĻ°ā§āĻ¨" + specified: "āĻĄāĻžāĻ‡āĻ°ā§‡āĻ•ā§āĻŸ āĻ¨ā§‹āĻŸ" + specifiedDescription: "āĻļā§āĻ§ā§āĻŽāĻžāĻ¤ā§āĻ° āĻ¨āĻŋāĻ°ā§āĻĻāĻŋāĻˇā§āĻŸ āĻŦā§āĻ¯āĻžāĻŦāĻšāĻžāĻ°āĻ•āĻžāĻ°ā§€āĻ° āĻ¨āĻŋāĻ•āĻŸ āĻĒāĻžāĻ āĻžāĻ¨" + localOnly: "āĻļā§āĻ§ā§āĻŽāĻžāĻ¤ā§āĻ° āĻ˛ā§‹āĻ•āĻžāĻ˛" + localOnlyDescription: "āĻ°āĻŋāĻŽā§‹āĻŸ āĻŦā§āĻ¯āĻžāĻŦāĻšāĻžāĻ°āĻ•āĻžāĻ°ā§€āĻĻā§‡āĻ° āĻ¨āĻŋāĻ•āĻŸ āĻĻā§ƒāĻļā§āĻ¯āĻŽāĻžāĻ¨ āĻ¨ā§Ÿ" +_postForm: + replyPlaceholder: "āĻ¨ā§‹āĻŸāĻŸāĻŋāĻ° āĻœāĻŦāĻžāĻŦ āĻĻāĻŋāĻ¨..." + quotePlaceholder: "āĻ¨ā§‹āĻŸāĻŸāĻŋāĻ•ā§‡ āĻ‰āĻĻā§āĻ§ā§ƒāĻ¤ āĻ•āĻ°ā§āĻ¨..." + channelPlaceholder: "āĻšā§āĻ¯āĻžāĻ¨ā§‡āĻ˛ā§‡ āĻĒā§‹āĻ¸ā§āĻŸ āĻ•āĻ°ā§āĻ¨..." + _placeholders: + a: "āĻ†āĻĒāĻ¨āĻŋ āĻāĻ–āĻ¨ āĻ•āĻŋ āĻ•āĻ°āĻ›ā§‡āĻ¨?" + b: "āĻ†āĻĒāĻ¨āĻžāĻ° āĻ†āĻļā§‡ āĻĒāĻžāĻļā§‡ āĻ•āĻŋ āĻšāĻšā§āĻ›ā§‡?" + c: "āĻ†āĻĒāĻ¨āĻŋ āĻ•āĻŋ āĻ­āĻžāĻŦāĻ›ā§‡āĻ¨?" + d: "āĻ†āĻĒāĻ¨āĻŋ āĻ•āĻŋ āĻŦāĻ˛āĻ¤ā§‡ āĻšāĻžāĻ¨?" + e: "āĻ˛ā§‡āĻ–āĻž āĻļā§āĻ°ā§ āĻ•āĻ°ā§āĻ¨..." + f: "āĻ†āĻĒāĻ¨āĻžāĻ° āĻ˛ā§‡āĻ–āĻžāĻ° āĻœāĻ¨ā§āĻ¯ āĻ…āĻĒā§‡āĻ•ā§āĻˇāĻž āĻ•āĻ°āĻ›āĻŋ..." +_profile: + name: "āĻ¨āĻžāĻŽ" + username: "āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ°āĻ•āĻžāĻ°ā§€āĻ° āĻ¨āĻžāĻŽ" + description: "āĻ†āĻĒāĻ¨āĻžāĻ° āĻ¸āĻŽā§āĻĒāĻ°ā§āĻ•ā§‡" + youCanIncludeHashtags: "āĻšā§āĻ¯āĻžāĻļāĻŸā§āĻ¯āĻžāĻ— āĻ…āĻ¨ā§āĻ¤āĻ°ā§āĻ­ā§āĻ•ā§āĻ¤ āĻ•āĻ°āĻž āĻ¯ā§‡āĻ¤ā§‡ āĻĒāĻžāĻ°ā§‡āĨ¤" + metadata: "āĻ…āĻ¤āĻŋāĻ°āĻŋāĻ•ā§āĻ¤ āĻ¤āĻĨā§āĻ¯" + metadataEdit: "āĻ…āĻ¤āĻŋāĻ°āĻŋāĻ•ā§āĻ¤ āĻ¤āĻĨā§āĻ¯ āĻ¸āĻŽā§āĻĒāĻžāĻĻāĻ¨āĻž āĻ•āĻ°ā§āĻ¨" + metadataDescription: "āĻ†āĻĒāĻ¨āĻŋ āĻ†āĻĒāĻ¨āĻžāĻ° āĻĒā§āĻ°ā§‹āĻĢāĻžāĻ‡āĻ˛ā§‡ āĻāĻ•āĻŸāĻŋ āĻŸā§‡āĻŦāĻŋāĻ˛ āĻšāĻŋāĻ¸āĻžāĻŦā§‡ āĻšāĻžāĻ°āĻŸāĻŋ āĻ…āĻ¤āĻŋāĻ°āĻŋāĻ•ā§āĻ¤ āĻ¤āĻĨā§āĻ¯ āĻĻā§‡āĻ–āĻžāĻ¤ā§‡ āĻĒāĻžāĻ°ā§‡āĻ¨āĨ¤" + metadataLabel: "āĻ˛ā§‡āĻŦā§‡āĻ˛" + metadataContent: "āĻŦāĻŋāĻˇāĻ¯āĻŧāĻŦāĻ¸ā§āĻ¤ā§" + changeAvatar: "āĻ…ā§āĻ¯āĻžāĻ­āĻžāĻŸāĻžāĻ° āĻĒāĻ°āĻŋāĻŦāĻ°ā§āĻ¤āĻ¨ āĻ•āĻ°ā§āĻ¨" + changeBanner: "āĻŦā§āĻ¯āĻžāĻ¨āĻžāĻ° āĻĒāĻ°āĻŋāĻŦāĻ°ā§āĻ¤āĻ¨ āĻ•āĻ°ā§āĻ¨" +_exportOrImport: + allNotes: "āĻ¸āĻ•āĻ˛ āĻ¨ā§‹āĻŸ" + followingList: "āĻ…āĻ¨ā§āĻ¸āĻ°āĻŖ āĻ•āĻ°āĻž āĻšāĻšā§āĻ›ā§‡" + muteList: "āĻŽāĻŋāĻ‰āĻŸ" + blockingList: "āĻŦā§āĻ˛āĻ•" + userLists: "āĻ˛āĻŋāĻ¸ā§āĻŸ" + excludeMutingUsers: "āĻŽāĻŋāĻ‰āĻŸāĻ•ā§ƒāĻ¤ āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ°āĻ•āĻžāĻ°ā§€āĻĻā§‡āĻ° āĻŦāĻžāĻĻ āĻĻāĻŋāĻ¨" + excludeInactiveUsers: "āĻ…āĻŦā§āĻ¯āĻžāĻŦāĻšā§ƒāĻ¤ āĻ…ā§āĻ¯āĻžāĻ•āĻžāĻ‰āĻ¨ā§āĻŸ āĻŦāĻžāĻĻ āĻĻāĻŋāĻ¨" +_charts: + federation: "āĻĢā§‡āĻĄāĻŋāĻ­āĻžāĻ°ā§āĻ¸" + apRequest: "āĻ…āĻ¨ā§āĻ°ā§‹āĻ§āĻ¸āĻŽā§‚āĻš" + usersIncDec: "āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ°āĻ•āĻžāĻ°ā§€āĻĻā§‡āĻ° āĻ¸āĻ‚āĻ–ā§āĻ¯āĻžāĻ° āĻĒāĻ°āĻŋāĻŦāĻ°ā§āĻ¤āĻ¨" + usersTotal: "āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ°āĻ•āĻžāĻ°ā§€āĻĻā§‡āĻ° āĻ¸āĻ‚āĻ–ā§āĻ¯āĻž" + activeUsers: "āĻ¸āĻ•ā§āĻ°āĻŋā§Ÿ āĻŦā§āĻ¯āĻžāĻŦāĻšāĻžāĻ°āĻ•āĻžāĻ°ā§€" + notesIncDec: "āĻ¨ā§‹āĻŸā§‡āĻ° āĻ¸āĻ‚āĻ–ā§āĻ¯āĻžāĻ° āĻĒāĻ°āĻŋāĻŦāĻ°ā§āĻ¤āĻ¨" + localNotesIncDec: "āĻ˛ā§‹āĻ•āĻžāĻ˛ āĻ¨ā§‹āĻŸā§‡āĻ° āĻ¸āĻ‚āĻ–ā§āĻ¯āĻžāĻ° āĻĒāĻ°āĻŋāĻŦāĻ°ā§āĻ¤āĻ¨" + remoteNotesIncDec: "āĻ°āĻŋāĻŽā§‹āĻŸ āĻ¨ā§‹āĻŸā§‡āĻ° āĻ¸āĻ‚āĻ–ā§āĻ¯āĻžāĻ° āĻĒāĻ°āĻŋāĻŦāĻ°ā§āĻ¤āĻ¨" + notesTotal: "āĻ¨ā§‹āĻŸā§‡āĻ° āĻ¸āĻ‚āĻ–ā§āĻ¯āĻž" + filesIncDec: "āĻĢāĻžāĻ‡āĻ˛ā§‡āĻ° āĻ¸āĻ‚āĻ–ā§āĻ¯āĻžāĻ° āĻĒāĻ°āĻŋāĻŦāĻ°ā§āĻ¤āĻ¨" + filesTotal: "āĻĢāĻžāĻ‡āĻ˛ā§‡āĻ° āĻ¸āĻ‚āĻ–ā§āĻ¯āĻž" + storageUsageIncDec: "āĻ¸ā§āĻŸā§‹āĻ°ā§‡āĻœā§‡āĻ° āĻŦā§āĻ¯āĻžāĻŦāĻšāĻžāĻ°ā§‡āĻ° āĻĒāĻ°āĻŋāĻŦāĻ°ā§āĻ¤āĻ¨" + storageUsageTotal: "āĻŽā§‹āĻŸ āĻ¸ā§āĻŸā§‹āĻ°ā§‡āĻœā§‡āĻ° āĻŦā§āĻ¯āĻžāĻŦāĻšāĻžāĻ°" +_instanceCharts: + requests: "āĻ…āĻ¨ā§āĻ°ā§‹āĻ§āĻ¸āĻŽā§‚āĻš" + users: "āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ°āĻ•āĻžāĻ°ā§€āĻĻā§‡āĻ° āĻ¸āĻ‚āĻ–ā§āĻ¯āĻžāĻ° āĻĒāĻ°āĻŋāĻŦāĻ°ā§āĻ¤āĻ¨" + usersTotal: "āĻ•ā§āĻ°āĻŽāĻŦāĻ°ā§āĻ§āĻŽāĻžāĻ¨ āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ°āĻ•āĻžāĻ°ā§€āĻĻā§‡āĻ° āĻ¸āĻ‚āĻ–ā§āĻ¯āĻž" + notes: "āĻ¨ā§‹āĻŸā§‡āĻ° āĻ¸āĻ‚āĻ–ā§āĻ¯āĻžāĻ° āĻĒāĻ°āĻŋāĻŦāĻ°ā§āĻ¤āĻ¨" + notesTotal: "āĻ•ā§āĻ°āĻŽāĻŦāĻ°ā§āĻ§āĻŽāĻžāĻ¨ āĻ¨ā§‹āĻŸā§‡āĻ° āĻ¸āĻ‚āĻ–ā§āĻ¯āĻž" + ff: "āĻ…āĻ¨ā§āĻ¸āĻ°āĻŖāĻ•āĻžāĻ°ā§€ / āĻ…āĻ¨ā§āĻ¸āĻ°āĻŖ āĻ•āĻ°āĻž āĻŦā§āĻ¯āĻžāĻŦāĻšāĻžāĻ°āĻ•āĻžāĻ°ā§€āĻĻā§‡āĻ° āĻ¸āĻ‚āĻ–ā§āĻ¯āĻžāĻ° āĻĒāĻ°āĻŋāĻŦāĻ°ā§āĻ¤āĻ¨" + ffTotal: "āĻ…āĻ¨ā§āĻ¸āĻ°āĻŖāĻ•āĻžāĻ°ā§€ / āĻ…āĻ¨ā§āĻ¸āĻ°āĻŖ āĻ•āĻ°āĻž āĻŦā§āĻ¯āĻžāĻŦāĻšāĻžāĻ°āĻ•āĻžāĻ°ā§€āĻĻā§‡āĻ° āĻ•ā§āĻ°āĻŽāĻŦāĻ°ā§āĻ§āĻŽāĻžāĻ¨ āĻ¸āĻ‚āĻ–ā§āĻ¯āĻž" + cacheSize: "āĻ•ā§āĻ¯āĻžāĻļ āĻ¸āĻžāĻ‡āĻœā§‡āĻ° āĻĒāĻ°āĻŋāĻŦāĻ°ā§āĻ¤āĻ¨" + cacheSizeTotal: "āĻ•ā§āĻ°āĻŽāĻŦāĻ°ā§āĻ§āĻŽāĻžāĻ¨ āĻ•ā§āĻ¯āĻžāĻļ āĻ¸āĻžāĻ‡āĻœ" + files: "āĻĢāĻžāĻ‡āĻ˛ā§‡āĻ° āĻ¸āĻ‚āĻ–ā§āĻ¯āĻžāĻ° āĻĒāĻ°āĻŋāĻŦāĻ°ā§āĻ¤āĻ¨" + filesTotal: "āĻ•ā§āĻ°āĻŽāĻŦāĻ°ā§āĻ§āĻŽāĻžāĻ¨ āĻĢāĻžāĻ‡āĻ˛ā§‡āĻ° āĻ¸āĻ‚āĻ–ā§āĻ¯āĻž" +_timelines: + home: "āĻŽā§‚āĻ˛ āĻĒāĻžāĻ¤āĻž" + local: "āĻ¸ā§āĻĨāĻžāĻ¨ā§€āĻ¯āĻŧ" + social: "āĻ¸āĻžāĻŽāĻžāĻœāĻŋāĻ•" + global: "āĻ—ā§āĻ˛ā§‹āĻŦāĻžāĻ˛" +_pages: + newPage: "āĻ¨āĻ¤ā§āĻ¨ āĻĒā§ƒāĻˇā§āĻ āĻž āĻŦāĻžāĻ¨āĻžāĻ¨" + editPage: "āĻĒā§ƒāĻˇā§āĻ āĻžāĻŸāĻŋ āĻ¸āĻŽā§āĻĒāĻžāĻĻāĻ¨āĻž āĻ•āĻ°ā§āĻ¨" + readPage: "āĻ‰ā§ŽāĻ¸ āĻĻā§‡āĻ–āĻ›ā§‡āĻ¨" + created: "āĻĒā§ƒāĻˇā§āĻ āĻž āĻ¤ā§ˆāĻ°āĻŋ āĻ•āĻ°āĻž āĻšā§Ÿā§‡āĻ›ā§‡" + updated: "āĻĒā§ƒāĻˇā§āĻ āĻž āĻ¸āĻŽā§āĻĒāĻžāĻĻāĻ¨āĻž āĻ•āĻ°āĻž āĻšā§Ÿā§‡āĻ›ā§‡" + deleted: "āĻĒā§ƒāĻˇā§āĻ āĻž āĻŽā§āĻ›ā§‡ āĻĢā§‡āĻ˛āĻž āĻšā§Ÿā§‡āĻ›ā§‡" + pageSetting: "āĻĒā§ƒāĻˇā§āĻ āĻžāĻ° āĻ¸ā§‡āĻŸāĻŋāĻ‚āĻ¸" + nameAlreadyExists: "āĻĒā§ƒāĻˇā§āĻ āĻžāĻ° URLāĻŸāĻŋ āĻ‡āĻ¤āĻŋāĻŽāĻ§ā§āĻ¯ā§‡āĻ‡ āĻŦā§āĻ¯āĻžāĻŦāĻšāĻžāĻ° āĻ•āĻ°āĻž āĻšā§Ÿā§‡āĻ›ā§‡" + invalidNameTitle: "āĻĒā§ƒāĻˇā§āĻ āĻžāĻ° URL āĻ…āĻŦā§ˆāĻ§" + invalidNameText: "āĻ¨āĻŋāĻļā§āĻšāĻŋāĻ¤ āĻ•āĻ°ā§āĻ¨ āĻ¯ā§‡ āĻāĻŸāĻŋ āĻĢāĻžāĻāĻ•āĻž āĻ¨āĻ¯āĻŧ" + editThisPage: "āĻĒā§ƒāĻˇā§āĻ āĻžāĻŸāĻŋ āĻ¸āĻŽā§āĻĒāĻžāĻĻāĻ¨āĻž āĻ•āĻ°ā§āĻ¨" + viewSource: "āĻ‰ā§ŽāĻ¸ āĻĻā§‡āĻ–ā§āĻ¨" + viewPage: "āĻ†āĻĒāĻ¨āĻžāĻ° āĻĒā§‡āĻœāĻ—ā§āĻ˛āĻŋ āĻĻā§‡āĻ–ā§āĻ¨" + like: "āĻĒāĻ›āĻ¨ā§āĻĻ" + unlike: "āĻĒāĻ›āĻ¨ā§āĻĻ āĻ¸āĻ°āĻžāĻ¨" + my: "āĻ†āĻŽāĻžāĻ° āĻĒā§ƒāĻˇā§āĻ āĻžāĻ—ā§āĻ˛āĻŋ" + liked: "āĻĒāĻ›āĻ¨ā§āĻĻ āĻ•āĻ°āĻž āĻĒā§ƒāĻˇā§āĻ āĻžāĻ—ā§āĻ˛āĻŋ" + featured: "āĻœāĻ¨āĻĒā§āĻ°āĻŋāĻ¯āĻŧ" + inspector: "āĻ‡āĻ¨āĻŋāĻ¸ā§āĻĒā§‡āĻ•ā§āĻŸāĻ°" + contents: "āĻŦāĻŋāĻˇā§ŸāĻŦāĻ¸ā§āĻ¤ā§" + content: "āĻĒā§ƒāĻˇā§āĻ āĻžāĻ° āĻŦā§āĻ˛āĻ•" + variables: "āĻšāĻ˛āĻ•āĻ—ā§āĻ˛āĻŋ" + title: "āĻļāĻŋāĻ°ā§‹āĻ¨āĻžāĻŽ" + url: "āĻĒā§ƒāĻˇā§āĻ āĻžāĻ° URL" + summary: "āĻĒā§ƒāĻˇā§āĻ āĻžāĻ° āĻŦāĻ°ā§āĻŖāĻ¨āĻž" + alignCenter: "āĻ¸ā§‡āĻ¨ā§āĻŸāĻžāĻ°" + hideTitleWhenPinned: "āĻĒāĻŋāĻ¨ āĻ•āĻ°āĻž āĻšāĻ˛ā§‡ āĻŸāĻžāĻ‡āĻŸā§‡āĻ˛ āĻ˛ā§āĻ•āĻžāĻ¨" + font: "āĻĢāĻ¨ā§āĻŸ" + fontSerif: "āĻ¸ā§‡āĻ°āĻŋāĻĢ" + fontSansSerif: "āĻ¸ā§āĻ¯āĻžāĻ¨ā§āĻ¸ āĻ¸ā§‡āĻ°āĻŋāĻĢ" + eyeCatchingImageSet: "āĻĨāĻžāĻŽā§āĻŦāĻ¨ā§‡āĻ‡āĻ˛ āĻ¸ā§‡āĻŸ āĻ•āĻ°ā§āĻ¨" + eyeCatchingImageRemove: "āĻĨāĻžāĻŽā§āĻŦāĻ¨ā§‡āĻ‡āĻ˛ āĻ¸āĻ°āĻžāĻ¨" + chooseBlock: "āĻŦā§āĻ˛āĻ• āĻ¯ā§‹āĻ— āĻ•āĻ°ā§āĻ¨" + selectType: "āĻ§āĻ°āĻ¨ āĻ¨āĻŋāĻ°ā§āĻŦāĻžāĻšāĻ¨ āĻ•āĻ°ā§āĻ¨" + enterVariableName: "āĻšāĻ˛āĻ•ā§‡āĻ° āĻ¨āĻžāĻŽ āĻ˛āĻŋāĻ–ā§āĻ¨" + variableNameIsAlreadyUsed: "āĻšāĻ˛āĻ•ā§‡āĻ° āĻ¨āĻžāĻŽāĻŸāĻŋ āĻ‡āĻ¤āĻŋāĻĒā§‚āĻ°ā§āĻŦā§‡ āĻŦā§āĻ¯āĻžāĻŦāĻšā§ƒāĻ¤ āĻšā§Ÿā§‡āĻ›ā§‡" + contentBlocks: "āĻŦāĻŋāĻˇā§ŸāĻŦāĻ¸ā§āĻ¤ā§" + inputBlocks: "āĻ‡āĻ¨āĻĒā§āĻŸ" + specialBlocks: "āĻŦāĻŋāĻļā§‡āĻˇ" + blocks: + text: "āĻ˛ā§‡āĻ–āĻž" + textarea: "āĻŸā§‡āĻ•ā§āĻ¸āĻŸ āĻāĻ°āĻŋā§ŸāĻž" + 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: + id: "āĻ¨ā§‹āĻŸ ID" + idDescription: "āĻ†āĻĒāĻ¨āĻŋ āĻāĻ° āĻŦāĻĻāĻ˛ā§‡ āĻ¨ā§‹āĻŸā§‡āĻ° URL āĻĒā§‡āĻ¸ā§āĻŸ āĻ•āĻ°āĻ¤ā§‡ āĻĒāĻžāĻ°ā§‡āĻ¨." + detailed: "āĻŦāĻŋāĻ¸ā§āĻ¤āĻžāĻ°āĻŋāĻ¤ āĻĻā§‡āĻ–ā§āĻ¨" + switch: "āĻ¸ā§āĻ‡āĻš" + _switch: + name: "āĻšāĻ˛āĻ•ā§‡āĻ° āĻ¨āĻžāĻŽ" + text: "āĻļāĻŋāĻ°ā§‹āĻ¨āĻžāĻŽ" + default: "āĻĄāĻŋāĻĢāĻ˛ā§āĻŸ āĻŽāĻžāĻ¨" + counter: "āĻ•āĻžāĻ‰āĻ¨ā§āĻŸāĻžāĻ°" + _counter: + name: "āĻšāĻ˛āĻ•ā§‡āĻ° āĻ¨āĻžāĻŽ" + text: "āĻļāĻŋāĻ°ā§‹āĻ¨āĻžāĻŽ" + inc: "āĻāĻ­āĻžāĻŦā§‡ āĻŽāĻžāĻ¨ āĻŦāĻžāĻĄāĻŧāĻžāĻ¨" + _button: + text: "āĻļāĻŋāĻ°ā§‹āĻ¨āĻžāĻŽ" + colored: "āĻ°āĻ™ā§āĻ—āĻŋāĻ¨" + action: "āĻŦāĻžāĻŸāĻ¨ā§‡ āĻ•ā§āĻ˛āĻŋāĻ• āĻ•āĻ°āĻ˛ā§‡ āĻ¯āĻž āĻšāĻŦā§‡" + _action: + dialog: "āĻĄāĻžā§ŸāĻžāĻ˛āĻ— āĻĻā§‡āĻ–āĻžāĻ¨ " + _dialog: + content: "āĻŦāĻŋāĻˇāĻ¯āĻŧāĻŦāĻ¸ā§āĻ¤ā§" + resetRandom: "āĻ°â€ā§āĻ¯āĻžāĻ¨āĻĄāĻŽ āĻ¸āĻŋāĻĄ āĻ°āĻŋāĻ¸ā§‡āĻŸ āĻ•āĻ°ā§āĻ¨" + pushEvent: "āĻ‡āĻ­ā§‡āĻ¨ā§āĻŸ āĻĒāĻžāĻ āĻžāĻ¨" + _pushEvent: + event: "āĻ‡āĻ­ā§‡āĻ¨ā§āĻŸā§‡āĻ° āĻ¨āĻžāĻŽ" + message: "āĻšāĻžāĻ˛ā§ āĻšāĻ˛ā§‡ āĻĒā§āĻ°āĻĻāĻ°ā§āĻļāĻ¨ā§‡āĻ° āĻœāĻ¨ā§āĻ¯ āĻŦāĻžāĻ°ā§āĻ¤āĻž" + variable: "āĻĒāĻžāĻ āĻžāĻ¨ā§‹ āĻšāĻ˛āĻ•" + no-variable: "āĻ•āĻŋāĻ›ā§āĻ‡ āĻ¨āĻž" + callAiScript: "AiScript āĻšāĻžāĻ˛āĻžāĻ¨" + _callAiScript: + functionName: "āĻĢāĻžāĻ‚āĻļāĻ¨ā§‡āĻ° āĻ¨āĻžāĻŽ" + radioButton: "āĻŦāĻšā§āĻ¨āĻŋāĻ°ā§āĻŦāĻžāĻšāĻ¨ā§€" + _radioButton: + name: "āĻšāĻ˛āĻ•ā§‡āĻ° āĻ¨āĻžāĻŽ" + title: "āĻļāĻŋāĻ°ā§‹āĻ¨āĻžāĻŽ" + values: "āĻŦāĻŋāĻ•āĻ˛ā§āĻĒāĻ—ā§āĻ˛āĻŋāĻ•ā§‡ āĻ†āĻ˛āĻžāĻĻāĻž āĻ˛āĻžāĻ‡āĻ¨ā§‡ āĻ˛āĻŋāĻ–ā§āĻ¨" + default: "āĻĄāĻŋāĻĢāĻ˛ā§āĻŸ āĻŽāĻžāĻ¨" + script: + categories: + flow: "āĻ¨āĻŋāĻ¯āĻŧāĻ¨ā§āĻ¤ā§āĻ°āĻŖ" + logical: "āĻ˛āĻœāĻŋāĻ•ā§āĻ¯āĻžāĻ˛ āĻ…āĻĒāĻžāĻ°ā§‡āĻļāĻ¨" + operation: "āĻšāĻŋāĻ¸āĻžāĻŦ-āĻ¨āĻŋāĻ•āĻžāĻļ" + comparison: "āĻ¤ā§āĻ˛āĻ¨āĻž" + random: "āĻ°â€ā§āĻ¯āĻžāĻ¨ā§āĻĄāĻŽ" + value: "āĻŽāĻžāĻ¨" + fn: "āĻĢāĻžāĻ‚āĻļāĻ¨" + text: "āĻŸā§‡āĻ•ā§āĻ¸āĻŸ āĻŽā§āĻ¯āĻžāĻ¨āĻŋāĻĒā§āĻ˛ā§‡āĻļāĻ¨" + convert: "āĻ°ā§āĻĒāĻžāĻ¨ā§āĻ¤āĻ°" + list: "āĻ˛āĻŋāĻ¸ā§āĻŸ" + blocks: + text: "āĻ˛ā§‡āĻ–āĻž" + multiLineText: "āĻ˛ā§‡āĻ–āĻž (āĻāĻ•āĻžāĻ§āĻŋāĻ• āĻ˛āĻžāĻ‡āĻ¨)" + textList: "āĻ˛ā§‡āĻ–āĻžāĻ° āĻ˛āĻŋāĻ¸ā§āĻŸ" + _textList: + info: "āĻĒā§āĻ°āĻ¤āĻŋāĻŸāĻŋ āĻāĻ¨ā§āĻŸā§āĻ°āĻŋāĻ•ā§‡ āĻ†āĻ˛āĻžāĻĻāĻž āĻ˛āĻžāĻ‡āĻ¨ā§‡ āĻ˛āĻŋāĻ–ā§āĻ¨" + strLen: "āĻ˛ā§‡āĻ–āĻžāĻ° āĻĻā§ˆāĻ°ā§āĻ˜ā§āĻ¯" + _strLen: + arg1: "āĻ˛ā§‡āĻ–āĻž" + strPick: "āĻ…āĻ•ā§āĻˇāĻ° āĻŦā§‡āĻ° āĻ•āĻ°ā§‡ āĻ†āĻ¨ā§āĻ¨" + _strPick: + arg1: "āĻ˛ā§‡āĻ–āĻž" + arg2: "āĻ…āĻ•ā§āĻˇāĻ°ā§‡āĻ° āĻ…āĻŦāĻ¸ā§āĻĨāĻžāĻ¨" + strReplace: "āĻ˛ā§‡āĻ–āĻž āĻĒā§āĻ°āĻ¤āĻŋāĻ¸ā§āĻĨāĻžāĻĒāĻ¨" + _strReplace: + arg1: "āĻ˛ā§‡āĻ–āĻž" + arg2: "āĻ¯ā§‡ āĻ˛ā§‡āĻ–āĻž āĻĒā§āĻ°āĻ¤āĻŋāĻ¸ā§āĻĨāĻžāĻĒāĻ¨ āĻ•āĻ°āĻž āĻšāĻŦā§‡" + arg3: "āĻ¯āĻž āĻĻā§āĻŦāĻžāĻ°āĻž āĻĒā§āĻ°āĻ¤āĻŋāĻ¸ā§āĻĨāĻžāĻĒāĻ¨ āĻ•āĻ°āĻž āĻšāĻŦā§‡" + strReverse: "āĻ˛ā§‡āĻ–āĻž āĻ‰āĻ˛ā§āĻŸāĻžāĻ¨" + _strReverse: + arg1: "āĻ˛ā§‡āĻ–āĻž" + join: "āĻ˛ā§‡āĻ–āĻž āĻ¯ā§āĻ•ā§āĻ¤ āĻ•āĻ°ā§āĻ¨" + _join: + arg1: "āĻ˛āĻŋāĻ¸ā§āĻŸ" + arg2: "āĻŦāĻŋāĻ­āĻžāĻœāĻ•" + add: "āĻ¯ā§‹āĻ—" + _add: + arg1: "A" + arg2: "B" + subtract: "āĻŦāĻŋāĻ¯āĻŧā§‹āĻ—" + _subtract: + arg1: "A" + arg2: "B" + multiply: "āĻ—ā§āĻ¨" + _multiply: + arg1: "A" + arg2: "B" + divide: "āĻ­āĻžāĻ—" + _divide: + arg1: "A" + arg2: "B" + mod: "āĻ­āĻžāĻ—āĻļā§‡āĻˇ" + _mod: + arg1: "A" + arg2: "B" + round: "āĻĻāĻļāĻŽāĻŋāĻ• āĻ°āĻžāĻ‰āĻ¨ā§āĻĄ āĻ•āĻ°ā§āĻ¨" + _round: + arg1: "āĻ¸āĻ‚āĻ–ā§āĻ¯āĻž" + eq: "A āĻ“ B āĻ¸āĻŽāĻžāĻ¨" + _eq: + arg1: "A" + arg2: "B" + notEq: "A āĻ“ B āĻ¸āĻŽāĻžāĻ¨ āĻ¨āĻž" + _notEq: + arg1: "A" + arg2: "B" + and: "A āĻāĻŦāĻ‚ B" + _and: + arg1: "A" + arg2: "B" + or: "A āĻ…āĻĨāĻŦāĻž B" + _or: + arg1: "A" + arg2: "B" + lt: "< A , B āĻšāĻ¤ā§‡ āĻ•āĻŽ" + _lt: + arg1: "A" + arg2: "B" + gt: "> A , B āĻšāĻ¤ā§‡ āĻŦā§‡āĻļā§€" + _gt: + arg1: "A" + arg2: "B" + ltEq: "<= A , B āĻšāĻ¤ā§‡ āĻ•āĻŽ āĻŦāĻž āĻ¸āĻŽāĻžāĻ¨" + _ltEq: + arg1: "A" + arg2: "B" + gtEq: ">= A , B āĻšāĻ¤ā§‡ āĻŦā§‡āĻļā§€ āĻŦāĻž āĻ¸āĻŽāĻžāĻ¨" + _gtEq: + arg1: "A" + arg2: "B" + if: "āĻ¯āĻĻāĻŋ" + _if: + arg1: "āĻ¯āĻĻāĻŋ" + arg2: "āĻ¤āĻžāĻšāĻ˛ā§‡" + arg3: "āĻ¤āĻžāĻ›āĻžā§œāĻž" + not: "āĻ¨āĻž" + _not: + arg1: "āĻ¨āĻž" + random: "āĻ°â€ā§āĻ¯āĻžāĻ¨ā§āĻĄāĻŽ" + _random: + arg1: "āĻ¸āĻŽā§āĻ­āĻžāĻŦā§āĻ¯āĻ¤āĻž" + rannum: "āĻ°â€ā§āĻ¯āĻžāĻ¨āĻĄāĻŽ āĻ¸āĻ‚āĻ–ā§āĻ¯āĻž" + _rannum: + arg1: "āĻ¨ā§āĻ¯ā§‚āĻ¨āĻ¤āĻŽ āĻŽāĻžāĻ¨" + arg2: "āĻ¸āĻ°ā§āĻŦā§‹āĻšā§āĻš āĻŽāĻžāĻ¨" + randomPick: "āĻ¤āĻžāĻ˛āĻŋāĻ•āĻž āĻĨā§‡āĻ•ā§‡ āĻĻā§ˆāĻŦāĻšā§ŸāĻ¨ āĻ•āĻ°ā§āĻ¨" + _randomPick: + arg1: "āĻ˛āĻŋāĻ¸ā§āĻŸ" + dailyRandom: "āĻ°â€ā§āĻ¯āĻžāĻ¨ā§āĻĄāĻŽ āĻ¸āĻ‚āĻ–ā§āĻ¯āĻž (āĻĒā§āĻ°āĻ¤āĻŋāĻŸāĻŋ āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ°āĻ•āĻžāĻ°ā§€āĻ° āĻœāĻ¨ā§āĻ¯ āĻĒā§āĻ°āĻ¤āĻŋāĻĻāĻŋāĻ¨ āĻĒāĻ°āĻŋāĻŦāĻ°ā§āĻ¤ā§€āĻ¤ āĻšā§Ÿ)" + _dailyRandom: + arg1: "āĻ¸āĻŽā§āĻ­āĻžāĻŦā§āĻ¯āĻ¤āĻž" + dailyRannum: "āĻ°â€ā§āĻ¯āĻžāĻ¨ā§āĻĄāĻŽ āĻ¸āĻ‚āĻ–ā§āĻ¯āĻž (āĻĒā§āĻ°āĻ¤āĻŋāĻŸāĻŋ āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ°āĻ•āĻžāĻ°ā§€āĻ° āĻœāĻ¨ā§āĻ¯ āĻĒā§āĻ°āĻ¤āĻŋāĻĻāĻŋāĻ¨ āĻĒāĻ°āĻŋāĻŦāĻ°ā§āĻ¤ā§€āĻ¤ āĻšā§Ÿ)" + _dailyRannum: + arg1: "āĻ¨ā§āĻ¯ā§‚āĻ¨āĻ¤āĻŽ āĻŽāĻžāĻ¨" + arg2: "āĻ¸āĻ°ā§āĻŦā§‹āĻšā§āĻš āĻŽāĻžāĻ¨" + dailyRandomPick: "āĻ¤āĻžāĻ˛āĻŋāĻ•āĻž āĻĨā§‡āĻ•ā§‡ āĻāĻ˛ā§‹āĻŽā§‡āĻ˛ā§‹āĻ­āĻžāĻŦā§‡ āĻ¨āĻŋāĻ°ā§āĻŦāĻžāĻšāĻ¨ āĻ•āĻ°ā§āĻ¨ (āĻĒā§āĻ°āĻ¤āĻŋāĻŸāĻŋ āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ°āĻ•āĻžāĻ°ā§€āĻ° āĻœāĻ¨ā§āĻ¯ āĻĒā§āĻ°āĻ¤āĻŋāĻĻāĻŋāĻ¨ āĻĒāĻ°āĻŋāĻŦāĻ°ā§āĻ¤ā§€āĻ¤ āĻšā§Ÿ)" + _dailyRandomPick: + arg1: "āĻ˛āĻŋāĻ¸ā§āĻŸ" + seedRandom: "āĻ°â€ā§āĻ¯āĻžāĻ¨āĻĄāĻŽ (āĻ¸ā§€āĻĄ āĻĻā§āĻŦāĻžāĻ°āĻž)" + _seedRandom: + arg1: "āĻ¸ā§€āĻĄ" + arg2: "āĻ¸āĻŽā§āĻ­āĻžāĻŦā§āĻ¯āĻ¤āĻž" + seedRannum: "āĻ°â€ā§āĻ¯āĻžāĻ¨āĻĄāĻŽ āĻ¸āĻ‚āĻ–ā§āĻ¯āĻž (āĻ¸ā§€āĻĄ āĻĻā§āĻŦāĻžāĻ°āĻž)" + _seedRannum: + arg1: "āĻ¸ā§€āĻĄ" + arg2: "āĻ¨ā§āĻ¯ā§‚āĻ¨āĻ¤āĻŽ āĻŽāĻžāĻ¨" + arg3: "āĻ¸āĻ°ā§āĻŦā§‹āĻšā§āĻš āĻŽāĻžāĻ¨" + seedRandomPick: "āĻ¤āĻžāĻ˛āĻŋāĻ•āĻž āĻĨā§‡āĻ•ā§‡ āĻĻā§ˆāĻŦāĻšā§ŸāĻ¨ āĻ•āĻ°ā§āĻ¨ (āĻ¸ā§€āĻĄ āĻĻā§āĻŦāĻžāĻ°āĻž)" + _seedRandomPick: + arg1: "āĻ¸ā§€āĻĄ" + arg2: "āĻ˛āĻŋāĻ¸ā§āĻŸ" + DRPWPM: "āĻ¸āĻŽā§āĻ­āĻžāĻŦā§āĻ¯āĻ¤āĻž āĻ¸āĻš āĻāĻ•āĻŸāĻŋ āĻ¤āĻžāĻ˛āĻŋāĻ•āĻž āĻĨā§‡āĻ•ā§‡ āĻāĻ˛ā§‹āĻŽā§‡āĻ˛ā§‹āĻ­āĻžāĻŦā§‡ āĻ¨āĻŋāĻ°ā§āĻŦāĻžāĻšāĻ¨ āĻ•āĻ°ā§āĻ¨ (āĻĒā§āĻ°āĻ¤āĻŋāĻŸāĻŋ āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ°āĻ•āĻžāĻ°ā§€āĻ° āĻœāĻ¨ā§āĻ¯ āĻĒā§āĻ°āĻ¤āĻŋāĻĻāĻŋāĻ¨)" + _DRPWPM: + arg1: "āĻ˛ā§‡āĻ–āĻžāĻ° āĻ˛āĻŋāĻ¸ā§āĻŸ" + pick: "āĻ¤āĻžāĻ˛āĻŋāĻ•āĻž āĻĨā§‡āĻ•ā§‡ āĻ¨āĻŋāĻ°ā§āĻŦāĻžāĻšāĻ¨ āĻ•āĻ°ā§āĻ¨" + _pick: + arg1: "āĻ˛āĻŋāĻ¸ā§āĻŸ" + arg2: "āĻ…āĻŦāĻ¸ā§āĻĨāĻžāĻ¨" + listLen: "āĻ˛āĻŋāĻ¸ā§āĻŸā§‡āĻ° āĻĻā§ˆāĻ°ā§āĻ˜ā§āĻ¯ āĻĒāĻžāĻ¨" + _listLen: + arg1: "āĻ˛āĻŋāĻ¸ā§āĻŸ" + number: "āĻ¸āĻ‚āĻ–ā§āĻ¯āĻž" + stringToNumber: "āĻĒāĻžāĻ ā§āĻ¯ āĻĨā§‡āĻ•ā§‡ āĻ¸āĻ‚āĻ–ā§āĻ¯āĻž" + _stringToNumber: + arg1: "āĻ˛ā§‡āĻ–āĻž" + numberToString: "āĻ¸āĻ‚āĻ–ā§āĻ¯āĻž āĻĨā§‡āĻ•ā§‡ āĻĒāĻžāĻ ā§āĻ¯" + _numberToString: + arg1: "āĻ¸āĻ‚āĻ–ā§āĻ¯āĻž" + splitStrByLine: "āĻĒāĻžāĻ ā§āĻ¯āĻ•ā§‡ āĻ˛āĻžāĻ‡āĻ¨ā§‡ āĻŦāĻŋāĻ­āĻ•ā§āĻ¤ āĻ•āĻ°ā§āĻ¨" + _splitStrByLine: + arg1: "āĻ˛ā§‡āĻ–āĻž" + ref: "āĻšāĻ˛āĻ•" + aiScriptVar: "AiScript āĻšāĻ˛āĻ•" + fn: "āĻĢāĻžāĻ‚āĻļāĻ¨" + _fn: + slots: "āĻ¸ā§āĻ˛āĻŸāĻ—ā§āĻ˛āĻŋ" + slots-info: "āĻĒā§āĻ°āĻ¤āĻŋāĻŸāĻŋ āĻ¸ā§āĻ˛āĻŸāĻ•ā§‡ āĻ†āĻ˛āĻžāĻĻāĻž āĻ˛āĻžāĻ‡āĻ¨ā§‡ āĻ˛āĻŋāĻ–ā§āĻ¨" + arg1: "āĻ†āĻ‰āĻŸāĻĒā§āĻŸ" + for: "for-āĻ˛ā§āĻĒ" + _for: + arg1: "āĻ•āĻ¤āĻŦāĻžāĻ° āĻšāĻ˛āĻŦā§‡" + arg2: "āĻ…ā§āĻ¯āĻžāĻ•āĻļāĻ¨" + typeError: "āĻ¸ā§āĻ˛āĻŸ {slot}, {expect} āĻ§āĻ°āĻ¨ā§‡āĻ° āĻŽāĻžāĻ¨ āĻ—ā§āĻ°āĻšāĻŖ āĻ•āĻ°ā§‡, āĻ•āĻŋāĻ¨ā§āĻ¤ā§ {actual} āĻ§āĻ°āĻ¨ā§‡āĻ° āĻŽāĻžāĻ¨ āĻĻā§‡āĻ“ā§ŸāĻž āĻšā§Ÿā§‡āĻ›ā§‡!" + thereIsEmptySlot: "āĻ¸ā§āĻ˛āĻŸ {slot} āĻ–āĻžāĻ˛āĻŋīŧ" + types: + string: "āĻ˛ā§‡āĻ–āĻž" + number: "āĻ¸āĻ‚āĻ–ā§āĻ¯āĻž" + boolean: "āĻĢā§āĻ˛ā§āĻ¯āĻžāĻ—" + array: "āĻ˛āĻŋāĻ¸ā§āĻŸ" + stringArray: "āĻ˛ā§‡āĻ–āĻžāĻ° āĻ˛āĻŋāĻ¸ā§āĻŸ" + emptySlot: "āĻ–āĻžāĻ˛āĻŋ āĻ¸ā§āĻ˛āĻŸ" + enviromentVariables: "āĻāĻ¨āĻ­āĻžāĻ‡āĻ°āĻ¨āĻŽā§‡āĻ¨ā§āĻŸ āĻ­ā§āĻ¯āĻžāĻ°āĻŋā§Ÿā§‡āĻŦāĻ˛" + pageVariables: "āĻĒā§‡āĻœā§‡āĻ° āĻšāĻ˛āĻ•" + argVariables: "āĻ‡āĻ¨āĻĒā§āĻŸā§‡āĻ° āĻœāĻžā§ŸāĻ—āĻž" +_relayStatus: + requesting: "āĻ…āĻĒā§‡āĻ•ā§āĻˇāĻŽāĻžāĻ¨" + accepted: "āĻ…āĻ¨ā§āĻŽā§‹āĻĻāĻŋāĻ¤" + rejected: "āĻĒā§āĻ°āĻ¤ā§āĻ¯āĻžāĻ–āĻŋāĻ¤" +_notification: + fileUploaded: "āĻĢāĻžāĻ‡āĻ˛ āĻ¸āĻĢāĻ˛āĻ­āĻžāĻŦā§‡ āĻ†āĻĒāĻ˛ā§‹āĻĄ āĻ•āĻ°āĻž āĻšā§Ÿā§‡āĻ›ā§‡" + youGotMention: "{name} āĻ†āĻĒāĻ¨āĻžāĻ•ā§‡ āĻ‰āĻ˛ā§āĻ˛ā§‡āĻ–ā§āĻ¯ āĻ•āĻ°ā§‡āĻ›ā§‡" + youGotReply: "{name} āĻ†āĻĒāĻ¨āĻžāĻ•ā§‡ āĻœāĻŦāĻžāĻŦ āĻĻāĻŋā§Ÿā§‡āĻ›ā§‡" + youGotQuote: "{name} āĻ†āĻĒāĻ¨āĻžāĻ•ā§‡ āĻ‰āĻĻā§āĻ§ā§ƒāĻ¤ āĻ•āĻ°ā§‡āĻ›ā§‡" + youRenoted: "{name} āĻāĻ° Renote" + youGotPoll: "{name} āĻ†āĻĒāĻ¨āĻžāĻ° āĻĒā§‹āĻ˛ā§‡ āĻ­ā§‹āĻŸ āĻĻāĻŋā§Ÿā§‡āĻ›ā§‡" + youGotMessagingMessageFromUser: "{name} āĻ†āĻĒāĻ¨āĻžāĻ•ā§‡ āĻŽā§‡āĻ¸ā§‡āĻœ āĻ•āĻ°ā§‡āĻ›ā§‡" + youGotMessagingMessageFromGroup: "{name} āĻ—ā§āĻ°ā§āĻĒā§‡ āĻāĻ•āĻŸāĻŋ āĻ¨āĻ¤ā§āĻ¨ āĻŽā§‡āĻ¸ā§‡āĻœ āĻ†āĻ›ā§‡" + youWereFollowed: "āĻ†āĻĒāĻ¨āĻžāĻ•ā§‡ āĻ…āĻ¨ā§āĻ¸āĻ°āĻŖ āĻ•āĻ°āĻ›ā§‡" + youReceivedFollowRequest: "āĻ…āĻ¨ā§āĻ¸āĻ°āĻŖ āĻ•āĻ°āĻžāĻ° āĻœāĻ¨ā§āĻ¯ āĻ…āĻ¨ā§āĻ°ā§‹āĻ§ āĻĒāĻžāĻ“ā§ŸāĻž āĻ—ā§‡āĻ›ā§‡" + yourFollowRequestAccepted: "āĻ†āĻĒāĻ¨āĻžāĻ° āĻ…āĻ¨ā§āĻ¸āĻ°āĻŖ āĻ•āĻ°āĻžāĻ° āĻ…āĻ¨ā§āĻ°ā§‹āĻ§ āĻ—ā§ƒāĻšā§€āĻ¤ āĻšā§Ÿā§‡āĻ›ā§‡" + youWereInvitedToGroup: "āĻ†āĻĒāĻ¨āĻŋ āĻāĻ•āĻŸāĻŋ āĻ—ā§āĻ°ā§āĻĒā§‡ āĻ†āĻŽāĻ¨ā§āĻ¤ā§āĻ°āĻŋāĻ¤ āĻšā§Ÿā§‡āĻ›ā§‡āĻ¨" + _types: + all: "āĻ¸āĻ•āĻ˛" + follow: "āĻ…āĻ¨ā§āĻ¸āĻ°āĻŖ āĻ•āĻ°āĻž āĻšāĻšā§āĻ›ā§‡" + mention: "āĻ‰āĻ˛ā§āĻ˛ā§‡āĻ–" + reply: "āĻ‰āĻ¤ā§āĻ¤āĻ° āĻĻāĻŋāĻ¨" + renote: "āĻ°āĻŋāĻ¨ā§‹āĻŸ" + quote: "āĻ‰āĻĻā§āĻ§ā§ƒāĻ¤āĻŋ" + reaction: "āĻĒā§āĻ°āĻ¤āĻŋāĻ•ā§āĻ°āĻŋāĻ¯āĻŧāĻž" + pollVote: "āĻĒā§‹āĻ˛ā§‡ āĻ­ā§‹āĻŸ āĻ†āĻ›ā§‡" + receiveFollowRequest: "āĻĒā§āĻ°āĻžāĻĒā§āĻ¤ āĻ…āĻ¨ā§āĻ¸āĻ°āĻŖā§‡āĻ° āĻ…āĻ¨ā§āĻ°ā§‹āĻ§āĻ¸āĻŽā§‚āĻš" + followRequestAccepted: "āĻ—ā§ƒāĻšā§€āĻ¤ āĻ…āĻ¨ā§āĻ¸āĻ°āĻŖā§‡āĻ° āĻ…āĻ¨ā§āĻ°ā§‹āĻ§āĻ¸āĻŽā§‚āĻš" + groupInvited: "āĻ—ā§āĻ°ā§āĻĒā§‡āĻ° āĻ†āĻŽāĻ¨ā§āĻ¤ā§āĻ°āĻ¨āĻ¸āĻŽā§‚āĻš" + app: "āĻ˛āĻŋāĻ™ā§āĻ• āĻ•āĻ°āĻž āĻ…ā§āĻ¯āĻžāĻĒ āĻĨā§‡āĻ•ā§‡ āĻŦāĻŋāĻœā§āĻžāĻĒā§āĻ¤āĻŋ" +_deck: + alwaysShowMainColumn: "āĻ¸āĻ°ā§āĻŦāĻĻāĻž āĻŽā§‡āĻ‡āĻ¨ āĻ•āĻ˛āĻžāĻŽ āĻĻā§‡āĻ–āĻžāĻ¨" + columnAlign: "āĻ•āĻ˛āĻžāĻŽ āĻ¸āĻžāĻœāĻžāĻ¨" + columnMargin: "āĻ•āĻ˛āĻžāĻŽā§‡āĻ° āĻŽāĻ§ā§āĻ¯āĻŦāĻ°ā§āĻ¤ā§€ āĻŽāĻžāĻ°ā§āĻœāĻŋāĻ¨" + columnHeaderHeight: "āĻ•āĻ˛āĻžāĻŽā§‡āĻ° āĻšā§‡āĻĄāĻžāĻ°ā§‡āĻ° āĻ‰āĻšā§āĻšāĻ¤āĻž" + addColumn: "āĻ•āĻ˛āĻžāĻŽ āĻ¯ā§āĻ•ā§āĻ¤ āĻ•āĻ°ā§āĻ¨" + swapLeft: "āĻŦāĻžāĻŽā§‡ āĻ¸āĻ°āĻžāĻ¨" + swapRight: "āĻĄāĻžāĻ¨ā§‡ āĻ¸āĻ°āĻžāĻ¨" + swapUp: "āĻ‰āĻĒāĻ°ā§‡ āĻ‰āĻ āĻžāĻ¨" + swapDown: "āĻ¨āĻŋāĻšā§‡ āĻ¨āĻžāĻŽāĻžāĻ¨" + stackLeft: "āĻŦāĻžāĻŽ āĻ•āĻ˛āĻžāĻŽā§‡ āĻ¸āĻžāĻœāĻžāĻ¨" + popRight: "āĻĄāĻžāĻ¨āĻĻāĻŋāĻ•ā§‡ āĻ°āĻžāĻ–ā§āĻ¨" + profile: "āĻĒā§āĻ°ā§‹āĻĢāĻžāĻ‡āĻ˛" + _columns: + main: "āĻĒā§āĻ°āĻ§āĻžāĻ¨" + widgets: "āĻ‰āĻ‡āĻœā§‡āĻŸāĻ—ā§āĻ˛āĻŋ" + notifications: "āĻŦāĻŋāĻœā§āĻžāĻĒā§āĻ¤āĻŋ" + tl: "āĻŸāĻžāĻ‡āĻŽāĻ˛āĻžāĻ‡āĻ¨" + antenna: "āĻ…ā§āĻ¯āĻžāĻ¨ā§āĻŸā§‡āĻ¨āĻž" + list: "āĻ˛āĻŋāĻ¸ā§āĻŸ" + mentions: "āĻ‰āĻ˛ā§āĻ˛ā§‡āĻ–āĻ¸āĻŽā§‚āĻš" + direct: "āĻĄāĻžāĻ‡āĻ°ā§‡āĻ•ā§āĻŸ āĻ¨ā§‹āĻŸāĻ—ā§āĻ˛āĻŋ" diff --git a/locales/de-DE.yml b/locales/de-DE.yml index 2f327a905c..c5bf407407 100644 --- a/locales/de-DE.yml +++ b/locales/de-DE.yml @@ -141,6 +141,8 @@ 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 Benuzter in der Chronik an angezeigt." autoAcceptFollowed: "Follow-Anfragen von Benutzern, denen du folgst, automatisch akzeptieren" addAccount: "Benutzerkonto hinzufÃŧgen" loginFailed: "Anmeldung fehlgeschlagen" @@ -235,6 +237,8 @@ resetAreYouSure: "Wirklich zurÃŧcksetzen?" 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" fromUrl: "Von einer URL" uploadFromUrl: "Von einer URL hochladen" @@ -591,6 +595,8 @@ smtpSecure: "FÃŧr SMTP-Verbindungen implizit SSL/TLS verwenden" smtpSecureInfo: "Schalte dies aus, falls du STARTTLS verwendest" testEmail: "Email-Versand testen" wordMute: "Wort-Stummschaltung" +regexpError: "Regular Expression error" +regexpErrorDescription: "Error in the regular expression on line {line} in your {tab} word mutes:" instanceMute: "Instanzstummschaltungen" userSaysSomething: "{name} hat etwas gesagt" makeActive: "Aktivieren" @@ -820,6 +826,13 @@ leaveGroupConfirm: "MÃļchtest du \"{name}\" wirklich verlassen?" 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." +overridedDeviceKind: "Gerätetyp" +smartphone: "Smartphone" +tablet: "Tablet" +auto: "Automatisch" +themeColor: "Instanzfarbe" +size: "GrÃļße" +numberOfColumn: "Spaltenanzahl" _emailUnavailable: used: "Diese Email-Adresse wird bereits verwendet" format: "Das Format dieser Email-Adresse ist ungÃŧltig" @@ -1256,8 +1269,8 @@ _exportOrImport: excludeMutingUsers: "Stummgeschaltete Benutzer aussortieren" excludeInactiveUsers: "Inaktive Benutzer aussortieren" _charts: - federationInstancesIncDec: "Unterschied in der Anzahl von fÃļrderierenden Instanzen" - federationInstancesTotal: "Anzahl aller fÃļderierenden Instanzen" + federation: "FÃļderation" + apRequest: "Anfragen" usersIncDec: "Unterschied in der Anzahl von Benutzern" usersTotal: "Anzahl aller Benutzer" activeUsers: "Aktive Benutzer" diff --git a/locales/en-US.yml b/locales/en-US.yml index 6bbe848210..53434d7e60 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -141,6 +141,8 @@ 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 Misskey's internal systems to treat this account as a bot." flagAsCat: "Mark this account as a cat" flagAsCatDescription: "Enable this option to mark this account as a cat." +flagShowTimelineReplies: "Show replies in timeline" +flagShowTimelineRepliesDescription: "Shows replies of users to notes 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" @@ -235,6 +237,8 @@ resetAreYouSure: "Really reset?" 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." fromDrive: "From Drive" fromUrl: "From URL" uploadFromUrl: "Upload from a URL" @@ -820,6 +824,13 @@ leaveGroupConfirm: "Are you sure you want to leave \"{name}\"?" useDrawerReactionPickerForMobile: "Display reaction picker as drawer on mobile" welcomeBackWithName: "Welcome back, {name}" clickToFinishEmailVerification: "Please click [{ok}] to complete email verification." +overridedDeviceKind: "Device type" +smartphone: "Smartphone" +tablet: "Tablet" +auto: "Auto" +themeColor: "Theme Color" +size: "Size" +numberOfColumn: "Number of columns" _emailUnavailable: used: "This email address is already being used" format: "The format of this email address is invalid" @@ -1256,8 +1267,8 @@ _exportOrImport: excludeMutingUsers: "Exclude muted users" excludeInactiveUsers: "Exclude inactive users" _charts: - federationInstancesIncDec: "Difference in # of federating instances" - federationInstancesTotal: "Total # of federating instances" + federation: "Federation" + apRequest: "Requests" usersIncDec: "Difference in # of users" usersTotal: "Total # of users" activeUsers: "Active users" diff --git a/locales/eo-UY.yml b/locales/eo-UY.yml index 0689834a1e..062bf85aa9 100644 --- a/locales/eo-UY.yml +++ b/locales/eo-UY.yml @@ -9,7 +9,7 @@ username: "Uzantnomo" password: "Pasvorto" forgotPassword: "Ĉu vi forgesis pasvorton?" fetchingAsApObject: "Informpetado de la Fediversoâ€Ļ" -ok: "OK" +ok: "Bone" gotIt: "Kompreni" cancel: "Nuligi" enterUsername: "Entajpu uzantnomon" @@ -65,6 +65,8 @@ files: "Dosieroj" download: "Elŝuti" driveFileDeleteConfirm: "Ĉu vi certas, ke vi volas forviŝi la dosieron \"{name}\"? Tio ankaÅ­ forviŝos la notojn kiuj citas ĝin." unfollowConfirm: "Ĉu vi certas, ke vi volas ĉesi sekvi {name}?" +exportRequested: "Vi petis elporton. Ĝi povas bezoni longan tempon. ElportaÄĩo estos aldonita al disko post elportado." +importRequested: "Vi petis enportadon. Ĝi povas bezoni longan tempon. " lists: "Listoj" noLists: "Neniu listo" note: "Noti" @@ -73,11 +75,14 @@ following: "Sekvata" followers: "Sekvantoj" followsYou: "Sekvas vin" createList: "Krei liston" -manageLists: "Bonteni liston" +manageLists: "Bonteni la listojn" error: "Eraro" somethingHappened: "Problemo okazis" retry: "Provi denove" +pageLoadError: "Fuŝiĝis enlegi paĝon." +pageLoadErrorDescription: "Ordinare tio okazas pro la retkondiĉo aÅ­ la staplo de retumilo.\nPurigu la staplon, aÅ­ refaru poste. " serverIsDead: "La servilo ne respondas. Vole atendu iom kaj penu denove." +youShouldUpgradeClient: "Por montri ĉi paĝon, bonvolu enlegi refoje kaj uzi klienton novversian." enterListName: "Entajpu nomon de la listo" privacy: "Privateco" makeFollowManuallyApprove: "Eksekvi vin devas peti al vi" @@ -132,6 +137,8 @@ settingGuide: "Agordaj rekomendoj" cacheRemoteFiles: "Stapli forajn dosierojn" flagAsBot: "Marki kiel esti uzanto de roboto" flagAsCat: "Marki kiel esti kato" +flagAsCatDescription: "Flagu por montri ke la konton havas kato." +flagShowTimelineReplies: "Montri respondon de notoj en templinio." autoAcceptFollowed: "AÅ­tomate akcepti la peton de sekvado far uzantoj kiujn vi sekvas" addAccount: "Aldoni konton" loginFailed: "Saluto malsukcesis" @@ -178,6 +185,7 @@ noUsers: "Neniu uzanto" editProfile: "Redakti profilon" noteDeleteConfirm: "Ĉu vi certas ke vi volas forviŝi la noton?" pinLimitExceeded: "Vi ne povas alpingli pli" +intro: "La instalado de Misskey finiĝis! Volu krei administran konton." done: "Fini" processing: "Prilaboradoâ€Ļ" preview: "AntaÅ­montro" @@ -216,6 +224,7 @@ resetAreYouSure: "Ĉu vi certas restarigi?" saved: "Konservita" messaging: "Retbabili" upload: "Alŝuti" +keepOriginalUploading: "Konservi la originalan bildon" fromDrive: "De la disko" fromUrl: "De URL" uploadFromUrl: "Alŝuti de URL" @@ -326,6 +335,7 @@ antennaExcludeKeywords: "Krom ĉefterminoj" notifyAntenna: "Oni sciigos novajn notojn" withFileAntenna: "Nur kun aldonaÄĩo" enableServiceworker: "Aktivigi ServiceWorker" +caseSensitive: "Distingi usklecon" withReplies: "Inkluzive respondoj" connectedTo: "Sekva konto estas konektita" notesAndReplies: "Kun respondoj" @@ -363,6 +373,7 @@ notFound: "Ne trovita" uploadFolder: "Dosierujo implicita por alŝuto" cacheClear: "Malplenigi staplon" markAsReadAllNotifications: "Marki ĉiujn sciigojn kiel legita" +markAsReadAllUnreadNotes: "Marki ĉiujn afiŝojn kiel legita" markAsReadAllTalkMessages: "Marki ĉiujn retbabiladojn kiel legita" help: "Manlibro de uzado" inputMessageHere: "Entajpu mesaĝon tie" @@ -385,6 +396,7 @@ next: "Sekve" retype: "Retajpu" noteOf: "Noto de {user}" inviteToGroup: "Inviti al grupo" +maxNoteTextLength: "Limnombro de leteroj en noto" quoteAttached: "Kun citaÄĩo" quoteQuestion: "Ĉu vi volas aldoni citaÄĩon?" noMessagesYet: "AnkoraÅ­ neniu mesaĝo" @@ -393,6 +405,7 @@ onlyOneFileCanBeAttached: "Oni povas aldoni nur unu dosieron po mesaĝo." signinRequired: "Bonvolu saluti" invitations: "Inviti" invitationCode: "Kodo de invito" +checking: "kontrolante..." available: "Disposabla" unavailable: "Ne disponebla" usernameInvalidFormat: "La uzantnomo povas enhavi minusklajn kaj majusklajn literojn, numerojn, nur kaj '_'." @@ -404,6 +417,7 @@ strongPassword: "Forta pasvorto" passwordMatched: "Konforma" passwordNotMatched: "Nekonforma" signinWith: "Saluti kun {x}" +signinFailed: "Fuŝiĝis ensaluti. Bonvolu kontroli uzantnomon kaj pasvorton." or: "AÅ­" language: "Lingvo" uiLanguage: "Lingvo de fasado" @@ -430,6 +444,7 @@ total: "Entute" appearance: "EksteraÄĩo" clientSettings: "Agordoj de kliento" accountSettings: "Agordoj de konto" +promote: "Reklamata" numberOfDays: "Nombro de tagoj" hideThisNote: "Kaŝi la noton" showFeaturedNotesInTimeline: "Montri en via templinio notojn de la tendenco" @@ -465,18 +480,20 @@ scratchpad: "Malneta redaktilo" output: "Elmeto" script: "Skripto" disablePagesScript: "Malebligi AiScript en la paĝoj" +updateRemoteUser: "Aktualigi informon de foraj uzantoj" deleteAllFiles: "Forviŝi ĉiujn dosierojn" deleteAllFilesConfirm: "Ĉu vi certas, ke vi volas forviŝi ĉiujn dosierojn?" removeAllFollowing: "Ĉesi sekvi ĉiujn sekvatojn" userSuspended: "La uzanto estas flostigita." userSilenced: "La uzanto estas mutigita." +yourAccountSuspendedTitle: "La konto estas frostigita." menu: "Menuo" addItem: "Aldoni novaÄĩon" deletedNote: "Forviŝita noto" invisibleNote: "Malpublikigita noto" enableInfiniteScroll: "Ebligi infinitan rulumon" visibility: "Videbleco" -poll: "Balotujo" +poll: "Enketo" useCw: "Kaŝi enhavo" enablePlayer: "Vidigi la filmeton" disablePlayer: "Malfermi la filmeton" @@ -499,6 +516,7 @@ generateAccessToken: "Generi aÅ­tentikigan pecon" permission: "Permesoj" enableAll: "Ebligi ĉiujn" disableAll: "Malebligi ĉiujn" +tokenRequested: "Alirpermeso al konto" notificationType: "Tipo de sciigoj" edit: "Redakti" emailServer: "Retpoŝta servilo" @@ -521,7 +539,7 @@ overview: "Resumo" logs: "Protokoloj" delayed: "Prokrasto " database: "Datumbazo" -channel: "Kanalo" +channel: "Kanaloj" create: "Krei" notificationSetting: "Agordoj de sciigoj" useGlobalSetting: "Oni uzas malloka agordo" @@ -530,6 +548,7 @@ regenerateLoginToken: "Regeneri la aÅ­tentikigan pecon" fileIdOrUrl: "Dosiera identigilo aÅ­ URL" behavior: "Konduto" sample: "Ekzemplo" +reporter: "Informanto" send: "Sendi" openInNewTab: "Malfermi en nova langeto" editTheseSettingsMayBreakAccount: "Redakti tiujn agordojn povas damaĝi vian konton." @@ -543,6 +562,7 @@ createNew: "Krei novan" optional: "Opciaj" public: "Publika" i18nInfo: "Misskey estas tradukata en diversaj lingvoj de volontuloj. Oni povas kontribui ĉe {link}." +manageAccessTokens: "Bonteni la aÅ­tentikigajn pecojn" accountInfo: "Kontaj Informoj" notesCount: "La nombro de notoj" repliesCount: "La nombro de respondoj senditaj" @@ -553,6 +573,8 @@ followingCount: "La nombro de sekvatoj" followersCount: "La nombro de sekvantoj" sentReactionsCount: "La nombro de la reagoj senditaj" receivedReactionsCount: "La nombro de la reagoj ricevitaj" +pollVotesCount: "Nombro de voĉdonado" +pollVotedCount: "La nombro de la voĉoj ricevitaj en siaj enketoj" yes: "Jes" no: "Ne" driveFilesCount: "La nombro de la dosieroj sur la disko" @@ -587,6 +609,7 @@ createdAt: "Kreita je" updatedAt: "Laste ĝisdatigita" saveConfirm: "Ĉu vi konservas la ŝanĝon?" deleteConfirm: "Ĉu certas forviŝi?" +invalidValue: "Nevalida valoro" closeAccount: "Forigi konton" currentVersion: "La aktuala versio" latestVersion: "La plej nova versio" @@ -596,7 +619,7 @@ inUse: "Uzata" editCode: "Redakti kodon" receiveAnnouncementFromInstance: "Ricevi informojn sciigintajn de la nodo" emailNotification: "Sciigoj per retpoŝto" -inChannelSearch: "Serĉi en kanalo" +inChannelSearch: "Serĉi en la kanalo" useReactionPickerForContextMenu: "Dekstre-klaki por malfermi la elektilon de reagoj" typingUsers: "{users} nun skribasâ€Ļ" clear: "Vakigi" @@ -608,14 +631,18 @@ userInfo: "Informoj de uzanto" unknown: "Nekonata" online: "Surkonektita" offline: "Forkonektita" +notRecommended: "Evitindaj" instanceBlocking: "Bloki specifajn nodojn" selectAccount: "Elekti konton" user: "Uzantoj" administration: "Bontenado" accounts: "Kontoj" +configure: "Agordi" recentPosts: "Novaj afiŝoj" +popularPosts: "Plej viditaj" shareWithNote: "Kundividi en noto" ads: "ReklamaÄĩo" +expiration: "Limtempo" memo: "Memorigilo" high: "Alta" middle: "Meza" @@ -623,18 +650,25 @@ low: "Malalta" emailNotConfiguredWarning: "Vi ne agordis retpoŝtadreso." customCss: "Personecigita CSS" global: "Malloka" +squareAvatars: "Montri bildsimbolon kiel kvadrata" sent: "Sendi" received: "Ricevita" searchResult: "Serĉorezultoj" hashtags: "Kradvorto" troubleshooting: "Problemsolvi" learnMore: "Lernu pli" +misskeyUpdated: "Misskey ĝisdatiĝis!" +whatIsNew: "Montri novaÄĩojn" translate: "Traduki" translatedFrom: "Tradukita el {x}" +accountDeletionInProgress: "La konto estas forviŝanta." +resolved: "Solvita" +unresolved: "Nesolvita" breakFollow: "Ĉesigi la sekvadon al vi" itsOn: "Ŝaltita" emailRequiredForSignup: "Registri konton devas konformi retpoŝtadreson" unread: "Nelegita" +filter: "Filtrilo" controlPanel: "Ŝaltpodio" manageAccounts: "Bonteni la kontojn" classic: "Klasika" @@ -644,10 +678,15 @@ ffVisibility: "Videbleco de viaj sekvatoj/sekvantoj" ffVisibilityDescription: "Oni permesas agordi tiuln kiuj povas vidi la homojn kiujn vi sekvas, kaj la homojn kiuj sekvas vin." continueThread: "Pli vidi la mesaĝaron" incorrectPassword: "Nevalida pasvorto" +voteConfirm: "Ĉu vi voĉdonas {choice}n?" +hide: "Kaŝi" leaveGroup: "Eliĝi el la grupo" leaveGroupConfirm: "Ĉu vi certas ke vi volas eliĝi el la grupo {name}?" welcomeBackWithName: "Bonrevenon, {name}!" clickToFinishEmailVerification: "Volu klaki [{ok}] por fini la konfirmon de vian retadreson" +smartphone: "Saĝtelefono" +tablet: "Platkomputilo" +auto: "AÅ­tomate" _emailUnavailable: used: "La retpoŝto jam estas uzita." format: "Nevalida formato." @@ -658,14 +697,18 @@ _ffVisibility: followers: "Nur al sekvantoj" private: "Malpublikigita" _signup: + almostThere: "PreskaÅ­ plenumita" emailAddressInfo: "Entajpu vian retpoŝton" _accountDelete: accountDelete: "Forigi konton" + requestAccountDelete: "Peti forviŝi konton" + started: "Forviŝado komenciĝis." _ad: back: "Nuligi" _forgotPassword: enterEmail: "Entajpu la retpoŝton kiun vi registrigis al via konto. Ligilo por restarigi pasvorton estos sendita al la retadreso." _gallery: + my: "Miaj afiŝoj" liked: "Ŝatitaj notoj" like: "Ŝati" _email: @@ -687,6 +730,7 @@ _aboutMisskey: allContributors: "Ĉiuj kontribuantoj" source: "Fontkodo" translation: "Traduki Misskey" + donate: "Mondonaci al Misskey" patrons: "Mecenatoj" _mfm: dummy: "Misskey evoluigas la mondon de Fediverso" @@ -724,6 +768,7 @@ _channel: owned: "Bontenitaj de vi" following: "Sekvado" usersCount: "{n} partoprenantoj" + notesCount: "{n} notoj" _menuDisplay: sideFull: "Flanko" sideIcon: "Flanko (bildsimbolo)" @@ -735,9 +780,15 @@ _wordMute: hard: "Per la servilo" mutedNotes: "Silentigitaj notoj" _theme: + explore: "Serĉi koloraron" + install: "Instali koloraron" manage: "Bonteni kolorarojn" code: "Kolorara kodo" description: "Priskribo" + installedThemes: "Instalita koloraro" + make: "Krei koloraron" + addConstant: "Aldoni konstanton" + constant: "Konstanto" defaultValue: "ImplicitaÄĩa valoro" color: "Koloro" func: "Funkcio" @@ -745,12 +796,14 @@ _theme: lighten: "Brileco" keys: bg: "Fono" + shadow: "Ombro" navBg: "Fono de flanka stango" link: "Ligilo" hashtag: "Kradvorto" mention: "Mencioj" mentionMe: "Mencio al vi" renote: "Plusendita" + infoBg: "Fono de informo" buttonBg: "Fono de butono" driveFolderBg: "Fono de dosierujo de la disko" messageBg: "Fono de la retbabilejo" @@ -823,6 +876,7 @@ _widgets: timeline: "Templinio" clock: "Horloĝo" activity: "Aktiveco" + photos: "Fotoj" federation: "FederaÄĩo" slideshow: "Bildoprezento" button: "Butono" @@ -830,15 +884,21 @@ _widgets: aichan: "Ai" _cw: show: "Vidi pli" + chars: "{count} literoj" files: "{count} dosiero(j)" _poll: - choiceN: "Balotilo {n}" - noMore: "Oni ne povas aldoni pli." - infinite: "Neniam" + choiceN: "Ebla voĉdono {n}" + noMore: "Oni ne povas aldoni pli" + canMultipleVote: "Permesi plurelekton" + expiration: "Limtempo" deadlineTime: "hor" - votesCount: "{n} balotiloj" - vote: "Baloti" - closed: "Oni jam balotis ĝin" + duration: "DaÅ­ro" + votesCount: "{n} voĉoj" + totalVotes: "Sume {n} voĉoj" + vote: "Voĉdoni" + showResult: "Vidi la rezultojn" + voted: "Voĉdonita" + closed: "Finita" _visibility: public: "Publika" publicDescription: "Publikigi al ĉiuj en la Fediverso" @@ -876,7 +936,7 @@ _exportOrImport: blockingList: "Blokitoj" userLists: "Listoj" _charts: - federationInstancesTotal: "La totala nombro de nodoj federantaj" + federation: "FederaÄĩo" usersTotal: "La totala nombro de la uzantoj" activeUsers: "La nombro de la uzantoj aktivaj" notesTotal: "La totala nombro de notoj" @@ -965,6 +1025,45 @@ _pages: _join: arg1: "Listoj" arg2: "apartigilo" + _add: + arg1: "A" + arg2: "B" + _subtract: + arg1: "A" + arg2: "B" + _multiply: + arg1: "A" + arg2: "B" + _divide: + arg1: "A" + arg2: "B" + _mod: + arg1: "A" + arg2: "B" + _eq: + arg1: "A" + arg2: "B" + _notEq: + arg1: "A" + arg2: "B" + _and: + arg1: "A" + arg2: "B" + _or: + arg1: "A" + arg2: "B" + _lt: + arg1: "A" + arg2: "B" + _gt: + arg1: "A" + arg2: "B" + _ltEq: + arg1: "A" + arg2: "B" + _gtEq: + arg1: "A" + arg2: "B" random: "Hazardo" _randomPick: arg1: "Listoj" @@ -1004,7 +1103,7 @@ _notification: youGotReply: "{name} respondis" youGotQuote: "{name} citis" youRenoted: "{name} plusendis" - youGotPoll: "{name} balotis" + youGotPoll: "{name} voĉdonis" youGotMessagingMessageFromUser: "{name} sendis al vi mesaĝon" youGotMessagingMessageFromGroup: "Oni sendis al la grupo {name} mesaĝon" youWereFollowed: "Eksekvis vin" @@ -1019,6 +1118,7 @@ _notification: renote: "Plusendoj" quote: "Citi" reaction: "Reagoj" + pollVote: "Voĉdonoj en balotoj" receiveFollowRequest: "Ricevi peton de sekvado" followRequestAccepted: "Akceptita peto de sekvado" groupInvited: "Invitita al grupo" diff --git a/locales/es-ES.yml b/locales/es-ES.yml index a9339acf7b..1e85e7d8c4 100644 --- a/locales/es-ES.yml +++ b/locales/es-ES.yml @@ -1091,8 +1091,8 @@ _exportOrImport: blockingList: "Bloqueados" userLists: "Listas" _charts: - federationInstancesIncDec: "VariaciÃŗn de instancias federando" - federationInstancesTotal: "Total de instancias federando" + federation: "FederaciÃŗn" + apRequest: "Pedidos" usersIncDec: "VariaciÃŗn de usuarios" usersTotal: "Total de usuarios" activeUsers: "Cantidad de usuarios activos" diff --git a/locales/fr-FR.yml b/locales/fr-FR.yml index 58dd000ccc..1deda414ce 100644 --- a/locales/fr-FR.yml +++ b/locales/fr-FR.yml @@ -141,6 +141,7 @@ 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." 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" addAccount: "Ajouter un compte" loginFailed: "Échec de la connexion" @@ -235,6 +236,7 @@ resetAreYouSure: "Voulez-vous rÊinitialiser ?" saved: "EnregistrÊ" messaging: "Discuter" upload: "TÊlÊverser" +keepOriginalUploading: "Garder l’image d’origine" fromDrive: "Depuis le Drive" fromUrl: "Depuis une URL" uploadFromUrl: "TÊlÊverser via une URL" @@ -743,6 +745,7 @@ notRecommended: "DÊconseillÊ" botProtection: "Protection contre les bots" instanceBlocking: "Instances bloquÊes" selectAccount: "SÊlectionner un compte" +switchAccount: "Changer de compte" enabled: "ActivÊ" disabled: "DÊsactivÊ" quickAction: "Actions rapides" @@ -803,11 +806,13 @@ makeReactionsPublic: "Rendre les rÊactions publiques" makeReactionsPublicDescription: "Ceci rendra la liste de toutes vos rÊactions donnÊes publique." classic: "Classique" muteThread: "Mettre ce thread en sourdine" +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." continueThread: "Afficher la suite du fil" deleteAccountConfirm: "Votre compte sera supprimÊ. Êtes vous certain ?" incorrectPassword: "Le mot de passe est incorrect." +voteConfirm: "Confirmez-vous votre vote pour ÂĢ {choice} Âģ ?" hide: "Masquer" leaveGroup: "Quitter le groupe" leaveGroupConfirm: "Êtes vous sÃģr de vouloir quitter \"{name}\" ?" @@ -969,6 +974,8 @@ _wordMute: hard: "Strict" mutedNotes: "Notes filtrÊes" _instanceMute: + instanceMuteDescription2: "SÊparer avec de nouvelles lignes" + title: "Masque les notes venant des instances listÊes." heading: "Instances à mettre en sourdine" _theme: explore: "Explorer les thèmes" @@ -1241,10 +1248,11 @@ _exportOrImport: muteList: "Comptes masquÊs" blockingList: "Comptes bloquÊs" userLists: "Listes" + excludeMutingUsers: "Exclure les utilisateur¡rice¡s mis en sourdine" excludeInactiveUsers: "Exclure les utilisateur¡rice¡s inactifs" _charts: - federationInstancesIncDec: "Variation du nombre d'instances fÊdÊrÊes" - federationInstancesTotal: "Nombre total d'instances fÊdÊrÊes" + federation: "FÊdÊration" + apRequest: "RequÃĒtes" usersIncDec: "Variation du nombre d'utilisateur¡rice¡s" usersTotal: "Nombre des utilisateur¡rice¡s au total" activeUsers: "Nombre d'utilisateurices actif¡ve¡s" diff --git a/locales/id-ID.yml b/locales/id-ID.yml index a1d52f6ef1..be766e72c5 100644 --- a/locales/id-ID.yml +++ b/locales/id-ID.yml @@ -106,6 +106,7 @@ clickToShow: "Klik untuk melihat" 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" rememberNoteVisibility: "Ingat pengaturan visibilitas catatan" attachCancel: "Hapus lampiran" @@ -140,6 +141,8 @@ 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." 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" addAccount: "Tambahkan akun" loginFailed: "Gagal untuk masuk" @@ -234,6 +237,8 @@ resetAreYouSure: "Yakin mau atur ulang?" 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." fromDrive: "Dari Drive" fromUrl: "Dari URL" uploadFromUrl: "Unggah dari URL" @@ -446,6 +451,7 @@ uiLanguage: "Bahasa antarmuka pengguna" groupInvited: "Telah diundang ke grup" aboutX: "Tentang {x}" useOsNativeEmojis: "Gunakan Emoji bawaan sistem operasi" +disableDrawer: "Jangan gunakan menu bergaya laci" youHaveNoGroups: "Kamu tidak memiliki grup" joinOrCreateGroup: "Bergabunglah dengan grup atau kamu dapat membuat grupmu sendiri." noHistory: "Tidak ada riwayat" @@ -589,6 +595,7 @@ smtpSecure: "Gunakan SSL/TLS implisit untuk koneksi SMTP" smtpSecureInfo: "Matikan ini ketika menggunakan STARTTLS" testEmail: "Tes pengiriman surel" wordMute: "Bisukan kata" +instanceMute: "Bisuka instansi" userSaysSomething: "{name} mengatakan sesuatu" makeActive: "Aktifkan" display: "Tampilkan" @@ -616,8 +623,11 @@ reportAbuse: "Laporkan" reportAbuseOf: "Laporkan {name}" 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." send: "Kirim" abuseMarkAsResolved: "Tandai laporan sebagai selesai" openInNewTab: "Buka di tab baru" @@ -679,6 +689,7 @@ center: "Tengah" wide: "Lebar" narrow: "Sempit" 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" onlineUsersCount: "{n} orang sedang daring" @@ -739,6 +750,7 @@ notRecommended: "Tidak disarankan" botProtection: "Perlindungan Bot" instanceBlocking: "Instansi yang diblokir" selectAccount: "Pilih akun" +switchAccount: "Ganti akun" enabled: "Aktif" disabled: "Nonaktif" quickAction: "Aksi cepat" @@ -787,6 +799,7 @@ pubSub: "Akun Pub/Sub" lastCommunication: "Komunikasi terakhir" resolved: "Selesai" unresolved: "Belum selesai" +breakFollow: "Batalkan mengikuti" itsOn: "Aktif" itsOff: "Nonaktif" emailRequiredForSignup: "Membutuhkan alamat surel untuk mendaftar" @@ -806,6 +819,15 @@ deleteAccountConfirm: "Akun akan dihapus. Apakah kamu yakin?" incorrectPassword: "Kata sandi salah." voteConfirm: "Konfirmasi suara kamu untuk ({choice})īŧŸ" hide: "Sembunyikan" +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." +overridedDeviceKind: "Tipe perangkat" +smartphone: "Ponsel" +tablet: "Tablet" +auto: "Otomatis" _emailUnavailable: used: "Alamat surel ini telah digunakan" format: "Format tidak valid." @@ -963,6 +985,11 @@ _wordMute: 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." + instanceMuteDescription2: "Pisah dengan baris baru" + title: "Sembunyikan note dari instansi terdaftar." + heading: "Daftar instansi yang akan dibisukan" _theme: explore: "Jelajahi tema" install: "Pasang tema" @@ -1234,9 +1261,11 @@ _exportOrImport: muteList: "Bisukan" blockingList: "Blokir" userLists: "Daftar" + excludeMutingUsers: "Kecualikan pengguna yang dibisukan" + excludeInactiveUsers: "Kecualikan pengguna tidak aktif" _charts: - federationInstancesIncDec: "Perbedaan jumlah # instansi yang memfederasi" - federationInstancesTotal: "Jumlah # instansi yang memfederasi" + federation: "Federasi" + apRequest: "Permintaan" usersIncDec: "Perbedaan dalam # pengguna" usersTotal: "Jumlah # pengguna" activeUsers: "Pengguna aktif" diff --git a/locales/it-IT.yml b/locales/it-IT.yml index d13e53625f..c4ec4232a5 100644 --- a/locales/it-IT.yml +++ b/locales/it-IT.yml @@ -1177,8 +1177,8 @@ _exportOrImport: blockingList: "Account bloccati" userLists: "Liste" _charts: - federationInstancesIncDec: "Variazione del numero di istanze federate" - federationInstancesTotal: "Numero totale di istanze federate" + federation: "Federazione" + apRequest: "Richieste" usersIncDec: "Variazione del numero di utenti" usersTotal: "Numero totale di utenti" activeUsers: "Numero di utenti attivi" diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index b3279d78b8..f4c9d19981 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -141,6 +141,8 @@ flagAsBot: "BotとしãĻč¨­åŽš" flagAsBotDescription: "こぎã‚ĸã‚Ģã‚ĻãƒŗトがプログナムãĢよãŖãĻ運į”¨ã•ã‚Œã‚‹å ´åˆã¯ã€ã“ぎフナグをã‚ĒãƒŗãĢしぞす。ã‚ĒãƒŗãĢすると、反åŋœãŽé€ŖéŽ–ã‚’é˜˛ããŸã‚ãŽãƒ•ãƒŠã‚°ã¨ã—ãĻäģ–ぎ開į™ē者ãĢåŊšįĢ‹ãŖたり、MisskeyãŽã‚ˇã‚šãƒ†ãƒ ä¸Šã§ãŽæ‰ąã„ãŒBotãĢ合ãŖたもぎãĢãĒりぞす。" flagAsCat: "CatとしãĻč¨­åŽš" flagAsCatDescription: "こぎã‚ĸã‚Ģã‚ĻãƒŗトがįŒĢであることをį¤ēす場合は、こぎフナグをã‚ĒãƒŗãĢしぞす。" +flagShowTimelineReplies: "ã‚ŋイムナイãƒŗãĢノãƒŧトへぎčŋ”äŋĄã‚’襨į¤ēする" +flagShowTimelineRepliesDescription: "ã‚ĒãƒŗãĢすると、ã‚ŋイムナイãƒŗãĢãƒĻãƒŧã‚ļãƒŧぎノãƒŧトäģĨ外ãĢもそぎãƒĻãƒŧã‚ļãƒŧぎäģ–ぎノãƒŧトへぎčŋ”äŋĄã‚’襨į¤ēしぞす。" autoAcceptFollowed: "フりロãƒŧ中ãƒĻãƒŧã‚ļãƒŧからぎフりロãƒĒクをč‡Ē動æ‰ŋčĒ" addAccount: "ã‚ĸã‚Ģã‚ĻãƒŗトをčŋŊ加" loginFailed: "ログイãƒŗãĢå¤ąæ•—ã—ãžã—ãŸ" @@ -235,6 +237,8 @@ resetAreYouSure: "ãƒĒã‚ģットしぞすかīŧŸ" saved: "äŋå­˜ã—ぞした" messaging: "チãƒŖット" upload: "ã‚ĸップロãƒŧド" +keepOriginalUploading: "ã‚ĒãƒĒジナãƒĢį”ģ像をäŋæŒ" +keepOriginalUploadingDescription: "į”ģ像をã‚ĸップロãƒŧドする時ãĢã‚ĒãƒĒジナãƒĢį‰ˆã‚’äŋæŒã—ぞす。ã‚ĒフãĢするとã‚ĸップロãƒŧド時ãĢブナã‚Ļã‚ļでWebå…Ŧ開į”¨į”ģ像をį”Ÿæˆã—ぞす。" fromDrive: "ドナイブから" fromUrl: "URLから" uploadFromUrl: "URLã‚ĸップロãƒŧド" @@ -591,6 +595,8 @@ smtpSecure: "SMTP æŽĨįļšãĢ暗éģ™įš„ãĒSSL/TLSをäŊŋį”¨ã™ã‚‹" smtpSecureInfo: "STARTTLSäŊŋį”¨æ™‚はã‚ĒフãĢしぞす。" testEmail: "配äŋĄãƒ†ã‚šãƒˆ" wordMute: "ワãƒŧドミãƒĨãƒŧト" +regexpError: "æ­ŖčĻčĄ¨įžã‚¨ãƒŠãƒŧ" +regexpErrorDescription: "{tab}ワãƒŧドミãƒĨãƒŧトぎ{line}行į›ŽãŽæ­ŖčĻčĄ¨įžãĢエナãƒŧがį™ēį”Ÿã—ぞした:" instanceMute: "イãƒŗã‚šã‚ŋãƒŗ゚ミãƒĨãƒŧト" userSaysSomething: "{name}がäŊ•ã‹ã‚’č¨€ã„ãžã—ãŸ" makeActive: "ã‚ĸクテã‚ŖブãĢする" @@ -820,6 +826,13 @@ leaveGroupConfirm: "「{name}」から抜けぞすかīŧŸ" useDrawerReactionPickerForMobile: "ãƒĸバイãƒĢデバイ゚ぎときドロワãƒŧã§čĄ¨į¤ē" welcomeBackWithName: "おかえりãĒさい、{name}さん" clickToFinishEmailVerification: "[{ok}]をæŠŧしãĻã€ãƒĄãƒŧãƒĢã‚ĸドãƒŦ゚ぎįĸēčĒã‚’厌äē†ã—ãĻください。" +overridedDeviceKind: "デバイ゚ã‚ŋイプ" +smartphone: "゚マãƒŧトフりãƒŗ" +tablet: "ã‚ŋブãƒŦット" +auto: "č‡Ē動" +themeColor: "テãƒŧマã‚Ģナãƒŧ" +size: "ã‚ĩイã‚ē" +numberOfColumn: "列ぎ数" _emailUnavailable: used: "æ—ĸãĢäŊŋį”¨ã•ã‚ŒãĻいぞす" @@ -1294,8 +1307,8 @@ _exportOrImport: excludeInactiveUsers: "äŊŋわれãĻいãĒいã‚ĸã‚Ģã‚Ļãƒŗトを除外" _charts: - federationInstancesIncDec: "é€Ŗ合ぎåĸ—減" - federationInstancesTotal: "é€ŖåˆãŽåˆč¨ˆ" + federation: "é€Ŗ合" + apRequest: "ãƒĒクエ゚ト" usersIncDec: "ãƒĻãƒŧã‚ļãƒŧぎåĸ—減" usersTotal: "ãƒĻãƒŧã‚ļãƒŧãŽåˆč¨ˆ" activeUsers: "ã‚ĸクテã‚ŖブãƒĻãƒŧã‚ļãƒŧ数" diff --git a/locales/ja-KS.yml b/locales/ja-KS.yml index 45ab9684d2..b5b4e72576 100644 --- a/locales/ja-KS.yml +++ b/locales/ja-KS.yml @@ -81,6 +81,8 @@ somethingHappened: "ãĒんかã‚ĸã‚Ģãƒŗことがčĩˇã“ãŖたで" retry: "もãŖãēんやるīŧŸ" pageLoadError: "ペãƒŧジぎčĒ­ãŋčžŧãŋãĢå¤ąæ•—ã—ãĻしもうたでâ€Ļ" pageLoadErrorDescription: "これは晎通、ネットワãƒŧクかブナã‚Ļã‚ļキãƒŖãƒƒã‚ˇãƒĨが原因やからね。キãƒŖãƒƒã‚ˇãƒĨをクãƒĒã‚ĸã™ã‚‹ã‹ã€ã‚‚ã†ãĄãŖとだけ垅ãŖãĻくれへんかīŧŸ" +serverIsDead: "The server is not responding. Please wait for a while before trying again." +youShouldUpgradeClient: "To display this page, please reload and use a new version client. " enterListName: "ãƒĒ゚ト名をå…ĨれãĻや" privacy: "ãƒ—ãƒŠã‚¤ãƒã‚ˇãƒŧ" makeFollowManuallyApprove: "č‡Ē分がčĒã‚ãŸäēēだけがこぎã‚ĸã‚Ģã‚ĻãƒŗトをフりロãƒŧできるようãĢする" @@ -104,6 +106,7 @@ clickToShow: "æŠŧしたらčĻ‹ãˆã‚‹ã§" sensitive: "ãĄã‚‡ãŖとã‚ĸã‚Ģãƒŗやつやで" add: "åĸ—やす" reaction: "ãƒĒã‚ĸã‚¯ã‚ˇãƒ§ãƒŗ" +reactionSetting: "Reaction that will be displayed in Picker. " reactionSettingDescription2: "ドナッグでä¸Ļãŗæ›ŋえ、クãƒĒックで削除、īŧ‹ã‚’æŠŧしãĻčŋŊ加やで。" rememberNoteVisibility: "å…Ŧ開į¯„å›˛čĻšãˆã¨ã„ãĻ" attachCancel: "ぎãŖけるぎやめる" @@ -138,6 +141,8 @@ flagAsBot: "Botやで" flagAsBotDescription: "もしこぎã‚ĸã‚Ģã‚ĻãƒŗトがプログナムãĢよãŖãĻ運į”¨ã•ã‚Œã‚‹ã‚“ã‚„ãŖたら、こぎフナグをã‚ĒãƒŗãĢしãĻたぎむで。ã‚ĒãƒŗãĢすると、反åŋœãŽé€ŖéŽ–ã‚’é˜˛ããŸã‚ãŽãƒ•ãƒŠã‚°ã¨ã—ãĻäģ–ぎ開į™ē者ãĢåŊšįĢ‹ãŖたり、MisskeyãŽã‚ˇã‚šãƒ†ãƒ ä¸Šã§ãŽæ‰ąã„ãŒBotãĢ合ãŖたもんãĢãĒるんやで。" flagAsCat: "Catやで" flagAsCatDescription: "ワãƒŦ、įŒĢãĄã‚ƒã‚“ãĒらこぎフナグをつけãĻãŋīŧŸ" +flagShowTimelineReplies: "It will display the reply to the note in the timeline. " +flagShowTimelineRepliesDescription: "It will display the reply to notes other than the user notes in the timeline when you turn it on. " autoAcceptFollowed: "フりロãƒŧしとるãƒĻãƒŧã‚ļãƒŧからぎフりロãƒŧãƒĒクエ゚トを勝手ãĢč¨ąå¯ã—ã¨ã" addAccount: "ã‚ĸã‚Ģã‚ĻãƒŗトをčŋŊ加" loginFailed: "ログイãƒŗãĢå¤ąæ•—ã—ãĻしもうたâ€Ļ" @@ -232,6 +237,8 @@ resetAreYouSure: "ãƒĒã‚ģットしãĻええんīŧŸ" saved: "äŋå­˜ã—たでīŧ" messaging: "チãƒŖット" upload: "ã‚ĸップロãƒŧド" +keepOriginalUploading: "Retain the original image. " +keepOriginalUploadingDescription: "When uploading the clip, the original version will be retained. Turning it of then uploading will produce images for public use. " fromDrive: "ドナイブから" fromUrl: "URLから" uploadFromUrl: "URLã‚ĸップロãƒŧド" @@ -884,6 +891,8 @@ _exportOrImport: blockingList: "ブロック" userLists: "ãƒĒ゚ト" _charts: + federation: "é€Ŗ合" + apRequest: "ãƒĒクエ゚ト" usersTotal: "ãƒĻãƒŧã‚ļãƒŧãŽåˆč¨ˆ" activeUsers: "ã‚ĸクテã‚ŖブãƒĻãƒŧã‚ļãƒŧ数" notesIncDec: "ノãƒŧトぎåĸ—減" diff --git a/locales/ko-KR.yml b/locales/ko-KR.yml index 7451603a66..116e397ff5 100644 --- a/locales/ko-KR.yml +++ b/locales/ko-KR.yml @@ -106,6 +106,7 @@ clickToShow: "클ëĻ­í•˜ė—Ŧ ëŗ´ę¸°" sensitive: "ė—´ëžŒėŖŧė˜" add: "ėļ”ę°€" reaction: "ëĻŦė•Ąė…˜" +reactionSetting: "ė„ íƒę¸°ė— 표ė‹œí•  ëĻŦė•Ąė…˜" reactionSettingDescription2: "끌ė–´ė„œ ėˆœė„œ ëŗ€ę˛Ŋ, 클ëĻ­í•´ė„œ ė‚­ė œ, īŧ‹ëĨŧ 눌ëŸŦė„œ ėļ”가할 ėˆ˜ ėžˆėŠĩ니다." rememberNoteVisibility: "ęŗĩ개 범ėœ„ëĨŧ 기ė–ĩ하기" attachCancel: "ė˛¨ëļ€ ėˇ¨ė†Œ" @@ -140,6 +141,8 @@ flagAsBot: "나는 봇ėž…니다" flagAsBotDescription: "ė´ ęŗ„ė •ė„ ėžë™í™”된 ėˆ˜ë‹¨ėœŧ로 ėš´ėšŠí•  ę˛Ŋėš°ė— 활ė„ąí™”í•´ ėŖŧė„¸ėš”. ė´ 플래그ëĨŧ 활ė„ąí™”하늴, 다ëĨ¸ 봇ė´ ė´ëĨŧ ė°¸ęŗ í•˜ė—Ŧ 봇 ëŧëĻŦė˜ ëŦ´í•œ ė—°ė‡„ 반ė‘ė„ 회í”ŧ하거나, ė´ ęŗ„ė •ė˜ ė‹œėŠ¤í…œ ėƒė—ė„œė˜ ėˇ¨ę¸‰ė´ Bot ėš´ė˜ė— ėĩœė í™”되는 등ė˜ ëŗ€í™”ę°€ ėƒęšë‹ˆë‹¤." flagAsCat: "나는 ęŗ ė–‘ė´ë‹¤ëƒĨ" flagAsCatDescription: "ė´ ęŗ„ė •ė´ ęŗ ė–‘ė´ëŧ늴 활ė„ąí™” 해ėŖŧė„¸ėš”." +flagShowTimelineReplies: "타ėž„ëŧė¸ė— 노트ė˜ ë‹ĩ글ė„ 표ė‹œí•˜ę¸°" +flagShowTimelineRepliesDescription: "ė´ ė„¤ė •ė„ 활ė„ąí™”하늴 타ėž„ëŧė¸ė— 다ëĨ¸ ėœ ė € 간ė˜ ë‹ĩ글ė„ 표ė‹œí•Šë‹ˆë‹¤." autoAcceptFollowed: "팔로ėš° ė¤‘ė¸ ėœ ė €ëĄœëļ€í„°ė˜ 팔로ėš° ėš”ė˛­ė„ ėžë™ ėˆ˜ëŊ" addAccount: "ęŗ„ė • ėļ”ę°€" loginFailed: "로그ė¸ė— ė‹¤íŒ¨í–ˆėŠĩ니다" @@ -234,6 +237,8 @@ resetAreYouSure: "ė´ˆę¸°í™” 하ė‹œę˛ ėŠĩ니까?" saved: "ė €ėžĨ하ė˜€ėŠĩ니다" messaging: "대화" upload: "ė—…ëĄœë“œ" +keepOriginalUploading: "ė›ëŗ¸ ė´ë¯¸ė§€ëĨŧ ėœ ė§€" +keepOriginalUploadingDescription: "ė´ë¯¸ė§€ëĨŧ ė—…ëĄœë“œí•  때ė— ė›ëŗ¸ė„ 그대로 ėœ ė§€í•Šë‹ˆë‹¤. 비활ė„ąí™”하늴 ė—…ëĄœë“œí•  때 브ëŧėš°ė €ė—ė„œ ė›š ęŗĩ개ėšŠ ė´ë¯¸ė§€ëĨŧ ėƒė„ąí•Šë‹ˆë‹¤." fromDrive: "드ëŧė´ë¸Œė—ė„œ" fromUrl: "URL로ëļ€í„°" uploadFromUrl: "URL ė—…ëĄœë“œ" @@ -446,6 +451,7 @@ uiLanguage: "UI 표ė‹œ ė–¸ė–´" groupInvited: "꡸ëŖšė— ė´ˆëŒ€ë˜ė—ˆėŠĩ니다" aboutX: "{x}ė— 대하ė—Ŧ" useOsNativeEmojis: "OS 기ëŗ¸ ė´ëĒ¨ė§€ëĨŧ ė‚ŦėšŠ" +disableDrawer: "드로ė–´ 메뉴ëĨŧ ė‚ŦėšŠí•˜ė§€ ė•Šę¸°" youHaveNoGroups: "꡸ëŖšė´ ė—†ėŠĩ니다" joinOrCreateGroup: "다ëĨ¸ ꡸ëŖšė˜ ė´ˆëŒ€ëĨŧ 받거나, ė§ė ‘ ėƒˆ ꡸ëŖšė„ 만들ė–´ ëŗ´ė„¸ėš”." noHistory: "기록ė´ ė—†ėŠĩ니다" @@ -617,8 +623,11 @@ reportAbuse: "ė‹ ęŗ " reportAbuseOf: "{name}ė„ ė‹ ęŗ í•˜ę¸°" fillAbuseReportDescription: "ė‹ ęŗ í•˜ë ¤ëŠ” ė´ėœ ëĨŧ ėžė„¸ížˆ ė•Œë ¤ėŖŧė„¸ėš”. 특ė • 게ė‹œëŦŧė„ ė‹ ęŗ í•  때ė—ëŠ” 게ė‹œëŦŧė˜ URL도 íŦ함해 ėŖŧė„¸ėš”." abuseReported: "ė‹ ęŗ ëĨŧ ëŗ´ëƒˆėŠĩ니다. ė‹ ęŗ í•´ ėŖŧė…”ė„œ 감ė‚Ŧ합니다." +reporter: "ė‹ ęŗ ėž" reporteeOrigin: "í”ŧė‹ ęŗ ėž" reporterOrigin: "ė‹ ęŗ ėž" +forwardReport: "ëĻŦëĒ¨íŠ¸ ė¸ėŠ¤í„´ėŠ¤ė—ë„ ė‹ ęŗ  내ėšŠ ëŗ´ë‚´ę¸°" +forwardReportIsAnonymous: "ëĻŦëĒ¨íŠ¸ ė¸ėŠ¤í„´ėŠ¤ė—ė„œëŠ” 나ė˜ ė •ëŗ´ëĨŧ ëŗŧ ėˆ˜ ė—†ėœŧ늰, ėĩëĒ…ė˜ ė‹œėŠ¤í…œ ęŗ„ė •ėœŧ로 표ė‹œëŠë‹ˆë‹¤." send: "ė „ė†Ą" abuseMarkAsResolved: "해결됨ėœŧ로 표ė‹œ" openInNewTab: "ėƒˆ 탭ė—ė„œ ė—´ę¸°" @@ -680,6 +689,7 @@ center: "가ėš´ë°" wide: "넓게" narrow: "ėĸę˛Œ" reloadToApplySetting: "ė´ ė„¤ė •ė„ ė ėšŠí•˜ë ¤ëŠ´ 페ė´ė§€ëĨŧ ėƒˆëĄœęŗ ėš¨í•´ė•ŧ 합니다. 바로 ėƒˆëĄœęŗ ėš¨í•˜ė‹œę˛ ėŠĩ니까?" +needReloadToApply: "ëŗ€ę˛Ŋ ė‚Ŧ항ė€ ėƒˆëĄœęŗ ėš¨í•˜ëŠ´ ė ėšŠëŠë‹ˆë‹¤." showTitlebar: "타ė´í‹€ 바ëĨŧ 표ė‹œí•˜ę¸°" clearCache: "ėēė‹œ 비ėš°ę¸°" onlineUsersCount: "{n}ëĒ…ė´ ė ‘ė† ė¤‘" @@ -740,6 +750,7 @@ notRecommended: "ėļ”ė˛œí•˜ė§€ ė•ŠėŒ" botProtection: "Bot ë°Šė–´" instanceBlocking: "ė¸ėŠ¤í„´ėŠ¤ ė°¨ë‹¨" selectAccount: "ęŗ„ė • ė„ íƒ" +switchAccount: "ęŗ„ė • 바꾸기" enabled: "활ė„ąí™”" disabled: "비활ė„ąí™”" quickAction: "ëš ëĨ¸ 동ėž‘" @@ -808,6 +819,15 @@ deleteAccountConfirm: "ęŗ„ė •ė´ ė‚­ė œë˜ęŗ  되돌ëĻ´ ėˆ˜ ė—†ę˛Œ 됩니다. incorrectPassword: "비밀번호가 ė˜Ŧ바ëĨ´ė§€ ė•ŠėŠĩ니다." voteConfirm: "\"{choice}\"ė— íˆŦ표하ė‹œę˛ ėŠĩ니까?" hide: "ėˆ¨ę¸°ę¸°" +leaveGroup: "꡸ëŖš 나가기" +leaveGroupConfirm: "\"{name}\"ė—ė„œ 나갈까ėš”?" +useDrawerReactionPickerForMobile: "ëĒ¨ë°”ėŧė—ė„œ 드로ė–´ 메뉴로 표ė‹œ" +welcomeBackWithName: "환ė˜í•Šë‹ˆë‹¤, {name}님" +clickToFinishEmailVerification: "[{ok}]ëĨŧ 눌ëŸŦ ė´ëŠ”ėŧ ė¸ėĻė„ ė™„ëŖŒí•˜ė„¸ėš”." +overridedDeviceKind: "ėžĨėš˜ ėœ í˜•" +smartphone: "ėŠ¤ë§ˆíŠ¸í°" +tablet: "태블ëĻŋ" +auto: "ėžë™" _emailUnavailable: used: "ė´ 메ėŧ ėŖŧė†ŒëŠ” ė‚ŦėšŠė¤‘ėž…니다" format: "형ė‹ė´ ė˜Ŧ바ëĨ´ė§€ ė•ŠėŠĩ니다" @@ -1244,8 +1264,8 @@ _exportOrImport: excludeMutingUsers: "뮤트한 ėœ ė € ė œė™¸í•˜ę¸°" excludeInactiveUsers: "휴면 ė¤‘ė¸ ęŗ„ė • ė œė™¸í•˜ę¸°" _charts: - federationInstancesIncDec: "ė—°í•Š ė¸ėŠ¤í„´ėŠ¤ ėˆ˜ ėĻę°" - federationInstancesTotal: "ė—°í•Š ė¸ėŠ¤í„´ėŠ¤ ėˆ˜ 합ęŗ„" + federation: "ė—°í•Š" + apRequest: "ėš”ė˛­" usersIncDec: "ėœ ė € ėˆ˜ ėĻę°" usersTotal: "ėœ ė € ėˆ˜ 합ęŗ„" activeUsers: "활ė„ą ėœ ė € ėˆ˜" diff --git a/locales/nl-NL.yml b/locales/nl-NL.yml index 386357f2d3..38f9a88af8 100644 --- a/locales/nl-NL.yml +++ b/locales/nl-NL.yml @@ -291,6 +291,8 @@ _exportOrImport: userLists: "Lijsten" excludeMutingUsers: "Negeer gedempte gebruikers" excludeInactiveUsers: "Negeer inactieve gebruikers" +_charts: + federation: "Federatie" _timelines: home: "Startpagina" _pages: diff --git a/locales/pl-PL.yml b/locales/pl-PL.yml index 27772663bc..0b57a3a46f 100644 --- a/locales/pl-PL.yml +++ b/locales/pl-PL.yml @@ -1084,7 +1084,8 @@ _exportOrImport: blockingList: "Zablokuj" userLists: "Listy" _charts: - federationInstancesTotal: "Łącznie sfederowanych instancji" + federation: "Federacja" + apRequest: "Åģądania" usersTotal: "Łącznie # uÅŧytkownikÃŗw" activeUsers: "Aktywni uÅŧytkownicy" _instanceCharts: diff --git a/locales/ru-RU.yml b/locales/ru-RU.yml index 7de9f8ff95..b29d2173c8 100644 --- a/locales/ru-RU.yml +++ b/locales/ru-RU.yml @@ -235,6 +235,7 @@ resetAreYouSure: "На ŅĐ°ĐŧĐžĐŧ Đ´ĐĩĐģĐĩ ŅĐąŅ€ĐžŅĐ¸Ņ‚ŅŒ?" saved: "ĐĄĐžŅ…Ņ€Đ°ĐŊĐĩĐŊĐž" messaging: "ХООйŅ‰ĐĩĐŊиŅ" upload: "ЗаĐŗŅ€ŅƒĐˇĐ¸Ņ‚ŅŒ" +keepOriginalUploading: "ĐĄĐžŅ…Ņ€Đ°ĐŊиŅ‚ŅŒ иŅŅ…ОдĐŊĐžĐĩ иСОйŅ€Đ°ĐļĐĩĐŊиĐĩ" fromDrive: "ĐĄ ÂĢдиŅĐēĐ°Âģ" fromUrl: "По ŅŅŅ‹ĐģĐēĐĩ" uploadFromUrl: "ЗаĐŗŅ€ŅƒĐˇĐ¸Ņ‚ŅŒ ĐŋĐž ŅŅŅ‹ĐģĐēĐĩ" @@ -743,6 +744,7 @@ notRecommended: "НĐĩ Ņ€ĐĩĐēĐžĐŧĐĩĐŊĐ´ŅƒĐĩŅ‚ŅŅ" botProtection: "БоŅ‚ОСаŅ‰Đ¸Ņ‚Đ°" instanceBlocking: "БĐģĐžĐēиŅ€ĐžĐ˛ĐēĐ° иĐŊŅŅ‚Đ°ĐŊŅĐžĐ˛" selectAccount: "ВŅ‹ĐąĐĩŅ€Đ¸Ņ‚Đĩ ŅƒŅ‡Ņ‘Ņ‚ĐŊŅƒŅŽ СаĐŋиŅŅŒ" +switchAccount: "ĐĄĐŧĐĩĐŊиŅ‚ŅŒ ŅƒŅ‡Ņ‘Ņ‚ĐŊŅƒŅŽ СаĐŋиŅŅŒ" enabled: "ВĐēĐģ." disabled: "ОŅ‚ĐēĐģ." quickAction: "БŅ‹ŅŅ‚Ņ€ĐžĐĩ Đ´ĐĩĐšŅŅ‚виĐĩ" @@ -1249,8 +1251,8 @@ _exportOrImport: excludeMutingUsers: "За иŅĐēĐģŅŽŅ‡ĐĩĐŊиĐĩĐŧ СаĐŗĐģŅƒŅˆĐĩĐŊĐŊŅ‹Ņ… ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģĐĩĐš" excludeInactiveUsers: "БĐĩС ĐŊĐĩĐ°ĐēŅ‚ивĐŊŅ‹Ņ… ŅƒŅ‡Ņ‘Ņ‚ĐŊŅ‹Ņ… СаĐŋиŅĐĩĐš" _charts: - federationInstancesIncDec: "ИСĐŧĐĩĐŊĐĩĐŊиĐĩ вĐŊĐĩŅˆĐŊиŅ… ŅĐ˛ŅĐˇĐĩĐš" - federationInstancesTotal: "КоĐģиŅ‡ĐĩŅŅ‚вО вĐŊĐĩŅˆĐŊиŅ… ŅĐ˛ŅĐˇĐĩĐš" + federation: "ФĐĩĐ´ĐĩŅ€Đ°Ņ†Đ¸Ņ" + apRequest: "ЗаĐŋŅ€ĐžŅŅ‹" usersIncDec: "ИСĐŧĐĩĐŊĐĩĐŊиĐĩ Ņ‡Đ¸ŅĐģĐ° ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģĐĩĐš" usersTotal: "КоĐģиŅ‡ĐĩŅŅ‚вО ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģĐĩĐš" activeUsers: "АĐēŅ‚ивĐŊŅ‹Đĩ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģи" diff --git a/locales/sk-SK.yml b/locales/sk-SK.yml new file mode 100644 index 0000000000..a501591a8b --- /dev/null +++ b/locales/sk-SK.yml @@ -0,0 +1,1641 @@ +--- +_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! 🚀" +monthAndDay: "{day}. {month}." +search: "HÄžadaÅĨ" +notifications: "OznÃĄmenia" +username: "Meno pouŞívateÄža" +password: "Heslo" +forgotPassword: "ZabudnutÊ heslo" +fetchingAsApObject: "Načítam Ãēdaje z Fediverzu" +ok: "OK" +gotIt: "Rozumiem!" +cancel: "ZruÅĄiÅĨ" +enterUsername: "Zadajte meno pouŞívateÄža" +renotedBy: "{user} preposlal/a" +noNotes: "ÅŊiadne poznÃĄmky" +noNotifications: "ÅŊiadne oznÃĄmenia" +instance: "InÅĄtancia" +settings: "Nastavenia" +basicSettings: "VÅĄeobecnÊ nastavenia" +otherSettings: "RozÅĄÃ­renÊ nastavenia" +openInWindow: "OtvoriÅĨ v novom okne" +profile: "Profil" +timeline: "ČasovÃĄ os" +noAccountDescription: "Tento pouŞívateÄž zatiaÄž nenapísal o sebe." +login: "PrihlÃĄsiÅĨ sa" +loggingIn: "Prebieha prihlasovanie" +logout: "OdhlÃĄsiÅĨ" +signup: "RegistrovaÅĨ" +uploading: "NahrÃĄvanie..." +save: "UloÅžiÅĨ" +users: "PouŞívatelia" +addUser: "PridaÅĨ pouŞívateÄža" +favorite: "PÃĄÄi sa mi" +favorites: "ObÄžÃēbenÊ" +unfavorite: "NepÃĄÄi sa mi" +favorited: "PridanÊ do obÄžÃēbenÃŊch" +alreadyFavorited: "UÅž je medzi obÄžÃēbenÃŊmi" +cantFavorite: "Nepodarilo sa pridaÅĨ medzi obÄžÃēbenÊ." +pin: "PripnÃēÅĨ" +unpin: "OdopnÃēÅĨ" +copyContent: "KopírovaÅĨ obsah" +copyLink: "KopírovaÅĨ odkaz" +delete: "OdstrÃĄniÅĨ" +deleteAndEdit: "OdstrÃĄniÅĨ a upraviÅĨ" +deleteAndEditConfirm: "Naozaj chcete odstrÃĄniÅĨ tÃēto poznÃĄmku a upraviÅĨ ju? Stratíte tÃŊm vÅĄetky reakcie a odpovede na ňu." +addToList: "PridaÅĨ do zoznamu" +sendMessage: "OdoslaÅĨ sprÃĄvu" +copyUsername: "KopírovaÅĨ meno pouŞívateÄža" +searchUser: "HÄžadaÅĨ pouŞívateÄžov" +reply: "OdpovedaÅĨ" +loadMore: "ZobraziÅĨ viac" +showMore: "ZobraziÅĨ viac" +youGotNewFollower: "MÃĄte novÊho sledujÃēceho" +receiveFollowRequest: "ÅŊiadosÅĨ o sledovanie prijatÃĄ" +followRequestAccepted: "ÅŊiadosÅĨ o sledovanie akceptovanÃĄ" +mention: "Zmienka" +mentions: "Zmienky" +directNotes: "Priame poznÃĄmky" +importAndExport: "Import a export" +import: "ImportovaÅĨ" +export: "ExportovaÅĨ" +files: "SÃēbor/y" +download: "StiahnuÅĨ" +driveFileDeleteConfirm: "Naozaj chcete odstrÃĄniÅĨ sÃēbor \"{name}\"? PoznÃĄmky s tÃŊmto sÃēborom sa odstrÃĄnia tieÅž." +unfollowConfirm: "Naozaj uÅž nechcete sledovaÅĨ {name}?" +exportRequested: "VyÅžiadali ste export. MôŞe to chvíĞu trvaÅĨ. Po skončení pribudne na vaÅĄom disku." +importRequested: "PoÅžiadali ste o export. MôŞe to chvíĞu trvaÅĨ." +lists: "Zoznamy" +noLists: "NemÃĄte Åžiadne zoznamy" +note: "PoznÃĄmka" +notes: "PoznÃĄmky" +following: "Sledujete" +followers: "SledujÃēci" +followsYou: "SledujÃē vÃĄs" +createList: "VytvoriÅĨ zoznam" +manageLists: "SpravovaÅĨ zoznamy" +error: "Chyba" +somethingHappened: "Ups. Niečo sa nepodarilo." +retry: "OpakovaÅĨ" +pageLoadError: "Nepodarilo sa načítaÅĨ strÃĄnku" +pageLoadErrorDescription: "Toto môŞe byÅĨ spôsobenÊ problÊmami so sieÅĨou alebo cachou prehliadača. SkÃēste vyčistiÅĨ cache a potom skÃēsiÅĨ znova po chvíli." +serverIsDead: "Tento server nereaguje. Prosím chvíĞu počkajte a skÃēste znova." +youShouldUpgradeClient: "Na pozretie tejto strÃĄnky prosím obnovte svojho klienta." +enterListName: "Zadajte nÃĄzov zoznamu" +privacy: "SÃēkromie" +makeFollowManuallyApprove: "ÅŊiadosti o sledovanie treba schvÃĄliÅĨ" +defaultNoteVisibility: "PredvolenÃĄ viditeÄžnosÅĨ" +follow: "SledovaÅĨ" +followRequest: "PoÅžiadaÅĨ o sledovanie" +followRequests: "ÅŊiadosti o sledovanie" +unfollow: "NesledovaÅĨ" +followRequestPending: "ÅŊiadosÅĨ o sledovanie čakÃĄ" +enterEmoji: "Zadajte emoji" +renote: "PreposlaÅĨ" +unrenote: "VrÃĄtiÅĨ preposlanie" +renoted: "PreposlanÊ." +cantRenote: "Tento príspevok sa nedÃĄ preposlaÅĨ." +cantReRenote: "Odpoveď nemôŞe byÅĨ odstrÃĄnenÃĄ." +quote: "CitovaÅĨ" +pinnedNote: "PripnutÊ poznÃĄmky" +pinned: "PripnÃēÅĨ" +you: "Vy" +clickToShow: "Kliknutím zobrazíte" +sensitive: "NSFW" +add: "PridaÅĨ" +reaction: "Reakcie" +reactionSetting: "Reakcie zobrazenÊ vo vÃŊbere reakcií" +reactionSettingDescription2: "Ťahaním preusporiadate, kliknutím odstrÃĄnite, Stlačením \"+\" pridÃĄte" +rememberNoteVisibility: "ZapamätaÅĨ nastavenia viditeÄžnosti poznÃĄmky" +attachCancel: "OdstrÃĄniÅĨ prílohu" +markAsSensitive: "OznačiÅĨ ako NSFW" +unmarkAsSensitive: "OdznačiÅĨ NSFW" +enterFileName: "Zadajte nÃĄzov sÃēboru" +mute: "VypnÃēÅĨ zvuk" +unmute: "ZapnÃēÅĨ zvuk" +block: "ZablokovaÅĨ" +unblock: "OdblokovaÅĨ" +suspend: "ZmraziÅĨ" +unsuspend: "OdmraziÅĨ" +blockConfirm: "Naozaj chcete zablokovaÅĨ tento Ãēčet?" +unblockConfirm: "Naozaj chcete odblokovaÅĨ tento Ãēčet?" +suspendConfirm: "Naozaj chcete zmraziÅĨ tento Ãēčet?" +unsuspendConfirm: "Naozaj chcete odmraziÅĨ tento Ãēčet?" +selectList: "Vyberte zoznam" +selectAntenna: "Vyberte antÊnu" +selectWidget: "Vyberte widget" +editWidgets: "UpraviÅĨ widget" +editWidgetsExit: "Hotovo" +customEmojis: "VlastnÊ emoji" +emoji: "Emoji" +emojis: "Emoji" +emojiName: "NÃĄzov emoji" +emojiUrl: "URL obrÃĄzku" +addEmoji: "PridaÅĨ emoji" +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." +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" +flagShowTimelineRepliesDescription: "Keď je zapnutÊ, na časovej osi sa zobrazia odpovede k poznÃĄmkam pouŞívateÄžov okrem samotnÃŊch poznÃĄmok." +autoAcceptFollowed: "Automaticky prijaÅĨ sledovanie od Ãēčtov, ktorÊ sledujete" +addAccount: "PridaÅĨ Ãēčet" +loginFailed: "PrihlÃĄsenie sa nepodarilo." +showOnRemote: "ZobraziÅĨ na vzdialenom serveri" +general: "VÅĄeobecnÊ" +wallpaper: "Tapeta" +setWallpaper: "NastaviÅĨ tapetu" +removeWallpaper: "OdstrÃĄniÅĨ tapetu" +searchWith: "HÄžadaÅĨ: {q}" +youHaveNoLists: "NemÃĄte Åžiadne zoznamy" +followConfirm: "Naozaj chcete sledovaÅĨ {name}?" +proxyAccount: "Proxy Ãēčet" +proxyAccountDescription: "Proxy Ãēčet je Ãēčet, ktorÃŊ za určitÃŊch podmienok sleduje pouŞívateÄžov na diaÄžku vaÅĄÃ­m menom. Napríklad keď pouŞívateÄž zaradí vzdialenÊho pouŞívateÄža do zoznamu, pokiaÄž nikto nesleduje pouŞívateÄža na zozname, aktivita nebude doručenÃĄ na server, takÅže namiesto toho bude pouŞívateÄža sledova proxy Ãēčet." +host: "Host" +selectUser: "Vyberte pouŞívateÄža" +recipient: "PrijímateÄž" +annotation: "KomentÃĄre" +federation: "FederÃĄcia" +instances: "InÅĄtancia" +registeredAt: "RegistrÃĄcia" +latestRequestSentAt: "PoslednÃĄ odoslanÃĄ poÅžiadavka" +latestRequestReceivedAt: "PoslednÃĄ prijatÃĄ poÅžiadavka" +latestStatus: "PoslednÃŊ status" +storageUsage: "VyuÅžitÊ ÃēloÅžisko" +charts: "Grafy" +perHour: "za hodinu" +perDay: "za deň" +stopActivityDelivery: "ZastaviÅĨ posielanie aktivít" +blockThisInstance: "BlokovaÅĨ tento server" +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äÅĨ" +network: "SieÅĨ" +disk: "Disk" +instanceInfo: "InformÃĄcie o serveri" +statistics: "Å tatistiky" +clearQueue: "VyčistiÅĨ frontu" +clearQueueConfirmTitle: "Naozaj chcete zruÅĄiÅĨ vÅĄetky Ãēlohy vo fronte?" +clearQueueConfirmText: "VÅĄetky nedoručenÊ poznÃĄmky čakajÃēce vo fronte nebudÃē federovanÊ. Zvyčajne tÃĄto operÃĄcia nie je potrebnÃĄ." +clearCachedFiles: "VyprÃĄzdniÅĨ cache" +clearCachedFilesConfirm: "Naozaj chcete odstrÃĄniÅĨ vÅĄetky nacachovanÊ vzdialenÊ sÃēbory?" +blockedInstances: "BlokovanÊ servery" +blockedInstancesDescription: "Zoznam blokovanÃŊch serverov na riadkoch. BlokovanÊ servery nebudÃē môcÅĨ komunikovaÅĨ s tÃŊmto serverom." +muteAndBlock: "Umlčania a blokÃĄcie" +mutedUsers: "Umlčaní pouŞívatelia" +blockedUsers: "Blokovaní pouŞívatelia" +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." +done: "Hotovo" +processing: "Pracujem..." +preview: "NÃĄhÄžad" +default: "PredvolenÊ" +noCustomEmojis: "ÅŊiadne emoji" +noJobs: "ÅŊiadne Ãēlohy" +federating: "FederÃĄcia" +blocked: "BlokovanÊ" +suspended: "ZmrazenÊ" +all: "VÅĄetko" +subscribing: "Odoberanie" +publishing: "Zverejňovanie" +notResponding: "NeodpovedÃĄ" +instanceFollowing: "Sledujem na serveri" +instanceFollowers: "SledujÃēci zo servera" +instanceUsers: "PouŞívatelia servera" +changePassword: "ZmeniÅĨ heslo" +security: "Zabezpečenie" +retypedNotMatch: "ZadanÊ vstupy nesÃēhlasia" +currentPassword: "AktuÃĄlne heslo" +newPassword: "NovÊ heslo" +newPasswordRetype: "NovÊ heslo (znovu)" +attachFile: "PriloÅžiÅĨ sÃēbor" +more: "Viac!" +featured: "ObÄžÃēbenÊ poznÃĄmky" +usernameOrUserId: "Meno pouŞívateÄža alebo ID pouŞívateÄža" +noSuchUser: "PouŞívateÄž sa nenaÅĄiel" +lookup: "VyhÄžadaÅĨ" +announcements: "Oznamy" +imageUrl: "URL obrÃĄzku" +remove: "OdstrÃĄniÅĨ" +removed: "OdstrÃĄnenÊ" +removeAreYouSure: "Naozaj chcete odstrÃĄniÅĨ \"{x}\"?" +deleteAreYouSure: "Naozaj chcete odstrÃĄniÅĨ \"{x}\"?" +resetAreYouSure: "Naozaj resetovaÅĨ?" +saved: "UloÅženÊ" +messaging: "Chat" +upload: "NahraÅĨ sÃēbor" +keepOriginalUploading: "ZachovaÅĨ pôvodnÃŊ obrÃĄzok" +keepOriginalUploadingDescription: "UloŞí pôvodnÃŊ obrÃĄzok ako je. Ak je vypnutÊ, verzia pre web sa vygeneruje pri nahratí." +fromDrive: "Z disku" +fromUrl: "Z URL" +uploadFromUrl: "NahraÅĨ z URL adresy" +uploadFromUrlDescription: "URL adresa nahrÃĄvanÊho sÃēboru" +uploadFromUrlRequested: "Upload vyÅžiadanÃŊ" +uploadFromUrlMayTakeTime: "NahrÃĄvanie môŞe nejakÃŊ čas trvaÅĨ." +explore: "ObjavovaÅĨ" +messageRead: "PrečítanÊ" +noMoreHistory: "To je vÅĄetko" +startMessaging: "ZačaÅĨ chat" +nUsersRead: "prečítanÊ {n} pouŞívateÄžmi" +agreeTo: "SÃēhlasím s {0}" +tos: "Podmienky pouŞívania" +start: "ZačaÅĨ" +home: "Domov" +remoteUserCaution: "Tieto informÃĄcie nemusia byÅĨ aktuÃĄlne, keďŞe pouŞívateÄž je na vzdialenom serveri." +activity: "Aktivita" +images: "ObrÃĄzky" +birthday: "DÃĄtum narodenia" +yearsOld: "{age} rokov" +registeredDate: "DÃĄtum registrÃĄcie" +location: "Lokalita" +theme: "TÊma" +themeForLightMode: "TÊma pri svetlom reÅžime" +themeForDarkMode: "TÊma pri tmavom reÅžime" +light: "SvetlÃĄ" +dark: "TmavÃĄ" +lightThemes: "SvetlÃĄ tÊma" +darkThemes: "TmavÃĄ tÊma" +syncDeviceDarkMode: "SynchronizovaÅĨ tmavÃē tÊmu s nastavení vÃĄÅĄho systÊmu" +drive: "Disk" +fileName: "NÃĄzov sÃēboru" +selectFile: "Vyberte sÃēbor" +selectFiles: "Vyberte sÃēbory" +selectFolder: "Vyberte priečinok" +selectFolders: "Vyberte priečinky" +renameFile: "PremenovaÅĨ sÃēbor" +folderName: "NÃĄzov priečinka" +createFolder: "VytvoriÅĨ priečinok" +renameFolder: "PremenovaÅĨ priečinok" +deleteFolder: "OdstrÃĄniÅĨ priečinok" +addFile: "PridaÅĨ sÃēbor" +emptyDrive: "VÃĄÅĄ disk je prÃĄzdny" +emptyFolder: "Tento priečinok je prÃĄzdny" +unableToDelete: "NedÃĄ sa odstrÃĄniÅĨ" +inputNewFileName: "Zadajte novÃŊ nÃĄzov" +inputNewDescription: "Zadajte novÃŊ popis" +inputNewFolderName: "Zadajte novÃŊ nÃĄzov priečinka" +circularReferenceFolder: "CieÄžovÃŊ priečinok je podpriečinkom priečinka, ktorÃŊ chcete presunÃēÅĨ." +hasChildFilesOrFolders: "NemôŞete odstrÃĄniÅĨ priečinok sÃē sÃēbormi." +copyUrl: "KopírovaÅĨ URL" +rename: "PremenovaÅĨ" +avatar: "Avatar" +banner: "BAnner" +nsfw: "NSFW" +whenServerDisconnected: "Keď sa stratí spojenie so serverom" +disconnectedFromServer: "Spojenie so serverom bolo preruÅĄenÊ" +reload: "ObnoviÅĨ" +doNothing: "IgnorovaÅĨ" +reloadConfirm: "Chcete obnoviÅĨ časovÃē os?" +watch: "SledovaÅĨ" +unwatch: "NesledovaÅĨ" +accept: "SÃēhlasím" +reject: "NesÃēhlasím" +normal: "NormÃĄlne" +instanceName: "NÃĄzov servera" +instanceDescription: "Popis servera" +maintainerName: "SprÃĄvca" +maintainerEmail: "E-mailovÃĄ adresa sprÃĄvcu" +tosUrl: "URL zmluvnÃŊch podmienok" +thisYear: "Rok" +thisMonth: "Mesiac" +today: "Dnes" +dayX: "{day}" +monthX: "{month}" +yearX: "{year}" +pages: "StrÃĄnky" +integration: "IntegrÃĄcia" +connectService: "PripojiÅĨ" +disconnectService: "OdpojiÅĨ" +enableLocalTimeline: "PovoliÅĨ lokÃĄlnu časovÃē os" +enableGlobalTimeline: "PovoliÅĨ globÃĄlnu časovÃē os" +disablingTimelinesInfo: "AdministrÃĄtori a moderÃĄtori majÃē vÅždy prístup ku vÅĄetkÃŊm časovÃŊm osiam, aj keď sÃē vypnutÊ." +registration: "RegistrÃĄcia" +enableRegistration: "PovoliÅĨ registrÃĄciu novÃŊch pouŞívateÄžov" +invite: "PozvaÅĨ" +proxyRemoteFiles: "Proxy vzdialenÃŊch sÃēborov" +proxyRemoteFilesDescription: "Ak je zapnutÊ, vzdialenÊ sÃēbory, ktorÊ nie sÃē uloÅženÊ lokÃĄlne alebo boli odstrÃĄnenÊ kvôli obmedzeniam ÃēloÅžiska, budÃē vyÅžiadanÊ cez proxy, vrÃĄtane generovani miniatÃēr. Neovplyvní to ÃēloÅžisko na serveri." +driveCapacityPerLocalAccount: "Kapacita disku pre pouŞívateÄža" +driveCapacityPerRemoteAccount: "Kapacita disku pre vzdialenÊho pouŞívateÄža" +inMb: "V megabajtoch" +iconUrl: "Favicon URL" +bannerUrl: "URL obrÃĄzku bannera" +backgroundImageUrl: "URL obrÃĄzku pozadia" +basicInfo: "ZÃĄkladnÊ informÃĄcie" +pinnedUsers: "Pripnutí pouŞívatelia" +pinnedUsersDescription: "Zoznam mien pouŞívateÄžov oddelenÃŊch riadkami, ktorÃŊ budÃē pripnutí v zÃĄloÅžke \"ObjavovaÅĨ\"." +pinnedPages: "PripnutÊ strÃĄnky" +pinnedPagesDescription: "Na kaÅždÃŊ riadok zadajte cesty strÃĄnok, ktorÊ chcete pripnÃēÅĨ na vrch strÃĄnky tohoto servera." +pinnedClipId: "ID pripnutÊho klipu" +pinnedNotes: "PripnutÊ poznÃĄmky" +hcaptcha: "hCaptcha" +enableHcaptcha: "ZapnÃēÅĨ hCaptchu" +hcaptchaSiteKey: "Site key" +hcaptchaSecretKey: "Secret key" +recaptcha: "reCAPTCHA" +enableRecaptcha: "ZapnÃēÅĨ ReCAPTCHA" +recaptchaSiteKey: "Site key" +recaptchaSecretKey: "Secret key" +avoidMultiCaptchaConfirm: "PouÅžitie viacerÃŊch Captcha systÊmov môŞe sposobiÅĨ problÊmy. Chcete radÅĄej vypnÃēÅĨ ostatnÊ Captcha systÊmy? MôŞete ich povoliÅĨ viacerÊ stlačení ZruÅĄiÅĨ." +antennas: "AntÊny" +manageAntennas: "SpravovaÅĨ antÊny" +name: "NÃĄzov" +antennaSource: "Zdroj antÊny" +antennaKeywords: "PočÃēvanÊ kÄžÃēčovÊ slovÃĄ" +antennaExcludeKeywords: "VylÃēčenÊ kÄžÃēčovÊ slovÃĄ" +antennaKeywordsDescription: "OddeÄžte medzerami pre podmienku AND alebo novÃŊmi riadkami pre podmienku OR." +notifyAntenna: "UpozorniÅĨ na novÊ poznÃĄmky" +withFileAntenna: "Len poznÃĄmky so sÃēbormi" +enableServiceworker: "PovoliÅĨ Service Worker" +antennaUsersDescription: "Zoznam pouŞívateÄžov jeden na riadok" +caseSensitive: "RozliÅĄuje malÊ a veÄžkÊ písmenÃĄ" +withReplies: "VrÃĄtane odpovedí" +connectedTo: "NasledujÃēce Ãēčty sÃē pripojenÊ" +notesAndReplies: "PoznÃĄmky a odpovede" +withFiles: "VrÃĄtane sÃēborov" +silence: "Ticho" +silenceConfirm: "Naozaj chcete utÃ­ÅĄiÅĨ tohoto pouŞívateÄža?" +unsilence: "VrÃĄtiÅĨ utÃ­ÅĄenie" +unsilenceConfirm: "Naozaj chcete vrÃĄtiÅĨ utÃ­ÅĄenie tohoto pouŞívateÄža?" +popularUsers: "PopulÃĄrni pouŞívatelia" +recentlyUpdatedUsers: "PouŞívatelia s najnovÅĄou aktivitou" +recentlyRegisteredUsers: "NajnovÅĄÃ­ pouŞívatelia" +recentlyDiscoveredUsers: "Naposledy objavení pouŞívatelia" +exploreUsersCount: "Existuje {count} pouŞívateÄžov" +exploreFediverse: "ObjavovaÅĨ Fediverzum" +popularTags: "PopulÃĄrne značky" +userList: "Zoznamy" +about: "InformÃĄcie" +aboutMisskey: "O Misskey" +administrator: "AdministrÃĄtor" +token: "Token" +twoStepAuthentication: "DvojfaktorovÃĄ autentifikÃĄcia" +moderator: "ModerÃĄtor" +nUsersMentioned: "{n} pouŞívateÄžov spomenulo" +securityKey: "BezpečnostnÃŊ kÄžÃēč" +securityKeyName: "NÃĄzov kÄžÃēča" +registerSecurityKey: "RegistrovaÅĨ bezpečnostnÃŊ kÄžÃēč" +lastUsed: "Naposledy pouÅžitÊ" +unregister: "OdregistrovaÅĨ" +passwordLessLogin: "NastaviÅĨ bezheslovÊ prihlÃĄsenie" +resetPassword: "ResetovaÅĨ heslo" +newPasswordIs: "NovÊ heslo je \"{password}\"" +reduceUiAnimation: "Menej UI animÃĄcií" +share: "ZdieÄžaÅĨ" +notFound: "NenÃĄjdenÊ" +notFoundDescription: "NenaÅĄla sa Åžiadna strÃĄnka na zadanej URL." +uploadFolder: "PredvolenÃŊ priečinok pre nahrÃĄvanie" +cacheClear: "VyčistiÅĨ cache" +markAsReadAllNotifications: "OznačiÅĨ vÅĄetky oznÃĄmenia ako prečítanÊ" +markAsReadAllUnreadNotes: "OznačiÅĨ vÅĄetky poznÃĄmky ako prečítanÊ" +markAsReadAllTalkMessages: "OznačiÅĨ vÅĄetky sprÃĄvy ako prečítanÊ" +help: "Pomoc" +inputMessageHere: "Sem napÃ­ÅĄte sprÃĄvu" +close: "ZavrieÅĨ" +group: "Skupina" +groups: "Skupiny" +createGroup: "VytvoriÅĨ skupinu" +ownedGroups: "VlastnenÊ skupiny" +joinedGroups: "Členstvo v skupinÃĄch" +invites: "PozvaÅĨ" +groupName: "NÃĄzov skupiny" +members: "Členovia" +transfer: "Presun" +messagingWithUser: "SÃēkromnÃŊ chat" +messagingWithGroup: "SkupinovÃŊ chat" +title: "Nadpis" +text: "Text" +enable: "PovoliÅĨ" +next: "ĎalÅĄÃ­" +retype: "Zadajte znovu" +noteOf: "PoznÃĄmky pouŞívateÄža {user}" +inviteToGroup: "PozvaÅĨ do skupiny" +maxNoteTextLength: "MaximÃĄlny počet znakov poznÃĄmky" +quoteAttached: "CitovanÊ" +quoteQuestion: "PripojiÅĨ ako citÃĄt?" +noMessagesYet: "ZatiaÄž Åžiadne sprÃĄvy" +newMessageExists: "MÃĄte novÃē sprÃĄvu" +onlyOneFileCanBeAttached: "Ku sprÃĄve môŞete priloÅžiÅĨ len jeden sÃēbor" +signinRequired: "PrihlÃĄste sa, prosím!" +invitations: "PozvaÅĨ" +invitationCode: "KÃŗd pozvÃĄnky" +checking: "Overujem..." +available: "DostupnÊ" +unavailable: "NedostupnÊ" +usernameInvalidFormat: "PovolenÊ sÃē písmenÃĄ, čísla a _." +tooShort: "PríliÅĄ krÃĄtke" +tooLong: "PríliÅĄ dlhÊ" +weakPassword: "SlabÊ heslo" +normalPassword: "DobrÊ heslo" +strongPassword: "SilnÊ heslo" +passwordMatched: "HeslÃĄ sÃē rovnakÊ" +passwordNotMatched: "HeslÃĄ nie sÃē rovnakÊ" +signinWith: "PrihlÃĄsiÅĨ sa pouÅžitím {x}" +signinFailed: "NedÃĄ sa prihlÃĄsiÅĨ. Skontrolujte prosím meno pouŞívateÄža a heslo." +tapSecurityKey: "Ťuknite na bezpečnostnÃŊ kÄžÃēč" +or: "Alebo" +language: "Jazyk" +uiLanguage: "Jazyk pouŞívateÄžskÊho prostredia" +groupInvited: "PozvaÅĨ do skupiny" +aboutX: "O {x}" +useOsNativeEmojis: "PouŞívaÅĨ natívne emoji z OS" +disableDrawer: "NepouŞívaÅĨ ÅĄuflíkovÊ menu" +youHaveNoGroups: "NemÃĄte Åžiadne skupiny" +joinOrCreateGroup: "PoÅžiadajte o pozvanie do existujÃēcej skupiny alebo vytvorte novÃē." +noHistory: "ÅŊiadna histÃŗria" +signinHistory: "HistÃŗria prihlÃĄsení" +disableAnimatedMfm: "VypnÃēÅĨ MFM s animÃĄciou" +doing: "Pracujem..." +category: "KategÃŗrie" +tags: "Značky" +docSource: "Zdroj tohoto dokumentu" +createAccount: "VytvoriÅĨ Ãēčet" +existingAccount: "ExistujÃēci Ãēčet" +regenerate: "PregenerovaÅĨ" +fontSize: "VeÄžkosÅĨ písma" +noFollowRequests: "NemÃĄte nijakÊ čakajÃēce Åžiadosti o sledovanie" +openImageInNewTab: "OtvoriÅĨ obrÃĄzok v novom tabe" +dashboard: "PrehÄžad" +local: "LokÃĄlne" +remote: "VzdialenÊ" +total: "Celkom" +weekOverWeekChanges: "MedzitÃŊÅždňovÊ zmeny" +dayOverDayChanges: "MedzidennÊ zmeny" +appearance: "VzhÄžad" +clientSettings: "Nastavenia klienta" +accountSettings: "Nastavenia Ãēčtu" +promotion: "PropagÃĄcia" +promote: "PropagovaÅĨ" +numberOfDays: "Počet dní" +hideThisNote: "SkryÅĨ tÃēto poznÃĄmku" +showFeaturedNotesInTimeline: "ZobraziÅĨ vÃŊznamnÊ poznÃĄmky v časovej osi" +objectStorage: "ObjektovÊ ÃēloÅžisko" +useObjectStorage: "PouÅžiÅĨ objektovÊ ÃēloÅžisko" +objectStorageBaseUrl: "ZÃĄkladnÃĄ URL" +objectStorageBaseUrlDesc: "URL pouÅžitÃĄ ako referencia. Zadajte URL svojho CDN alebo Proxy ak niektorÊ pouŞívate. S3: 'https://.s3.amazonaws.com', GCS: 'https://storage.googleapis.com/' atď." +objectStorageBucket: "Bucket" +objectStorageBucketDesc: "Prosím zadajte nÃĄzov bucketu od svojho poskytovateÄža." +objectStoragePrefix: "Prefix" +objectStoragePrefixDesc: "SÃēbory budÃē ukladanÊ do priečinkov pod tÃŊmto prefixom." +objectStorageEndpoint: "Endpoint" +objectStorageEndpointDesc: "Nechajte prÃĄzdne ak pouŞívate AWS S3, inak zadajte endpoint ako \"\" alebo \":\". ZÃĄleŞí to od sluÅžby, ktorÃē pouŞívate." +objectStorageRegion: "RegiÃŗn" +objectStorageRegionDesc: "Zadajte regiÃŗn ako 'xx-east-1'. Ak vaÅĄa sluÅžba nerozliÅĄuje regiÃŗny, nechajte prÃĄzdne alebo zadajte 'us-east-1'." +objectStorageUseSSL: "PouÅžiÅĨ SSL" +objectStorageUseSSLDesc: "Vypnite to ak nechcete pouÅžiÅĨ HTTPS na API spojenia." +objectStorageUseProxy: "PripÃĄjaÅĨ cez Proxy" +objectStorageUseProxyDesc: "Vypnite ak nechcete, aby spojenia na API iÅĄli cez Proxy" +objectStorageSetPublicRead: "Pri nahratí nastaviÅĨ \"public-read\"" +serverLogs: "Logy servera" +deleteAll: "OdstrÃĄniÅĨ vÅĄetko" +showFixedPostForm: "ZobraziÅĨ formulÃĄr na novÊ príspevky nad časovou osou" +newNoteRecived: "SÃē novÊ poznÃĄmky" +sounds: "Zvuky" +listen: "PočÃēvaÅĨ" +none: "ÅŊiadne" +showInPage: "ZobraziÅĨ v strÃĄnke" +popout: "Pop-out" +volume: "HlasitosÅĨ" +masterVolume: "CelkovÃĄ hlasitosÅĨ" +details: "Detaily" +chooseEmoji: "VybraÅĨ emoji" +unableToProcess: "OperÃĄciu sa nepodarilo dokončiÅĨ." +recentUsed: "Neposledy pouÅžitÊ" +install: "NainÅĄtalovaÅĨ" +uninstall: "OdinÅĄtalovaÅĨ" +installedApps: "AutorizovanÊ aplikÃĄcie" +nothing: "Nič tu nie je" +installedDate: "DÃĄtum autorizÃĄcie" +lastUsedDate: "Naposledy pouÅžitÊ" +state: "Status" +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." +output: "VÃŊstup" +script: "Skript" +disablePagesScript: "VypnÃēÅĨ AiScript na strÃĄnkach" +updateRemoteUser: "AktualizovaÅĨ informÃĄcie o vzdialenom Ãēčte" +deleteAllFiles: "OdstrÃĄniÅĨ vÅĄetky sÃēbory" +deleteAllFilesConfirm: "Naozaj chcete odstrÃĄniÅĨ vÅĄetky sÃēbory" +removeAllFollowing: "ZruÅĄiÅĨ sledovani vÅĄetkÃŊch pouŞívateÄžov" +removeAllFollowingDescription: "TÃŊmto zruÅĄÃ­te sledovanie vÅĄetkÃŊch pouŞívateÄžov z {host}. Spustite to prosím, keď server napríklad uÅž neexistuje." +userSuspended: "Tento pouŞívateÄž je zmrazenÃŊ." +userSilenced: "Tento pouŞívateÄž je umlčanÃŊ." +yourAccountSuspendedTitle: "Tento Ãēčet je zmrazenÃŊ" +yourAccountSuspendedDescription: "Tento Ãēčet bol zmrazenÃŊ, lebo poruÅĄoval zmluvnÊ podmienky. Kontaktujte administrÃĄtora ak chcete viac podrobností. Prosím nevytvÃĄrajte novÃŊ Ãēčet." +menu: "Menu" +divider: "OddeÄžovač" +addItem: "PridaÅĨ poloÅžku" +relays: "Prenos" +addRelay: "PridaÅĨ prenos" +inboxUrl: "Inbox URL" +addedRelays: "PridanÊ prenosy" +serviceworkerInfo: "Musí byÅĨ zapnutÊ pre push notifikÃĄcie." +deletedNote: "OdstrÃĄnenÊ príspevky" +invisibleNote: "SkrytÊ príspevky" +enableInfiniteScroll: "ZapnÃēÅĨ nekonečnÊ skrolovanie" +visibility: "ViditeÄžnosÅĨ" +poll: "Hlasovanie" +useCw: "SkryÅĨ obsah" +enablePlayer: "OtvoriÅĨ video prehrÃĄvač" +disablePlayer: "ZavrieÅĨ video prehrÃĄvač" +expandTweet: "RozÅĄÃ­riÅĨ tweet" +themeEditor: "Editor tÊm" +description: "Popis" +describeFile: "PridaÅĨ nadpis" +enterFileDescription: "Zadajte nadpis" +author: "Autor" +leaveConfirm: "MÃĄte neuloÅženÊ zmeny. Chcete ich zahodiÅĨ?" +manage: "AdministrÃĄcia" +plugins: "Pluginy" +deck: "Deck" +useBlurEffectForModal: "PouÅžiÅĨ efekt rozmazania na oknÃĄ" +useFullReactionPicker: "PouÅžiÅĨ plnÃē veÄžkosÅĨ vÃŊberu reakcií" +width: "Šírka" +height: "VÃŊÅĄka" +large: "VeÄžkÊ" +medium: "StrednÊ" +small: "MalÊ" +generateAccessToken: "VygenerovaÅĨ prístupovÃŊ token" +permission: "OprÃĄvnenia" +enableAll: "PovoliÅĨ vÅĄetko" +disableAll: "VypnÃēÅĨ vÅĄetko" +tokenRequested: "PovoliÅĨ prístup k Ãēčtu" +pluginTokenRequestedDescription: "Tento plugin bude môcÅĨ pouŞívaÅĨ oprÃĄvnenia nastavenÊ tu." +notificationType: "Typ oznÃĄmenia" +edit: "UpraviÅĨ" +useStarForReactionFallback: "PouÅžiÅĨ ★ keď emoji reakcie nie je znÃĄme" +emailServer: "Email server" +enableEmail: "ZapnÃēÅĨ email" +emailConfigInfo: "PouŞíva sa na overenie emaily pri registrÃĄcii alebo pri zabudnutí hesla" +email: "Email" +emailAddress: "EmailovÃĄ adresa" +smtpConfig: "Nastavenia SMTP servera" +smtpHost: "Host" +smtpPort: "Port" +smtpUser: "Meno pouŞívateÄža" +smtpPass: "Heslo" +emptyToDisableSmtpAuth: "Vynechaním mena hesla vypnete SMTP verifikÃĄciu" +smtpSecure: "PouÅžiÅĨ implicitnÊ SSL/TLS pre SMTP spojenia" +smtpSecureInfo: "Toto vypnite keď pouŞívate STARTTLS" +testEmail: "Doručenie testovacieho emailu" +wordMute: "StÃ­ÅĄenie slova" +regexpError: "Chyba v regulÃĄrnom vÃŊraze" +regexpErrorDescription: "Na riadku {line} sa vyskytla chyba v stÃ­ÅĄenom slove {tab}." +instanceMute: "StÃ­ÅĄenÊ servery" +userSaysSomething: "{name} niečo povedal/a" +makeActive: "AktivovaÅĨ" +display: "ZobraziÅĨ" +copy: "KopírovaÅĨ" +metrics: "Metriky" +overview: "PrehÄžad" +logs: "Logy" +delayed: "OneskorenÊ" +database: "DatabÃĄza" +channel: "KanÃĄly" +create: "VytvoriÅĨ" +notificationSetting: "Nastavenia oznÃĄmení" +notificationSettingDesc: "Vyberte typ oznÃĄmení na zobrazenie" +useGlobalSetting: "PouÅžiÅĨ globÃĄlne nastavenie" +useGlobalSettingDesc: "Ak je zapnutÊ, pouÅžijÃē sa oznÃĄmenia vÃĄÅĄho Ãēčtu. Ak je vypnutÊ, pouÅžijÃē sa jednotlivÊ nastavenia." +other: "Ostatní" +regenerateLoginToken: "PregenerovaÅĨ prihlasovací token" +regenerateLoginTokenDescription: "Pregeneruje token interne pouŞívanÃŊ počas prihlÃĄsenia. NormÃĄlne toto netreba robiÅĨ. Ak sa pregeneruje, vÅĄetky zariadenia sa odhlÃĄsia." +setMultipleBySeparatingWithSpace: "ViacerÊ poloÅžky oddeÄžte medzerami." +fileIdOrUrl: "ID alebo URL sÃēboru" +behavior: "SprÃĄvanie" +sample: "UkÃĄÅžka" +abuseReports: "NahlÃĄsenia" +reportAbuse: "NahlÃĄsiÅĨ" +reportAbuseOf: "NahlÃĄsiÅĨ {name}" +fillAbuseReportDescription: "Prosím vyplňte podrobnosti nahlÃĄsenia. Ak sa tÃŊka konkrÊtnej poznÃĄmky, prosím napÃ­ÅĄte jej URL." +abuseReported: "VaÅĄe nahlÃĄsenie je odoslanÊ. VeÄžmi pekne ďakujeme." +reporter: "NahlÃĄsil" +reporteeOrigin: "Pôvod nahlÃĄsenÊho" +reporterOrigin: "Pôvod nahlasovača" +forwardReport: "PreposlaÅĨ nahlÃĄsenie na server" +forwardReportIsAnonymous: "Namiesto vÃĄÅĄho Ãēčtu bude zobrazenÃŊ anonymnÃŊ systÊmovÃŊ Ãēčet na vzdialenom serveri ako autor nahlÃĄsenia." +send: "PoslaÅĨ" +abuseMarkAsResolved: "OznačiÅĨ nahlÃĄsenia ako vyrieÅĄenÊ" +openInNewTab: "OtvoriÅĨ v novom tabe" +openInSideView: "OtvoriÅĨ v bočnom paneli" +defaultNavigationBehaviour: "PredvolenÊ sprÃĄvanie navigÃĄcie" +editTheseSettingsMayBreakAccount: "Úpravou tÃŊchto nastavení si môŞete pokaziÅĨ Ãēčet." +instanceTicker: "InformÃĄcie servera o poznÃĄmkach" +waitingFor: "ČakÃĄ sa na {x}" +random: "NÃĄhodnÊ" +system: "SystÊm" +switchUi: "PrepnÃēÅĨ UI" +desktop: "Desktop" +clip: "Klip" +createNew: "VytvoriÅĨ novÃŊ" +optional: "VoliteÄžnÊ" +createNewClip: "VytvoriÅĨ novÃŊ klip" +public: "VerejnÊ" +i18nInfo: "Misskey 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" +repliesCount: "Počet odoslanÃŊch odpovedí" +renotesCount: "Počet preposlanÃŊch poznÃĄmok" +repliedCount: "Počet odpovedí prijatÃŊch" +renotedCount: "Počet preposlaní prijatÃŊch" +followingCount: "Počet sledovanÃŊch Ãēčtov" +followersCount: "Počet sledujÃēcich" +sentReactionsCount: "Počet poslanÃŊch reakcií" +receivedReactionsCount: "Počet prijatÃŊch reakcií" +pollVotesCount: "Počet odoslanÃŊch hlasov" +pollVotedCount: "Počet prijatÃŊch hlasov" +yes: "Áno" +no: "Nie" +driveFilesCount: "Počet sÃēborov na disku" +driveUsage: "VyuÅžitÊ miesto na disku" +noCrawle: "OdmietaÅĨ indexovanie crawlerov" +noCrawleDescription: "PoÅžiadaÅĨ vyhÄžadÃĄvače, aby neindexovali vÃĄÅĄ profil, poznÃĄmky, strÃĄnky, atď." +lockedAccountInfo: "PokÃŊm nenastavíte viditeÄžnosÅĨ poznÃĄmok na \"Len pre sledujÃēcich\", vaÅĄe príspevky bude vidieÅĨ hocikto, aj keď vyÅžadujete manuÃĄlne potvrdenie sledovania." +alwaysMarkSensitive: "Predvolene označovaÅĨ ako NSFW" +loadRawImages: "NačítaÅĨ originÃĄlne obrÃĄzky namiesto miniatÃēr" +disableShowingAnimatedImages: "NeprehrÃĄvaÅĨ animovanÊ obrÃĄzky" +verificationEmailSent: "Odoslali sme overovací email. Overenie dokončíte kliknutím na odkaz v emaili." +notSet: "NenastavenÊ" +emailVerified: "Email overenÃŊ" +noteFavoritesCount: "Počet obÄžÃēbenÃŊch poznÃĄmok" +pageLikesCount: "Počet obÄžÃēbenÃŊch strÃĄnok" +pageLikedCount: "Počet prijatÃŊch \"pÃĄÄi sa mi\"" +contact: "Kontakt" +useSystemFont: "PouÅžiÅĨ predvolenÊ systÊmovÊ písmo" +clips: "Klip" +experimentalFeatures: "ExperimentÃĄlne funkcie" +developer: "VÃŊvojÃĄr" +makeExplorable: "SpraviÅĨ Ãēčet viditeÄžnÃŊ v \"ObjavovaÅĨ\"" +makeExplorableDescription: "Ak toto vypnete, vÃĄÅĄ Ãēčet sa nezobrazí v sekcii \"Objavovat\"." +showGapBetweenNotesInTimeline: "ZobraziÅĨ medzeru medzi príspevkami časovej osi." +duplicate: "DuplikovaÅĨ" +left: "NaÄžavo" +center: "Stred" +wide: "Å iroko" +narrow: "Úzko" +reloadToApplySetting: "Toto nastavenia sa prejaví aÅž po obnovení strÃĄnky. ObnoviÅĨ teraz?" +needReloadToApply: "Toto nastavenie sa prejaví aÅž po obnovení strÃĄnky." +showTitlebar: "ZobraziÅĨ riadok s nadpisom" +clearCache: "VyprÃĄzdniÅĨ cache" +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ď." +myTheme: "Moja tÊma" +backgroundColor: "Pozadie" +accentColor: "Akcent" +textColor: "Text" +saveAs: "UloÅžiÅĨ ako..." +advanced: "RozÅĄÃ­renÊ" +value: "Hodnoty" +createdAt: "VytvorenÊ" +updatedAt: "UpravenÊ" +saveConfirm: "UloÅžiÅĨ zmeny?" +deleteConfirm: "Naozaj odstrÃĄniÅĨ?" +invalidValue: "NesprÃĄvna hodnota." +registry: "Register" +closeAccount: "ZavrieÅĨ Ãēčet" +currentVersion: "AktuÃĄlna verzia" +latestVersion: "NajnovÅĄia verzia" +youAreRunningUpToDateClient: "PouŞívate najnovÅĄiu verziu vÃĄÅĄho klienta." +newVersionOfClientAvailable: "Je dostupnÃĄ novÅĄia verzia vÃĄÅĄho klienta." +usageAmount: "VyuÅžitie" +capacity: "Kapacita" +inUse: "PouÅžitÊ" +editCode: "UpraviÅĨ kÃŗd" +apply: "PouÅžiÅĨ" +receiveAnnouncementFromInstance: "PrijaÅĨ notifikÃĄcie z tohoto servera" +emailNotification: "EmailovÊ upozornenia" +publish: "ZverejniÅĨ" +inChannelSearch: "HÄžadaÅĨ v kanÃĄli" +useReactionPickerForContextMenu: "OtvoriÅĨ vÃŊber reakcií na pravÃŊ klik" +typingUsers: "{users} pÃ­ÅĄe/u" +jumpToSpecifiedDate: "SkočiÅĨ na konkrÊtny dÃĄtum" +showingPastTimeline: "PrÃĄve vidíte starÃē časovÃē os" +clear: "VrÃĄtiÅĨ" +markAllAsRead: "OznačiÅĨ vÅĄetko ako prečítanÊ" +goBack: "SpäÅĨ" +unlikeConfirm: "Naozaj odstrÃĄniÅĨ vÃĄÅĄ like?" +fullView: "PlnÃŊ pohÄžad" +quitFullView: "ZavrieÅĨ plnÃŊ pohÄžad" +addDescription: "PridaÅĨ popis" +userPagePinTip: "Tu môŞete zobraziÅĨ poznÃĄmky zvolením \"PripnÃēÅĨ na profil\" z menu jednotlivÃŊch poznÃĄmok." +notSpecifiedMentionWarning: "TÃĄto poznÃĄmka obsahuje spomenutÃŊch pouŞívateÄžov, ktorí nie sÃē medzi adresÃĄtmi." +info: "InformÃĄcie" +userInfo: "InformÃĄcie o pouŞívateÄžovi" +unknown: "NeznÃĄme" +onlineStatus: "Online status" +hideOnlineStatus: "SkryÅĨ online status" +hideOnlineStatusDescription: "Skrytie vÃĄÅĄho online statusu zníŞi pohodlnosÅĨ niektorÃŊch funkcií ako napríklad vyhÄžadÃĄvanie." +online: "Online" +active: "Aktívny" +offline: "Offline" +notRecommended: "NeodporÃēčanÊ" +botProtection: "Bot ochrana" +instanceBlocking: "BlokovanÊ servery" +selectAccount: "Vyberte Ãēčet" +switchAccount: "PrepnÃēt Ãēčet" +enabled: "ZapnutÊ" +disabled: "VypnutÊ" +quickAction: "RÃŊchle akcie" +user: "PouŞívatelia" +administration: "Spravovanie" +accounts: "Účty" +switch: "PrepnÃēÅĨ" +noMaintainerInformationWarning: "InformÃĄcie sprÃĄvcu nie sÃē nastavenÊ." +noBotProtectionWarning: "Ochrana proti botom nie je nastavenÃĄ." +configure: "KonfigurovaÅĨ" +postToGallery: "VytvoriÅĨ novÃŊ príspevok v galÊrii" +gallery: "GalÊria" +recentPosts: "NajnovÅĄie príspevky" +popularPosts: "PopulÃĄrne príspevky" +shareWithNote: "ZdieÄžaÅĨ s poznÃĄmkou" +ads: "Reklamy" +expiration: "UkončiÅĨ hlasovanie" +memo: "Memo" +priority: "Priorita" +high: "VysokÃĄ" +middle: "StrednÊ" +low: "MÃĄlo" +emailNotConfiguredWarning: "Nie je nastavenÃĄ emailovÃĄ adresa." +ratio: "Pomer" +previewNoteText: "ZobraziÅĨ nÃĄhÄžad" +customCss: "VlastnÊ CSS" +customCssWarn: "Toto nastavenie by sa malo pouŞívaÅĨ iba ak viete čo robíte. Zadanie nesprÃĄvnych hodnôt môŞe spôsobiÅĨ nenormÃĄlne sprÃĄvanie klienta." +global: "GlobÃĄlne" +squareAvatars: "ZobrazovaÅĨ ÅĄtvorcovÊ avatary" +sent: "PoslaÅĨ" +received: "PrijatÊ" +searchResult: "VÃŊsledky hÄžadania" +hashtags: "Hashtagy" +troubleshooting: "RieÅĄenie problÊmov" +useBlurEffect: "PouŞívaÅĨ efekty rozmazania v UI" +learnMore: "ZistiÅĨ viac" +misskeyUpdated: "Misskey sa aktualizoval!" +whatIsNew: "Čo je novÊ?" +translate: "PreloÅžiÅĨ" +translatedFrom: "PreloÅženÊ z {x}" +accountDeletionInProgress: "Odstraňovanie Ãēčtu prebieha" +usernameInfo: "Meno, ktorÊ odliÅĄuje vÃĄÅĄ Ãēčet od ostatnÃŊch na tomto serveri. MôŞete pouÅžiÅĨ abecedu (a~z, A~Z), čísla (0~9) alebo podtrÅžník (_). PouŞívateÄžskÊ menÃĄ sa nedajÃē neskôr zmeniÅĨ." +aiChanMode: "Ai reÅžim" +keepCw: "NechaÅĨ varovania obsahu" +pubSub: "Pub/Sub Ãēčty" +lastCommunication: "PoslednÃĄ komunikÃĄcia" +resolved: "VyrieÅĄenÊ" +unresolved: "NevyrieÅĄenÊ" +breakFollow: "NesledovaÅĨ" +itsOn: "ZapnutÊ" +itsOff: "VypnutÊ" +emailRequiredForSignup: "RegistrÃĄcia vyÅžaduje emailovÃē adresu" +unread: "NeprečítanÊ" +filter: "Filter" +controlPanel: "OvlÃĄdací panel" +manageAccounts: "SprÃĄva Ãēčtov" +makeReactionsPublic: "Reakcie sÃē verejnÊ" +makeReactionsPublicDescription: "Toto spraví vÅĄetky vaÅĄe minulÊ reakcie viditeÄžnÊ verejnosti." +classic: "Klasika" +muteThread: "ZtÃ­ÅĄiÅĨ vlÃĄkno" +unmuteThread: "ZruÅĄiÅĨ stÃ­ÅĄenie vlÃĄkna" +ffVisibility: "ViditeÄžnosÅĨ sledujÃēcich/sledovanÃŊch" +ffVisibilityDescription: "UmoŞňuje nastaviÅĨ kto vidí koho sledujete a kto vÃĄs sleduje." +continueThread: "ZobraziÅĨ pokračovanie vlÃĄkna" +deleteAccountConfirm: "Toto nezvrÃĄtiteÄžne vymaÅže vÃĄÅĄ Ãēčet. PokračovaÅĨ?" +incorrectPassword: "NesprÃĄvne heslo." +voteConfirm: "Potvrdzujete svoj hlas za \"{choice}\"?" +hide: "SkryÅĨ" +leaveGroup: "OpustiÅĨ skupiny" +leaveGroupConfirm: "Naozaj chcete opustiÅĨ \"{name}\"?" +useDrawerReactionPickerForMobile: "ZobraziÅĨ vÃŊber reakcií ako ÅĄuflík na mobile" +welcomeBackWithName: "Vitajte späÅĨ, {name}" +clickToFinishEmailVerification: "Kliknutím na [{ok}] dokončíte overeniu emailu." +overridedDeviceKind: "Typ zariadenia" +smartphone: "SmartfÃŗn" +tablet: "Tablet" +auto: "Automaticky" +themeColor: "Farba tÊmy" +size: "VeÄžkosÅĨ" +numberOfColumn: "Počet stÄēpcov" +_emailUnavailable: + used: "TÃĄto emailovÃĄ adresa sa uÅž pouŞíva" + format: "FormÃĄt emailovej adresy je nesprÃĄvny" + disposable: "JednorÃĄzovÊ emailovÊ adresy sa nemôŞu pouŞívaÅĨ." + mx: "Tento emailovÃŊ server nefunguje." + smtp: "Tento emailovÃŊ server neodpovedÃĄ." +_ffVisibility: + public: "ZverejniÅĨ" + followers: "Len viditeÄžní sledujÃēci" + private: "SÃēkromnÊ" +_signup: + almostThere: "Skoro na konci" + emailAddressInfo: "Prosím zadajte svoju emailovÃē adresu!" + emailSent: "Na vaÅĄu emailovÃē adresu ({email}) sme odoslali email. Vytvorenie Ãēčtu dokončíte kliknutím na odkaz v emaili." +_accountDelete: + accountDelete: "OdstrÃĄniÅĨ Ãēčet" + mayTakeTime: "KeďŞe odstrÃĄnenie Ãēčtu je nÃĄročnÃŊ proces, môŞe to nejakÃŊ čas trvaÅĨ. ZÃĄleŞí koÄžko obsahu ste vytvorili a koÄžko sÃēborov ste nahrali." + sendEmail: "Po odstrÃĄnení Ãēčtu vÃĄm poÅĄleme email na emailovÃē adresu zadanÃē pri registrÃĄcii tohoto Ãēčtu." + requestAccountDelete: "PoÅžiadaÅĨ o zmazanie Ãēčtu" + started: "Odstraňovanie začalo." + inProgress: "Odstraňovanie prebieha" +_ad: + back: "SpäÅĨ" + reduceFrequencyOfThisAd: "TÃēto reklamu zobrazovaÅĨ menej" +_forgotPassword: + enterEmail: "Zadajte emailovÃē adresu, ktorÃē ste pouÅžili pri registrÃĄcii. PoÅĄleme vÃĄm na ňu odkaz, cez ktorÃŊ si môŞete obnoviÅĨ heslo." + ifNoEmail: "Ak ste pri registrÃĄcii nepouÅžili email, prosím kontaktujte administrÃĄtora." + contactAdmin: "Tento server nepodporuje pouŞívanie emailovÃŊch adries, prosím kontaktuje administrÃĄtor, ktorÃŊ vÃĄm resetuje heslo." +_gallery: + my: "Moja galÊria" + liked: "ObÄžÃēbenÊ príspevky" + like: "PÃĄÄi sa mi" + unlike: "NepÃĄÄi sa mi" +_email: + _follow: + title: "MÃĄte novÊho sledujÃēceho" + _receiveFollowRequest: + title: "Dostali ste ÅžiadosÅĨ o sledovanie" +_plugin: + install: "InÅĄtalova pluginy" + installWarn: "Prosím neinÅĄtalujte nedôveryhodnÊ pluginy." + manage: "Spravovanie pluginov" +_registry: + scope: "OblasÅĨ" + key: "KÄžÃēč" + keys: "KÄžÃēče" + domain: "DomÊna" + createKey: "VytvoriÅĨ kÄžÃēč" +_aboutMisskey: + 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" + morePatrons: "Takisto oceňujeme podporu mnoÃŊch ďalÅĄÃ­ch, ktorí tu nie sÃē uvedení. Ďakujeme! đŸĨ°" + patrons: "Prispievatelia" +_nsfw: + respect: "SkryÅĨ NSFW mÊdiÃĄ" + ignore: "NeskrÃŊvaÅĨ NSFW mÊdiÃĄ" + 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" + mention: "Zmienka" + mentionDescription: "PouŞívateÄža spomeniete pouŞítím zavinÃĄÄa a mena pouŞívateÄža" + hashtag: "Hashtag" + hashtagDescription: "MôŞete zadaÅĨ hashtag pouÅžitím mrieÅžky a textu" + url: "URL" + urlDescription: "URL sa dajÃē zobraziÅĨ." + link: "Odkaz" + linkDescription: "JednotlivÊ časti texty sa dajÃē zobraziÅĨ ako URL." + bold: "TučnÊ" + boldDescription: "ZvÃŊrazní písmenÃĄ tÃŊm, Åže budÃē tučnejÅĄie." + small: "MalÊ" + smallDescription: "Zobrazí obsah malÃŊ a tenkÃŊ." + center: "VystrediÅĨ prvky" + centerDescription: "Zobrazí obsah v strede" + inlineCode: "KÃŗd (inline)" + inlineCodeDescription: "Zobrazí kÃŗd so zvÃŊraznením syntaxe." + blockCode: "KÃŗd (blok)" + blockCodeDescription: "Zobrazí viacriadkovÃŊ kÃŗd so zvÃŊraznením syntaxe v bloku." + inlineMath: "Vzorec (inline)" + inlineMathDescription: "Zobrazí matematickÃŊ vzorec (KaTeX) v riadku." + blockMath: "Vzorec (blok)" + blockMathDescription: "Zobrazí viacriadkovÃŊ matematickÃŊ vzorec (KaTeX) v bloku" + quote: "CitovaÅĨ" + quoteDescription: "Zobrazí obsah ako citÃĄt." + emoji: "VlastnÊ emoji" + emojiDescription: "Pridaním dvojbodiek pred a za nÃĄzov vlastnej emoji, sa dÃĄ zobraziÅĨ vlastnÃĄ emoji." + search: "HÄžadaÅĨ" + searchDescription: "Zobrazí vyhÄžadÃĄvacie pole so zadanÃŊm textom." + flip: "PreklopiÅĨ" + flipDescription: "Preklopí obsah horizontÃĄlne alebo vertikÃĄlne" + jelly: "AnimÃĄcia (ÅželÊ)" + jellyDescription: "Obsah sa bude hÃŊbaÅĨ ako ÅželÊ." + tada: "AnimÃĄcia (tadÃĄ)" + tadaDescription: "Obsah sa bude hÃŊbaÅĨ ako Tada!" + jump: "AnimÃĄcia (skok)" + jumpDescription: "Obsah skočí." + bounce: "AnimÃĄcia (odraz)" + bounceDescription: "Obsah sa bude odrÃĄÅžaÅĨ." + shake: "AnimÃĄcia (trasenie)" + shakeDescription: "Obsah sa bude triasÅĨ." + twitch: "AnimÃĄcia (myknutie)" + twitchDescription: "Obsahu dÃĄ animÃĄciu silnÊho trasenia." + spin: "AnimÃĄcia (rotÃĄcia)" + spinDescription: "Obsahu pridÃĄ otÃĄÄajÃēcu animÃĄciu." + x2: "VeÄžkÃŊ" + x2Description: "Zobrazí obsah vÃ¤ÄÅĄÃ­." + x3: "VeÄžmi veÄžkÃŊ" + x3Description: "Zobrazí obsah eÅĄte vÃ¤ÄÅĄÃ­." + x4: "NeuveriteÄžne veÄžkÃŊ" + x4Description: "Zobrazí obsah eÅĄte viac veÄžkÃŊ neÅž veÄžmi veÄžkÃŊ." + blur: "Rozmazanie" + blurDescription: "TÃŊmto efektom môŞe byÅĨ obsah rozmazanÃŊ. Zaostrí sa keď ned neho príde kurzor." + font: "Písmo" + fontDescription: "Nastaví písmo, ktorÃŊm sa zobrazí text." + rainbow: "DÃēha" + rainbowDescription: "Zobrazí obsah vo farbÃĄch dÃēhy." + sparkle: "Trblietky" + sparkleDescription: "Obsahu dodÃĄ trblietajÃēci efekt." + rotate: "OtÃĄÄaÅĨ" + rotateDescription: "Otočí obsah o určitÃŊ uhol." +_instanceTicker: + none: "Nikdy nezobrazovaÅĨ" + remote: "ZobraziÅĨ pre vzdialenÃŊch pouŞívateÄžov" + always: "ZobraziÅĨ vÅždy" +_serverDisconnectedBehavior: + reload: "Automaticky obnoviÅĨ" + dialog: "ZobraziÅĨ okno s varovaním" + quiet: "ZobraziÅĨ neruÅĄivÊ varovanie" +_channel: + create: "VytvoriÅĨ kanÃĄl" + edit: "UpraviÅĨ kanÃĄl" + setBanner: "NastaviÅĨ banner" + removeBanner: "OdstrÃĄniÅĨ banner" + featured: "Trendy" + owned: "VlastnenÊ" + following: "SledovanÊ" + usersCount: "{n} Ãēčastníkov" + notesCount: "{n} poznÃĄmok" +_menuDisplay: + sideFull: "Strana" + sideIcon: "Strana (Ikony)" + top: "Hore" + hide: "SkryÅĨ" +_wordMute: + muteWords: "UmlčanÊ slovÃĄ" + muteWordsDescription: "Medzerami oddeÄžte pre podmienku AND a novÃŊmi riadkami pre podmienku OR." + muteWordsDescription2: "RegulÃĄrne vÃŊrazy sa pouÅžijÃē keď pouÅžijete okolo lomítka." + softDescription: "Skryje poznÃĄmky z časovej osi, ktorÊ spÄēňajÃē podmienky." + hardDescription: "ZabrÃĄni poznÃĄmky spÄēňajÃēce mnoÅžinu podmienok, aby boli pridanÊ do časovej osi. NavyÅĄe tieto poznÃĄmky nepribudnÃē v časovej osi ani keď sa podmienky zmenia." + soft: "MäkkÊ" + hard: "TvrdÊ" + mutedNotes: "UmlčanÊ poznÃĄmky" +_instanceMute: + instanceMuteDescription: "Toto umlčí vÅĄetky poznÃĄmky/preposlania zo zoznamu serverov, vrÃĄtane tÃŊch, na ktorÊ pouŞívatelia odpovedajÃē z umlčanÊho servera." + instanceMuteDescription2: "OddeÄžte novÃŊmi riadkami" + title: "Skryje poznÃĄmky z uvedenÃŊch serverov." + heading: "Zoznam umlčanÃŊch inÅĄtancií" +_theme: + explore: "ObjavovaÅĨ tÊmy" + install: "NainÅĄtalovaÅĨ tÊmu" + manage: "SpravovaÅĨ tÊmy" + code: "KÃŗd tÊmy" + description: "Popis" + installed: "{name} je nainÅĄtalovanÃĄ" + installedThemes: "NainÅĄtalovanÊ tÊmy" + builtinThemes: "VstavanÊ tÊmy" + alreadyInstalled: "TÃĄto tÊma je uÅž nainÅĄtalovanÃĄ" + invalid: "FormÃĄt tejto tÊmy je nesprÃĄvny" + make: "VytvoriÅĨ tÊmu" + base: "ZÃĄklad" + addConstant: "PridaÅĨ konÅĄtantu" + constant: "KonÅĄtanta" + defaultValue: "PredvolenÃĄ hodnota" + color: "Farba" + refProp: "Odkaz na vlastnosÅĨ" + refConst: "Odkaz na konÅĄtantu" + key: "KÄžÃēč" + func: "Funkcie" + funcKind: "Typ funkcie" + argument: "Argument" + basedProp: "OdkazovanÃĄ vlastnosÅĨ" + alpha: "PriehÄžadnosÅĨ" + darken: "StmaviÅĨ" + lighten: "ZosvetliÅĨ" + inputConstantName: "Zadajte nÃĄzov tejto konÅĄtanty" + importInfo: "Ak sem zadÃĄte kÃŗd tÊmy, môŞete ju importovaÅĨ do editora tÊm." + deleteConstantConfirm: "Naozaj chcete odstrÃĄniÅĨ konÅĄtantu {const}?" + keys: + accent: "Akcent" + bg: "Pozadie" + fg: "Text" + focus: "Fokus" + indicator: "IndikÃĄtor" + panel: "Panel" + shadow: "Tieň" + header: "Hlavička" + navBg: "Pozadie bočnÊho panela" + navFg: "Text bočnÊho panela" + navHoverFg: "Text bočnÊho panela (pod kurzorom)" + navActive: "Text bočnÊho panela (aktívny)" + navIndicator: "IndikÃĄtor bočnÊho panela" + link: "Odkaz" + hashtag: "Hashtag" + mention: "Zmienka" + mentionMe: "Zmienky (mňa)" + renote: "PreposlaÅĨ" + modalBg: "Pozadie modÃĄlu" + divider: "OddeÄžovač" + scrollbarHandle: "RÃēčka scrollbaru" + scrollbarHandleHover: "RÃēčka scrollbaru (pod kurzorom)" + dateLabelFg: "Text dÃĄtovÊho popisku" + infoBg: "Pozadie informÃĄcií" + infoFg: "InformačnÃŊ text" + infoWarnBg: "Pozadie varovania" + infoWarnFg: "Text varovania" + cwBg: "CW pozadie tlačidla" + cwFg: "CW text tlačidla" + cwHoverBg: "CW pozadie tlačidla (pod kurzorom)" + toastBg: "Pozadie upozornenia" + toastFg: "Text upozornenia" + buttonBg: "Pozadie tlačidla" + buttonHoverBg: "Pozadie tlačidla (pod kurzorom)" + inputBorder: "Okraj vstupnÊho poÄža" + listItemHoverBg: "Pozadie poloÅžky zoznamu (pod kurzorom)" + driveFolderBg: "Pozadie priečinu disku" + wallpaperOverlay: "Vrstvenie pozadia" + badge: "Odznak" + messageBg: "Pozadie chatu" + accentDarken: "Akcent (stmavenÊ)" + accentLighten: "Akcent (zosvetlenÊ)" + fgHighlighted: "ZvÃŊraznenÃŊ text" +_sfx: + note: "PoznÃĄmky" + noteMy: "VlastnÃĄ poznÃĄmka" + notification: "OznÃĄmenia" + chat: "Chat" + chatBg: "Chat (pozadie)" + antenna: "AntÊny" + channel: "Upozornenia kanÃĄla" +_ago: + unknown: "NeznÃĄme" + future: "BudÃēcnosÅĨ" + justNow: "Teraz" + secondsAgo: "pred {n} sekundami" + minutesAgo: "pred {n} minÃētami" + hoursAgo: "pred {n} hodinami" + daysAgo: "pred {n} dňami" + weeksAgo: "pred {n} tÃŊÅždňami" + monthsAgo: "pred {n} mesiacmi" + yearsAgo: "pred {n} rokmi" +_time: + second: "s" + minute: "min" + hour: "hod" + day: "dní" +_tutorial: + title: "Ako pouŞívaÅĨ Misskey" + step1_1: "Vitajte!" + step1_2: "TÃĄto strÃĄnka sa volÃĄ \"časovÃĄ os\". Zobrazuje chronologicky zoradenÊ \"poznÃĄmky\" od Äžudí, ktorÃŊch sledujete." + step1_3: "VaÅĄa časovÃĄ os je teraz prÃĄzdna pretoÅže ste nepridali Åžiadne poznÃĄmky ani nikoho zatiaÄž nesledujete." + step2_1: "Podˇme dokončiÅĨ nastavenia vÃĄÅĄho profilu pred napísaním poznÃĄmky alebo sledovaním niekoho." + step2_2: "Poskytnutím informÃĄcií o vÃĄs uÄžahčíte ostatnÃŊm, či chcÃē vidieÅĨ alebo sledovaÅĨ vaÅĄe poznÃĄmky." + step3_1: "Dokončili ste nastavovanie svojho profilu?" + step3_2: "Poďme vyskÃēÅĄaÅĨ napísaÅĨ poznÃĄmku. MôŞete to spraviÅĨ stlačením ikony ceruzky na vrchu obrazovky." + step3_3: "Vyplňte polia a stlačte tlačítko vpravo hore." + step3_4: "NemÃĄte čo povedaÅĨ? SkÃēste \"len si nastavujem môj msky\"!" + step4_1: "Napísali ste svoju prvÃē poznÃĄmku?" + step4_2: "HurÃĄ! Teraz by vaÅĄa prvÃĄ poznÃĄmka mala byÅĨ na vaÅĄej časovej osi." + step5_1: "Teraz skÃēsme oÅživiÅĨ časovÃē os sledovaním nejakÃŊch Äžudí." + step5_2: "{featured} zobrazí populÃĄrne poznÃĄmku na tomto serveri. {explore} môŞete objavovaÅĨ populÃĄrnych pouŞívateÄžov. SkÃēste tam nÃĄjsÅĨ Äžudí, ktorÃŊch by ste radi sledovali!" + step5_3: "Ak chcete sledovaÅĨ ďalÅĄÃ­ch pouŞívateÄžov, kliknite na ich ikonu a stlačte tlačidlo \"SledovaÅĨ\" na ich profile." + step5_4: "Ak mÃĄ niektorÃŊ pouŞívateÄž ikonu zÃĄmku vedÄža svojho mena, znamenÃĄ to, Åže môŞe trvaÅĨ určitÃŊ čas, kÃŊm danÃŊ pouŞívateÄž schvÃĄli vaÅĄu ÅžiadosÅĨ o sledovanie." + step6_1: "Teraz by ste mali vidieÅĨ poznÃĄmky ďalÅĄÃ­ch pouŞívateÄžov na svojej časovej osi." + step6_2: "MôŞete daÅĨ \"reakcie\" na poznÃĄmky ďalÅĄÃ­ch Äžudí ako rÃŊchlu odpoveď." + step6_3: "Reakciu pridÃĄte kliknutím na \"+\" niekoho poznÃĄmke a vybratím emoji, ktorou chcete reagovaÅĨ." + step7_1: "Gralujeme! Dokončili ste zÃĄkladnÊho sprievodcu Misskey." + step7_2: "Ak sa chcete naučiÅĨ viac o Misskey, skÃēste sekciu {help}." + step7_3: "A teraz, veÄža ÅĄÅĨastia, bavte sa s Misskey! 🚀" +_2fa: + alreadyRegistered: "UÅž ste zaregistrovali 2-faktorovÊ autentifikačnÊ zariadenie." + registerDevice: "RegistrovaÅĨ novÊ zariadenie" + registerKey: "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." + step3: "Nastavenie dokončíte zadaním tokenu z vaÅĄej aplikÃĄcie." + step4: "Od teraz, vÅĄetky ďalÅĄie prihlÃĄsenia budÃē vyÅžadovaÅĨ prihlasovací token." + securityKeyInfo: "Okrem odtlačku prsta alebo PIN autentifikÃĄcie si môŞete nastaviÅĨ autentifikÃĄciu cez hardvÊrovÃŊ bezpečnostnÃŊ kÄžÃēč podporujÃēci FIDO2 a tak eÅĄte viac zabezpečiÅĨ svoj Ãēčet." +_permissions: + "read:account": "VidieÅĨ informÃĄcie o vaÅĄom Ãēčte" + "write:account": "UpraviÅĨ informÃĄcie o vaÅĄom Ãēčte" + "read:blocks": "VidieÅĨ zoznam blokovanÃŊch pouŞívateÄžov" + "write:blocks": "UpraviÅĨ zoznam blokovanÃŊch pouŞívateÄžov" + "read:drive": "Prístup k sÃēborom a priečinkom na disku" + "write:drive": "UpraviÅĨ alebo odstrÃĄniÅĨ sÃēbory a priečinky na disku" + "read:favorites": "VidieÅĨ vÃĄÅĄ zoznam obÄžÃēbenÃŊch" + "write:favorites": "UpraviÅĨ vÃĄÅĄ zoznam obÄžÃēbenÃŊch" + "read:following": "VidieÅĨ koho sledujete" + "write:following": "SledovaÅĨ alebo nesledovaÅĨ ďalÅĄie Ãēčty" + "read:messaging": "VidieÅĨ vaÅĄe chaty" + "write:messaging": "PísaÅĨ alebo odstraňovaÅĨ sprÃĄvy v chate" + "read:mutes": "VidieÅĨ vÃĄÅĄ zoznam stÃ­ÅĄenÃŊch pouŞívateÄžov" + "write:mutes": "UpravovaÅĨ zoznam stÃ­ÅĄenÃŊch pouŞívateÄžov" + "write:notes": "PísaÅĨ alebo odstrÃĄniÅĨ poznÃĄmky" + "read:notifications": "VidieÅĨ vaÅĄe oznÃĄmenia" + "write:notifications": "PracovaÅĨ s vaÅĄimi notifikÃĄciami" + "read:reactions": "VidieÅĨ vaÅĄe reakcie" + "write:reactions": "UpravovaÅĨ vaÅĄe reakcie" + "write:votes": "HlasovaÅĨ v hlasovaniach" + "read:pages": "VidieÅĨ vaÅĄe strÃĄnky" + "write:pages": "UpraviÅĨ alebo odstrÃĄniÅĨ vaÅĄe strÃĄnky" + "read:page-likes": "VidieÅĨ vaÅĄe pÃĄÄiky na strÃĄnkach" + "write:page-likes": "UpraviÅĨ pÃĄÄiky na strÃĄnkach" + "read:user-groups": "VidieÅĨ vaÅĄe skupiny" + "write:user-groups": "UpraviÅĨ alebo odstrÃĄniÅĨ vaÅĄe skupiny" + "read:channels": "ČítaÅĨ vaÅĄe kanÃĄly" + "write:channels": "UpravovaÅĨ vaÅĄe kanÃĄly" + "read:gallery": "VidieÅĨ vaÅĄu galÊriu" + "write:gallery": "UpravovaÅĨ vaÅĄu galÊriu" + "read:gallery-likes": "VidieÅĨ zoznam obÄžÃēbenÃŊch príspevkov z galÊrie" + "write:gallery-likes": "UpraviÅĨ zoznam obÄžÃēbenÃŊch príspevov z galÊrie" +_auth: + shareAccess: "Prajete si povoliÅĨ \"{name}\", aby mal prístup k tomuto Ãēčtu?" + shareAccessAsk: "Naozaj chcete povoliÅĨ tejto aplikÃĄcii prístup k tomuto Ãēčtu?" + permissionAsk: "TÃĄto aplikÃĄcia vyÅžaduje nasledujÃēce nastavenia" + pleaseGoBack: "Prosím prejdite späÅĨ na aplikÃĄciu" + callback: "Vraciam sa späÅĨ na aplikÃĄciu" + denied: "Prístup zamietnutÃŊ" +_antennaSources: + all: "VÅĄetky poznÃĄmky" + homeTimeline: "PoznÃĄmky od sledovanÊho pouŞívateÄža" + users: "PoznÃĄmky od konkrÊtneho pouŞívateÄža" + userList: "PoznÃĄmky od pouŞívateÄžov v zozname" + userGroup: "PoznÃĄmky od pouŞívateÄžov z konkrÊtnej skupiny." +_weekday: + sunday: "NedeÄža" + monday: "Pondelok" + tuesday: "Utorok" + wednesday: "Streda" + thursday: "Å tvrtok" + friday: "Piatok" + saturday: "Sobota" +_widgets: + memo: "PrilepenÊ poznÃĄmky" + notifications: "OznÃĄmenia" + timeline: "ČasovÃĄ os" + calendar: "KalendÃĄr" + trends: "Trendy" + clock: "Hodiny" + rss: "RSS čítačka" + activity: "Aktivita" + photos: "Fotky" + digitalClock: "DigitÃĄlne hodiny" + federation: "FederÃĄcia" + postForm: "NapísaÅĨ poznÃĄmku" + slideshow: "PrezentÃĄcia" + button: "Tlačidlo" + onlineUsers: "Online pouŞívatelia" + jobQueue: "Fronta Ãēloh" + serverMetric: "Metriky servera" + aiscript: "Konzola AiScript" + aichan: "Ai" +_cw: + hide: "SkryÅĨ" + show: "ZobraziÅĨ viac" + chars: "{count} znakov" + files: "{count} sÃēbor/ov" +_poll: + noOnlyOneChoice: "Treba aspoň dve voÄžby" + choiceN: "VoÄžba {n}" + noMore: "NemôŞete pridaÅĨ viac volieb" + canMultipleVote: "PovoliÅĨ hlasovaÅĨ za viac volieb." + expiration: "UkončiÅĨ hlasovanie" + infinite: "Nikdy" + at: "KonkrÊtny dÃĄtum..." + after: "UkončiÅĨ po..." + deadlineDate: "DÃĄtum ukončenia" + deadlineTime: "hod" + duration: "Trvanie" + votesCount: "{n} hlasov" + totalVotes: "{n} hlasov celkom" + vote: "HlasovaÅĨ" + showResult: "VidieÅĨ vÃŊsledky hlasovania" + voted: "ZahlasovanÊ" + closed: "Skončilo" + remainingDays: "zostÃĄva {d} dní {h} hodín" + remainingHours: "zostÃĄva {h} hodín {m} minÃēt" + remainingMinutes: "zostÃĄva {m} minÃēt {s} sekÃēnd" + remainingSeconds: "zostÃĄva {s} sekÃēnd" +_visibility: + public: "VerejnÊ" + publicDescription: "VaÅĄa poznÃĄmku bude viditeÄžnÃĄ vÅĄetkÃŊm pouŞívateÄžom" + home: "Domov" + homeDescription: "PridaÅĨ iba na domÃĄcu časovÃē os" + followers: "SledujÃēci" + followersDescription: "ViditeÄžnÊ iba tÃŊm, ktorí vÃĄs sledujÃē" + specified: "Priame" + specifiedDescription: "ViditeÄžnÊ iba pre konkrÊtnych pouŞívateÄžov" + localOnly: "Iba lokÃĄlne" + localOnlyDescription: "VzdialenÃŊ pouŞívatelia nebudÃē vidieÅĨ" +_postForm: + replyPlaceholder: "Odpoveď na tÃēto poznÃĄmku..." + quotePlaceholder: "Citovanie tejto poznÃĄmky..." + channelPlaceholder: "PoslaÅĨ do kanÃĄla..." + _placeholders: + a: "Čo mÃĄte v plÃĄne?" + b: "Čo sa deje?" + c: "O čom rozmÃŊÅĄÄžaÅĄ?" + d: "Čo chcete povedaÅĨ?" + e: "Začnite písaÅĨ..." + f: "ČakÃĄ sa na písanie..." +_profile: + name: "NÃĄzov" + username: "Meno pouŞívateÄža" + description: "Bio" + 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." + metadataLabel: "Popisok" + metadataContent: "Obsah" + changeAvatar: "ZmeniÅĨ avatara" + changeBanner: "ZmeniÅĨ banner" +_exportOrImport: + allNotes: "VÅĄetky poznÃĄmky" + followingList: "Sledujete" + muteList: "VypnÃēÅĨ zvuk" + blockingList: "ZablokovaÅĨ" + userLists: "Zoznamy" + excludeMutingUsers: "VylÃēčiÅĨ stÃ­ÅĄenÃŊch pouŞívateÄžov" + excludeInactiveUsers: "VylÃēčiÅĨ neaktívnych pouŞívateÄžov" +_charts: + federation: "FederÃĄcia" + apRequest: "ÅŊiadosti" + usersIncDec: "Rozdiel v počte pouŞívateÄžov" + usersTotal: "CelkovÃŊ počet pouŞívateÄžov" + activeUsers: "Aktívni pouŞívatelia" + notesIncDec: "Rozdiel v počte poznÃĄmok" + localNotesIncDec: "Rozdiel v počte lokÃĄlnych poznÃĄmok" + remoteNotesIncDec: "Rozdiel v počte vzdialenÃŊch poznÃĄmok" + notesTotal: "CelkovÃŊ počet poznÃĄmok" + filesIncDec: "Rozdiel v počte sÃēborov" + filesTotal: "CelkovÃŊ počet sÃēborov" + storageUsageIncDec: "Rozdiel vyuÅžitÊho ÃēloÅžiska" + storageUsageTotal: "CelkovÊ vyuÅžitÊ ÃēloÅžisko" +_instanceCharts: + requests: "ÅŊiadosti" + users: "Rozdiel v počte pouŞívateÄžov" + usersTotal: "Celkom spolu počet pouŞívateÄžov" + notes: "Rozdiel v počte poznÃĄmok" + notesTotal: "Celkom spolu počet poznÃĄmok" + ff: "Rozdiel v počte sledovanÃŊch/sledujÃēcich" + ffTotal: "Celkom spolu počet sledovanÃŊch / sledujÃēcich" + cacheSize: "Rozdiel vo veÄžkosti cache" + cacheSizeTotal: "Celkom spolu veÄžkosÅĨ cache" + files: "Rozdiel v počte sÃēborov" + filesTotal: "Celkom spolu počet sÃēborov" +_timelines: + home: "Domov" + local: "LokÃĄlne" + social: "SociÃĄlne" + global: "GlobÃĄlne" +_pages: + newPage: "VytvoriÅĨ novÃē strÃĄnku" + editPage: "UpraviÅĨ tÃēto strÃĄnku" + readPage: "Zobrazenie zdroja aktívne" + created: "StrÃĄnka ÃēspeÅĄne vytvorenÃĄ" + updated: "StrÃĄnka ÃēspeÅĄne upravenÃĄ" + deleted: "StrÃĄnka ÃēspeÅĄne odstrÃĄnenÃĄ" + pageSetting: "Nastavenia strÃĄnky" + nameAlreadyExists: "ZadanÃĄ URL strÃĄnku uÅž existuje" + invalidNameTitle: "ZadanÃĄ URL strÃĄnku je nesprÃĄvna" + invalidNameText: "Uistite sa, Åže nadpis strÃĄnky nie je prÃĄzdny" + editThisPage: "UpraviÅĨ tÃēto strÃĄnku" + viewSource: "UkÃĄzaÅĨ zdroj" + viewPage: "UkÃĄzaÅĨ vaÅĄe strÃĄnky" + like: "PÃĄÄi sa mi" + unlike: "NepÃĄÄi sa mi" + my: "Moje strÃĄnky" + liked: "ObÄžÃēbenÊ strÃĄnky" + featured: "VÃŊznačnÊ" + inspector: "InÅĄpektor" + contents: "Obsah" + content: "Blok strÃĄnky" + variables: "PremennÊ" + title: "Nadpis" + url: "URL strÃĄnky" + summary: "Zhrnutie strÃĄnky" + alignCenter: "VystrediÅĨ prvky" + hideTitleWhenPinned: "SkryÅĨ nadpis strÃĄnky keď je pripnutÃĄ na profil" + font: "Písmo" + fontSerif: "PätkovÊ" + fontSansSerif: "BezpätkovÊ" + eyeCatchingImageSet: "NastaviÅĨ miniatÃēru" + eyeCatchingImageRemove: "OdstrÃĄniÅĨ miniatÃēru" + chooseBlock: "PridaÅĨ blok" + selectType: "Vyberte typ" + enterVariableName: "Zadajte meno premennej" + variableNameIsAlreadyUsed: "Meno premennej s uÅž pouŞíva" + contentBlocks: "Obsah" + inputBlocks: "Vstup" + specialBlocks: "Å peciÃĄlne" + blocks: + text: "Text" + textarea: "TextovÊ pole" + section: "Sekcia" + image: "ObrÃĄzky" + button: "Tlačidlo" + if: "Ak" + _if: + variable: "PremennÊ" + post: "NapísaÅĨ poznÃĄmku" + _post: + text: "Obsah" + attachCanvasImage: "Príspevok s obrÃĄzkom na plÃĄtne" + canvasId: "ID plÃĄtna" + textInput: "TextovÃŊ vstup" + _textInput: + name: "Meno premennej" + text: "Nadpis" + default: "PredvolenÃĄ hodnota" + textareaInput: "ViacriadkovÃŊ textovÃŊ vstup" + _textareaInput: + name: "Meno premennej" + text: "Nadpis" + default: "PredvolenÃĄ hodnota" + numberInput: "ČíselnÃŊ vstup" + _numberInput: + name: "Meno premennej" + text: "Nadpis" + default: "PredvolenÃĄ hodnota" + canvas: "PlÃĄtno" + _canvas: + id: "ID plÃĄtna" + width: "Šírka" + height: "VÃŊÅĄka" + note: "VloÅženÃĄ poznÃĄmka" + _note: + id: "ID poznÃĄmky" + idDescription: "Alebo môŞete vloÅžiÅĨ URL poznÃĄmky sem" + detailed: "PodrobnÃŊ pohÄžad" + switch: "PrepnÃēÅĨ" + _switch: + name: "Meno premennej" + text: "Nadpis" + default: "PredvolenÃĄ hodnota" + counter: "Počítadlo" + _counter: + name: "Meno premennej" + text: "Nadpis" + inc: "PripočítaÅĨ" + _button: + text: "Nadpis" + colored: "FarebnÊ" + action: "OperÃĄcia po stlačení tlačidla" + _action: + dialog: "ZobraziÅĨ dialÃŗg" + _dialog: + content: "Obsah" + resetRandom: "ResetovaÅĨ zdroj nÃĄhodnosti" + pushEvent: "PoslaÅĨ udalosÅĨ" + _pushEvent: + event: "NÃĄzov udalosti" + message: "ZobrazenÃĄ sprÃĄva po aktivÃĄcii" + variable: "OdoslanÃĄ premennÃĄ" + no-variable: "ÅŊiadne" + callAiScript: "SpustiÅĨ AiScript" + _callAiScript: + functionName: "NÃĄzov funkcie" + radioButton: "MoÅžnosÅĨ" + _radioButton: + name: "Meno premennej" + title: "Nadpis" + values: "Zoznam moÅžností oddelenÊ novÃŊmi riadkami" + default: "PredvolenÃĄ hodnota" + script: + categories: + flow: "Riadenie behu" + logical: "LogickÃĄ operÃĄcia" + operation: "VÃŊpočet" + comparison: "Porovnanie" + random: "NÃĄhodnÊ" + value: "Hodnoty" + fn: "Funkcie" + text: "TextovÊ operÃĄcie" + convert: "TransformÃĄcie" + list: "Zoznamy" + blocks: + text: "Text" + multiLineText: "Text (viacriadkovÃŊ)" + textList: "Zoznam textov" + _textList: + info: "OddeÄžte kaÅždÃē poloÅžku novÃŊm riadkom" + strLen: "DÄēÅžka textu" + _strLen: + arg1: "Text" + strPick: "VybraÅĨ znak" + _strPick: + arg1: "Text" + arg2: "Pozícia znaku" + strReplace: "NÃĄhradnÃŊ text" + _strReplace: + arg1: "Text" + arg2: "NahradenÃŊ text" + arg3: "NahradiÅĨ s" + strReverse: "OtočiÅĨ text" + _strReverse: + arg1: "Text" + join: "SpojiÅĨ texty" + _join: + arg1: "Zoznamy" + arg2: "OddeÄžovač" + add: "PridaÅĨ" + _add: + arg1: "A" + arg2: "B" + subtract: "OdčítaÅĨ" + _subtract: + arg1: "A" + arg2: "B" + multiply: "NÃĄsobiÅĨ" + _multiply: + arg1: "A" + arg2: "B" + divide: "DeliÅĨ" + _divide: + arg1: "A" + arg2: "B" + mod: "ZvyÅĄok po delení" + _mod: + arg1: "A" + arg2: "B" + round: "ZaokrÃēhliÅĨ" + _round: + arg1: "Číslo" + eq: "A a B sa rovnajÃē" + _eq: + arg1: "A" + arg2: "B" + notEq: "A a B sa nerovnajÃē" + _notEq: + arg1: "A" + arg2: "B" + and: "A a zÃĄroveň B" + _and: + arg1: "A" + arg2: "B" + or: "A alebo B" + _or: + arg1: "A" + arg2: "B" + lt: "< A je menÅĄie ako B" + _lt: + arg1: "A" + arg2: "B" + gt: "> A je vÃ¤ÄÅĄie ako B" + _gt: + arg1: "A" + arg2: "B" + ltEq: "<= A je menÅĄie alebo rovnÊ B" + _ltEq: + arg1: "A" + arg2: "B" + gtEq: ">= A je vÃ¤ÄÅĄie alebo rovnÊ B" + _gtEq: + arg1: "A" + arg2: "B" + if: "Vetva" + _if: + arg1: "Ak" + arg2: "Potom" + arg3: "Inak" + not: "Opak" + _not: + arg1: "Opak" + random: "NÃĄhodnÊ" + _random: + arg1: "PravdepodobnosÅĨ" + rannum: "NÃĄhodnÊ číslo" + _rannum: + arg1: "MinimÃĄlna hodnota" + arg2: "MaximÃĄlna hodnota" + randomPick: "NÃĄhodnÃŊ vÃŊber zo zoznamu" + _randomPick: + arg1: "Zoznam" + dailyRandom: "NÃĄhodne (zmení sa raz denne pre kaÅždÊho pouŞívateÄža)" + _dailyRandom: + arg1: "PravdepodobnosÅĨ" + dailyRannum: "NÃĄhodnÊ číslo (Mení sa denne pre kaÅždÊho pouŞívateÄža)" + _dailyRannum: + arg1: "MinimÃĄlna hodnota" + arg2: "MaximÃĄlna hodnota" + dailyRandomPick: "NÃĄhodnÃŊ vÃŊber zo zoznamu (Mení sa denne pre kaÅždÊho pouŞívateÄža)" + _dailyRandomPick: + arg1: "Zoznam" + seedRandom: "NÃĄhodne (so seedom)" + _seedRandom: + arg1: "Seed" + arg2: "PravdepodobnosÅĨ" + seedRannum: "NÃĄhodnÊ číslo (so seedom)" + _seedRannum: + arg1: "Seed" + arg2: "MinimÃĄlna hodnota" + arg3: "MaximÃĄlna hodnota" + seedRandomPick: "NÃĄhodnÃŊ vÃŊber zo zoznamu (so seedom)" + _seedRandomPick: + arg1: "Seed" + arg2: "Zoznam" + DRPWPM: "NÃĄhodnÃŊ vÃŊber z vÃĄÅženÊho zoznamu (Mení sa denne pre kaÅždÊho pouŞívateÄža)" + _DRPWPM: + arg1: "Zoznam textov" + pick: "VybraÅĨ zo zoznamu" + _pick: + arg1: "Zoznam" + arg2: "Pozícia" + listLen: "ZískaÅĨ dÄēÅžku zoznamu" + _listLen: + arg1: "Zoznam" + number: "Číslo" + stringToNumber: "Text na číslo" + _stringToNumber: + arg1: "Text" + numberToString: "Číslo na text" + _numberToString: + arg1: "Číslo" + splitStrByLine: "Rozdelí text po riadkoch" + _splitStrByLine: + arg1: "Text" + ref: "PremennÊ" + aiScriptVar: "AiScript premennÃĄ" + fn: "Funkcie" + _fn: + slots: "Sloty" + slots-info: "OddeÄžte kaÅždÃŊ slot novÃŊm riadkom" + arg1: "VÃŊstup" + for: "For cyklus" + _for: + arg1: "Počet opakovaní" + arg2: "Akcia" + typeError: "Slot {slot} akceptuje hodnoty typu \"{expect}\", ale dodanÃĄ hodnota je typu \"{actual}\"!" + thereIsEmptySlot: "Slot {slot} je prÃĄzdny!" + types: + string: "Text" + number: "Číslo" + boolean: "Boolean" + array: "Zoznamy" + stringArray: "Zoznam textov" + emptySlot: "PrÃĄzdny slot" + enviromentVariables: "PremennÊ prostredia" + pageVariables: "PremennÊ strÃĄnky" + argVariables: "VstupnÊ sloty" +_relayStatus: + requesting: "ČakÃĄ sa" + accepted: "AkceptovanÊ" + rejected: "OdmietnutÊ" +_notification: + fileUploaded: "SÃēbor sa ÃēspeÅĄne nahral" + youGotMention: "{name} vÃĄs spomenul/a" + youGotReply: "{name} vÃĄm odpovedal/a" + youGotQuote: "{name} vÃĄs citoval/a" + youRenoted: "{name} preposlal/a vaÅĄu poznÃĄmku" + youGotPoll: "{name} hlasoval/a" + youGotMessagingMessageFromUser: "{name} vÃĄm poslal/a sprÃĄvu" + youGotMessagingMessageFromGroup: "PriÅĄla sprÃĄva do skupiny {name}" + youWereFollowed: "MÃĄte novÊho sledujÃēceho" + youReceivedFollowRequest: "Dostali ste ÅžiadosÅĨ o sledovanie" + yourFollowRequestAccepted: "VaÅĄa ÅžiadosÅĨ o sledovanie bola prijatÃĄ" + youWereInvitedToGroup: "PozvaÅĨ do skupiny" + _types: + all: "VÅĄetky" + follow: "Sledujete" + mention: "Zmienka" + reply: "Odpovede" + renote: "PreposlaÅĨ" + quote: "CitovaÅĨ" + reaction: "Reakcie" + pollVote: "Hlasy v hlasovaniach" + receiveFollowRequest: "DoručenÊ Åžiadosti o sledovanie" + followRequestAccepted: "SchvÃĄlenÊ Åžiadosti o sledovanie" + groupInvited: "PozvÃĄnky do skupín" + app: "OznÃĄmenia z prepojenÃŊch aplikÃĄcií" +_deck: + alwaysShowMainColumn: "VÅždy zobraziÅĨ v hlavnom stÄēpci" + columnAlign: "ZarovnaÅĨ stÄēpce" + columnMargin: "Rozostup medzi stÄēpcami" + columnHeaderHeight: "VÃŊÅĄka hlavičky stÄēpca" + addColumn: "PridaÅĨ stÄēpec" + swapLeft: "VymeniÅĨ vÄžavo" + swapRight: "VymeniÅĨ vpravo" + swapUp: "VymeniÅĨ hore" + swapDown: "VymeniÅĨ s nasledujÃēcim" + stackLeft: "PriloÅžiÅĨ do ÄžavÊho stÄēpca" + popRight: "VybraÅĨ napravo" + profile: "Profil" + _columns: + main: "HlavnÃŊ" + widgets: "Widgety" + notifications: "OznÃĄmenia" + tl: "ČasovÃĄ os" + antenna: "AntÊny" + list: "Zoznam" + mentions: "Zmienky" + direct: "Priame poznÃĄmky" diff --git a/locales/uk-UA.yml b/locales/uk-UA.yml index 2dd6056011..588df8d325 100644 --- a/locales/uk-UA.yml +++ b/locales/uk-UA.yml @@ -1038,7 +1038,8 @@ _exportOrImport: blockingList: "ЗабĐģĐžĐēŅƒĐ˛Đ°Ņ‚и" userLists: "ĐĄĐŋиŅĐēи" _charts: - federationInstancesTotal: "ЗаĐŗĐ°ĐģŅŒĐŊĐ° ĐēŅ–ĐģŅŒĐēŅ–ŅŅ‚ŅŒ Ņ„ĐĩĐ´ĐĩŅ€Đ°Ņ‚ивĐŊиŅ… Ņ–ĐŊŅŅ‚Đ°ĐŊŅŅ–в" + federation: "ФĐĩĐ´Ņ–вĐĩŅ€Ņ" + apRequest: "ЗаĐŋиŅ‚и" usersTotal: "ЗаĐŗĐ°ĐģŅŒĐŊĐ° ĐēŅ–ĐģŅŒĐēŅ–ŅŅ‚ŅŒ ĐēĐžŅ€Đ¸ŅŅ‚ŅƒĐ˛Đ°Ņ‡Ņ–в" activeUsers: "АĐēŅ‚ивĐŊŅ– ĐēĐžŅ€Đ¸ŅŅ‚ŅƒĐ˛Đ°Ņ‡Ņ–" notesTotal: "ЗаĐŗĐ°ĐģŅŒĐŊĐ° ĐēŅ–ĐģŅŒĐēŅ–ŅŅ‚ŅŒ ĐŊĐžŅ‚Đ°Ņ‚ĐžĐē" diff --git a/locales/zh-CN.yml b/locales/zh-CN.yml index 5815c92f43..86d3fb5cdd 100644 --- a/locales/zh-CN.yml +++ b/locales/zh-CN.yml @@ -141,6 +141,8 @@ flagAsBot: "čŋ™æ˜¯ä¸€ä¸Ēæœē器äēēč´Ļåˇ" flagAsBotDescription: "åĻ‚æžœæ­¤å¸æˆˇį”ąį¨‹åēæŽ§åˆļīŧŒč¯ˇå¯į”¨æ­¤éĄšã€‚启į”¨åŽīŧŒæ­¤æ ‡åŋ—可äģĨ帎劊å…ļäģ–åŧ€å‘äēēå‘˜é˜˛æ­ĸæœē器äēē之间äē§į”Ÿæ— é™äē’动įš„čĄŒä¸ēīŧŒåšļ莊Misskeyįš„内部įŗģįģŸå°†æ­¤å¸æˆˇč¯†åˆĢä¸ēæœē器äēē。" flagAsCat: "将čŋ™ä¸Ēč´ĻæˆˇčŽžåŽšä¸ē一åĒįŒĢ" flagAsCatDescription: "åĻ‚果您æƒŗčĄ¨æ˜Žæ­¤å¸æˆˇæ˜¯ä¸€åĒįŒĢīŧŒč¯ˇæ‰“åŧ€æ­¤æ ‡åŋ—。" +flagShowTimelineReplies: "在æ—ļ间įēŋ上昞į¤ē帖子įš„回复" +flagShowTimelineRepliesDescription: "启į”¨æ—ļīŧŒæ—ļ间įēŋ除äē†æ˜žį¤ēį”¨æˆˇįš„帖子外īŧŒčŋ˜äŧšæ˜žį¤ēå…ļäģ–į”¨æˆˇå¯šå¸–子įš„回复。" autoAcceptFollowed: "č‡ĒåŠ¨å…čŽ¸å…ŗæŗ¨" addAccount: "æˇģ加č´Ļæˆˇ" loginFailed: "į™ģåŊ•å¤ąč´Ĩ" @@ -235,6 +237,8 @@ resetAreYouSure: "æĸ复éģ˜čŽ¤čŽžįŊŽīŧŸ" saved: "åˇ˛äŋå­˜" messaging: "čŠå¤Š" upload: "æœŦ地上äŧ " +keepOriginalUploading: "äŋį•™åŽŸå›ž" +keepOriginalUploadingDescription: "上äŧ å›žį‰‡æ—ļäŋį•™åŽŸå§‹å›žį‰‡ã€‚å…ŗ闭æ—ļīŧŒæĩč§ˆå™¨äŧšåœ¨ä¸Šäŧ æ—ļį”Ÿæˆä¸€åŧ į”¨äēŽweb发布įš„回į‰‡ã€‚" fromDrive: "äģŽįŊ‘į›˜ä¸­" fromUrl: "äģŽ URL" uploadFromUrl: "äģŽįŊ‘址上äŧ " @@ -591,6 +595,8 @@ smtpSecure: "在 SMTP čŋžæŽĨ中äŊŋį”¨éšåŧ SSL / TLS" smtpSecureInfo: "äŊŋį”¨STARTTLSæ—ļå…ŗ闭。" testEmail: "邮äģļ发送æĩ‹č¯•" wordMute: "æ–‡å­—åąč”Ŋ" +regexpError: "æ­Ŗåˆ™čĄ¨čžžåŧé”™č¯¯" +regexpErrorDescription: "{tab} åąč”Ŋ文字įš„įŦŦ {line} 行įš„æ­Ŗåˆ™čĄ¨čžžåŧæœ‰é”™č¯¯īŧš" instanceMute: "厞䞋įš„åąč”Ŋ" userSaysSomething: "{name}č¯´äē†äģ€äšˆ" makeActive: "启į”¨" @@ -619,8 +625,11 @@ reportAbuse: "丞æŠĨ" reportAbuseOf: "丞æŠĨ{name}" fillAbuseReportDescription: "č¯ˇåĄĢ写丞æŠĨįš„č¯Ļįģ†åŽŸå› ã€‚åĻ‚果有寚斚发įš„帖子īŧŒč¯ˇåŒæ—ļåĄĢ写URL地址。" abuseReported: "å†…åŽšåˇ˛å‘é€ã€‚æ„Ÿč°ĸ您įš„æŠĨ告。" +reporter: "æŠĨå‘Šč€…" reporteeOrigin: "丞æŠĨæĨæē" reporterOrigin: "丞æŠĨ者æĨæē" +forwardReport: "将æŠĨ告čŊŦ发įģ™čŋœį¨‹åŽžäž‹" +forwardReportIsAnonymous: "在čŋœį¨‹åŽžäž‹ä¸Šæ˜žį¤ēįš„æŠĨå‘Šč€…æ˜¯åŒŋ名įš„įŗģįģŸč´ĻåˇīŧŒč€Œä¸æ˜¯æ‚¨įš„č´Ļåˇã€‚" send: "发送" abuseMarkAsResolved: "处į†åŽŒæ¯•" openInNewTab: "在新标į­žéĄĩ中打åŧ€" @@ -817,6 +826,13 @@ leaveGroupConfirm: "įĄŽåŽšįĻģåŧ€ã€Œ{name}」īŧŸ" useDrawerReactionPickerForMobile: "在į§ģåŠ¨čŽžå¤‡ä¸ŠäŊŋį”¨æŠŊåą‰æ˜žį¤ē" welcomeBackWithName: "æŦĸčŋŽå›žæĨīŧŒ{name}" clickToFinishEmailVerification: "į‚šå‡ģ [{ok}] 厌成į”ĩ子邎äģļåœ°å€čŽ¤č¯ã€‚" +overridedDeviceKind: "čŽžå¤‡įąģ型" +smartphone: "æ™ēčƒŊ手æœē" +tablet: "åšŗæŋ" +auto: "č‡Ē动" +themeColor: "ä¸ģéĸ˜éĸœč‰˛" +size: "大小" +numberOfColumn: "列数" _emailUnavailable: used: "åˇ˛įģčĸĢäŊŋį”¨čŋ‡" format: "无效įš„æ ŧåŧ" @@ -944,7 +960,7 @@ _mfm: rotateDescription: "旋čŊŦ指厚įš„角åēĻ。" _instanceTicker: none: "不昞į¤ē" - remote: "昞į¤ēįģ™čŋœį¨‹į”¨æˆˇ" + remote: "äģ…昞į¤ēčŋœį¨‹į”¨æˆˇįš„" always: "始įģˆæ˜žį¤ē" _serverDisconnectedBehavior: reload: "č‡Ē动重čŊŊ" @@ -1253,8 +1269,8 @@ _exportOrImport: excludeMutingUsers: "æŽ’é™¤åąč”Ŋį”¨æˆˇ" excludeInactiveUsers: "排除不æ´ģ衃į”¨æˆˇ" _charts: - federationInstancesIncDec: "č”åˆīŧšåĸžåŠ /减少" - federationInstancesTotal: "č”åˆæ€ģ数" + federation: "č”åˆ" + apRequest: "č¯ˇæą‚" usersIncDec: "į”¨æˆˇæ•°é‡īŧšåĸžåŠ /减少" usersTotal: "į”¨æˆˇæ€ģ数" activeUsers: "æ´ģ衃į”¨æˆˇæ•°" diff --git a/locales/zh-TW.yml b/locales/zh-TW.yml index 798398a6a9..b10871ec1d 100644 --- a/locales/zh-TW.yml +++ b/locales/zh-TW.yml @@ -1114,6 +1114,8 @@ _exportOrImport: blockingList: "封鎖" userLists: "清喎" _charts: + federation: "įĢ™å°č¯é‚Ļ" + apRequest: "čĢ‹æą‚" usersIncDec: "äŊŋį”¨č€…åĸ—減" usersTotal: "äŊŋį”¨č€…åˆå…ą" activeUsers: "æ´ģčēäŊŋį”¨č€…" diff --git a/package.json b/package.json index d945672987..68421f0e37 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "12.102.1", + "version": "12.107.0", "codename": "indigo", "repository": { "type": "git", @@ -42,11 +42,9 @@ "js-yaml": "4.1.0" }, "devDependencies": { - "@redocly/openapi-core": "1.0.0-beta.79", - "@types/fluent-ffmpeg": "2.1.20", - "@typescript-eslint/parser": "5.10.0", + "@typescript-eslint/parser": "5.12.0", "cross-env": "7.0.3", - "cypress": "9.3.1", + "cypress": "9.5.0", "start-server-and-test": "1.14.0", "typescript": "4.5.5" } diff --git a/packages/backend/migration/1629833361000-AddShowTLReplies.js b/packages/backend/migration/1629833361000-AddShowTLReplies.js new file mode 100644 index 0000000000..bfd4ab7ff7 --- /dev/null +++ b/packages/backend/migration/1629833361000-AddShowTLReplies.js @@ -0,0 +1,15 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +class addShowTLReplies1629833361000 { + constructor() { + 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.'`); + } + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "showTimelineReplies"`); + } +} +exports.addShowTLReplies1629833361000 = addShowTLReplies1629833361000; diff --git a/packages/backend/migration/1643963705770-chart-v4.js b/packages/backend/migration/1643963705770-chart-v4.js new file mode 100644 index 0000000000..91b0a747e6 --- /dev/null +++ b/packages/backend/migration/1643963705770-chart-v4.js @@ -0,0 +1,63 @@ +const { MigrationInterface, QueryRunner } = require("typeorm"); + +module.exports = class 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 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 new file mode 100644 index 0000000000..9ff05be63b --- /dev/null +++ b/packages/backend/migration/1643966656277-chart-v5.js @@ -0,0 +1,27 @@ +const { MigrationInterface, QueryRunner } = require("typeorm"); + +module.exports = class 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 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 new file mode 100644 index 0000000000..86feade9d1 --- /dev/null +++ b/packages/backend/migration/1643967331284-chart-v6.js @@ -0,0 +1,343 @@ +const { MigrationInterface, QueryRunner } = require("typeorm"); + +module.exports = class 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 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 new file mode 100644 index 0000000000..6169cb0144 --- /dev/null +++ b/packages/backend/migration/1644010796173-convert-hard-mutes.js @@ -0,0 +1,65 @@ +const RE2 = require('re2'); +const { MigrationInterface, QueryRunner } = require("typeorm"); + +module.exports = class 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)); + + 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)); + + 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 new file mode 100644 index 0000000000..a982b6cb68 --- /dev/null +++ b/packages/backend/migration/1644058404077-chart-v7.js @@ -0,0 +1,501 @@ +const { MigrationInterface, QueryRunner } = require("typeorm"); + +module.exports = class 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(`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`); + } + + 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`); + } +} diff --git a/packages/backend/migration/1644059847460-chart-v8.js b/packages/backend/migration/1644059847460-chart-v8.js new file mode 100644 index 0000000000..7c5db0db33 --- /dev/null +++ b/packages/backend/migration/1644059847460-chart-v8.js @@ -0,0 +1,25 @@ +const { MigrationInterface, QueryRunner } = require("typeorm"); + +module.exports = class 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`); + + 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 new file mode 100644 index 0000000000..bc607067b3 --- /dev/null +++ b/packages/backend/migration/1644060125705-chart-v9.js @@ -0,0 +1,25 @@ +const { MigrationInterface, QueryRunner } = require("typeorm"); + +module.exports = class 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`); + + 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 new file mode 100644 index 0000000000..6ca568718f --- /dev/null +++ b/packages/backend/migration/1644073149413-chart-v10.js @@ -0,0 +1,35 @@ +const { MigrationInterface, QueryRunner } = require("typeorm"); + +module.exports = class 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 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 new file mode 100644 index 0000000000..40b1c3072b --- /dev/null +++ b/packages/backend/migration/1644095659741-chart-v11.js @@ -0,0 +1,91 @@ +const { MigrationInterface, QueryRunner } = require("typeorm"); + +module.exports = class 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 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 new file mode 100644 index 0000000000..226de6ece9 --- /dev/null +++ b/packages/backend/migration/1644328606241-chart-v12.js @@ -0,0 +1,27 @@ +const { MigrationInterface, QueryRunner } = require("typeorm"); + +module.exports = class 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 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 new file mode 100644 index 0000000000..ed36659b34 --- /dev/null +++ b/packages/backend/migration/1644331238153-chart-v13.js @@ -0,0 +1,19 @@ +const { MigrationInterface, QueryRunner } = require("typeorm"); + +module.exports = class 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 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 new file mode 100644 index 0000000000..8496cc2d42 --- /dev/null +++ b/packages/backend/migration/1644344266289-chart-v14.js @@ -0,0 +1,47 @@ +const { MigrationInterface, QueryRunner } = require("typeorm"); + +module.exports = class 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 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 new file mode 100644 index 0000000000..4fda26f9e8 --- /dev/null +++ b/packages/backend/migration/1644395759931-instance-theme-color.js @@ -0,0 +1,13 @@ +const { MigrationInterface, QueryRunner } = require("typeorm"); + +module.exports = class instanceThemeColor1644395759931 { + name = 'instanceThemeColor1644395759931' + + 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"`); + } +} diff --git a/packages/backend/migration/1644481657998-chart-v15.js b/packages/backend/migration/1644481657998-chart-v15.js new file mode 100644 index 0000000000..4eee83f612 --- /dev/null +++ b/packages/backend/migration/1644481657998-chart-v15.js @@ -0,0 +1,31 @@ +const { MigrationInterface, QueryRunner } = require("typeorm"); + +module.exports = class 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 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 new file mode 100644 index 0000000000..6c149d8069 --- /dev/null +++ b/packages/backend/migration/1644551208096-following-indexes.js @@ -0,0 +1,15 @@ +const { MigrationInterface, QueryRunner } = require("typeorm"); + +module.exports = class 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 down(queryRunner) { + await queryRunner.query(`DROP INDEX "public"."IDX_fcdafee716dfe9c3b5fde90f30"`); + await queryRunner.query(`DROP INDEX "public"."IDX_4ccd2239268ebbd1b35e318754"`); + } +} diff --git a/packages/backend/package.json b/packages/backend/package.json index 3d3a901f34..b9433f7f35 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -19,12 +19,11 @@ "@koa/cors": "3.1.0", "@koa/multer": "3.0.0", "@koa/router": "9.0.1", - "@sinonjs/fake-timers": "7.1.2", + "@sinonjs/fake-timers": "9.1.0", "@syuilo/aiscript": "0.11.1", "@types/bcryptjs": "2.4.2", - "@types/bull": "3.15.7", + "@types/bull": "3.15.8", "@types/cbor": "6.0.0", - "@types/dateformat": "3.0.1", "@types/escape-regexp": "0.0.1", "@types/glob": "7.2.0", "@types/is-url": "1.2.30", @@ -42,8 +41,8 @@ "@types/koa__cors": "3.1.1", "@types/koa__multer": "2.0.4", "@types/koa__router": "8.0.11", - "@types/mocha": "8.2.3", - "@types/node": "17.0.10", + "@types/mocha": "9.1.0", + "@types/node": "17.0.18", "@types/node-fetch": "3.0.3", "@types/nodemailer": "6.4.4", "@types/oauth": "0.9.1", @@ -56,43 +55,40 @@ "@types/ratelimiter": "3.4.3", "@types/redis": "4.0.11", "@types/rename": "1.0.4", - "@types/request-stats": "3.0.0", "@types/sanitize-html": "2.6.2", - "@types/seedrandom": "2.4.28", + "@types/seedrandom": "3.0.1", "@types/sharp": "0.29.5", - "@types/sinonjs__fake-timers": "6.0.4", + "@types/sinonjs__fake-timers": "8.1.1", "@types/speakeasy": "2.0.7", "@types/throttle-debounce": "2.1.0", "@types/tinycolor2": "1.4.3", "@types/tmp": "0.2.3", "@types/uuid": "8.3.4", "@types/web-push": "3.3.2", - "@types/webpack": "5.28.0", - "@types/webpack-stream": "3.2.12", - "@types/websocket": "1.0.4", + "@types/websocket": "1.0.5", "@types/ws": "8.2.2", - "@typescript-eslint/eslint-plugin": "5.10.0", - "@typescript-eslint/parser": "5.10.0", + "@typescript-eslint/eslint-plugin": "5.12.0", + "@typescript-eslint/parser": "5.12.0", "abort-controller": "3.0.0", + "ajv": "8.10.0", "archiver": "5.3.0", "autobind-decorator": "2.4.0", "autwh": "0.1.0", - "aws-sdk": "2.1061.0", + "aws-sdk": "2.1067.0", "bcryptjs": "2.4.3", - "blurhash": "1.1.4", - "broadcast-channel": "4.9.0", - "bull": "4.2.1", + "blurhash": "1.1.5", + "broadcast-channel": "4.10.0", + "bull": "4.5.5", "cacheable-lookup": "6.0.4", "cafy": "15.2.1", "cbor": "8.1.0", "chalk": "4.1.2", "cli-highlight": "2.1.11", "content-disposition": "0.5.4", - "crc-32": "1.2.0", - "dateformat": "4.5.1", + "date-fns": "2.28.0", "deep-email-validator": "0.1.21", "escape-regexp": "0.0.1", - "eslint": "8.7.0", + "eslint": "8.9.0", "eslint-plugin-import": "2.25.4", "eventemitter3": "4.0.7", "feed": "4.2.2", @@ -105,7 +101,7 @@ "ip-cidr": "3.0.4", "is-svg": "4.3.2", "js-yaml": "4.1.0", - "jsdom": "16.7.0", + "jsdom": "19.0.0", "json5": "2.2.0", "json5-loader": "4.0.1", "jsonld": "5.2.0", @@ -122,19 +118,19 @@ "langmap": "0.0.16", "mfm-js": "0.21.0", "mime-types": "2.1.34", - "misskey-js": "0.0.13", - "mocha": "8.4.0", + "misskey-js": "0.0.14", + "mocha": "9.2.0", "ms": "3.0.0-canary.1", "multer": "1.4.4", "nested-property": "4.0.0", - "node-fetch": "2.6.1", + "node-fetch": "2.6.7", "nodemailer": "6.7.2", "os-utils": "0.0.14", "parse5": "6.0.1", - "pg": "8.7.1", + "pg": "8.7.3", "portscanner": "2.2.0", "private-ip": "2.3.3", - "probe-image-size": "7.2.2", + "probe-image-size": "7.2.3", "promise-limit": "2.7.0", "pug": "3.0.2", "punycode": "2.1.1", @@ -147,42 +143,41 @@ "redis-lock": "0.1.4", "reflect-metadata": "0.1.13", "rename": "1.0.4", - "request-stats": "3.0.0", "require-all": "3.0.0", "rndstr": "1.0.0", "s-age": "1.1.2", - "sanitize-html": "2.6.1", + "sanitize-html": "2.7.0", "seedrandom": "3.0.5", - "sharp": "0.29.3", + "sharp": "0.30.1", "speakeasy": "2.0.0", "strict-event-emitter-types": "2.0.0", "stringz": "2.1.0", "style-loader": "3.3.1", "summaly": "2.5.0", "syslog-pro": "1.0.0", - "systeminformation": "5.9.9", + "systeminformation": "5.11.3", "throttle-debounce": "3.0.1", "tinycolor2": "1.4.2", "tmp": "0.2.1", "ts-loader": "9.2.6", - "ts-node": "10.4.0", + "ts-node": "10.5.0", "tsc-alias": "1.4.1", "tsconfig-paths": "3.12.0", "twemoji-parser": "13.1.0", - "typeorm": "0.2.41", + "typeorm": "0.2.43", "typescript": "4.5.5", "ulid": "2.3.0", "unzipper": "0.10.11", "uuid": "8.3.2", "web-push": "3.4.5", "websocket": "1.0.34", - "ws": "8.4.2", + "ws": "8.5.0", "xev": "2.0.1" }, "devDependencies": { - "@redocly/openapi-core": "1.0.0-beta.79", + "@redocly/openapi-core": "1.0.0-beta.82", "@types/fluent-ffmpeg": "2.1.20", "cross-env": "7.0.3", - "execa": "6.0.0" + "execa": "6.1.0" } } diff --git a/packages/backend/@types/hcaptcha.d.ts b/packages/backend/src/@types/hcaptcha.d.ts similarity index 100% rename from packages/backend/@types/hcaptcha.d.ts rename to packages/backend/src/@types/hcaptcha.d.ts diff --git a/packages/backend/@types/http-signature.d.ts b/packages/backend/src/@types/http-signature.d.ts similarity index 100% rename from packages/backend/@types/http-signature.d.ts rename to packages/backend/src/@types/http-signature.d.ts diff --git a/packages/backend/@types/jsrsasign.d.ts b/packages/backend/src/@types/jsrsasign.d.ts similarity index 100% rename from packages/backend/@types/jsrsasign.d.ts rename to packages/backend/src/@types/jsrsasign.d.ts diff --git a/packages/backend/@types/koa-json-body.d.ts b/packages/backend/src/@types/koa-json-body.d.ts similarity index 100% rename from packages/backend/@types/koa-json-body.d.ts rename to packages/backend/src/@types/koa-json-body.d.ts diff --git a/packages/backend/@types/koa-slow.d.ts b/packages/backend/src/@types/koa-slow.d.ts similarity index 100% rename from packages/backend/@types/koa-slow.d.ts rename to packages/backend/src/@types/koa-slow.d.ts diff --git a/packages/backend/@types/langmap.d.ts b/packages/backend/src/@types/langmap.d.ts similarity index 100% rename from packages/backend/@types/langmap.d.ts rename to packages/backend/src/@types/langmap.d.ts diff --git a/packages/backend/@types/os-utils.d.ts b/packages/backend/src/@types/os-utils.d.ts similarity index 100% rename from packages/backend/@types/os-utils.d.ts rename to packages/backend/src/@types/os-utils.d.ts diff --git a/packages/backend/@types/package.json.d.ts b/packages/backend/src/@types/package.json.d.ts similarity index 100% rename from packages/backend/@types/package.json.d.ts rename to packages/backend/src/@types/package.json.d.ts diff --git a/packages/backend/@types/probe-image-size.d.ts b/packages/backend/src/@types/probe-image-size.d.ts similarity index 100% rename from packages/backend/@types/probe-image-size.d.ts rename to packages/backend/src/@types/probe-image-size.d.ts diff --git a/packages/backend/src/daemons/server-stats.ts b/packages/backend/src/daemons/server-stats.ts index 47c46042eb..115e88f150 100644 --- a/packages/backend/src/daemons/server-stats.ts +++ b/packages/backend/src/daemons/server-stats.ts @@ -36,8 +36,8 @@ export default function() { tx: round(Math.max(0, netStats.tx_sec)), }, fs: { - r: round(Math.max(0, fsStats.rIO_sec)), - w: round(Math.max(0, fsStats.wIO_sec)), + r: round(Math.max(0, fsStats.rIO_sec ?? 0)), + w: round(Math.max(0, fsStats.wIO_sec ?? 0)), }, }; ev.emit('serverStats', stats); @@ -51,9 +51,9 @@ export default function() { } // CPU STAT -function cpuUsage() { +function cpuUsage(): Promise { return new Promise((res, rej) => { - osUtils.cpuUsage((cpuUsage: number) => { + osUtils.cpuUsage((cpuUsage) => { res(cpuUsage); }); }); diff --git a/packages/backend/src/mfm/from-html.ts b/packages/backend/src/mfm/from-html.ts index fc6d3b3062..21e5ebb7a1 100644 --- a/packages/backend/src/mfm/from-html.ts +++ b/packages/backend/src/mfm/from-html.ts @@ -5,9 +5,7 @@ import { URL } from 'url'; const urlRegex = /^https?:\/\/[\w\/:%#@$&?!()\[\]~.,=+\-]+/; const urlRegexFull = /^https?:\/\/[\w\/:%#@$&?!()\[\]~.,=+\-]+$/; -export function fromHtml(html: string, hashtagNames?: string[]): string | null { - if (html == null) return null; - +export function fromHtml(html: string, hashtagNames?: string[]): string { const dom = parse5.parseFragment(html); let text = ''; diff --git a/packages/backend/src/misc/before-shutdown.ts b/packages/backend/src/misc/before-shutdown.ts index 33abf5fb4d..93ac7a1f39 100644 --- a/packages/backend/src/misc/before-shutdown.ts +++ b/packages/backend/src/misc/before-shutdown.ts @@ -24,14 +24,14 @@ const SHUTDOWN_TIMEOUT = 15000; * down the process. * @type {BeforeShutdownListener[]} */ -const shutdownListeners = []; +const shutdownListeners: ((signalOrEvent: string) => void)[] = []; /** * Listen for signals and execute given `fn` function once. * @param {string[]} signals System signals to listen to. * @param {function(string)} fn Function to execute on shutdown. */ -const processOnce = (signals, fn) => { +const processOnce = (signals: string[], fn: (signalOrEvent: string) => void) => { for (const sig of signals) { process.once(sig, fn); } @@ -41,7 +41,7 @@ const processOnce = (signals, fn) => { * Sets a forced shutdown mechanism that will exit the process after `timeout` milliseconds. * @param {number} timeout Time to wait before forcing shutdown (milliseconds) */ -const forceExitAfter = timeout => () => { +const forceExitAfter = (timeout: number) => () => { setTimeout(() => { // Force shutdown after timeout console.warn(`Could not close resources gracefully after ${timeout}ms: forcing shutdown`); @@ -55,7 +55,7 @@ const forceExitAfter = timeout => () => { * be logged out as a warning, but won't prevent other callbacks from executing. * @param {string} signalOrEvent The exit signal or event name received on the process. */ -async function shutdownHandler(signalOrEvent) { +async function shutdownHandler(signalOrEvent: string) { if (process.env.NODE_ENV === 'test') return process.exit(0); console.warn(`Shutting down: received [${signalOrEvent}] signal`); @@ -64,7 +64,9 @@ async function shutdownHandler(signalOrEvent) { try { await listener(signalOrEvent); } catch (err) { - console.warn(`A shutdown handler failed before completing with: ${err.message || err}`); + if (err instanceof Error) { + console.warn(`A shutdown handler failed before completing with: ${err.message || err}`); + } } } @@ -78,7 +80,7 @@ async function shutdownHandler(signalOrEvent) { * @param {BeforeShutdownListener} listener The shutdown listener to register. * @returns {BeforeShutdownListener} Echoes back the supplied `listener`. */ -export function beforeShutdown(listener) { +export function beforeShutdown(listener: () => void) { shutdownListeners.push(listener); return listener; } diff --git a/packages/backend/src/misc/check-word-mute.ts b/packages/backend/src/misc/check-word-mute.ts index e2e871dd2b..dedda3cdf6 100644 --- a/packages/backend/src/misc/check-word-mute.ts +++ b/packages/backend/src/misc/check-word-mute.ts @@ -11,26 +11,31 @@ type UserLike = { id: User['id']; }; -export async function checkWordMute(note: NoteLike, me: UserLike | null | undefined, mutedWords: string[][]): Promise { +export async function checkWordMute(note: NoteLike, me: UserLike | null | undefined, mutedWords: Array): Promise { // č‡Ē分č‡ĒčēĢ if (me && (note.userId === me.id)) return false; - const words = mutedWords - // Clean up - .map(xs => xs.filter(x => x !== '')) - .filter(xs => xs.length > 0); - - if (words.length > 0) { + if (mutedWords.length > 0) { if (note.text == null) return false; - const matched = words.some(and => - and.every(keyword => { - const regexp = keyword.match(/^\/(.+)\/(.*)$/); - if (regexp) { + const matched = mutedWords.some(filter => { + if (Array.isArray(filter)) { + return filter.every(keyword => note.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(note.text!); + } catch (err) { + // This should never happen due to input sanitisation. + return false; } - return note.text!.includes(keyword); - })); + } + }); if (matched) return true; } diff --git a/packages/backend/src/misc/download-url.ts b/packages/backend/src/misc/download-url.ts index 8e1f7b9e24..ba2fa9fae0 100644 --- a/packages/backend/src/misc/download-url.ts +++ b/packages/backend/src/misc/download-url.ts @@ -38,7 +38,9 @@ export async function downloadUrl(url: string, path: string): Promise { https: httpsAgent, }, http2: false, // default - retry: 0, + retry: { + limit: 0, + }, }).on('response', (res: Got.Response) => { if ((process.env.NODE_ENV === 'production' || process.env.NODE_ENV === 'test') && !config.proxy && res.ip) { if (isPrivateIp(res.ip)) { @@ -75,7 +77,7 @@ export async function downloadUrl(url: string, path: string): Promise { logger.succ(`Download finished: ${chalk.cyan(url)}`); } -function isPrivateIp(ip: string) { +function isPrivateIp(ip: string): boolean { for (const net of config.allowedPrivateNetworks || []) { const cidr = new IPCIDR(net); if (cidr.contains(ip)) { diff --git a/packages/backend/src/misc/gen-identicon.ts b/packages/backend/src/misc/gen-identicon.ts index 5cedd7afaf..fca67fcf21 100644 --- a/packages/backend/src/misc/gen-identicon.ts +++ b/packages/backend/src/misc/gen-identicon.ts @@ -39,7 +39,7 @@ const sideN = Math.floor(n / 2); */ export function genIdenticon(seed: string, stream: WriteStream): Promise { const rand = gen.create(seed); - const canvas = p.make(size, size); + const canvas = p.make(size, size, undefined); const ctx = canvas.getContext('2d'); ctx.fillStyle = bg; 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 23d8ceb1b7..04ff191e41 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,3 @@ -export function isDuplicateKeyValueError(e: Error): boolean { - return e.message.startsWith('duplicate key value'); +export function isDuplicateKeyValueError(e: unknown | Error): boolean { + return (e as any).message && (e as Error).message.startsWith('duplicate key value'); } diff --git a/packages/backend/src/misc/schema.ts b/packages/backend/src/misc/schema.ts index 2dae954af9..fbe5a1e421 100644 --- a/packages/backend/src/misc/schema.ts +++ b/packages/backend/src/misc/schema.ts @@ -65,16 +65,18 @@ export const refs = { // Packed = SchemaTypeDef; ã¨ã™ã‚‹ã¨åą•é–‹ã•ã‚ŒãĻマã‚Ļ゚ホバãƒŧ時ãĢåž‹čĄ¨į¤ēがäŊŋいį‰ŠãĢãĒらãĒくãĒる // ObjTypeを指厚するとīŧˆãĒぜかīŧ‰åą•é–‹ã•ã‚ŒãšãĢPacked<'Hoge'>ã¨čĄ¨į¤ēされる -type PackedDef; allOf?: ReadonlyArray }> = - r['allOf'] extends ReadonlyArray ? UnionToIntersection> : - r['oneOf'] extends ReadonlyArray ? UnionSchemaType : - r['properties'] extends Obj ? ObjType : +type PackedDef; allOf?: ReadonlyArray }> = + r['allOf'] extends ReadonlyArray ? UnionToIntersection> : + r['oneOf'] extends ReadonlyArray ? UnionSchemaType : + r['properties'] extends Obj ? ObjType : never; export type Packed = PackedDef; -type TypeStringef = 'boolean' | 'number' | 'string' | 'array' | 'object' | 'any'; +type TypeStringef = 'null' | 'boolean' | 'integer' | 'number' | 'string' | 'array' | 'object' | 'any'; type StringDefToType = + T extends 'null' ? null : T extends 'boolean' ? boolean : + T extends 'integer' ? number : T extends 'number' ? number : T extends 'string' ? string | Date : T extends 'array' ? ReadonlyArray : @@ -83,17 +85,18 @@ type StringDefToType = // https://swagger.io/specification/?sbsearch=optional#schema-object type OfSchema = { - readonly anyOf?: ReadonlyArray; - readonly oneOf?: ReadonlyArray; - readonly allOf?: ReadonlyArray; + readonly anyOf?: ReadonlyArray; + readonly oneOf?: ReadonlyArray; + readonly allOf?: ReadonlyArray; } -export interface MinimumSchema extends OfSchema { +export interface Schema extends OfSchema { readonly type?: TypeStringef; readonly nullable?: boolean; readonly optional?: boolean; - readonly items?: MinimumSchema; + readonly items?: Schema; readonly properties?: Obj; + readonly required?: ReadonlyArray>; readonly description?: string; readonly example?: any; readonly format?: string; @@ -104,26 +107,33 @@ export interface MinimumSchema extends OfSchema { readonly minLength?: number; } -export interface Schema extends MinimumSchema { - readonly nullable: boolean; - readonly optional: boolean; -} - -type NonUndefinedPropertyNames = { - [K in keyof T]: T[K]['optional'] extends true ? never : K +type OptionalPropertyNames = { + [K in keyof T]: T[K]['optional'] extends true ? K : never }[keyof T]; -type UndefinedPropertyNames = { - [K in keyof T]: T[K]['optional'] extends true ? K : never +type NonOptionalPropertyNames = { + [K in keyof T]: T[K]['optional'] extends false ? K : never +}[keyof T]; + +type DefaultPropertyNames = { + [K in keyof T]: T[K]['default'] extends null ? K : + T[K]['default'] extends string ? K : + T[K]['default'] extends number ? K : + T[K]['default'] extends boolean ? K : + T[K]['default'] extends Record ? K : + never }[keyof T]; export interface Obj { [key: string]: Schema; } -export type ObjType = - { -readonly [P in UndefinedPropertyNames]?: SchemaType } & - { -readonly [P in NonUndefinedPropertyNames]: SchemaType }; +export type ObjType> = + { -readonly [P in keyof s]?: SchemaType } & + { -readonly [P in RequiredProps[number]]: SchemaType } & + { -readonly [P in OptionalPropertyNames]?: SchemaType } & + { -readonly [P in NonOptionalPropertyNames]: SchemaType } & + { -readonly [P in DefaultPropertyNames]: SchemaType }; -type NullOrUndefined

= +type NullOrUndefined

= p['nullable'] extends true ? p['optional'] extends true ? (T | null | undefined) @@ -137,10 +147,12 @@ type UnionToIntersection = (U extends any ? (k: U) => void : never) extends ( // https://github.com/misskey-dev/misskey/pull/8144#discussion_r785287552 // 単į´”ãĢSchemaTypeDefã§åˆ¤åŽšã™ã‚‹ã ã‘ã§ã¯ãƒ€ãƒĄ -type UnionSchemaType = X extends any ? SchemaType : never; +type UnionSchemaType = X extends any ? SchemaType : never; type ArrayUnion = T extends any ? Array : never; -export type SchemaTypeDef

= +export type SchemaTypeDef

= + p['type'] extends 'null' ? null : + p['type'] extends 'integer' ? number : p['type'] extends 'number' ? number : p['type'] extends 'string' ? ( p['enum'] extends readonly string[] ? @@ -151,22 +163,22 @@ export type SchemaTypeDef

= p['type'] extends 'boolean' ? boolean : p['type'] extends 'object' ? ( p['ref'] extends keyof typeof refs ? Packed : - p['properties'] extends NonNullable ? ObjType : - p['anyOf'] extends ReadonlyArray ? UnionSchemaType & Partial>> : - p['allOf'] extends ReadonlyArray ? UnionToIntersection> : + p['properties'] extends NonNullable ? ObjType> : + p['anyOf'] extends ReadonlyArray ? UnionSchemaType & Partial>> : + p['allOf'] extends ReadonlyArray ? UnionToIntersection> : any ) : p['type'] extends 'array' ? ( p['items'] extends OfSchema ? ( - p['items']['anyOf'] extends ReadonlyArray ? UnionSchemaType>[] : - p['items']['oneOf'] extends ReadonlyArray ? ArrayUnion>> : - p['items']['allOf'] extends ReadonlyArray ? UnionToIntersection>>[] : + p['items']['anyOf'] extends ReadonlyArray ? UnionSchemaType>[] : + p['items']['oneOf'] extends ReadonlyArray ? ArrayUnion>> : + p['items']['allOf'] extends ReadonlyArray ? UnionToIntersection>>[] : never ) : - p['items'] extends NonNullable ? SchemaTypeDef[] : + p['items'] extends NonNullable ? SchemaTypeDef[] : any[] ) : - p['oneOf'] extends ReadonlyArray ? UnionSchemaType : + p['oneOf'] extends ReadonlyArray ? UnionSchemaType : any; -export type SchemaType

= NullOrUndefined>; +export type SchemaType

= NullOrUndefined>; diff --git a/packages/backend/src/models/entities/following.ts b/packages/backend/src/models/entities/following.ts index c3631e8501..ad387e5188 100644 --- a/packages/backend/src/models/entities/following.ts +++ b/packages/backend/src/models/entities/following.ts @@ -41,6 +41,7 @@ export class Following { public follower: User | null; //#region Denormalized fields + @Index() @Column('varchar', { length: 128, nullable: true, comment: '[Denormalized]', @@ -59,6 +60,7 @@ export class Following { }) public followerSharedInbox: string | null; + @Index() @Column('varchar', { length: 128, nullable: true, comment: '[Denormalized]', diff --git a/packages/backend/src/models/entities/meta.ts b/packages/backend/src/models/entities/meta.ts index c462d5ade6..1426c79c51 100644 --- a/packages/backend/src/models/entities/meta.ts +++ b/packages/backend/src/models/entities/meta.ts @@ -88,6 +88,12 @@ export class Meta { }) public pinnedClipId: Clip['id'] | null; + @Column('varchar', { + length: 512, + nullable: true, + }) + public themeColor: string | null; + @Column('varchar', { length: 512, nullable: true, diff --git a/packages/backend/src/models/entities/user.ts b/packages/backend/src/models/entities/user.ts index 0aa01ba00a..e4d9a3ced9 100644 --- a/packages/backend/src/models/entities/user.ts +++ b/packages/backend/src/models/entities/user.ts @@ -225,6 +225,12 @@ export class User { }) 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, diff --git a/packages/backend/src/models/index.ts b/packages/backend/src/models/index.ts index 67da347395..da64063cef 100644 --- a/packages/backend/src/models/index.ts +++ b/packages/backend/src/models/index.ts @@ -61,6 +61,7 @@ import { RegistryItem } from './entities/registry-item'; import { Ad } from './entities/ad'; import { PasswordResetRequest } from './entities/password-reset-request'; import { UserPending } from './entities/user-pending'; +import { InstanceRepository } from './repositories/instance'; export const Announcements = getRepository(Announcement); export const AnnouncementReads = getRepository(AnnouncementRead); @@ -89,7 +90,7 @@ export const UserNotePinings = getRepository(UserNotePining); export const UsedUsernames = getRepository(UsedUsername); export const Followings = getCustomRepository(FollowingRepository); export const FollowRequests = getCustomRepository(FollowRequestRepository); -export const Instances = getRepository(Instance); +export const Instances = getCustomRepository(InstanceRepository); export const Emojis = getCustomRepository(EmojiRepository); export const DriveFiles = getCustomRepository(DriveFileRepository); export const DriveFolders = getCustomRepository(DriveFolderRepository); diff --git a/packages/backend/src/models/repositories/abuse-user-report.ts b/packages/backend/src/models/repositories/abuse-user-report.ts index 943b65eb64..144195855b 100644 --- a/packages/backend/src/models/repositories/abuse-user-report.ts +++ b/packages/backend/src/models/repositories/abuse-user-report.ts @@ -12,7 +12,7 @@ export class AbuseUserReportRepository extends Repository { return await awaitAll({ id: report.id, - createdAt: report.createdAt, + createdAt: report.createdAt.toISOString(), comment: report.comment, resolved: report.resolved, reporterId: report.reporterId, diff --git a/packages/backend/src/models/repositories/drive-folder.ts b/packages/backend/src/models/repositories/drive-folder.ts index b2e6cee9b8..cc7e4ca55b 100644 --- a/packages/backend/src/models/repositories/drive-folder.ts +++ b/packages/backend/src/models/repositories/drive-folder.ts @@ -6,13 +6,6 @@ import { Packed } from '@/misc/schema'; @EntityRepository(DriveFolder) export class DriveFolderRepository extends Repository { - public validateFolderName(name: string): boolean { - return ( - (name.trim().length > 0) && - (name.length <= 200) - ); - } - public async pack( src: DriveFolder['id'] | DriveFolder, options?: { diff --git a/packages/backend/src/models/repositories/federation-instance.ts b/packages/backend/src/models/repositories/federation-instance.ts deleted file mode 100644 index 426fd5bfc3..0000000000 --- a/packages/backend/src/models/repositories/federation-instance.ts +++ /dev/null @@ -1,2 +0,0 @@ -import config from '@/config/index'; - diff --git a/packages/backend/src/models/repositories/instance.ts b/packages/backend/src/models/repositories/instance.ts new file mode 100644 index 0000000000..6097f58ba6 --- /dev/null +++ b/packages/backend/src/models/repositories/instance.ts @@ -0,0 +1,39 @@ +import { EntityRepository, Repository } from 'typeorm'; +import { Instance } from '@/models/entities/instance'; +import { Packed } from '@/misc/schema'; + +@EntityRepository(Instance) +export class InstanceRepository extends Repository { + public async pack( + instance: Instance, + ): Promise> { + return { + id: instance.id, + caughtAt: instance.caughtAt.toISOString(), + host: instance.host, + usersCount: instance.usersCount, + notesCount: instance.notesCount, + followingCount: instance.followingCount, + followersCount: instance.followersCount, + latestRequestSentAt: instance.latestRequestSentAt ? instance.latestRequestSentAt.toISOString() : null, + lastCommunicatedAt: instance.lastCommunicatedAt.toISOString(), + isNotResponding: instance.isNotResponding, + isSuspended: instance.isSuspended, + softwareName: instance.softwareName, + softwareVersion: instance.softwareVersion, + openRegistrations: instance.openRegistrations, + name: instance.name, + description: instance.description, + maintainerName: instance.maintainerName, + maintainerEmail: instance.maintainerEmail, + iconUrl: instance.iconUrl, + infoUpdatedAt: instance.infoUpdatedAt ? instance.infoUpdatedAt.toISOString() : null, + }; + } + + public packMany( + instances: Instance[], + ) { + return Promise.all(instances.map(x => this.pack(x))); + } +} diff --git a/packages/backend/src/models/repositories/messaging-message.ts b/packages/backend/src/models/repositories/messaging-message.ts index 0a342430b9..d01d82c368 100644 --- a/packages/backend/src/models/repositories/messaging-message.ts +++ b/packages/backend/src/models/repositories/messaging-message.ts @@ -6,10 +6,6 @@ import { User } from '@/models/entities/user'; @EntityRepository(MessagingMessage) export class MessagingMessageRepository extends Repository { - public validateText(text: string): boolean { - return text.trim().length <= 1000 && text.trim() != ''; - } - public async pack( src: MessagingMessage['id'] | MessagingMessage, me?: { id: User['id'] } | null | undefined, diff --git a/packages/backend/src/models/repositories/moderation-logs.ts b/packages/backend/src/models/repositories/moderation-logs.ts index 1585d5bfcf..f530613bcc 100644 --- a/packages/backend/src/models/repositories/moderation-logs.ts +++ b/packages/backend/src/models/repositories/moderation-logs.ts @@ -12,7 +12,7 @@ export class ModerationLogRepository extends Repository { return await awaitAll({ id: log.id, - createdAt: log.createdAt, + createdAt: log.createdAt.toISOString(), type: log.type, info: log.info, userId: log.userId, diff --git a/packages/backend/src/models/repositories/note-favorite.ts b/packages/backend/src/models/repositories/note-favorite.ts index c5de55c0c0..f4cd64e393 100644 --- a/packages/backend/src/models/repositories/note-favorite.ts +++ b/packages/backend/src/models/repositories/note-favorite.ts @@ -13,7 +13,7 @@ export class NoteFavoriteRepository extends Repository { return { id: favorite.id, - createdAt: favorite.createdAt, + createdAt: favorite.createdAt.toISOString(), noteId: favorite.noteId, note: await Notes.pack(favorite.note || favorite.noteId, me), }; diff --git a/packages/backend/src/models/repositories/note.ts b/packages/backend/src/models/repositories/note.ts index 9a7fef4977..a7e44d9496 100644 --- a/packages/backend/src/models/repositories/note.ts +++ b/packages/backend/src/models/repositories/note.ts @@ -12,10 +12,6 @@ import { aggregateNoteEmojis, populateEmojis, prefetchEmojis } from '@/misc/popu @EntityRepository(Note) export class NoteRepository extends Repository { - public validateCw(x: string) { - return x.trim().length <= 100; - } - public async isVisibleForMe(note: Note, meId: User['id'] | null): Promise { // visibility が specified かつč‡Ē分が指厚されãĻいãĒかãŖãŸã‚‰éžčĄ¨į¤ē if (note.visibility === 'specified') { diff --git a/packages/backend/src/models/repositories/user.ts b/packages/backend/src/models/repositories/user.ts index 2b8398832d..aea94b3797 100644 --- a/packages/backend/src/models/repositories/user.ts +++ b/packages/backend/src/models/repositories/user.ts @@ -1,5 +1,5 @@ -import $ from 'cafy'; import { EntityRepository, Repository, In, Not } from 'typeorm'; +import * as Ajv from 'ajv'; import { User, ILocalUser, IRemoteUser } from '@/models/entities/user'; import { Notes, NoteUnreads, FollowRequests, Notifications, MessagingMessages, UserNotePinings, Followings, Blockings, Mutings, UserProfiles, UserSecurityKeys, UserGroupJoinings, Pages, Announcements, AnnouncementReads, Antennas, AntennaNotes, ChannelFollowings, Instances } from '../index'; import config from '@/config/index'; @@ -17,8 +17,26 @@ type IsMeAndIsUserDetailed : Packed<'UserLite'>; +const ajv = new Ajv(); + @EntityRepository(User) export class UserRepository extends Repository { + public localUsernameSchema = { type: 'string', pattern: /^\w{1,20}$/.toString().slice(1, -1) } as const; + public passwordSchema = { type: 'string', minLength: 1 } as const; + public nameSchema = { type: 'string', minLength: 1, maxLength: 50 } as const; + public descriptionSchema = { type: 'string', minLength: 1, maxLength: 500 } as const; + public locationSchema = { type: 'string', minLength: 1, maxLength: 50 } as const; + public birthdaySchema = { type: 'string', pattern: /^([0-9]{4})-([0-9]{2})-([0-9]{2})$/.toString().slice(1, -1) } as const; + + //#region Validators + public validateLocalUsername = ajv.compile(this.localUsernameSchema); + public validatePassword = ajv.compile(this.passwordSchema); + public validateName = ajv.compile(this.nameSchema); + public validateDescription = ajv.compile(this.descriptionSchema); + public validateLocation = ajv.compile(this.locationSchema); + public validateBirthday = ajv.compile(this.birthdaySchema); + //#endregion + public async getRelation(me: User['id'], target: User['id']) { const [following1, following2, followReq1, followReq2, toBlocking, fromBlocked, mute] = await Promise.all([ Followings.findOne({ @@ -220,6 +238,7 @@ export class UserRepository extends Repository { isModerator: user.isModerator || falsy, isBot: user.isBot || falsy, isCat: user.isCat || falsy, + showTimelineReplies: user.showTimelineReplies || falsy, instance: user.host ? Instances.findOne({ host: user.host }).then(instance => instance ? { name: instance.name, softwareName: instance.softwareName, @@ -350,13 +369,4 @@ export class UserRepository extends Repository { public isRemoteUser(user: User | { host: User['host'] }): boolean { return !this.isLocalUser(user); } - - //#region Validators - public validateLocalUsername = $.str.match(/^\w{1,20}$/); - public validatePassword = $.str.min(1); - public validateName = $.str.min(1).max(50); - public validateDescription = $.str.min(1).max(500); - public validateLocation = $.str.min(1).max(50); - public validateBirthday = $.str.match(/^([0-9]{4})-([0-9]{2})-([0-9]{2})$/); - //#endregion } diff --git a/packages/backend/src/models/schema/federation-instance.ts b/packages/backend/src/models/schema/federation-instance.ts index eef2f9e24f..41fc9d3ef7 100644 --- a/packages/backend/src/models/schema/federation-instance.ts +++ b/packages/backend/src/models/schema/federation-instance.ts @@ -34,14 +34,6 @@ export const packedFederationInstanceSchema = { type: 'number', optional: false, nullable: false, }, - driveUsage: { - type: 'number', - optional: false, nullable: false, - }, - driveFiles: { - type: 'number', - optional: false, nullable: false, - }, latestRequestSentAt: { type: 'string', optional: false, nullable: true, diff --git a/packages/backend/src/queue/index.ts b/packages/backend/src/queue/index.ts index f9994c3b59..62f372f3a8 100644 --- a/packages/backend/src/queue/index.ts +++ b/packages/backend/src/queue/index.ts @@ -6,7 +6,8 @@ import { envOption } from '../env'; import processDeliver from './processors/deliver'; import processInbox from './processors/inbox'; import processDb from './processors/db/index'; -import procesObjectStorage from './processors/object-storage/index'; +import processObjectStorage from './processors/object-storage/index'; +import processSystemQueue from './processors/system/index'; import { queueLogger } from './logger'; import { DriveFile } from '@/models/entities/drive-file'; import { getJobInfo } from './get-job-info'; @@ -255,12 +256,24 @@ export default function() { deliverQueue.process(config.deliverJobConcurrency || 128, processDeliver); inboxQueue.process(config.inboxJobConcurrency || 16, processInbox); processDb(dbQueue); - procesObjectStorage(objectStorageQueue); + processObjectStorage(objectStorageQueue); + + systemQueue.add('tickCharts', { + }, { + repeat: { cron: '55 * * * *' }, + }); systemQueue.add('resyncCharts', { }, { repeat: { cron: '0 0 * * *' }, }); + + systemQueue.add('cleanCharts', { + }, { + repeat: { cron: '0 0 * * *' }, + }); + + processSystemQueue(systemQueue); } export function destroy() { diff --git a/packages/backend/src/queue/processors/db/delete-account.ts b/packages/backend/src/queue/processors/db/delete-account.ts index 4b620842a4..3af181d1d4 100644 --- a/packages/backend/src/queue/processors/db/delete-account.ts +++ b/packages/backend/src/queue/processors/db/delete-account.ts @@ -31,7 +31,7 @@ export async function deleteAccount(job: Bull.Job): Promise order: { id: 1, }, - }); + }) as Note[]; if (notes.length === 0) { break; @@ -58,7 +58,7 @@ export async function deleteAccount(job: Bull.Job): Promise order: { id: 1, }, - }); + }) as DriveFile[]; if (files.length === 0) { break; diff --git a/packages/backend/src/queue/processors/db/export-blocking.ts b/packages/backend/src/queue/processors/db/export-blocking.ts index 01edaaeb63..f4de9ce005 100644 --- a/packages/backend/src/queue/processors/db/export-blocking.ts +++ b/packages/backend/src/queue/processors/db/export-blocking.ts @@ -4,7 +4,7 @@ import * as fs from 'fs'; import { queueLogger } from '../../logger'; import { addFile } from '@/services/drive/add-file'; -import * as dateFormat from 'dateformat'; +import { format as dateFormat } from 'date-fns'; import { getFullApAccount } from '@/misc/convert-host'; import { Users, Blockings } from '@/models/index'; import { MoreThan } from 'typeorm'; @@ -85,7 +85,7 @@ export async function exportBlocking(job: Bull.Job, done: any): P stream.end(); logger.succ(`Exported to: ${path}`); - const fileName = 'blocking-' + dateFormat(new Date(), 'yyyy-mm-dd-HH-MM-ss') + '.csv'; + const fileName = 'blocking-' + dateFormat(new Date(), 'yyyy-MM-dd-HH-mm-ss') + '.csv'; const driveFile = await addFile({ user, path, name: fileName, force: true }); logger.succ(`Exported to: ${driveFile.id}`); diff --git a/packages/backend/src/queue/processors/db/export-custom-emojis.ts b/packages/backend/src/queue/processors/db/export-custom-emojis.ts index 240a542fec..28d54661c5 100644 --- a/packages/backend/src/queue/processors/db/export-custom-emojis.ts +++ b/packages/backend/src/queue/processors/db/export-custom-emojis.ts @@ -7,7 +7,7 @@ const mime = require('mime-types'); const archiver = require('archiver'); import { queueLogger } from '../../logger'; import { addFile } from '@/services/drive/add-file'; -import * as dateFormat from 'dateformat'; +import { format as dateFormat } from 'date-fns'; import { Users, Emojis } from '@/models/index'; import { } from '@/queue/types'; import { downloadUrl } from '@/misc/download-url'; @@ -75,7 +75,7 @@ export async function exportCustomEmojis(job: Bull.Job, done: () => void): Promi await downloadUrl(emoji.originalUrl, emojiPath); downloaded = true; } catch (e) { // TODO: äŊ•åēĻか再čŠĻ行 - logger.error(e); + logger.error(e instanceof Error ? e : new Error(e as string)); } if (!downloaded) { @@ -110,7 +110,7 @@ export async function exportCustomEmojis(job: Bull.Job, done: () => void): Promi archiveStream.on('close', async () => { logger.succ(`Exported to: ${archivePath}`); - const fileName = 'custom-emojis-' + dateFormat(new Date(), 'yyyy-mm-dd-HH-MM-ss') + '.zip'; + const fileName = 'custom-emojis-' + dateFormat(new Date(), 'yyyy-MM-dd-HH-mm-ss') + '.zip'; const driveFile = await addFile({ user, path: archivePath, name: fileName, force: true }); logger.succ(`Exported to: ${driveFile.id}`); diff --git a/packages/backend/src/queue/processors/db/export-following.ts b/packages/backend/src/queue/processors/db/export-following.ts index 06572acec1..d63904aa41 100644 --- a/packages/backend/src/queue/processors/db/export-following.ts +++ b/packages/backend/src/queue/processors/db/export-following.ts @@ -4,7 +4,7 @@ import * as fs from 'fs'; import { queueLogger } from '../../logger'; import { addFile } from '@/services/drive/add-file'; -import * as dateFormat from 'dateformat'; +import { format as dateFormat } from 'date-fns'; import { getFullApAccount } from '@/misc/convert-host'; import { Users, Followings, Mutings } from '@/models/index'; import { In, MoreThan, Not } from 'typeorm'; @@ -51,7 +51,7 @@ export async function exportFollowing(job: Bull.Job, done: () => order: { id: 1, }, - }); + }) as Following[]; if (followings.length === 0) { break; @@ -86,7 +86,7 @@ export async function exportFollowing(job: Bull.Job, done: () => stream.end(); logger.succ(`Exported to: ${path}`); - const fileName = 'following-' + dateFormat(new Date(), 'yyyy-mm-dd-HH-MM-ss') + '.csv'; + const fileName = 'following-' + dateFormat(new Date(), 'yyyy-MM-dd-HH-mm-ss') + '.csv'; const driveFile = await addFile({ user, path, name: fileName, force: true }); logger.succ(`Exported to: ${driveFile.id}`); diff --git a/packages/backend/src/queue/processors/db/export-mute.ts b/packages/backend/src/queue/processors/db/export-mute.ts index 4a856f8ef9..9e917ccbf3 100644 --- a/packages/backend/src/queue/processors/db/export-mute.ts +++ b/packages/backend/src/queue/processors/db/export-mute.ts @@ -4,7 +4,7 @@ import * as fs from 'fs'; import { queueLogger } from '../../logger'; import { addFile } from '@/services/drive/add-file'; -import * as dateFormat from 'dateformat'; +import { format as dateFormat } from 'date-fns'; import { getFullApAccount } from '@/misc/convert-host'; import { Users, Mutings } from '@/models/index'; import { MoreThan } from 'typeorm'; @@ -85,7 +85,7 @@ export async function exportMute(job: Bull.Job, done: any): Promi stream.end(); logger.succ(`Exported to: ${path}`); - const fileName = 'mute-' + dateFormat(new Date(), 'yyyy-mm-dd-HH-MM-ss') + '.csv'; + const fileName = 'mute-' + dateFormat(new Date(), 'yyyy-MM-dd-HH-mm-ss') + '.csv'; const driveFile = await addFile({ user, path, name: fileName, force: true }); logger.succ(`Exported to: ${driveFile.id}`); diff --git a/packages/backend/src/queue/processors/db/export-notes.ts b/packages/backend/src/queue/processors/db/export-notes.ts index 305abf44cf..6719cf0bb7 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 'fs'; import { queueLogger } from '../../logger'; import { addFile } from '@/services/drive/add-file'; -import * as dateFormat from 'dateformat'; +import { format as dateFormat } from 'date-fns'; import { Users, Notes, Polls } from '@/models/index'; import { MoreThan } from 'typeorm'; import { Note } from '@/models/entities/note'; @@ -62,7 +62,7 @@ export async function exportNotes(job: Bull.Job, done: any): Prom order: { id: 1, }, - }); + }) as Note[]; if (notes.length === 0) { job.progress(100); @@ -94,7 +94,7 @@ export async function exportNotes(job: Bull.Job, done: any): Prom stream.end(); logger.succ(`Exported to: ${path}`); - const fileName = 'notes-' + dateFormat(new Date(), 'yyyy-mm-dd-HH-MM-ss') + '.json'; + const fileName = 'notes-' + dateFormat(new Date(), 'yyyy-MM-dd-HH-mm-ss') + '.json'; const driveFile = await addFile({ user, path, name: fileName, force: true }); logger.succ(`Exported to: ${driveFile.id}`); diff --git a/packages/backend/src/queue/processors/db/export-user-lists.ts b/packages/backend/src/queue/processors/db/export-user-lists.ts index f907cf9526..fcd2ba336c 100644 --- a/packages/backend/src/queue/processors/db/export-user-lists.ts +++ b/packages/backend/src/queue/processors/db/export-user-lists.ts @@ -4,7 +4,7 @@ import * as fs from 'fs'; import { queueLogger } from '../../logger'; import { addFile } from '@/services/drive/add-file'; -import * as dateFormat from 'dateformat'; +import { format as dateFormat } from 'date-fns'; import { getFullApAccount } from '@/misc/convert-host'; import { Users, UserLists, UserListJoinings } from '@/models/index'; import { In } from 'typeorm'; @@ -62,7 +62,7 @@ export async function exportUserLists(job: Bull.Job, done: any): stream.end(); logger.succ(`Exported to: ${path}`); - const fileName = 'user-lists-' + dateFormat(new Date(), 'yyyy-mm-dd-HH-MM-ss') + '.csv'; + const fileName = 'user-lists-' + dateFormat(new Date(), 'yyyy-MM-dd-HH-mm-ss') + '.csv'; const driveFile = await addFile({ user, path, name: fileName, force: true }); logger.succ(`Exported to: ${driveFile.id}`); 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 04e93671ed..b6c0126534 100644 --- a/packages/backend/src/queue/processors/db/import-custom-emojis.ts +++ b/packages/backend/src/queue/processors/db/import-custom-emojis.ts @@ -41,7 +41,9 @@ export async function importCustomEmojis(job: Bull.Job, don fs.writeFileSync(destPath, '', 'binary'); await downloadUrl(file.url, destPath); } catch (e) { // TODO: äŊ•åēĻか再čŠĻ行 - logger.error(e); + if (e instanceof Error || typeof e === 'string') { + logger.error(e); + } throw e; } diff --git a/packages/backend/src/queue/processors/db/import-user-lists.ts b/packages/backend/src/queue/processors/db/import-user-lists.ts index e060e86dd8..9b3c0ed60e 100644 --- a/packages/backend/src/queue/processors/db/import-user-lists.ts +++ b/packages/backend/src/queue/processors/db/import-user-lists.ts @@ -51,7 +51,6 @@ export async function importUserLists(job: Bull.Job, done: createdAt: new Date(), userId: user.id, name: listName, - userIds: [], }).then(x => UserLists.findOneOrFail(x.identifiers[0])); } @@ -67,9 +66,9 @@ export async function importUserLists(job: Bull.Job, done: target = await resolveUser(username, host); } - if (await UserListJoinings.findOne({ userListId: list.id, userId: target.id }) != null) continue; + if (await UserListJoinings.findOne({ userListId: list!.id, userId: target.id }) != null) continue; - pushUserToUserList(target, list); + pushUserToUserList(target, list!); } catch (e) { logger.warn(`Error in line:${linenum} ${e}`); } diff --git a/packages/backend/src/queue/processors/deliver.ts b/packages/backend/src/queue/processors/deliver.ts index 46aeb8cb7e..bd91dfc3b5 100644 --- a/packages/backend/src/queue/processors/deliver.ts +++ b/packages/backend/src/queue/processors/deliver.ts @@ -4,7 +4,7 @@ import request from '@/remote/activitypub/request'; import { registerOrFetchInstanceDoc } from '@/services/register-or-fetch-instance-doc'; import Logger from '@/services/logger'; import { Instances } from '@/models/index'; -import { instanceChart } from '@/services/chart/index'; +import { apRequestChart, federationChart, instanceChart } from '@/services/chart/index'; import { fetchInstanceMetadata } from '@/services/fetch-instance-metadata'; import { fetchMeta } from '@/misc/fetch-meta'; import { toPuny } from '@/misc/convert-host'; @@ -61,6 +61,8 @@ export default async (job: Bull.Job) => { fetchInstanceMetadata(i); instanceChart.requestSent(i.host, true); + apRequestChart.deliverSucc(); + federationChart.deliverd(i.host, true); }); return 'Success'; @@ -74,6 +76,8 @@ export default async (job: Bull.Job) => { }); instanceChart.requestSent(i.host, false); + apRequestChart.deliverFail(); + federationChart.deliverd(i.host, false); }); if (res instanceof StatusError) { diff --git a/packages/backend/src/queue/processors/inbox.ts b/packages/backend/src/queue/processors/inbox.ts index bfdebc0077..c189256c33 100644 --- a/packages/backend/src/queue/processors/inbox.ts +++ b/packages/backend/src/queue/processors/inbox.ts @@ -5,7 +5,7 @@ import perform from '@/remote/activitypub/perform'; import Logger from '@/services/logger'; import { registerOrFetchInstanceDoc } from '@/services/register-or-fetch-instance-doc'; import { Instances } from '@/models/index'; -import { instanceChart } from '@/services/chart/index'; +import { apRequestChart, federationChart, instanceChart } from '@/services/chart/index'; import { fetchMeta } from '@/misc/fetch-meta'; import { toPuny, extractDbHost } from '@/misc/convert-host'; import { getApId } from '@/remote/activitypub/type'; @@ -54,10 +54,12 @@ export default async (job: Bull.Job): Promise => { authUser = await dbResolver.getAuthUserFromApId(getApId(activity.actor)); } catch (e) { // å¯žčąĄãŒ4xxãĒら゚キップ - if (e instanceof StatusError && e.isClientError) { - return `skip: Ignored deleted actors on both ends ${activity.actor} - ${e.statusCode}`; + if (e instanceof StatusError) { + if (e.isClientError) { + return `skip: Ignored deleted actors on both ends ${activity.actor} - ${e.statusCode}`; + } + throw `Error in actor ${activity.actor} - ${e.statusCode || e}`; } - throw `Error in actor ${activity.actor} - ${e.statusCode || e}`; } } @@ -141,6 +143,8 @@ export default async (job: Bull.Job): Promise => { fetchInstanceMetadata(i); instanceChart.requestReceived(i.host); + apRequestChart.inbox(); + federationChart.inbox(i.host); }); // ã‚ĸクテã‚Ŗビテã‚Ŗをå‡Ļį† diff --git a/packages/backend/src/queue/processors/system/clean-charts.ts b/packages/backend/src/queue/processors/system/clean-charts.ts new file mode 100644 index 0000000000..3ae0f495f8 --- /dev/null +++ b/packages/backend/src/queue/processors/system/clean-charts.ts @@ -0,0 +1,28 @@ +import * as Bull from 'bull'; + +import { queueLogger } from '../../logger'; +import { activeUsersChart, driveChart, federationChart, hashtagChart, instanceChart, notesChart, perUserDriveChart, perUserFollowingChart, perUserNotesChart, perUserReactionsChart, usersChart, apRequestChart } from '@/services/chart/index'; + +const logger = queueLogger.createSubLogger('clean-charts'); + +export async function cleanCharts(job: Bull.Job>, done: any): Promise { + logger.info(`Clean charts...`); + + await Promise.all([ + federationChart.clean(), + notesChart.clean(), + usersChart.clean(), + activeUsersChart.clean(), + instanceChart.clean(), + perUserNotesChart.clean(), + driveChart.clean(), + perUserReactionsChart.clean(), + hashtagChart.clean(), + perUserFollowingChart.clean(), + perUserDriveChart.clean(), + apRequestChart.clean(), + ]); + + logger.succ(`All charts successfully cleaned.`); + done(); +} diff --git a/packages/backend/src/queue/processors/system/index.ts b/packages/backend/src/queue/processors/system/index.ts index 8460ea0a9b..1513ea4a84 100644 --- a/packages/backend/src/queue/processors/system/index.ts +++ b/packages/backend/src/queue/processors/system/index.ts @@ -1,8 +1,12 @@ import * as Bull from 'bull'; +import { tickCharts } from './tick-charts'; import { resyncCharts } from './resync-charts'; +import { cleanCharts } from './clean-charts'; const jobs = { + tickCharts, resyncCharts, + cleanCharts, } as Record> | Bull.ProcessPromiseFunction>>; export default function(dbQueue: Bull.Queue>) { diff --git a/packages/backend/src/queue/processors/system/tick-charts.ts b/packages/backend/src/queue/processors/system/tick-charts.ts new file mode 100644 index 0000000000..d53089f89c --- /dev/null +++ b/packages/backend/src/queue/processors/system/tick-charts.ts @@ -0,0 +1,28 @@ +import * as Bull from 'bull'; + +import { queueLogger } from '../../logger'; +import { activeUsersChart, driveChart, federationChart, hashtagChart, instanceChart, notesChart, perUserDriveChart, perUserFollowingChart, perUserNotesChart, perUserReactionsChart, usersChart, apRequestChart } from '@/services/chart/index'; + +const logger = queueLogger.createSubLogger('tick-charts'); + +export async function tickCharts(job: Bull.Job>, done: any): Promise { + logger.info(`Tick charts...`); + + await Promise.all([ + federationChart.tick(false), + notesChart.tick(false), + usersChart.tick(false), + activeUsersChart.tick(false), + instanceChart.tick(false), + perUserNotesChart.tick(false), + driveChart.tick(false), + perUserReactionsChart.tick(false), + hashtagChart.tick(false), + perUserFollowingChart.tick(false), + perUserDriveChart.tick(false), + apRequestChart.tick(false), + ]); + + logger.succ(`All charts successfully ticked.`); + done(); +} diff --git a/packages/backend/src/remote/activitypub/kernel/announce/note.ts b/packages/backend/src/remote/activitypub/kernel/announce/note.ts index e9158f7752..eae92d4180 100644 --- a/packages/backend/src/remote/activitypub/kernel/announce/note.ts +++ b/packages/backend/src/remote/activitypub/kernel/announce/note.ts @@ -42,11 +42,14 @@ export default async function(resolver: Resolver, actor: IRemoteUser, activity: renote = await resolveNote(targetUri); } catch (e) { // å¯žčąĄãŒ4xxãĒら゚キップ - if (e instanceof StatusError && e.isClientError) { - logger.warn(`Ignored announce target ${targetUri} - ${e.statusCode}`); - return; + if (e instanceof StatusError) { + if (e.isClientError) { + logger.warn(`Ignored announce target ${targetUri} - ${e.statusCode}`); + return; + } + + logger.warn(`Error in announce target ${targetUri} - ${e.statusCode || e}`); } - logger.warn(`Error in announce target ${targetUri} - ${e.statusCode || e}`); throw e; } diff --git a/packages/backend/src/remote/activitypub/kernel/flag/index.ts b/packages/backend/src/remote/activitypub/kernel/flag/index.ts index aec6d2daaa..d910e2ebe2 100644 --- a/packages/backend/src/remote/activitypub/kernel/flag/index.ts +++ b/packages/backend/src/remote/activitypub/kernel/flag/index.ts @@ -10,7 +10,7 @@ export default async (actor: IRemoteUser, activity: IFlag): Promise => { // å¯žčąĄãƒĻãƒŧã‚ļãƒŧは一į•Ē最初ぎãƒĻãƒŧã‚ļãƒŧ としãĻ あとはã‚ŗãƒĄãƒŗトとしãĻæ ŧį´ã™ã‚‹ const uris = getApIds(activity.object); - const userIds = uris.filter(uri => uri.startsWith(config.url + '/users/')).map(uri => uri.split('/').pop()); + const userIds = uris.filter(uri => uri.startsWith(config.url + '/users/')).map(uri => uri.split('/').pop()!); const users = await Users.find({ id: In(userIds), }); diff --git a/packages/backend/src/remote/activitypub/kernel/index.ts b/packages/backend/src/remote/activitypub/kernel/index.ts index 20df28eec6..a103e5a1be 100644 --- a/packages/backend/src/remote/activitypub/kernel/index.ts +++ b/packages/backend/src/remote/activitypub/kernel/index.ts @@ -25,8 +25,10 @@ export async function performActivity(actor: IRemoteUser, activity: IObject) { const act = await resolver.resolve(item); try { await performOneActivity(actor, act); - } catch (e) { - apLogger.error(e); + } catch (err) { + if (err instanceof Error || typeof err === 'string') { + apLogger.error(err); + } } } } else { diff --git a/packages/backend/src/remote/activitypub/misc/ld-signature.ts b/packages/backend/src/remote/activitypub/misc/ld-signature.ts index 946914bfaa..3b799c755c 100644 --- a/packages/backend/src/remote/activitypub/misc/ld-signature.ts +++ b/packages/backend/src/remote/activitypub/misc/ld-signature.ts @@ -24,7 +24,7 @@ export class LdSignature { } as { type: string; creator: string; - domain: string; + domain?: string; nonce: string; created: string; }; @@ -114,7 +114,7 @@ export class LdSignature { Accept: 'application/ld+json, application/json', }, timeout: this.loderTimeout, - agent: u => u.protocol == 'http:' ? httpAgent : httpsAgent, + agent: u => u.protocol === 'http:' ? httpAgent : httpsAgent, }).then(res => { if (!res.ok) { throw `${res.status} ${res.statusText}`; diff --git a/packages/backend/src/remote/activitypub/models/person.ts b/packages/backend/src/remote/activitypub/models/person.ts index 19a7a70903..12660a5441 100644 --- a/packages/backend/src/remote/activitypub/models/person.ts +++ b/packages/backend/src/remote/activitypub/models/person.ts @@ -164,6 +164,7 @@ export async function createPerson(uri: string, resolver?: Resolver): Promise { +export default async function renderNote(note: Note, dive = true, isTalk = false): Promise> { const getPromisedFiles = async (ids: string[]) => { if (!ids || ids.length === 0) return []; const items = await DriveFiles.find({ id: In(ids) }); diff --git a/packages/backend/src/remote/activitypub/renderer/ordered-collection.ts b/packages/backend/src/remote/activitypub/renderer/ordered-collection.ts index c4b4337af8..ff9a77be3d 100644 --- a/packages/backend/src/remote/activitypub/renderer/ordered-collection.ts +++ b/packages/backend/src/remote/activitypub/renderer/ordered-collection.ts @@ -6,7 +6,14 @@ * @param last URL of last page (optional) * @param orderedItems attached objects (optional) */ -export default function(id: string | null, totalItems: any, first?: string, last?: string, orderedItems?: Record) { +export default function(id: string | null, totalItems: any, first?: string, last?: string, orderedItems?: Record[]): { + id: string | null; + type: 'OrderedCollection'; + totalItems: any; + first?: string; + last?: string; + orderedItems?: Record[]; +} { const page: any = { id, type: 'OrderedCollection', diff --git a/packages/backend/src/server/activitypub/featured.ts b/packages/backend/src/server/activitypub/featured.ts index 40b8d8cc81..ed5bfc4267 100644 --- a/packages/backend/src/server/activitypub/featured.ts +++ b/packages/backend/src/server/activitypub/featured.ts @@ -32,7 +32,7 @@ export default async (ctx: Router.RouterContext) => { const rendered = renderOrderedCollection( `${config.url}/users/${userId}/collections/featured`, - renderedNotes.length, undefined, undefined, renderedNotes + renderedNotes.length, undefined, undefined, renderedNotes, ); ctx.body = renderActivity(rendered); diff --git a/packages/backend/src/server/api/api-handler.ts b/packages/backend/src/server/api/api-handler.ts index faa35d12d4..cd7b9615bc 100644 --- a/packages/backend/src/server/api/api-handler.ts +++ b/packages/backend/src/server/api/api-handler.ts @@ -5,7 +5,7 @@ import authenticate, { AuthenticationError } from './authenticate'; import call from './call'; import { ApiError } from './error'; -export default (endpoint: IEndpoint, ctx: Koa.Context) => new Promise((res) => { +export default (endpoint: IEndpoint, ctx: Koa.Context) => new Promise((res) => { const body = ctx.request.body; const reply = (x?: any, y?: ApiError) => { @@ -32,7 +32,7 @@ export default (endpoint: IEndpoint, ctx: Koa.Context) => new Promise((res) => { // Authentication authenticate(body['i']).then(([user, app]) => { // API invoking - call(endpoint.name, user, app, body, (ctx as any).file).then((res: any) => { + call(endpoint.name, user, app, body, ctx).then((res: any) => { reply(res); }).catch((e: ApiError) => { reply(e.httpStatusCode ? e.httpStatusCode : e.kind === 'client' ? 400 : 500, e); diff --git a/packages/backend/src/server/api/call.ts b/packages/backend/src/server/api/call.ts index 399ee65bde..ea457d9556 100644 --- a/packages/backend/src/server/api/call.ts +++ b/packages/backend/src/server/api/call.ts @@ -1,7 +1,8 @@ +import * as Koa from 'koa'; import { performance } from 'perf_hooks'; import { limiter } from './limiter'; import { User } from '@/models/entities/user'; -import endpoints from './endpoints'; +import endpoints, { IEndpoint } from './endpoints'; import { ApiError } from './error'; import { apiLogger } from './logger'; import { AccessToken } from '@/models/entities/access-token'; @@ -12,7 +13,7 @@ const accessDenied = { id: '56f35758-7dd5-468b-8439-5d6fb8ec9b8e', }; -export default async (endpoint: string, user: User | null | undefined, token: AccessToken | null | undefined, data: any, file?: any) => { +export default async (endpoint: string, user: User | null | undefined, token: AccessToken | null | undefined, data: any, ctx?: Koa.Context) => { const isSecure = user != null && token == null; const ep = endpoints.find(e => e.name === endpoint); @@ -66,7 +67,7 @@ export default async (endpoint: string, user: User | null | undefined, token: Ac if (ep.meta.requireCredential && ep.meta.limit && !user!.isAdmin && !user!.isModerator) { // Rate limit - await limiter(ep, user!).catch(e => { + await limiter(ep as IEndpoint & { meta: { limit: NonNullable } }, user!).catch(e => { throw new ApiError({ message: 'Rate limit exceeded. Please try again later.', code: 'RATE_LIMIT_EXCEEDED', @@ -76,9 +77,30 @@ export default async (endpoint: string, user: User | null | undefined, token: Ac }); } + // Cast non JSON input + if (ep.meta.requireFile) { + for (const k of Object.keys(ep.params)) { + const param = ep.params.properties![k]; + if (['boolean', 'number', 'integer'].includes(param.type ?? '') && typeof data[k] === 'string') { + try { + data[k] = JSON.parse(data[k]); + } catch (e) { + throw new ApiError({ + message: 'Invalid param.', + code: 'INVALID_PARAM', + id: '0b5f1631-7c1a-41a6-b399-cce335f34d85', + }, { + param: k, + reason: `cannot cast to ${param.type}`, + }); + } + } + } + } + // API invoking const before = performance.now(); - return await ep.exec(data, user, token, file).catch((e: Error) => { + return await ep.exec(data, user, token, ctx?.file).catch((e: Error) => { if (e instanceof ApiError) { throw e; } else { 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 fbc41b2c25..fac425b761 100644 --- a/packages/backend/src/server/api/common/generate-replies-query.ts +++ b/packages/backend/src/server/api/common/generate-replies-query.ts @@ -1,7 +1,7 @@ import { User } from '@/models/entities/user'; import { Brackets, SelectQueryBuilder } from 'typeorm'; -export function generateRepliesQuery(q: SelectQueryBuilder, me?: { id: User['id'] } | null) { +export function generateRepliesQuery(q: SelectQueryBuilder, me?: Pick | null) { if (me == null) { q.andWhere(new Brackets(qb => { qb .where(`note.replyId IS NULL`) // čŋ”äŋĄã§ã¯ãĒい @@ -10,7 +10,7 @@ export function generateRepliesQuery(q: SelectQueryBuilder, me?: { id: User .andWhere('note.replyUserId = note.userId'); })); })); - } else { + } else if (!me.showTimelineReplies) { q.andWhere(new Brackets(qb => { qb .where(`note.replyId IS NULL`) // čŋ”äŋĄã§ã¯ãĒい .orWhere('note.replyUserId = :meId', { meId: me.id }) // čŋ”äŋĄã ã‘おč‡Ē分ぎノãƒŧトへぎčŋ”äŋĄ diff --git a/packages/backend/src/server/api/common/signup.ts b/packages/backend/src/server/api/common/signup.ts index f8db7e374e..786c94cf59 100644 --- a/packages/backend/src/server/api/common/signup.ts +++ b/packages/backend/src/server/api/common/signup.ts @@ -21,13 +21,13 @@ export async function signup(opts: { let hash = passwordHash; // Validate username - if (!Users.validateLocalUsername.ok(username)) { + if (!Users.validateLocalUsername(username)) { throw new Error('INVALID_USERNAME'); } if (password != null && passwordHash == null) { // Validate password - if (!Users.validatePassword.ok(password)) { + if (!Users.validatePassword(password)) { throw new Error('INVALID_PASSWORD'); } diff --git a/packages/backend/src/server/api/define.ts b/packages/backend/src/server/api/define.ts index 71e5fadde0..061ade17bf 100644 --- a/packages/backend/src/server/api/define.ts +++ b/packages/backend/src/server/api/define.ts @@ -1,14 +1,14 @@ import * as fs from 'fs'; +import * as Ajv from 'ajv'; import { ILocalUser } from '@/models/entities/user'; import { IEndpointMeta } from './endpoints'; import { ApiError } from './error'; -import { SchemaType } from '@/misc/schema'; +import { Schema, SchemaType } from '@/misc/schema'; import { AccessToken } from '@/models/entities/access-token'; -type NonOptional = T extends undefined ? never : T; - type SimpleUserInfo = { id: ILocalUser['id']; + createdAt: ILocalUser['createdAt']; host: ILocalUser['host']; username: ILocalUser['username']; uri: ILocalUser['uri']; @@ -17,24 +17,27 @@ type SimpleUserInfo = { isAdmin: ILocalUser['isAdmin']; isModerator: ILocalUser['isModerator']; isSilenced: ILocalUser['isSilenced']; -}; - -type Params = { - [P in keyof T['params']]: NonNullable[P]['transform'] extends () => any - ? ReturnType[P]['transform']> - : NonNullable[P]['default'] extends null | number | string - ? NonOptional[P]['validator']['get']>[0]> - : ReturnType[P]['validator']['get']>[0]; + showTimelineReplies: ILocalUser['showTimelineReplies']; }; export type Response = Record | void; -type executor = - (params: Params, user: T['requireCredential'] extends true ? SimpleUserInfo : SimpleUserInfo | null, token: AccessToken | null, file?: any, cleanup?: () => any) => +// TODO: paramsぎ型をT['params']ぎ゚キãƒŧマ厚įžŠã‹ã‚‰æŽ¨čĢ–する +type executor = + (params: SchemaType, user: T['requireCredential'] extends true ? SimpleUserInfo : SimpleUserInfo | null, token: AccessToken | null, file?: any, cleanup?: () => any) => Promise>>; -export default function (meta: T, cb: executor) +const ajv = new Ajv({ + useDefaults: true, +}); + +ajv.addFormat('misskey:id', /^[a-z0-9]+$/); + +export default function (meta: T, paramDef: Ps, cb: executor) : (params: any, user: T['requireCredential'] extends true ? SimpleUserInfo : SimpleUserInfo | null, token: AccessToken | null, file?: any) => Promise { + + const validate = ajv.compile(paramDef); + return (params: any, user: T['requireCredential'] extends true ? SimpleUserInfo : SimpleUserInfo | null, token: AccessToken | null, file?: any) => { function cleanup() { fs.unlink(file.path, () => {}); @@ -46,42 +49,22 @@ export default function (meta: T, cb: executor) id: '4267801e-70d1-416a-b011-4ee502885d8b', })); - const [ps, pserr] = getParams(meta, params); - if (pserr) { + const valid = validate(params); + if (!valid) { if (file) cleanup(); - return Promise.reject(pserr); - } - return cb(ps, user, token, file, cleanup); - }; -} - -function getParams(defs: T, params: any): [Params, ApiError | null] { - if (defs.params == null) return [params, null]; - - const x: any = {}; - let err: ApiError | null = null; - Object.entries(defs.params).some(([k, def]) => { - const [v, e] = def.validator.get(params[k]); - if (e) { - err = new ApiError({ + const errors = validate.errors!; + const err = new ApiError({ message: 'Invalid param.', code: 'INVALID_PARAM', id: '3d81ceae-475f-4600-b2a8-2bc116157532', }, { - param: k, - reason: e.message, + param: errors[0].schemaPath, + reason: errors[0].message, }); - return true; - } else { - if (v === undefined && Object.prototype.hasOwnProperty.call(def, 'default')) { - x[k] = def.default; - } else { - x[k] = v; - } - if (def.transform) x[k] = def.transform(x[k]); - return false; + return Promise.reject(err); } - }); - return [x, err]; + + return cb(params, user, token, file, cleanup); + }; } diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts index bb4e972b8e..32dbdeb315 100644 --- a/packages/backend/src/server/api/endpoints.ts +++ b/packages/backend/src/server/api/endpoints.ts @@ -1,6 +1,4 @@ -import { fileURLToPath } from 'url'; import { dirname } from 'path'; -import { Context } from 'cafy'; import * as path from 'path'; import * as glob from 'glob'; import { Schema } from '@/misc/schema'; @@ -9,23 +7,11 @@ import { Schema } from '@/misc/schema'; const _filename = __filename; const _dirname = dirname(_filename); -export type Param = { - validator: Context; - transform?: any; - default?: any; - deprecated?: boolean; - ref?: string; -}; - export interface IEndpointMeta { readonly stability?: 'deprecated' | 'experimental' | 'stable'; readonly tags?: ReadonlyArray; - readonly params?: { - readonly [key: string]: Param; - }; - readonly errors?: { readonly [key: string]: { readonly message: string; @@ -99,12 +85,15 @@ export interface IEndpointMeta { * パãƒŧãƒŸãƒƒã‚ˇãƒ§ãƒŗぎ原įžãĢ刊į”¨ã•ã‚Œãžã™ã€‚ */ readonly kind?: string; + + readonly description?: string; } export interface IEndpoint { name: string; exec: any; meta: IEndpointMeta; + params: Schema; } const files = glob.sync('**/*.js', { @@ -118,6 +107,7 @@ const endpoints: IEndpoint[] = files.map(f => { name: f.replace('.js', ''), exec: ep.default, meta: ep.meta || {}, + params: ep.paramDef, }; }); diff --git a/packages/backend/src/server/api/endpoints/admin/abuse-user-reports.ts b/packages/backend/src/server/api/endpoints/admin/abuse-user-reports.ts index ed7b146d03..97b7bc7dba 100644 --- a/packages/backend/src/server/api/endpoints/admin/abuse-user-reports.ts +++ b/packages/backend/src/server/api/endpoints/admin/abuse-user-reports.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../define'; import { AbuseUserReports } from '@/models/index'; import { makePaginationQuery } from '../../common/make-pagination-query'; @@ -10,49 +8,6 @@ export const meta = { requireCredential: true, requireModerator: true, - params: { - limit: { - validator: $.optional.num.range(1, 100), - default: 10, - }, - - sinceId: { - validator: $.optional.type(ID), - }, - - untilId: { - validator: $.optional.type(ID), - }, - - state: { - validator: $.optional.nullable.str, - default: null, - }, - - reporterOrigin: { - validator: $.optional.str.or([ - 'combined', - 'local', - 'remote', - ]), - default: 'combined', - }, - - targetUserOrigin: { - validator: $.optional.str.or([ - 'combined', - 'local', - 'remote', - ]), - default: 'combined', - }, - - forwarded: { - validator: $.optional.bool, - default: false, - }, - }, - res: { type: 'array', optional: false, nullable: false, @@ -115,8 +70,22 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, + sinceId: { type: 'string', format: 'misskey:id' }, + untilId: { type: 'string', format: 'misskey:id' }, + state: { type: 'string', nullable: true, default: null }, + reporterOrigin: { type: 'string', enum: ['combined', 'local', 'remote'], default: "combined" }, + targetUserOrigin: { type: 'string', enum: ['combined', 'local', 'remote'], default: "combined" }, + forwarded: { type: 'boolean', default: false }, + }, + required: [], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps) => { +export default define(meta, paramDef, async (ps) => { const query = makePaginationQuery(AbuseUserReports.createQueryBuilder('report'), ps.sinceId, ps.untilId); switch (ps.state) { @@ -134,7 +103,7 @@ export default define(meta, async (ps) => { case 'remote': query.andWhere('report.targetUserHost IS NOT NULL'); break; } - const reports = await query.take(ps.limit!).getMany(); + const reports = await query.take(ps.limit).getMany(); return await AbuseUserReports.packMany(reports); }); 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 20f1232959..50b2eb0427 100644 --- a/packages/backend/src/server/api/endpoints/admin/accounts/create.ts +++ b/packages/backend/src/server/api/endpoints/admin/accounts/create.ts @@ -5,16 +5,6 @@ import { signup } from '../../../common/signup'; export const meta = { tags: ['admin'], - params: { - username: { - validator: Users.validateLocalUsername, - }, - - password: { - validator: Users.validatePassword, - }, - }, - res: { type: 'object', optional: false, nullable: false, @@ -28,8 +18,17 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + username: Users.localUsernameSchema, + password: Users.passwordSchema, + }, + required: ['username', 'password'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, _me) => { +export default define(meta, paramDef, async (ps, _me) => { const me = _me ? await Users.findOneOrFail(_me.id) : null; const noUsers = (await Users.count({ host: null, diff --git a/packages/backend/src/server/api/endpoints/admin/accounts/delete.ts b/packages/backend/src/server/api/endpoints/admin/accounts/delete.ts index 1701c1e3a7..99d61b2f32 100644 --- a/packages/backend/src/server/api/endpoints/admin/accounts/delete.ts +++ b/packages/backend/src/server/api/endpoints/admin/accounts/delete.ts @@ -1,26 +1,26 @@ -import $ from 'cafy'; import define from '../../../define'; import { Users } from '@/models/index'; import { doPostSuspend } from '@/services/suspend-user'; import { publishUserEvent } from '@/services/stream'; import { createDeleteAccountJob } from '@/queue'; -import { ID } from '@/misc/cafy-id'; export const meta = { tags: ['admin'], requireCredential: true, requireModerator: true, +} as const; - params: { - userId: { - validator: $.type(ID), - }, +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, async (ps, me) => { +export default define(meta, paramDef, async (ps, me) => { const user = await Users.findOne(ps.userId); if (user == null) { diff --git a/packages/backend/src/server/api/endpoints/admin/ad/create.ts b/packages/backend/src/server/api/endpoints/admin/ad/create.ts index 00ad2012fe..9883efefe7 100644 --- a/packages/backend/src/server/api/endpoints/admin/ad/create.ts +++ b/packages/backend/src/server/api/endpoints/admin/ad/create.ts @@ -1,4 +1,3 @@ -import $ from 'cafy'; import define from '../../../define'; import { Ads } from '@/models/index'; import { genId } from '@/misc/gen-id'; @@ -8,34 +7,24 @@ export const meta = { requireCredential: true, requireModerator: true, +} as const; - params: { - url: { - validator: $.str.min(1), - }, - memo: { - validator: $.str, - }, - place: { - validator: $.str, - }, - priority: { - validator: $.str, - }, - ratio: { - validator: $.num.int().min(0), - }, - expiresAt: { - validator: $.num.int(), - }, - imageUrl: { - validator: $.str.min(1), - }, +const paramDef = { + type: 'object', + properties: { + url: { type: 'string', minLength: 1 }, + memo: { type: 'string' }, + place: { type: 'string' }, + priority: { type: 'string' }, + ratio: { type: 'integer' }, + expiresAt: { type: 'integer' }, + imageUrl: { type: 'string', minLength: 1 }, }, + required: ['url', 'memo', 'place', 'priority', 'ratio', 'expiresAt', 'imageUrl'], } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps) => { +export default define(meta, paramDef, async (ps) => { await Ads.insert({ id: genId(), createdAt: new Date(), diff --git a/packages/backend/src/server/api/endpoints/admin/ad/delete.ts b/packages/backend/src/server/api/endpoints/admin/ad/delete.ts index c0124e2484..dfd873f016 100644 --- a/packages/backend/src/server/api/endpoints/admin/ad/delete.ts +++ b/packages/backend/src/server/api/endpoints/admin/ad/delete.ts @@ -1,6 +1,4 @@ -import $ from 'cafy'; import define from '../../../define'; -import { ID } from '@/misc/cafy-id'; import { Ads } from '@/models/index'; import { ApiError } from '../../../error'; @@ -10,12 +8,6 @@ export const meta = { requireCredential: true, requireModerator: true, - params: { - id: { - validator: $.type(ID), - }, - }, - errors: { noSuchAd: { message: 'No such ad.', @@ -25,8 +17,16 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + id: { type: 'string', format: 'misskey:id' }, + }, + required: ['id'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, me) => { +export default define(meta, paramDef, async (ps, me) => { const ad = await Ads.findOne(ps.id); if (ad == null) throw new ApiError(meta.errors.noSuchAd); diff --git a/packages/backend/src/server/api/endpoints/admin/ad/list.ts b/packages/backend/src/server/api/endpoints/admin/ad/list.ts index 7a83637f3b..83fdedef77 100644 --- a/packages/backend/src/server/api/endpoints/admin/ad/list.ts +++ b/packages/backend/src/server/api/endpoints/admin/ad/list.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../../define'; import { Ads } from '@/models/index'; import { makePaginationQuery } from '../../../common/make-pagination-query'; @@ -9,29 +7,24 @@ export const meta = { requireCredential: true, requireModerator: true, +} as const; - params: { - limit: { - validator: $.optional.num.range(1, 100), - default: 10, - }, - - sinceId: { - validator: $.optional.type(ID), - }, - - untilId: { - validator: $.optional.type(ID), - }, +const paramDef = { + type: 'object', + properties: { + limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, + 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, async (ps) => { +export default define(meta, paramDef, async (ps) => { const query = makePaginationQuery(Ads.createQueryBuilder('ad'), ps.sinceId, ps.untilId) .andWhere('ad.expiresAt > :now', { now: new Date() }); - const ads = await query.take(ps.limit!).getMany(); + const ads = await query.take(ps.limit).getMany(); return ads; }); diff --git a/packages/backend/src/server/api/endpoints/admin/ad/update.ts b/packages/backend/src/server/api/endpoints/admin/ad/update.ts index c2b09ab9cf..bf4bce5150 100644 --- a/packages/backend/src/server/api/endpoints/admin/ad/update.ts +++ b/packages/backend/src/server/api/endpoints/admin/ad/update.ts @@ -1,6 +1,4 @@ -import $ from 'cafy'; import define from '../../../define'; -import { ID } from '@/misc/cafy-id'; import { Ads } from '@/models/index'; import { ApiError } from '../../../error'; @@ -10,33 +8,6 @@ export const meta = { requireCredential: true, requireModerator: true, - params: { - id: { - validator: $.type(ID), - }, - memo: { - validator: $.str, - }, - url: { - validator: $.str.min(1), - }, - imageUrl: { - validator: $.str.min(1), - }, - place: { - validator: $.str, - }, - priority: { - validator: $.str, - }, - ratio: { - validator: $.num.int().min(0), - }, - expiresAt: { - validator: $.num.int(), - }, - }, - errors: { noSuchAd: { message: 'No such ad.', @@ -46,8 +17,23 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + id: { type: 'string', format: 'misskey:id' }, + memo: { type: 'string' }, + url: { type: 'string', minLength: 1 }, + imageUrl: { type: 'string', minLength: 1 }, + place: { type: 'string' }, + priority: { type: 'string' }, + ratio: { type: 'integer' }, + expiresAt: { type: 'integer' }, + }, + required: ['id', 'memo', 'url', 'imageUrl', 'place', 'priority', 'ratio', 'expiresAt'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, me) => { +export default define(meta, paramDef, async (ps, me) => { const ad = await Ads.findOne(ps.id); if (ad == null) throw new ApiError(meta.errors.noSuchAd); 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 24c4caa37d..8d2bda9781 100644 --- a/packages/backend/src/server/api/endpoints/admin/announcements/create.ts +++ b/packages/backend/src/server/api/endpoints/admin/announcements/create.ts @@ -1,4 +1,3 @@ -import $ from 'cafy'; import define from '../../../define'; import { Announcements } from '@/models/index'; import { genId } from '@/misc/gen-id'; @@ -9,18 +8,6 @@ export const meta = { requireCredential: true, requireModerator: true, - params: { - title: { - validator: $.str.min(1), - }, - text: { - validator: $.str.min(1), - }, - imageUrl: { - validator: $.nullable.str.min(1), - }, - }, - res: { type: 'object', optional: false, nullable: false, @@ -57,8 +44,18 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + title: { type: 'string', minLength: 1 }, + text: { type: 'string', minLength: 1 }, + imageUrl: { type: 'string', nullable: true, minLength: 1 }, + }, + required: ['title', 'text', 'imageUrl'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps) => { +export default define(meta, paramDef, async (ps) => { const announcement = await Announcements.insert({ id: genId(), createdAt: new Date(), diff --git a/packages/backend/src/server/api/endpoints/admin/announcements/delete.ts b/packages/backend/src/server/api/endpoints/admin/announcements/delete.ts index 5548f99006..f0141ca595 100644 --- a/packages/backend/src/server/api/endpoints/admin/announcements/delete.ts +++ b/packages/backend/src/server/api/endpoints/admin/announcements/delete.ts @@ -1,6 +1,4 @@ -import $ from 'cafy'; import define from '../../../define'; -import { ID } from '@/misc/cafy-id'; import { Announcements } from '@/models/index'; import { ApiError } from '../../../error'; @@ -10,12 +8,6 @@ export const meta = { requireCredential: true, requireModerator: true, - params: { - id: { - validator: $.type(ID), - }, - }, - errors: { noSuchAnnouncement: { message: 'No such announcement.', @@ -25,8 +17,16 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + id: { type: 'string', format: 'misskey:id' }, + }, + required: ['id'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, me) => { +export default define(meta, paramDef, async (ps, me) => { const announcement = await Announcements.findOne(ps.id); if (announcement == null) throw new ApiError(meta.errors.noSuchAnnouncement); 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 e5cc53ccdd..993f9877d2 100644 --- a/packages/backend/src/server/api/endpoints/admin/announcements/list.ts +++ b/packages/backend/src/server/api/endpoints/admin/announcements/list.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../../define'; import { Announcements, AnnouncementReads } from '@/models/index'; import { makePaginationQuery } from '../../../common/make-pagination-query'; @@ -10,21 +8,6 @@ export const meta = { requireCredential: true, requireModerator: true, - params: { - limit: { - validator: $.optional.num.range(1, 100), - default: 10, - }, - - sinceId: { - validator: $.optional.type(ID), - }, - - untilId: { - validator: $.optional.type(ID), - }, - }, - res: { type: 'array', optional: false, nullable: false, @@ -69,11 +52,21 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, + 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, async (ps) => { +export default define(meta, paramDef, async (ps) => { const query = makePaginationQuery(Announcements.createQueryBuilder('announcement'), ps.sinceId, ps.untilId); - const announcements = await query.take(ps.limit!).getMany(); + const announcements = await query.take(ps.limit).getMany(); for (const announcement of announcements) { (announcement as any).reads = await AnnouncementReads.count({ 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 f66293bb18..f905616b3e 100644 --- a/packages/backend/src/server/api/endpoints/admin/announcements/update.ts +++ b/packages/backend/src/server/api/endpoints/admin/announcements/update.ts @@ -1,6 +1,4 @@ -import $ from 'cafy'; import define from '../../../define'; -import { ID } from '@/misc/cafy-id'; import { Announcements } from '@/models/index'; import { ApiError } from '../../../error'; @@ -10,21 +8,6 @@ export const meta = { requireCredential: true, requireModerator: true, - params: { - id: { - validator: $.type(ID), - }, - title: { - validator: $.str.min(1), - }, - text: { - validator: $.str.min(1), - }, - imageUrl: { - validator: $.nullable.str.min(1), - }, - }, - errors: { noSuchAnnouncement: { message: 'No such announcement.', @@ -34,8 +17,19 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + id: { type: 'string', format: 'misskey:id' }, + title: { type: 'string', minLength: 1 }, + text: { type: 'string', minLength: 1 }, + imageUrl: { type: 'string', nullable: true, minLength: 1 }, + }, + required: ['id', 'title', 'text', 'imageUrl'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, me) => { +export default define(meta, paramDef, async (ps, me) => { const announcement = await Announcements.findOne(ps.id); if (announcement == null) throw new ApiError(meta.errors.noSuchAnnouncement); diff --git a/packages/backend/src/server/api/endpoints/admin/delete-all-files-of-a-user.ts b/packages/backend/src/server/api/endpoints/admin/delete-all-files-of-a-user.ts index 249e63a0f8..41e2c23488 100644 --- a/packages/backend/src/server/api/endpoints/admin/delete-all-files-of-a-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/delete-all-files-of-a-user.ts @@ -1,24 +1,24 @@ -import $ from 'cafy'; import define from '../../define'; import { deleteFile } from '@/services/drive/delete-file'; import { DriveFiles } from '@/models/index'; -import { ID } from '@/misc/cafy-id'; export const meta = { tags: ['admin'], requireCredential: true, requireModerator: true, +} as const; - params: { - userId: { - validator: $.type(ID), - }, +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, async (ps, me) => { +export default define(meta, paramDef, async (ps, me) => { const files = await DriveFiles.find({ userId: ps.userId, }); diff --git a/packages/backend/src/server/api/endpoints/admin/drive/clean-remote-files.ts b/packages/backend/src/server/api/endpoints/admin/drive/clean-remote-files.ts index acabbfef5c..9b127eb5d3 100644 --- a/packages/backend/src/server/api/endpoints/admin/drive/clean-remote-files.ts +++ b/packages/backend/src/server/api/endpoints/admin/drive/clean-remote-files.ts @@ -8,7 +8,13 @@ export const meta = { requireModerator: true, } as const; +const paramDef = { + type: 'object', + properties: {}, + required: [], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, me) => { +export default define(meta, paramDef, async (ps, me) => { createCleanRemoteFilesJob(); }); diff --git a/packages/backend/src/server/api/endpoints/admin/drive/cleanup.ts b/packages/backend/src/server/api/endpoints/admin/drive/cleanup.ts index 452e7069a8..1c63af6a17 100644 --- a/packages/backend/src/server/api/endpoints/admin/drive/cleanup.ts +++ b/packages/backend/src/server/api/endpoints/admin/drive/cleanup.ts @@ -10,8 +10,14 @@ export const meta = { requireModerator: true, } as const; +const paramDef = { + type: 'object', + properties: {}, + required: [], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, me) => { +export default define(meta, paramDef, async (ps, me) => { const files = await DriveFiles.find({ userId: IsNull(), }); diff --git a/packages/backend/src/server/api/endpoints/admin/drive/files.ts b/packages/backend/src/server/api/endpoints/admin/drive/files.ts index 264f549867..56a8c1c642 100644 --- a/packages/backend/src/server/api/endpoints/admin/drive/files.ts +++ b/packages/backend/src/server/api/endpoints/admin/drive/files.ts @@ -1,8 +1,6 @@ -import $ from 'cafy'; import define from '../../../define'; import { DriveFiles } from '@/models/index'; import { makePaginationQuery } from '../../../common/make-pagination-query'; -import { ID } from '@/misc/cafy-id'; export const meta = { tags: ['admin'], @@ -10,39 +8,6 @@ export const meta = { requireCredential: false, requireModerator: true, - params: { - limit: { - validator: $.optional.num.range(1, 100), - default: 10, - }, - - sinceId: { - validator: $.optional.type(ID), - }, - - untilId: { - validator: $.optional.type(ID), - }, - - type: { - validator: $.optional.nullable.str.match(/^[a-zA-Z0-9\/\-*]+$/), - }, - - origin: { - validator: $.optional.str.or([ - 'combined', - 'local', - 'remote', - ]), - default: 'local', - }, - - hostname: { - validator: $.optional.nullable.str, - default: null, - }, - }, - res: { type: 'array', optional: false, nullable: false, @@ -54,8 +19,21 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, + sinceId: { type: 'string', format: 'misskey:id' }, + untilId: { type: 'string', format: 'misskey:id' }, + type: { type: 'string', nullable: true, pattern: /^[a-zA-Z0-9\/\-*]+$/.toString().slice(1, -1) }, + origin: { type: 'string', enum: ['combined', 'local', 'remote'], default: "local" }, + hostname: { type: 'string', nullable: true, default: null }, + }, + required: [], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, me) => { +export default define(meta, paramDef, async (ps, me) => { const query = makePaginationQuery(DriveFiles.createQueryBuilder('file'), ps.sinceId, ps.untilId); if (ps.origin === 'local') { @@ -76,7 +54,7 @@ export default define(meta, async (ps, me) => { } } - const files = await query.take(ps.limit!).getMany(); + const files = await query.take(ps.limit).getMany(); return await DriveFiles.packMany(files, { detail: true, withUser: true, self: true }); }); diff --git a/packages/backend/src/server/api/endpoints/admin/drive/show-file.ts b/packages/backend/src/server/api/endpoints/admin/drive/show-file.ts index 5d9a1f2703..bb1ed1072a 100644 --- a/packages/backend/src/server/api/endpoints/admin/drive/show-file.ts +++ b/packages/backend/src/server/api/endpoints/admin/drive/show-file.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../../define'; import { ApiError } from '../../../error'; import { DriveFiles } from '@/models/index'; @@ -10,16 +8,6 @@ export const meta = { requireCredential: true, requireModerator: true, - params: { - fileId: { - validator: $.optional.type(ID), - }, - - url: { - validator: $.optional.str, - }, - }, - errors: { noSuchFile: { message: 'No such file.', @@ -161,8 +149,17 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + fileId: { type: 'string', format: 'misskey:id' }, + url: { type: 'string' }, + }, + required: [], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, me) => { +export default define(meta, paramDef, async (ps, me) => { const file = ps.fileId ? await DriveFiles.findOne(ps.fileId) : await DriveFiles.findOne({ where: [{ url: ps.url, diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/add-aliases-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/add-aliases-bulk.ts index f0fd73c276..9aeb04d247 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/add-aliases-bulk.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/add-aliases-bulk.ts @@ -1,6 +1,4 @@ -import $ from 'cafy'; import define from '../../../define'; -import { ID } from '@/misc/cafy-id'; import { Emojis } from '@/models/index'; import { getConnection, In } from 'typeorm'; import { ApiError } from '../../../error'; @@ -10,20 +8,23 @@ export const meta = { requireCredential: true, requireModerator: true, +} as const; - params: { - ids: { - validator: $.arr($.type(ID)), - }, - - aliases: { - validator: $.arr($.str), - }, +const paramDef = { + type: 'object', + properties: { + ids: { type: 'array', items: { + type: 'string', format: 'misskey:id', + } }, + aliases: { type: 'array', items: { + type: 'string', + } }, }, + required: ['ids', 'aliases'], } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps) => { +export default define(meta, paramDef, async (ps) => { const emojis = await Emojis.find({ id: In(ps.ids), }); 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 1dfeae262f..718ecae4b1 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/add.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/add.ts @@ -1,11 +1,9 @@ -import $ from 'cafy'; import define from '../../../define'; import { Emojis, DriveFiles } from '@/models/index'; import { genId } from '@/misc/gen-id'; import { getConnection } from 'typeorm'; import { insertModerationLog } from '@/services/insert-moderation-log'; import { ApiError } from '../../../error'; -import { ID } from '@/misc/cafy-id'; import rndstr from 'rndstr'; import { publishBroadcastStream } from '@/services/stream'; @@ -15,12 +13,6 @@ export const meta = { requireCredential: true, requireModerator: true, - params: { - fileId: { - validator: $.type(ID), - }, - }, - errors: { noSuchFile: { message: 'No such file.', @@ -30,8 +22,16 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + fileId: { type: 'string', format: 'misskey:id' }, + }, + required: ['fileId'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, me) => { +export default define(meta, paramDef, async (ps, me) => { const file = await DriveFiles.findOne(ps.fileId); if (file == null) throw new ApiError(meta.errors.noSuchFile); 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 17cbf208aa..fc20ff0a84 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts @@ -1,11 +1,9 @@ -import $ from 'cafy'; import define from '../../../define'; import { Emojis } from '@/models/index'; import { genId } from '@/misc/gen-id'; import { getConnection } from 'typeorm'; import { ApiError } from '../../../error'; import { DriveFile } from '@/models/entities/drive-file'; -import { ID } from '@/misc/cafy-id'; import { uploadFromUrl } from '@/services/drive/upload-from-url'; import { publishBroadcastStream } from '@/services/stream'; @@ -15,12 +13,6 @@ export const meta = { requireCredential: true, requireModerator: true, - params: { - emojiId: { - validator: $.type(ID), - }, - }, - errors: { noSuchEmoji: { message: 'No such emoji.', @@ -42,8 +34,16 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + emojiId: { type: 'string', format: 'misskey:id' }, + }, + required: ['emojiId'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, me) => { +export default define(meta, paramDef, async (ps, me) => { const emoji = await Emojis.findOne(ps.emojiId); if (emoji == null) { diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/delete-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/delete-bulk.ts index 797a5de672..792e72ff97 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/delete-bulk.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/delete-bulk.ts @@ -1,6 +1,4 @@ -import $ from 'cafy'; import define from '../../../define'; -import { ID } from '@/misc/cafy-id'; import { Emojis } from '@/models/index'; import { getConnection, In } from 'typeorm'; import { insertModerationLog } from '@/services/insert-moderation-log'; @@ -11,16 +9,20 @@ export const meta = { requireCredential: true, requireModerator: true, +} as const; - params: { - ids: { - validator: $.arr($.type(ID)), - }, +const paramDef = { + type: 'object', + properties: { + ids: { type: 'array', items: { + type: 'string', format: 'misskey:id', + } }, }, + required: ['ids'], } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, me) => { +export default define(meta, paramDef, async (ps, me) => { const emojis = await Emojis.find({ id: In(ps.ids), }); diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/delete.ts b/packages/backend/src/server/api/endpoints/admin/emoji/delete.ts index 1580439024..15d44b3a0e 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/delete.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/delete.ts @@ -1,6 +1,4 @@ -import $ from 'cafy'; import define from '../../../define'; -import { ID } from '@/misc/cafy-id'; import { Emojis } from '@/models/index'; import { getConnection } from 'typeorm'; import { insertModerationLog } from '@/services/insert-moderation-log'; @@ -12,12 +10,6 @@ export const meta = { requireCredential: true, requireModerator: true, - params: { - id: { - validator: $.type(ID), - }, - }, - errors: { noSuchEmoji: { message: 'No such emoji.', @@ -27,8 +19,16 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + id: { type: 'string', format: 'misskey:id' }, + }, + required: ['id'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, me) => { +export default define(meta, paramDef, async (ps, me) => { const emoji = await Emojis.findOne(ps.id); if (emoji == null) throw new ApiError(meta.errors.noSuchEmoji); diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/import-zip.ts b/packages/backend/src/server/api/endpoints/admin/emoji/import-zip.ts index 8856a38f24..5ee6dd4c8d 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/import-zip.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/import-zip.ts @@ -1,21 +1,22 @@ -import $ from 'cafy'; import define from '../../../define'; import { createImportCustomEmojisJob } from '@/queue/index'; import ms from 'ms'; -import { ID } from '@/misc/cafy-id'; export const meta = { secure: true, requireCredential: true, requireModerator: true, - params: { - fileId: { - validator: $.type(ID), - }, +} as const; + +const paramDef = { + type: 'object', + properties: { + fileId: { type: 'string', format: 'misskey:id' }, }, + required: ['fileId'], } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { createImportCustomEmojisJob(user, ps.fileId); }); 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 6e502547f5..865715968a 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 @@ -1,9 +1,7 @@ -import $ from 'cafy'; import define from '../../../define'; import { Emojis } from '@/models/index'; import { toPuny } from '@/misc/convert-host'; import { makePaginationQuery } from '../../../common/make-pagination-query'; -import { ID } from '@/misc/cafy-id'; export const meta = { tags: ['admin'], @@ -11,31 +9,6 @@ export const meta = { requireCredential: true, requireModerator: true, - params: { - query: { - validator: $.optional.nullable.str, - default: null, - }, - - host: { - validator: $.optional.nullable.str, - default: null, - }, - - limit: { - validator: $.optional.num.range(1, 100), - default: 10, - }, - - sinceId: { - validator: $.optional.type(ID), - }, - - untilId: { - validator: $.optional.type(ID), - }, - }, - res: { type: 'array', optional: false, nullable: false, @@ -77,8 +50,20 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + query: { type: 'string', nullable: true, default: null }, + host: { type: 'string', nullable: true, default: null }, + limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, + 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, async (ps) => { +export default define(meta, paramDef, async (ps) => { const q = makePaginationQuery(Emojis.createQueryBuilder('emoji'), ps.sinceId, ps.untilId); if (ps.host == null) { @@ -93,7 +78,7 @@ export default define(meta, async (ps) => { const emojis = await q .orderBy('emoji.id', 'DESC') - .take(ps.limit!) + .take(ps.limit) .getMany(); return Emojis.packMany(emojis); 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 76ef190f94..0e23132d99 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/list.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/list.ts @@ -1,8 +1,6 @@ -import $ from 'cafy'; import define from '../../../define'; import { Emojis } from '@/models/index'; import { makePaginationQuery } from '../../../common/make-pagination-query'; -import { ID } from '@/misc/cafy-id'; import { Emoji } from '@/models/entities/emoji'; export const meta = { @@ -11,26 +9,6 @@ export const meta = { requireCredential: true, requireModerator: true, - params: { - query: { - validator: $.optional.nullable.str, - default: null, - }, - - limit: { - validator: $.optional.num.range(1, 100), - default: 10, - }, - - sinceId: { - validator: $.optional.type(ID), - }, - - untilId: { - validator: $.optional.type(ID), - }, - }, - res: { type: 'array', optional: false, nullable: false, @@ -72,8 +50,19 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + query: { type: 'string', nullable: true, default: null }, + limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, + 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, async (ps) => { +export default define(meta, paramDef, async (ps) => { const q = makePaginationQuery(Emojis.createQueryBuilder('emoji'), ps.sinceId, ps.untilId) .andWhere(`emoji.host IS NULL`); @@ -81,7 +70,7 @@ export default define(meta, async (ps) => { if (ps.query) { //q.andWhere('emoji.name ILIKE :q', { q: `%${ps.query}%` }); - //const emojis = await q.take(ps.limit!).getMany(); + //const emojis = await q.take(ps.limit).getMany(); emojis = await q.getMany(); @@ -90,9 +79,9 @@ export default define(meta, async (ps) => { emoji.aliases.some(a => a.includes(ps.query!)) || emoji.category?.includes(ps.query!)); - emojis.splice(ps.limit! + 1); + emojis.splice(ps.limit + 1); } else { - emojis = await q.take(ps.limit!).getMany(); + emojis = await q.take(ps.limit).getMany(); } return Emojis.packMany(emojis); diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/remove-aliases-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/remove-aliases-bulk.ts index c49f84b7fb..e6ccbf684d 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/remove-aliases-bulk.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/remove-aliases-bulk.ts @@ -1,6 +1,4 @@ -import $ from 'cafy'; import define from '../../../define'; -import { ID } from '@/misc/cafy-id'; import { Emojis } from '@/models/index'; import { getConnection, In } from 'typeorm'; import { ApiError } from '../../../error'; @@ -10,20 +8,23 @@ export const meta = { requireCredential: true, requireModerator: true, +} as const; - params: { - ids: { - validator: $.arr($.type(ID)), - }, - - aliases: { - validator: $.arr($.str), - }, +const paramDef = { + type: 'object', + properties: { + ids: { type: 'array', items: { + type: 'string', format: 'misskey:id', + } }, + aliases: { type: 'array', items: { + type: 'string', + } }, }, + required: ['ids', 'aliases'], } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps) => { +export default define(meta, paramDef, async (ps) => { const emojis = await Emojis.find({ id: In(ps.ids), }); diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/set-aliases-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/set-aliases-bulk.ts index 06197820f0..30a1c28af6 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/set-aliases-bulk.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/set-aliases-bulk.ts @@ -1,6 +1,4 @@ -import $ from 'cafy'; import define from '../../../define'; -import { ID } from '@/misc/cafy-id'; import { Emojis } from '@/models/index'; import { getConnection, In } from 'typeorm'; import { ApiError } from '../../../error'; @@ -10,20 +8,23 @@ export const meta = { requireCredential: true, requireModerator: true, +} as const; - params: { - ids: { - validator: $.arr($.type(ID)), - }, - - aliases: { - validator: $.arr($.str), - }, +const paramDef = { + type: 'object', + properties: { + ids: { type: 'array', items: { + type: 'string', format: 'misskey:id', + } }, + aliases: { type: 'array', items: { + type: 'string', + } }, }, + required: ['ids', 'aliases'], } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps) => { +export default define(meta, paramDef, async (ps) => { await Emojis.update({ id: In(ps.ids), }, { diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/set-category-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/set-category-bulk.ts index f0645f111b..9679e4a369 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/set-category-bulk.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/set-category-bulk.ts @@ -1,6 +1,4 @@ -import $ from 'cafy'; import define from '../../../define'; -import { ID } from '@/misc/cafy-id'; import { Emojis } from '@/models/index'; import { getConnection, In } from 'typeorm'; import { ApiError } from '../../../error'; @@ -10,20 +8,21 @@ export const meta = { requireCredential: true, requireModerator: true, +} as const; - params: { - ids: { - validator: $.arr($.type(ID)), - }, - - category: { - validator: $.optional.nullable.str, - }, +const paramDef = { + type: 'object', + properties: { + ids: { type: 'array', items: { + type: 'string', format: 'misskey:id', + } }, + category: { type: 'string', nullable: true }, }, + required: ['ids'], } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps) => { +export default define(meta, paramDef, async (ps) => { await Emojis.update({ id: In(ps.ids), }, { 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 54a2cf9517..da5a5f005e 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/update.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/update.ts @@ -1,6 +1,4 @@ -import $ from 'cafy'; import define from '../../../define'; -import { ID } from '@/misc/cafy-id'; import { Emojis } from '@/models/index'; import { getConnection } from 'typeorm'; import { ApiError } from '../../../error'; @@ -11,24 +9,6 @@ export const meta = { requireCredential: true, requireModerator: true, - params: { - id: { - validator: $.type(ID), - }, - - name: { - validator: $.str, - }, - - category: { - validator: $.optional.nullable.str, - }, - - aliases: { - validator: $.arr($.str), - }, - }, - errors: { noSuchEmoji: { message: 'No such emoji.', @@ -38,8 +18,21 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + id: { type: 'string', format: 'misskey:id' }, + name: { type: 'string' }, + category: { type: 'string', nullable: true }, + aliases: { type: 'array', items: { + type: 'string', + } }, + }, + required: ['id', 'name', 'aliases'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps) => { +export default define(meta, paramDef, async (ps) => { const emoji = await Emojis.findOne(ps.id); if (emoji == null) throw new ApiError(meta.errors.noSuchEmoji); diff --git a/packages/backend/src/server/api/endpoints/admin/federation/delete-all-files.ts b/packages/backend/src/server/api/endpoints/admin/federation/delete-all-files.ts index db023c6f0b..e98de40ff8 100644 --- a/packages/backend/src/server/api/endpoints/admin/federation/delete-all-files.ts +++ b/packages/backend/src/server/api/endpoints/admin/federation/delete-all-files.ts @@ -1,4 +1,3 @@ -import $ from 'cafy'; import define from '../../../define'; import { deleteFile } from '@/services/drive/delete-file'; import { DriveFiles } from '@/models/index'; @@ -8,16 +7,18 @@ export const meta = { requireCredential: true, requireModerator: true, +} as const; - params: { - host: { - validator: $.str, - }, +const paramDef = { + type: 'object', + properties: { + host: { type: 'string' }, }, + required: ['host'], } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, me) => { +export default define(meta, paramDef, async (ps, me) => { const files = await DriveFiles.find({ userHost: ps.host, }); diff --git a/packages/backend/src/server/api/endpoints/admin/federation/refresh-remote-instance-metadata.ts b/packages/backend/src/server/api/endpoints/admin/federation/refresh-remote-instance-metadata.ts index b68252ef2e..d6e3a15c61 100644 --- a/packages/backend/src/server/api/endpoints/admin/federation/refresh-remote-instance-metadata.ts +++ b/packages/backend/src/server/api/endpoints/admin/federation/refresh-remote-instance-metadata.ts @@ -1,4 +1,3 @@ -import $ from 'cafy'; import define from '../../../define'; import { Instances } from '@/models/index'; import { toPuny } from '@/misc/convert-host'; @@ -9,16 +8,18 @@ export const meta = { requireCredential: true, requireModerator: true, +} as const; - params: { - host: { - validator: $.str, - }, +const paramDef = { + type: 'object', + properties: { + host: { type: 'string' }, }, + required: ['host'], } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, me) => { +export default define(meta, paramDef, async (ps, me) => { const instance = await Instances.findOne({ host: toPuny(ps.host) }); if (instance == null) { diff --git a/packages/backend/src/server/api/endpoints/admin/federation/remove-all-following.ts b/packages/backend/src/server/api/endpoints/admin/federation/remove-all-following.ts index 4de8ad1336..7c2d6dc391 100644 --- a/packages/backend/src/server/api/endpoints/admin/federation/remove-all-following.ts +++ b/packages/backend/src/server/api/endpoints/admin/federation/remove-all-following.ts @@ -1,4 +1,3 @@ -import $ from 'cafy'; import define from '../../../define'; import deleteFollowing from '@/services/following/delete'; import { Followings, Users } from '@/models/index'; @@ -8,16 +7,18 @@ export const meta = { requireCredential: true, requireModerator: true, +} as const; - params: { - host: { - validator: $.str, - }, +const paramDef = { + type: 'object', + properties: { + host: { type: 'string' }, }, + required: ['host'], } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, me) => { +export default define(meta, paramDef, async (ps, me) => { const followings = await Followings.find({ followerHost: ps.host, }); diff --git a/packages/backend/src/server/api/endpoints/admin/federation/update-instance.ts b/packages/backend/src/server/api/endpoints/admin/federation/update-instance.ts index 6ac2f1f467..04cad3b9f7 100644 --- a/packages/backend/src/server/api/endpoints/admin/federation/update-instance.ts +++ b/packages/backend/src/server/api/endpoints/admin/federation/update-instance.ts @@ -1,4 +1,3 @@ -import $ from 'cafy'; import define from '../../../define'; import { Instances } from '@/models/index'; import { toPuny } from '@/misc/convert-host'; @@ -8,20 +7,19 @@ export const meta = { requireCredential: true, requireModerator: true, +} as const; - params: { - host: { - validator: $.str, - }, - - isSuspended: { - validator: $.bool, - }, +const paramDef = { + type: 'object', + properties: { + host: { type: 'string' }, + isSuspended: { type: 'boolean' }, }, + required: ['host', 'isSuspended'], } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, me) => { +export default define(meta, paramDef, async (ps, me) => { const instance = await Instances.findOne({ host: toPuny(ps.host) }); if (instance == null) { diff --git a/packages/backend/src/server/api/endpoints/admin/get-index-stats.ts b/packages/backend/src/server/api/endpoints/admin/get-index-stats.ts index 9a2bccec77..9379379aef 100644 --- a/packages/backend/src/server/api/endpoints/admin/get-index-stats.ts +++ b/packages/backend/src/server/api/endpoints/admin/get-index-stats.ts @@ -6,13 +6,16 @@ export const meta = { requireModerator: true, tags: ['admin'], +} as const; - params: { - }, +const paramDef = { + type: 'object', + properties: {}, + required: [], } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, async () => { +export default define(meta, paramDef, async () => { const stats = await getConnection().query(`SELECT * FROM pg_indexes;`) .then(recs => { diff --git a/packages/backend/src/server/api/endpoints/admin/get-table-stats.ts b/packages/backend/src/server/api/endpoints/admin/get-table-stats.ts index 1c5f250676..28856cd41c 100644 --- a/packages/backend/src/server/api/endpoints/admin/get-table-stats.ts +++ b/packages/backend/src/server/api/endpoints/admin/get-table-stats.ts @@ -7,9 +7,6 @@ export const meta = { tags: ['admin'], - params: { - }, - res: { type: 'object', optional: false, nullable: false, @@ -22,8 +19,14 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: {}, + required: [], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async () => { +export default define(meta, paramDef, async () => { const sizes = await getConnection().query(` SELECT relname AS "table", reltuples as "count", pg_total_relation_size(C.oid) AS "size" diff --git a/packages/backend/src/server/api/endpoints/admin/invite.ts b/packages/backend/src/server/api/endpoints/admin/invite.ts index 3428709c04..a21777937c 100644 --- a/packages/backend/src/server/api/endpoints/admin/invite.ts +++ b/packages/backend/src/server/api/endpoints/admin/invite.ts @@ -9,8 +9,6 @@ export const meta = { requireCredential: true, requireModerator: true, - params: {}, - res: { type: 'object', optional: false, nullable: false, @@ -26,8 +24,14 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: {}, + required: [], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async () => { +export default define(meta, paramDef, async () => { const code = rndstr({ length: 8, chars: '2-9A-HJ-NP-Z', // [0-9A-Z] w/o [01IO] (32 patterns) diff --git a/packages/backend/src/server/api/endpoints/admin/moderators/add.ts b/packages/backend/src/server/api/endpoints/admin/moderators/add.ts index 0308cf2761..6b7e549c35 100644 --- a/packages/backend/src/server/api/endpoints/admin/moderators/add.ts +++ b/packages/backend/src/server/api/endpoints/admin/moderators/add.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../../define'; import { Users } from '@/models/index'; @@ -8,16 +6,18 @@ export const meta = { requireCredential: true, requireAdmin: true, +} as const; - params: { - userId: { - validator: $.type(ID), - }, +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, async (ps) => { +export default define(meta, paramDef, async (ps) => { const user = await Users.findOne(ps.userId as string); if (user == null) { diff --git a/packages/backend/src/server/api/endpoints/admin/moderators/remove.ts b/packages/backend/src/server/api/endpoints/admin/moderators/remove.ts index bdb976e9ec..585d6fbfa7 100644 --- a/packages/backend/src/server/api/endpoints/admin/moderators/remove.ts +++ b/packages/backend/src/server/api/endpoints/admin/moderators/remove.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../../define'; import { Users } from '@/models/index'; @@ -8,16 +6,18 @@ export const meta = { requireCredential: true, requireAdmin: true, +} as const; - params: { - userId: { - validator: $.type(ID), - }, +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, async (ps) => { +export default define(meta, paramDef, async (ps) => { const user = await Users.findOne(ps.userId as string); if (user == null) { 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 f2735ac9f8..16f522729b 100644 --- a/packages/backend/src/server/api/endpoints/admin/promo/create.ts +++ b/packages/backend/src/server/api/endpoints/admin/promo/create.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../../define'; import { ApiError } from '../../../error'; import { getNote } from '../../../common/getters'; @@ -11,16 +9,6 @@ export const meta = { requireCredential: true, requireModerator: true, - params: { - noteId: { - validator: $.type(ID), - }, - - expiresAt: { - validator: $.num.int(), - }, - }, - errors: { noSuchNote: { message: 'No such note.', @@ -36,8 +24,17 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + noteId: { type: 'string', format: 'misskey:id' }, + expiresAt: { type: 'integer' }, + }, + required: ['noteId', 'expiresAt'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const note = await getNote(ps.noteId).catch(e => { if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); throw e; @@ -51,7 +48,6 @@ export default define(meta, async (ps, user) => { await PromoNotes.insert({ noteId: note.id, - createdAt: new Date(), expiresAt: new Date(ps.expiresAt), userId: note.userId, }); diff --git a/packages/backend/src/server/api/endpoints/admin/queue/clear.ts b/packages/backend/src/server/api/endpoints/admin/queue/clear.ts index 3c8e7a27a2..27bb9cc858 100644 --- a/packages/backend/src/server/api/endpoints/admin/queue/clear.ts +++ b/packages/backend/src/server/api/endpoints/admin/queue/clear.ts @@ -7,12 +7,16 @@ export const meta = { requireCredential: true, requireModerator: true, +} as const; - params: {}, +const paramDef = { + type: 'object', + properties: {}, + required: [], } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, me) => { +export default define(meta, paramDef, async (ps, me) => { destroy(); insertModerationLog(me, 'clearQueue'); diff --git a/packages/backend/src/server/api/endpoints/admin/queue/deliver-delayed.ts b/packages/backend/src/server/api/endpoints/admin/queue/deliver-delayed.ts index 4760e2c310..38eac5a135 100644 --- a/packages/backend/src/server/api/endpoints/admin/queue/deliver-delayed.ts +++ b/packages/backend/src/server/api/endpoints/admin/queue/deliver-delayed.ts @@ -8,9 +8,6 @@ export const meta = { requireCredential: true, requireModerator: true, - params: { - }, - res: { type: 'array', optional: false, nullable: false, @@ -35,8 +32,14 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: {}, + required: [], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps) => { +export default define(meta, paramDef, async (ps) => { const jobs = await deliverQueue.getJobs(['delayed']); const res = [] as [string, number][]; diff --git a/packages/backend/src/server/api/endpoints/admin/queue/inbox-delayed.ts b/packages/backend/src/server/api/endpoints/admin/queue/inbox-delayed.ts index a95aabc506..9669cfd3b8 100644 --- a/packages/backend/src/server/api/endpoints/admin/queue/inbox-delayed.ts +++ b/packages/backend/src/server/api/endpoints/admin/queue/inbox-delayed.ts @@ -8,9 +8,6 @@ export const meta = { requireCredential: true, requireModerator: true, - params: { - }, - res: { type: 'array', optional: false, nullable: false, @@ -35,8 +32,14 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: {}, + required: [], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps) => { +export default define(meta, paramDef, async (ps) => { const jobs = await inboxQueue.getJobs(['delayed']); const res = [] as [string, number][]; diff --git a/packages/backend/src/server/api/endpoints/admin/queue/jobs.ts b/packages/backend/src/server/api/endpoints/admin/queue/jobs.ts deleted file mode 100644 index df0b4a8f13..0000000000 --- a/packages/backend/src/server/api/endpoints/admin/queue/jobs.ts +++ /dev/null @@ -1,82 +0,0 @@ -import { deliverQueue, inboxQueue, dbQueue, objectStorageQueue } from '@/queue/queues'; -import $ from 'cafy'; -import define from '../../../define'; - -export const meta = { - tags: ['admin'], - - requireCredential: true, - requireModerator: true, - - params: { - domain: { - validator: $.str.or(['deliver', 'inbox', 'db', 'objectStorage']), - }, - - state: { - validator: $.str.or(['active', 'waiting', 'delayed']), - }, - - limit: { - validator: $.optional.num, - default: 50, - }, - }, - - res: { - type: 'array', - optional: false, nullable: false, - items: { - type: 'object', - optional: false, nullable: false, - properties: { - id: { - type: 'string', - optional: false, nullable: false, - format: 'id', - }, - data: { - type: 'object', - optional: false, nullable: false, - }, - attempts: { - type: 'number', - optional: false, nullable: false, - }, - maxAttempts: { - type: 'number', - optional: false, nullable: false, - }, - timestamp: { - type: 'number', - optional: false, nullable: false, - }, - }, - }, - }, -} as const; - -// eslint-disable-next-line import/no-default-export -export default define(meta, async (ps) => { - const queue = - ps.domain === 'deliver' ? deliverQueue : - ps.domain === 'inbox' ? inboxQueue : - ps.domain === 'db' ? dbQueue : - ps.domain === 'objectStorage' ? objectStorageQueue : - null as never; - - const jobs = await queue.getJobs([ps.state], 0, ps.limit!); - - return jobs.map(job => { - const data = job.data; - delete data.content; - delete data.user; - return { - id: job.id, - data, - attempts: job.attemptsMade, - maxAttempts: job.opts ? job.opts.attempts : 0, - timestamp: job.timestamp, - }; - }); -}); 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 dab0be5dbc..3f7e6be5fb 100644 --- a/packages/backend/src/server/api/endpoints/admin/queue/stats.ts +++ b/packages/backend/src/server/api/endpoints/admin/queue/stats.ts @@ -7,8 +7,6 @@ export const meta = { requireCredential: true, requireModerator: true, - params: {}, - res: { type: 'object', optional: false, nullable: false, @@ -33,8 +31,14 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: {}, + required: [], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps) => { +export default define(meta, paramDef, async (ps) => { const deliverJobCounts = await deliverQueue.getJobCounts(); const inboxJobCounts = await inboxQueue.getJobCounts(); const dbJobCounts = await dbQueue.getJobCounts(); diff --git a/packages/backend/src/server/api/endpoints/admin/relays/add.ts b/packages/backend/src/server/api/endpoints/admin/relays/add.ts index 65890a00f7..ecf77b87df 100644 --- a/packages/backend/src/server/api/endpoints/admin/relays/add.ts +++ b/packages/backend/src/server/api/endpoints/admin/relays/add.ts @@ -1,5 +1,4 @@ import { URL } from 'url'; -import $ from 'cafy'; import define from '../../../define'; import { addRelay } from '@/services/relay'; import { ApiError } from '../../../error'; @@ -10,12 +9,6 @@ export const meta = { requireCredential: true, requireModerator: true, - params: { - inbox: { - validator: $.str, - }, - }, - errors: { invalidUrl: { message: 'Invalid URL', @@ -52,8 +45,16 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + inbox: { type: 'string' }, + }, + required: ['inbox'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { try { if (new URL(ps.inbox).protocol !== 'https:') throw 'https only'; } catch { diff --git a/packages/backend/src/server/api/endpoints/admin/relays/list.ts b/packages/backend/src/server/api/endpoints/admin/relays/list.ts index bdddf13374..6cdaa418a7 100644 --- a/packages/backend/src/server/api/endpoints/admin/relays/list.ts +++ b/packages/backend/src/server/api/endpoints/admin/relays/list.ts @@ -7,9 +7,6 @@ export const meta = { requireCredential: true, requireModerator: true, - params: { - }, - res: { type: 'array', optional: false, nullable: false, @@ -42,7 +39,13 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: {}, + required: [], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { return await listRelay(); }); diff --git a/packages/backend/src/server/api/endpoints/admin/relays/remove.ts b/packages/backend/src/server/api/endpoints/admin/relays/remove.ts index 4b04e620c1..d452c2e6a5 100644 --- a/packages/backend/src/server/api/endpoints/admin/relays/remove.ts +++ b/packages/backend/src/server/api/endpoints/admin/relays/remove.ts @@ -1,4 +1,3 @@ -import $ from 'cafy'; import define from '../../../define'; import { removeRelay } from '@/services/relay'; @@ -7,15 +6,17 @@ export const meta = { requireCredential: true, requireModerator: true, +} as const; - params: { - inbox: { - validator: $.str, - }, +const paramDef = { + type: 'object', + properties: { + inbox: { type: 'string' }, }, + required: ['inbox'], } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { return await removeRelay(ps.inbox); }); 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 b6cf1ee2d0..87424782c5 100644 --- a/packages/backend/src/server/api/endpoints/admin/reset-password.ts +++ b/packages/backend/src/server/api/endpoints/admin/reset-password.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../define'; import * as bcrypt from 'bcryptjs'; import rndstr from 'rndstr'; @@ -11,12 +9,6 @@ export const meta = { requireCredential: true, requireModerator: true, - params: { - userId: { - validator: $.type(ID), - }, - }, - res: { type: 'object', optional: false, nullable: false, @@ -31,8 +23,16 @@ export const meta = { }, } as const; +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, async (ps) => { +export default define(meta, paramDef, async (ps) => { const user = await Users.findOne(ps.userId as string); if (user == null) { diff --git a/packages/backend/src/server/api/endpoints/admin/resolve-abuse-user-report.ts b/packages/backend/src/server/api/endpoints/admin/resolve-abuse-user-report.ts index b00457f092..4139f77614 100644 --- a/packages/backend/src/server/api/endpoints/admin/resolve-abuse-user-report.ts +++ b/packages/backend/src/server/api/endpoints/admin/resolve-abuse-user-report.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../define'; import { AbuseUserReports, Users } from '@/models/index'; import { getInstanceActor } from '@/services/instance-actor'; @@ -12,22 +10,19 @@ export const meta = { requireCredential: true, requireModerator: true, +} as const; - params: { - reportId: { - validator: $.type(ID), - }, - - forward: { - validator: $.optional.boolean, - required: false, - default: false, - }, +const paramDef = { + type: 'object', + properties: { + reportId: { type: 'string', format: 'misskey:id' }, + forward: { type: 'boolean', default: false }, }, + required: ['reportId'], } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, me) => { +export default define(meta, paramDef, async (ps, me) => { const report = await AbuseUserReports.findOne(ps.reportId); if (report == null) { @@ -36,9 +31,9 @@ export default define(meta, async (ps, me) => { if (ps.forward && report.targetUserHost != null) { const actor = await getInstanceActor(); - const targetUser = await Users.findOne(report.targetUserId); + const targetUser = await Users.findOneOrFail(report.targetUserId); - deliver(actor, renderActivity(renderFlag(actor, [targetUser.uri], report.comment)), targetUser.inbox); + deliver(actor, renderActivity(renderFlag(actor, [targetUser.uri!], report.comment)), targetUser.inbox); } await AbuseUserReports.update(report.id, { diff --git a/packages/backend/src/server/api/endpoints/admin/resync-chart.ts b/packages/backend/src/server/api/endpoints/admin/resync-chart.ts deleted file mode 100644 index d80d2b0426..0000000000 --- a/packages/backend/src/server/api/endpoints/admin/resync-chart.ts +++ /dev/null @@ -1,22 +0,0 @@ -import define from '../../define'; -import { driveChart, notesChart, usersChart } from '@/services/chart/index'; -import { insertModerationLog } from '@/services/insert-moderation-log'; - -export const meta = { - tags: ['admin'], - - requireCredential: true, - requireModerator: true, -} as const; - -// eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, me) => { - insertModerationLog(me, 'chartResync'); - - driveChart.resync(); - notesChart.resync(); - usersChart.resync(); - - // TODO: ãƒĻãƒŧã‚ļãƒŧごとぎチãƒŖãƒŧトもキãƒĨãƒŧãĢå…ĨれãĻ更新する - // TODO: イãƒŗã‚šã‚ŋãƒŗ゚ごとぎチãƒŖãƒŧトもキãƒĨãƒŧãĢå…ĨれãĻ更新する -}); diff --git a/packages/backend/src/server/api/endpoints/admin/send-email.ts b/packages/backend/src/server/api/endpoints/admin/send-email.ts index c2972c35fa..2ebe7e0bc9 100644 --- a/packages/backend/src/server/api/endpoints/admin/send-email.ts +++ b/packages/backend/src/server/api/endpoints/admin/send-email.ts @@ -1,4 +1,3 @@ -import $ from 'cafy'; import define from '../../define'; import { sendEmail } from '@/services/send-email'; @@ -7,21 +6,19 @@ export const meta = { requireCredential: true, requireModerator: true, +} as const; - params: { - to: { - validator: $.str, - }, - subject: { - validator: $.str, - }, - text: { - validator: $.str, - }, +const paramDef = { + type: 'object', + properties: { + to: { type: 'string' }, + subject: { type: 'string' }, + text: { type: 'string' }, }, + required: ['to', 'subject', 'text'], } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps) => { +export default define(meta, paramDef, async (ps) => { await sendEmail(ps.to, ps.subject, ps.text, ps.text); }); diff --git a/packages/backend/src/server/api/endpoints/admin/server-info.ts b/packages/backend/src/server/api/endpoints/admin/server-info.ts index cd282e364c..44e069d603 100644 --- a/packages/backend/src/server/api/endpoints/admin/server-info.ts +++ b/packages/backend/src/server/api/endpoints/admin/server-info.ts @@ -10,9 +10,6 @@ export const meta = { tags: ['admin', 'meta'], - params: { - }, - res: { type: 'object', optional: false, nullable: false, @@ -90,8 +87,14 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: {}, + required: [], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async () => { +export default define(meta, paramDef, async () => { const memStats = await si.mem(); const fsStats = await si.fsSize(); const netInterface = await si.networkInterfaceDefault(); diff --git a/packages/backend/src/server/api/endpoints/admin/show-moderation-logs.ts b/packages/backend/src/server/api/endpoints/admin/show-moderation-logs.ts index 84e2b84bb5..c08acaeca9 100644 --- a/packages/backend/src/server/api/endpoints/admin/show-moderation-logs.ts +++ b/packages/backend/src/server/api/endpoints/admin/show-moderation-logs.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../define'; import { ModerationLogs } from '@/models/index'; import { makePaginationQuery } from '../../common/make-pagination-query'; @@ -10,21 +8,6 @@ export const meta = { requireCredential: true, requireModerator: true, - params: { - limit: { - validator: $.optional.num.range(1, 100), - default: 10, - }, - - sinceId: { - validator: $.optional.type(ID), - }, - - untilId: { - validator: $.optional.type(ID), - }, - }, - res: { type: 'array', optional: false, nullable: false, @@ -65,11 +48,21 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, + 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, async (ps) => { +export default define(meta, paramDef, async (ps) => { const query = makePaginationQuery(ModerationLogs.createQueryBuilder('report'), ps.sinceId, ps.untilId); - const reports = await query.take(ps.limit!).getMany(); + const reports = await query.take(ps.limit).getMany(); return await ModerationLogs.packMany(reports); }); 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 c2a6a294b5..b883aff494 100644 --- a/packages/backend/src/server/api/endpoints/admin/show-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/show-user.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../define'; import { Users } from '@/models/index'; @@ -9,158 +7,22 @@ export const meta = { requireCredential: true, requireModerator: true, - params: { - userId: { - validator: $.type(ID), - }, - }, - res: { type: 'object', nullable: false, optional: false, - properties: { - id: { - type: 'string', - nullable: false, optional: false, - format: 'id', - }, - createdAt: { - type: 'string', - nullable: false, optional: false, - format: 'date-time', - }, - updatedAt: { - type: 'string', - nullable: true, optional: false, - format: 'date-time', - }, - lastFetchedAt: { - type: 'string', - nullable: true, optional: false, - }, - username: { - type: 'string', - nullable: false, optional: false, - }, - name: { - type: 'string', - nullable: true, optional: false, - }, - folowersCount: { - type: 'number', - nullable: false, optional: true, - }, - followingCount: { - type: 'number', - nullable: false, optional: false, - }, - notesCount: { - type: 'number', - nullable: false, optional: false, - }, - avatarId: { - type: 'string', - nullable: true, optional: false, - }, - bannerId: { - type: 'string', - nullable: true, optional: false, - }, - tags: { - type: 'array', - nullable: false, optional: false, - items: { - type: 'string', - nullable: false, optional: false, - }, - }, - avatarUrl: { - type: 'string', - nullable: true, optional: false, - format: 'url', - }, - bannerUrl: { - type: 'string', - nullable: true, optional: false, - format: 'url', - }, - avatarBlurhash: { - type: 'any', - nullable: true, optional: false, - default: null, - }, - bannerBlurhash: { - type: 'any', - nullable: true, optional: false, - default: null, - }, - isSuspended: { - type: 'boolean', - nullable: false, optional: false, - }, - isSilenced: { - type: 'boolean', - nullable: false, optional: false, - }, - isLocked: { - type: 'boolean', - nullable: false, optional: false, - }, - isBot: { - type: 'boolean', - nullable: false, optional: false, - }, - isCat: { - type: 'boolean', - nullable: false, optional: false, - }, - isAdmin: { - type: 'boolean', - nullable: false, optional: false, - }, - isModerator: { - type: 'boolean', - nullable: false, optional: false, - }, - emojis: { - type: 'array', - nullable: false, optional: false, - items: { - type: 'string', - nullable: false, optional: false, - }, - }, - host: { - type: 'string', - nullable: true, optional: false, - }, - inbox: { - type: 'string', - nullable: true, optional: false, - }, - sharedInbox: { - type: 'string', - nullable: true, optional: false, - }, - featured: { - type: 'string', - nullable: true, optional: false, - }, - uri: { - type: 'string', - nullable: true, optional: false, - }, - token: { - type: 'string', - nullable: true, optional: false, - default: '', - }, - }, }, } as const; +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, async (ps, me) => { +export default define(meta, paramDef, async (ps, me) => { const user = await Users.findOne(ps.userId as string); if (user == null) { 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 d3dde99b72..c373021d0e 100644 --- a/packages/backend/src/server/api/endpoints/admin/show-users.ts +++ b/packages/backend/src/server/api/endpoints/admin/show-users.ts @@ -1,4 +1,3 @@ -import $ from 'cafy'; import define from '../../define'; import { Users } from '@/models/index'; @@ -8,61 +7,6 @@ export const meta = { requireCredential: true, requireModerator: true, - params: { - limit: { - validator: $.optional.num.range(1, 100), - default: 10, - }, - - offset: { - validator: $.optional.num.min(0), - default: 0, - }, - - sort: { - validator: $.optional.str.or([ - '+follower', - '-follower', - '+createdAt', - '-createdAt', - '+updatedAt', - '-updatedAt', - ]), - }, - - state: { - validator: $.optional.str.or([ - 'all', - 'available', - 'admin', - 'moderator', - 'adminOrModerator', - 'silenced', - 'suspended', - ]), - default: 'all', - }, - - origin: { - validator: $.optional.str.or([ - 'combined', - 'local', - 'remote', - ]), - default: 'local', - }, - - username: { - validator: $.optional.str, - default: null, - }, - - hostname: { - validator: $.optional.str, - default: null, - }, - }, - res: { type: 'array', nullable: false, optional: false, @@ -74,8 +18,22 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, + offset: { type: 'integer', default: 0 }, + sort: { type: 'string', enum: ['+follower', '-follower', '+createdAt', '-createdAt', '+updatedAt', '-updatedAt'] }, + state: { type: 'string', enum: ['all', 'available', 'admin', 'moderator', 'adminOrModerator', 'silenced', 'suspended'], default: "all" }, + origin: { type: 'string', enum: ['combined', 'local', 'remote'], default: "local" }, + username: { type: 'string', default: null }, + hostname: { type: 'string', default: null }, + }, + required: [], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, me) => { +export default define(meta, paramDef, async (ps, me) => { const query = Users.createQueryBuilder('user'); switch (ps.state) { @@ -111,7 +69,7 @@ export default define(meta, async (ps, me) => { default: query.orderBy('user.id', 'ASC'); break; } - query.take(ps.limit!); + query.take(ps.limit); query.skip(ps.offset); const users = await query.getMany(); diff --git a/packages/backend/src/server/api/endpoints/admin/silence-user.ts b/packages/backend/src/server/api/endpoints/admin/silence-user.ts index 872bd2a6ac..df547184ad 100644 --- a/packages/backend/src/server/api/endpoints/admin/silence-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/silence-user.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../define'; import { Users } from '@/models/index'; import { insertModerationLog } from '@/services/insert-moderation-log'; @@ -9,16 +7,18 @@ export const meta = { requireCredential: true, requireModerator: true, +} as const; - params: { - userId: { - validator: $.type(ID), - }, +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, async (ps, me) => { +export default define(meta, paramDef, async (ps, me) => { const user = await Users.findOne(ps.userId as string); if (user == null) { diff --git a/packages/backend/src/server/api/endpoints/admin/suspend-user.ts b/packages/backend/src/server/api/endpoints/admin/suspend-user.ts index 2bb1875fc0..31867a4055 100644 --- a/packages/backend/src/server/api/endpoints/admin/suspend-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/suspend-user.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../define'; import deleteFollowing from '@/services/following/delete'; import { Users, Followings, Notifications } from '@/models/index'; @@ -13,16 +11,18 @@ export const meta = { requireCredential: true, requireModerator: true, +} as const; - params: { - userId: { - validator: $.type(ID), - }, +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, async (ps, me) => { +export default define(meta, paramDef, async (ps, me) => { const user = await Users.findOne(ps.userId as string); if (user == null) { diff --git a/packages/backend/src/server/api/endpoints/admin/unsilence-user.ts b/packages/backend/src/server/api/endpoints/admin/unsilence-user.ts index a4c6ff2ade..42c7e776a8 100644 --- a/packages/backend/src/server/api/endpoints/admin/unsilence-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/unsilence-user.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../define'; import { Users } from '@/models/index'; import { insertModerationLog } from '@/services/insert-moderation-log'; @@ -9,16 +7,18 @@ export const meta = { requireCredential: true, requireModerator: true, +} as const; - params: { - userId: { - validator: $.type(ID), - }, +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, async (ps, me) => { +export default define(meta, paramDef, async (ps, me) => { const user = await Users.findOne(ps.userId as string); if (user == null) { diff --git a/packages/backend/src/server/api/endpoints/admin/unsuspend-user.ts b/packages/backend/src/server/api/endpoints/admin/unsuspend-user.ts index 5ab56d51c7..011f485277 100644 --- a/packages/backend/src/server/api/endpoints/admin/unsuspend-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/unsuspend-user.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../define'; import { Users } from '@/models/index'; import { insertModerationLog } from '@/services/insert-moderation-log'; @@ -10,16 +8,18 @@ export const meta = { requireCredential: true, requireModerator: true, +} as const; - params: { - userId: { - validator: $.type(ID), - }, +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, async (ps, me) => { +export default define(meta, paramDef, async (ps, me) => { const user = await Users.findOne(ps.userId as string); if (user == null) { 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 aa2d1222f7..b845eb4d5b 100644 --- a/packages/backend/src/server/api/endpoints/admin/update-meta.ts +++ b/packages/backend/src/server/api/endpoints/admin/update-meta.ts @@ -1,306 +1,107 @@ -import $ from 'cafy'; import define from '../../define'; import { getConnection } from 'typeorm'; import { Meta } from '@/models/entities/meta'; import { insertModerationLog } from '@/services/insert-moderation-log'; import { DB_MAX_NOTE_TEXT_LENGTH } from '@/misc/hard-limits'; -import { ID } from '@/misc/cafy-id'; export const meta = { tags: ['admin'], requireCredential: true, requireAdmin: true, - - params: { - disableRegistration: { - validator: $.optional.nullable.bool, - }, - - disableLocalTimeline: { - validator: $.optional.nullable.bool, - }, - - disableGlobalTimeline: { - validator: $.optional.nullable.bool, - }, - - useStarForReactionFallback: { - validator: $.optional.nullable.bool, - }, - - pinnedUsers: { - validator: $.optional.nullable.arr($.str), - }, - - hiddenTags: { - validator: $.optional.nullable.arr($.str), - }, - - blockedHosts: { - validator: $.optional.nullable.arr($.str), - }, - - mascotImageUrl: { - validator: $.optional.nullable.str, - }, - - bannerUrl: { - validator: $.optional.nullable.str, - }, - - errorImageUrl: { - validator: $.optional.nullable.str, - }, - - iconUrl: { - validator: $.optional.nullable.str, - }, - - backgroundImageUrl: { - validator: $.optional.nullable.str, - }, - - logoImageUrl: { - validator: $.optional.nullable.str, - }, - - name: { - validator: $.optional.nullable.str, - }, - - description: { - validator: $.optional.nullable.str, - }, - - maxNoteTextLength: { - validator: $.optional.num.min(0).max(DB_MAX_NOTE_TEXT_LENGTH), - }, - - localDriveCapacityMb: { - validator: $.optional.num.min(0), - }, - - remoteDriveCapacityMb: { - validator: $.optional.num.min(0), - }, - - cacheRemoteFiles: { - validator: $.optional.bool, - }, - - proxyRemoteFiles: { - validator: $.optional.bool, - }, - - emailRequiredForSignup: { - validator: $.optional.bool, - }, - - enableHcaptcha: { - validator: $.optional.bool, - }, - - hcaptchaSiteKey: { - validator: $.optional.nullable.str, - }, - - hcaptchaSecretKey: { - validator: $.optional.nullable.str, - }, - - enableRecaptcha: { - validator: $.optional.bool, - }, - - recaptchaSiteKey: { - validator: $.optional.nullable.str, - }, - - recaptchaSecretKey: { - validator: $.optional.nullable.str, - }, - - proxyAccountId: { - validator: $.optional.nullable.type(ID), - }, - - maintainerName: { - validator: $.optional.nullable.str, - }, - - maintainerEmail: { - validator: $.optional.nullable.str, - }, - - pinnedPages: { - validator: $.optional.arr($.str), - }, - - pinnedClipId: { - validator: $.optional.nullable.type(ID), - }, - - langs: { - validator: $.optional.arr($.str), - }, - - summalyProxy: { - validator: $.optional.nullable.str, - }, - - deeplAuthKey: { - validator: $.optional.nullable.str, - }, - - deeplIsPro: { - validator: $.optional.bool, - }, - - enableTwitterIntegration: { - validator: $.optional.bool, - }, - - twitterConsumerKey: { - validator: $.optional.nullable.str, - }, - - twitterConsumerSecret: { - validator: $.optional.nullable.str, - }, - - enableGithubIntegration: { - validator: $.optional.bool, - }, - - githubClientId: { - validator: $.optional.nullable.str, - }, - - githubClientSecret: { - validator: $.optional.nullable.str, - }, - - enableDiscordIntegration: { - validator: $.optional.bool, - }, - - discordClientId: { - validator: $.optional.nullable.str, - }, - - discordClientSecret: { - validator: $.optional.nullable.str, - }, - - enableEmail: { - validator: $.optional.bool, - }, - - email: { - validator: $.optional.nullable.str, - }, - - smtpSecure: { - validator: $.optional.bool, - }, - - smtpHost: { - validator: $.optional.nullable.str, - }, - - smtpPort: { - validator: $.optional.nullable.num, - }, - - smtpUser: { - validator: $.optional.nullable.str, - }, - - smtpPass: { - validator: $.optional.nullable.str, - }, - - enableServiceWorker: { - validator: $.optional.bool, - }, - - swPublicKey: { - validator: $.optional.nullable.str, - }, - - swPrivateKey: { - validator: $.optional.nullable.str, - }, - - tosUrl: { - validator: $.optional.nullable.str, - }, - - repositoryUrl: { - validator: $.optional.str, - }, - - feedbackUrl: { - validator: $.optional.str, - }, - - useObjectStorage: { - validator: $.optional.bool, - }, - - objectStorageBaseUrl: { - validator: $.optional.nullable.str, - }, - - objectStorageBucket: { - validator: $.optional.nullable.str, - }, - - objectStoragePrefix: { - validator: $.optional.nullable.str, - }, - - objectStorageEndpoint: { - validator: $.optional.nullable.str, - }, - - objectStorageRegion: { - validator: $.optional.nullable.str, - }, - - objectStoragePort: { - validator: $.optional.nullable.num, - }, - - objectStorageAccessKey: { - validator: $.optional.nullable.str, - }, - - objectStorageSecretKey: { - validator: $.optional.nullable.str, - }, - - objectStorageUseSSL: { - validator: $.optional.bool, - }, - - objectStorageUseProxy: { - validator: $.optional.bool, - }, - - objectStorageSetPublicRead: { - validator: $.optional.bool, - }, - - objectStorageS3ForcePathStyle: { - validator: $.optional.bool, - }, +} as const; + +const paramDef = { + type: 'object', + properties: { + disableRegistration: { type: 'boolean', nullable: true }, + disableLocalTimeline: { type: 'boolean', nullable: true }, + disableGlobalTimeline: { type: 'boolean', nullable: true }, + useStarForReactionFallback: { type: 'boolean', nullable: true }, + pinnedUsers: { type: 'array', nullable: true, items: { + type: 'string', + } }, + hiddenTags: { type: 'array', nullable: true, items: { + type: 'string', + } }, + blockedHosts: { type: 'array', nullable: true, items: { + type: 'string', + } }, + themeColor: { type: 'string', nullable: true }, + mascotImageUrl: { type: 'string', nullable: true }, + bannerUrl: { type: 'string', nullable: true }, + errorImageUrl: { type: 'string', nullable: true }, + iconUrl: { type: 'string', nullable: true }, + backgroundImageUrl: { type: 'string', nullable: true }, + logoImageUrl: { type: 'string', nullable: true }, + name: { type: 'string', nullable: true }, + description: { type: 'string', nullable: true }, + maxNoteTextLength: { type: 'integer', maximum: 8192 }, + localDriveCapacityMb: { type: 'integer' }, + remoteDriveCapacityMb: { type: 'integer' }, + cacheRemoteFiles: { type: 'boolean' }, + proxyRemoteFiles: { type: 'boolean' }, + emailRequiredForSignup: { type: 'boolean' }, + enableHcaptcha: { type: 'boolean' }, + hcaptchaSiteKey: { type: 'string', nullable: true }, + hcaptchaSecretKey: { type: 'string', nullable: true }, + enableRecaptcha: { type: 'boolean' }, + recaptchaSiteKey: { type: 'string', nullable: true }, + recaptchaSecretKey: { type: 'string', nullable: true }, + proxyAccountId: { type: 'string', format: 'misskey:id', nullable: true }, + maintainerName: { type: 'string', nullable: true }, + maintainerEmail: { type: 'string', nullable: true }, + pinnedPages: { type: 'array', items: { + type: 'string', + } }, + pinnedClipId: { type: 'string', format: 'misskey:id', nullable: true }, + langs: { type: 'array', items: { + type: 'string', + } }, + summalyProxy: { type: 'string', nullable: true }, + deeplAuthKey: { type: 'string', nullable: true }, + deeplIsPro: { type: 'boolean' }, + enableTwitterIntegration: { type: 'boolean' }, + twitterConsumerKey: { type: 'string', nullable: true }, + twitterConsumerSecret: { type: 'string', nullable: true }, + enableGithubIntegration: { type: 'boolean' }, + githubClientId: { type: 'string', nullable: true }, + githubClientSecret: { type: 'string', nullable: true }, + enableDiscordIntegration: { type: 'boolean' }, + discordClientId: { type: 'string', nullable: true }, + discordClientSecret: { type: 'string', nullable: true }, + enableEmail: { type: 'boolean' }, + email: { type: 'string', nullable: true }, + smtpSecure: { type: 'boolean' }, + smtpHost: { type: 'string', nullable: true }, + smtpPort: { type: 'integer', nullable: true }, + smtpUser: { type: 'string', nullable: true }, + smtpPass: { type: 'string', nullable: true }, + enableServiceWorker: { type: 'boolean' }, + swPublicKey: { type: 'string', nullable: true }, + swPrivateKey: { type: 'string', nullable: true }, + tosUrl: { type: 'string', nullable: true }, + repositoryUrl: { type: 'string' }, + feedbackUrl: { type: 'string' }, + useObjectStorage: { type: 'boolean' }, + objectStorageBaseUrl: { type: 'string', nullable: true }, + objectStorageBucket: { type: 'string', nullable: true }, + objectStoragePrefix: { type: 'string', nullable: true }, + objectStorageEndpoint: { type: 'string', nullable: true }, + objectStorageRegion: { type: 'string', nullable: true }, + objectStoragePort: { type: 'integer', nullable: true }, + objectStorageAccessKey: { type: 'string', nullable: true }, + objectStorageSecretKey: { type: 'string', nullable: true }, + objectStorageUseSSL: { type: 'boolean' }, + objectStorageUseProxy: { type: 'boolean' }, + objectStorageSetPublicRead: { type: 'boolean' }, + objectStorageS3ForcePathStyle: { type: 'boolean' }, }, + required: [], } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, me) => { +export default define(meta, paramDef, async (ps, me) => { const set = {} as Partial; if (typeof ps.disableRegistration === 'boolean') { @@ -331,6 +132,10 @@ export default define(meta, async (ps, me) => { set.blockedHosts = ps.blockedHosts.filter(Boolean); } + if (ps.themeColor !== undefined) { + set.themeColor = ps.themeColor; + } + if (ps.mascotImageUrl !== undefined) { set.mascotImageUrl = ps.mascotImageUrl; } diff --git a/packages/backend/src/server/api/endpoints/admin/vacuum.ts b/packages/backend/src/server/api/endpoints/admin/vacuum.ts index 4229ef0d29..07a4517bd4 100644 --- a/packages/backend/src/server/api/endpoints/admin/vacuum.ts +++ b/packages/backend/src/server/api/endpoints/admin/vacuum.ts @@ -1,4 +1,3 @@ -import $ from 'cafy'; import define from '../../define'; import { getConnection } from 'typeorm'; import { insertModerationLog } from '@/services/insert-moderation-log'; @@ -8,19 +7,19 @@ export const meta = { requireCredential: true, requireModerator: true, +} as const; - params: { - full: { - validator: $.bool, - }, - analyze: { - validator: $.bool, - }, +const paramDef = { + type: 'object', + properties: { + full: { type: 'boolean' }, + analyze: { type: 'boolean' }, }, + required: ['full', 'analyze'], } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, me) => { +export default define(meta, paramDef, async (ps, me) => { const params: string[] = []; if (ps.full) { diff --git a/packages/backend/src/server/api/endpoints/announcements.ts b/packages/backend/src/server/api/endpoints/announcements.ts index 0bd29607d6..bea3a287fc 100644 --- a/packages/backend/src/server/api/endpoints/announcements.ts +++ b/packages/backend/src/server/api/endpoints/announcements.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../define'; import { Announcements, AnnouncementReads } from '@/models/index'; import { makePaginationQuery } from '../common/make-pagination-query'; @@ -9,26 +7,6 @@ export const meta = { requireCredential: false, - params: { - limit: { - validator: $.optional.num.range(1, 100), - default: 10, - }, - - withUnreads: { - validator: $.optional.boolean, - default: false, - }, - - sinceId: { - validator: $.optional.type(ID), - }, - - untilId: { - validator: $.optional.type(ID), - }, - }, - res: { type: 'array', optional: false, nullable: false, @@ -73,11 +51,22 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, + withUnreads: { type: 'boolean', default: false }, + 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, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const query = makePaginationQuery(Announcements.createQueryBuilder('announcement'), ps.sinceId, ps.untilId); - const announcements = await query.take(ps.limit!).getMany(); + const announcements = await query.take(ps.limit).getMany(); if (user) { const reads = (await AnnouncementReads.find({ @@ -89,5 +78,9 @@ export default define(meta, async (ps, user) => { } } - return ps.withUnreads ? announcements.filter((a: any) => !a.isRead) : announcements; + return (ps.withUnreads ? announcements.filter((a: any) => !a.isRead) : announcements).map((a) => ({ + ...a, + createdAt: a.createdAt.toISOString(), + updatedAt: a.updatedAt?.toISOString() ?? null, + })); }); diff --git a/packages/backend/src/server/api/endpoints/antennas/create.ts b/packages/backend/src/server/api/endpoints/antennas/create.ts index 2092d177ba..16ba065b9d 100644 --- a/packages/backend/src/server/api/endpoints/antennas/create.ts +++ b/packages/backend/src/server/api/endpoints/antennas/create.ts @@ -1,8 +1,6 @@ -import $ from 'cafy'; import define from '../../define'; import { genId } from '@/misc/gen-id'; import { Antennas, UserLists, UserGroupJoinings } from '@/models/index'; -import { ID } from '@/misc/cafy-id'; import { ApiError } from '../../error'; import { publishInternalEvent } from '@/services/stream'; @@ -13,52 +11,6 @@ export const meta = { kind: 'write:account', - params: { - name: { - validator: $.str.range(1, 100), - }, - - src: { - validator: $.str.or(['home', 'all', 'users', 'list', 'group']), - }, - - userListId: { - validator: $.nullable.optional.type(ID), - }, - - userGroupId: { - validator: $.nullable.optional.type(ID), - }, - - keywords: { - validator: $.arr($.arr($.str)), - }, - - excludeKeywords: { - validator: $.arr($.arr($.str)), - }, - - users: { - validator: $.arr($.str), - }, - - caseSensitive: { - validator: $.bool, - }, - - withReplies: { - validator: $.bool, - }, - - withFile: { - validator: $.bool, - }, - - notify: { - validator: $.bool, - }, - }, - errors: { noSuchUserList: { message: 'No such user list.', @@ -80,8 +32,36 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + name: { type: 'string', minLength: 1, maxLength: 100 }, + src: { type: 'string', enum: ['home', 'all', 'users', 'list', 'group'] }, + userListId: { type: 'string', format: 'misskey:id', nullable: true }, + userGroupId: { type: 'string', format: 'misskey:id', nullable: true }, + keywords: { type: 'array', items: { + type: 'array', items: { + type: 'string', + }, + } }, + excludeKeywords: { type: 'array', items: { + type: 'array', items: { + type: 'string', + }, + } }, + users: { type: 'array', items: { + type: 'string', + } }, + caseSensitive: { type: 'boolean' }, + withReplies: { type: 'boolean' }, + withFile: { type: 'boolean' }, + notify: { type: 'boolean' }, + }, + required: ['name', 'src', 'keywords', 'excludeKeywords', 'users', 'caseSensitive', 'withReplies', 'withFile', 'notify'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { let userList; let userGroupJoining; diff --git a/packages/backend/src/server/api/endpoints/antennas/delete.ts b/packages/backend/src/server/api/endpoints/antennas/delete.ts index b2793fc70d..c140084778 100644 --- a/packages/backend/src/server/api/endpoints/antennas/delete.ts +++ b/packages/backend/src/server/api/endpoints/antennas/delete.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../define'; import { ApiError } from '../../error'; import { Antennas } from '@/models/index'; @@ -12,12 +10,6 @@ export const meta = { kind: 'write:account', - params: { - antennaId: { - validator: $.type(ID), - }, - }, - errors: { noSuchAntenna: { message: 'No such antenna.', @@ -27,8 +19,16 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + antennaId: { type: 'string', format: 'misskey:id' }, + }, + required: ['antennaId'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const antenna = await Antennas.findOne({ id: ps.antennaId, userId: user.id, diff --git a/packages/backend/src/server/api/endpoints/antennas/list.ts b/packages/backend/src/server/api/endpoints/antennas/list.ts index bb58912612..6910c07291 100644 --- a/packages/backend/src/server/api/endpoints/antennas/list.ts +++ b/packages/backend/src/server/api/endpoints/antennas/list.ts @@ -19,8 +19,14 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: {}, + required: [], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, me) => { +export default define(meta, paramDef, async (ps, me) => { const antennas = await Antennas.find({ userId: me.id, }); diff --git a/packages/backend/src/server/api/endpoints/antennas/notes.ts b/packages/backend/src/server/api/endpoints/antennas/notes.ts index eb7de901c5..283c01c2c7 100644 --- a/packages/backend/src/server/api/endpoints/antennas/notes.ts +++ b/packages/backend/src/server/api/endpoints/antennas/notes.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../define'; import readNote from '@/services/note/read'; import { Antennas, Notes, AntennaNotes } from '@/models/index'; @@ -16,33 +14,6 @@ export const meta = { kind: 'read:account', - params: { - antennaId: { - validator: $.type(ID), - }, - - limit: { - validator: $.optional.num.range(1, 100), - default: 10, - }, - - sinceId: { - validator: $.optional.type(ID), - }, - - untilId: { - validator: $.optional.type(ID), - }, - - sinceDate: { - validator: $.optional.num, - }, - - untilDate: { - validator: $.optional.num, - }, - }, - errors: { noSuchAntenna: { message: 'No such antenna.', @@ -62,8 +33,21 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + antennaId: { 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' }, + sinceDate: { type: 'integer' }, + untilDate: { type: 'integer' }, + }, + required: ['antennaId'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const antenna = await Antennas.findOne({ id: ps.antennaId, userId: user.id, @@ -92,7 +76,7 @@ export default define(meta, async (ps, user) => { generateBlockedUserQuery(query, user); const notes = await query - .take(ps.limit!) + .take(ps.limit) .getMany(); if (notes.length > 0) { diff --git a/packages/backend/src/server/api/endpoints/antennas/show.ts b/packages/backend/src/server/api/endpoints/antennas/show.ts index a37d37d31c..d8c08f2777 100644 --- a/packages/backend/src/server/api/endpoints/antennas/show.ts +++ b/packages/backend/src/server/api/endpoints/antennas/show.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../define'; import { ApiError } from '../../error'; import { Antennas } from '@/models/index'; @@ -11,12 +9,6 @@ export const meta = { kind: 'read:account', - params: { - antennaId: { - validator: $.type(ID), - }, - }, - errors: { noSuchAntenna: { message: 'No such antenna.', @@ -32,8 +24,16 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + antennaId: { type: 'string', format: 'misskey:id' }, + }, + required: ['antennaId'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, me) => { +export default define(meta, paramDef, async (ps, me) => { // Fetch the antenna const antenna = await Antennas.findOne({ id: ps.antennaId, diff --git a/packages/backend/src/server/api/endpoints/antennas/update.ts b/packages/backend/src/server/api/endpoints/antennas/update.ts index 900f725505..9f8564c2c0 100644 --- a/packages/backend/src/server/api/endpoints/antennas/update.ts +++ b/packages/backend/src/server/api/endpoints/antennas/update.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../define'; import { ApiError } from '../../error'; import { Antennas, UserLists, UserGroupJoinings } from '@/models/index'; @@ -12,56 +10,6 @@ export const meta = { kind: 'write:account', - params: { - antennaId: { - validator: $.type(ID), - }, - - name: { - validator: $.str.range(1, 100), - }, - - src: { - validator: $.str.or(['home', 'all', 'users', 'list', 'group']), - }, - - userListId: { - validator: $.nullable.optional.type(ID), - }, - - userGroupId: { - validator: $.nullable.optional.type(ID), - }, - - keywords: { - validator: $.arr($.arr($.str)), - }, - - excludeKeywords: { - validator: $.arr($.arr($.str)), - }, - - users: { - validator: $.arr($.str), - }, - - caseSensitive: { - validator: $.bool, - }, - - withReplies: { - validator: $.bool, - }, - - withFile: { - validator: $.bool, - }, - - notify: { - validator: $.bool, - }, - }, - errors: { noSuchAntenna: { message: 'No such antenna.', @@ -89,8 +37,37 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + antennaId: { type: 'string', format: 'misskey:id' }, + name: { type: 'string', minLength: 1, maxLength: 100 }, + src: { type: 'string', enum: ['home', 'all', 'users', 'list', 'group'] }, + userListId: { type: 'string', format: 'misskey:id', nullable: true }, + userGroupId: { type: 'string', format: 'misskey:id', nullable: true }, + keywords: { type: 'array', items: { + type: 'array', items: { + type: 'string', + }, + } }, + excludeKeywords: { type: 'array', items: { + type: 'array', items: { + type: 'string', + }, + } }, + users: { type: 'array', items: { + type: 'string', + } }, + caseSensitive: { type: 'boolean' }, + withReplies: { type: 'boolean' }, + withFile: { type: 'boolean' }, + notify: { type: 'boolean' }, + }, + required: ['antennaId', 'name', 'src', 'keywords', 'excludeKeywords', 'users', 'caseSensitive', 'withReplies', 'withFile', 'notify'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { // Fetch the antenna const antenna = await Antennas.findOne({ id: ps.antennaId, diff --git a/packages/backend/src/server/api/endpoints/ap/get.ts b/packages/backend/src/server/api/endpoints/ap/get.ts index ff8c677b91..c62ca6088b 100644 --- a/packages/backend/src/server/api/endpoints/ap/get.ts +++ b/packages/backend/src/server/api/endpoints/ap/get.ts @@ -1,4 +1,3 @@ -import $ from 'cafy'; import define from '../../define'; import Resolver from '@/remote/activitypub/resolver'; import { ApiError } from '../../error'; @@ -14,12 +13,6 @@ export const meta = { max: 30, }, - params: { - uri: { - validator: $.str, - }, - }, - errors: { }, @@ -29,8 +22,16 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + uri: { type: 'string' }, + }, + required: ['uri'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps) => { +export default define(meta, paramDef, async (ps) => { const resolver = new Resolver(); const object = await resolver.resolve(ps.uri); return object; diff --git a/packages/backend/src/server/api/endpoints/ap/show.ts b/packages/backend/src/server/api/endpoints/ap/show.ts index 7d17d8edce..e0b53403bb 100644 --- a/packages/backend/src/server/api/endpoints/ap/show.ts +++ b/packages/backend/src/server/api/endpoints/ap/show.ts @@ -1,4 +1,3 @@ -import $ from 'cafy'; import define from '../../define'; import config from '@/config/index'; import { createPerson } from '@/remote/activitypub/models/person'; @@ -24,12 +23,6 @@ export const meta = { max: 30, }, - params: { - uri: { - validator: $.str, - }, - }, - errors: { noSuchObject: { message: 'No such object.', @@ -75,8 +68,16 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + uri: { type: 'string' }, + }, + required: ['uri'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps) => { +export default define(meta, paramDef, async (ps) => { const object = await fetchAny(ps.uri); if (object) { return object; diff --git a/packages/backend/src/server/api/endpoints/app/create.ts b/packages/backend/src/server/api/endpoints/app/create.ts index fbe6690f1d..0f9610d2a9 100644 --- a/packages/backend/src/server/api/endpoints/app/create.ts +++ b/packages/backend/src/server/api/endpoints/app/create.ts @@ -1,4 +1,3 @@ -import $ from 'cafy'; import define from '../../define'; import { Apps } from '@/models/index'; import { genId } from '@/misc/gen-id'; @@ -10,26 +9,6 @@ export const meta = { requireCredential: false, - params: { - name: { - validator: $.str, - }, - - description: { - validator: $.str, - }, - - permission: { - validator: $.arr($.str).unique(), - }, - - // TODO: Check it is valid url - callbackUrl: { - validator: $.optional.nullable.str, - default: null, - }, - }, - res: { type: 'object', optional: false, nullable: false, @@ -37,8 +16,21 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + name: { type: 'string' }, + description: { type: 'string' }, + permission: { type: 'array', uniqueItems: true, items: { + type: 'string', + } }, + callbackUrl: { type: 'string', nullable: true }, + }, + required: ['name', 'description', 'permission'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { // Generate secret const secret = secureRndstr(32, true); diff --git a/packages/backend/src/server/api/endpoints/app/show.ts b/packages/backend/src/server/api/endpoints/app/show.ts index 9f4777b383..0ca7fcc2a8 100644 --- a/packages/backend/src/server/api/endpoints/app/show.ts +++ b/packages/backend/src/server/api/endpoints/app/show.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../define'; import { ApiError } from '../../error'; import { Apps } from '@/models/index'; @@ -7,12 +5,6 @@ import { Apps } from '@/models/index'; export const meta = { tags: ['app'], - params: { - appId: { - validator: $.type(ID), - }, - }, - errors: { noSuchApp: { message: 'No such app.', @@ -28,8 +20,16 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + appId: { type: 'string', format: 'misskey:id' }, + }, + required: ['appId'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user, token) => { +export default define(meta, paramDef, async (ps, user, token) => { const isSecure = user != null && token == null; // Lookup app diff --git a/packages/backend/src/server/api/endpoints/auth/accept.ts b/packages/backend/src/server/api/endpoints/auth/accept.ts index f028135ca5..4429119b1f 100644 --- a/packages/backend/src/server/api/endpoints/auth/accept.ts +++ b/packages/backend/src/server/api/endpoints/auth/accept.ts @@ -1,5 +1,4 @@ import * as crypto from 'crypto'; -import $ from 'cafy'; import define from '../../define'; import { ApiError } from '../../error'; import { AuthSessions, AccessTokens, Apps } from '@/models/index'; @@ -13,12 +12,6 @@ export const meta = { secure: true, - params: { - token: { - validator: $.str, - }, - }, - errors: { noSuchSession: { message: 'No such session.', @@ -28,8 +21,16 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + token: { type: 'string' }, + }, + required: ['token'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { // Fetch token const session = await AuthSessions .findOne({ token: ps.token }); diff --git a/packages/backend/src/server/api/endpoints/auth/session/generate.ts b/packages/backend/src/server/api/endpoints/auth/session/generate.ts index 98987eba5b..8d5c14e2f7 100644 --- a/packages/backend/src/server/api/endpoints/auth/session/generate.ts +++ b/packages/backend/src/server/api/endpoints/auth/session/generate.ts @@ -1,5 +1,4 @@ import { v4 as uuid } from 'uuid'; -import $ from 'cafy'; import config from '@/config/index'; import define from '../../../define'; import { ApiError } from '../../../error'; @@ -11,12 +10,6 @@ export const meta = { requireCredential: false, - params: { - appSecret: { - validator: $.str, - }, - }, - res: { type: 'object', optional: false, nullable: false, @@ -42,8 +35,16 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + appSecret: { type: 'string' }, + }, + required: ['appSecret'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps) => { +export default define(meta, paramDef, async (ps) => { // Lookup app const app = await Apps.findOne({ secret: ps.appSecret, diff --git a/packages/backend/src/server/api/endpoints/auth/session/show.ts b/packages/backend/src/server/api/endpoints/auth/session/show.ts index ae0d016cea..dfc1c06ee6 100644 --- a/packages/backend/src/server/api/endpoints/auth/session/show.ts +++ b/packages/backend/src/server/api/endpoints/auth/session/show.ts @@ -1,4 +1,3 @@ -import $ from 'cafy'; import define from '../../../define'; import { ApiError } from '../../../error'; import { AuthSessions } from '@/models/index'; @@ -8,12 +7,6 @@ export const meta = { requireCredential: false, - params: { - token: { - validator: $.str, - }, - }, - errors: { noSuchSession: { message: 'No such session.', @@ -44,8 +37,16 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + token: { type: 'string' }, + }, + required: ['token'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { // Lookup session const session = await AuthSessions.findOne({ token: ps.token, diff --git a/packages/backend/src/server/api/endpoints/auth/session/userkey.ts b/packages/backend/src/server/api/endpoints/auth/session/userkey.ts index fe0211ebe3..397d1e687b 100644 --- a/packages/backend/src/server/api/endpoints/auth/session/userkey.ts +++ b/packages/backend/src/server/api/endpoints/auth/session/userkey.ts @@ -1,4 +1,3 @@ -import $ from 'cafy'; import define from '../../../define'; import { ApiError } from '../../../error'; import { Apps, AuthSessions, AccessTokens, Users } from '@/models/index'; @@ -8,16 +7,6 @@ export const meta = { requireCredential: false, - params: { - appSecret: { - validator: $.str, - }, - - token: { - validator: $.str, - }, - }, - res: { type: 'object', optional: false, nullable: false, @@ -56,8 +45,17 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + appSecret: { type: 'string' }, + token: { type: 'string' }, + }, + required: ['appSecret', 'token'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps) => { +export default define(meta, paramDef, async (ps) => { // Lookup app const app = await Apps.findOne({ secret: ps.appSecret, diff --git a/packages/backend/src/server/api/endpoints/blocking/create.ts b/packages/backend/src/server/api/endpoints/blocking/create.ts index 6d555ff569..8fd8f33338 100644 --- a/packages/backend/src/server/api/endpoints/blocking/create.ts +++ b/packages/backend/src/server/api/endpoints/blocking/create.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import ms from 'ms'; import create from '@/services/blocking/create'; import define from '../../define'; @@ -19,12 +17,6 @@ export const meta = { kind: 'write:blocks', - params: { - userId: { - validator: $.type(ID), - }, - }, - errors: { noSuchUser: { message: 'No such user.', @@ -52,8 +44,16 @@ export const meta = { }, } as const; +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, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const blocker = await Users.findOneOrFail(user.id); // č‡Ē分č‡ĒčēĢ diff --git a/packages/backend/src/server/api/endpoints/blocking/delete.ts b/packages/backend/src/server/api/endpoints/blocking/delete.ts index 942cddaedf..ad00fd1e0b 100644 --- a/packages/backend/src/server/api/endpoints/blocking/delete.ts +++ b/packages/backend/src/server/api/endpoints/blocking/delete.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import ms from 'ms'; import deleteBlocking from '@/services/blocking/delete'; import define from '../../define'; @@ -19,12 +17,6 @@ export const meta = { kind: 'write:blocks', - params: { - userId: { - validator: $.type(ID), - }, - }, - errors: { noSuchUser: { message: 'No such user.', @@ -52,8 +44,16 @@ export const meta = { }, } as const; +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, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const blocker = await Users.findOneOrFail(user.id); // Check if the blockee is yourself diff --git a/packages/backend/src/server/api/endpoints/blocking/list.ts b/packages/backend/src/server/api/endpoints/blocking/list.ts index 9a4f662140..a315ccde9e 100644 --- a/packages/backend/src/server/api/endpoints/blocking/list.ts +++ b/packages/backend/src/server/api/endpoints/blocking/list.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../define'; import { Blockings } from '@/models/index'; import { makePaginationQuery } from '../../common/make-pagination-query'; @@ -11,21 +9,6 @@ export const meta = { kind: 'read:blocks', - params: { - limit: { - validator: $.optional.num.range(1, 100), - default: 30, - }, - - sinceId: { - validator: $.optional.type(ID), - }, - - untilId: { - validator: $.optional.type(ID), - }, - }, - res: { type: 'array', optional: false, nullable: false, @@ -37,13 +20,23 @@ export const meta = { }, } as const; +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, async (ps, me) => { +export default define(meta, paramDef, async (ps, me) => { const query = makePaginationQuery(Blockings.createQueryBuilder('blocking'), ps.sinceId, ps.untilId) .andWhere(`blocking.blockerId = :meId`, { meId: me.id }); const blockings = await query - .take(ps.limit!) + .take(ps.limit) .getMany(); return await Blockings.packMany(blockings, me); diff --git a/packages/backend/src/server/api/endpoints/channels/create.ts b/packages/backend/src/server/api/endpoints/channels/create.ts index 68cdf1143e..dc7afadfa2 100644 --- a/packages/backend/src/server/api/endpoints/channels/create.ts +++ b/packages/backend/src/server/api/endpoints/channels/create.ts @@ -1,10 +1,8 @@ -import $ from 'cafy'; import define from '../../define'; import { ApiError } from '../../error'; import { Channels, DriveFiles } from '@/models/index'; import { Channel } from '@/models/entities/channel'; import { genId } from '@/misc/gen-id'; -import { ID } from '@/misc/cafy-id'; export const meta = { tags: ['channels'], @@ -13,20 +11,6 @@ export const meta = { kind: 'write:channels', - params: { - name: { - validator: $.str.range(1, 128), - }, - - description: { - validator: $.nullable.optional.str.range(1, 2048), - }, - - bannerId: { - validator: $.nullable.optional.type(ID), - }, - }, - res: { type: 'object', optional: false, nullable: false, @@ -42,8 +26,18 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + name: { type: 'string', minLength: 1, maxLength: 128 }, + description: { type: 'string', nullable: true, minLength: 1, maxLength: 2048 }, + bannerId: { type: 'string', format: 'misskey:id', nullable: true }, + }, + required: ['name'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { let banner = null; if (ps.bannerId != null) { banner = await DriveFiles.findOne({ diff --git a/packages/backend/src/server/api/endpoints/channels/featured.ts b/packages/backend/src/server/api/endpoints/channels/featured.ts index ceadde907c..4d07e720a9 100644 --- a/packages/backend/src/server/api/endpoints/channels/featured.ts +++ b/packages/backend/src/server/api/endpoints/channels/featured.ts @@ -17,8 +17,14 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: {}, + required: [], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, me) => { +export default define(meta, paramDef, async (ps, me) => { const query = Channels.createQueryBuilder('channel') .where('channel.lastNotedAt IS NOT NULL') .orderBy('channel.lastNotedAt', 'DESC'); diff --git a/packages/backend/src/server/api/endpoints/channels/follow.ts b/packages/backend/src/server/api/endpoints/channels/follow.ts index bf580eea60..01156d1c2c 100644 --- a/packages/backend/src/server/api/endpoints/channels/follow.ts +++ b/packages/backend/src/server/api/endpoints/channels/follow.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../define'; import { ApiError } from '../../error'; import { Channels, ChannelFollowings } from '@/models/index'; @@ -13,12 +11,6 @@ export const meta = { kind: 'write:channels', - params: { - channelId: { - validator: $.type(ID), - }, - }, - errors: { noSuchChannel: { message: 'No such channel.', @@ -28,8 +20,16 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + channelId: { type: 'string', format: 'misskey:id' }, + }, + required: ['channelId'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const channel = await Channels.findOne({ id: ps.channelId, }); diff --git a/packages/backend/src/server/api/endpoints/channels/followed.ts b/packages/backend/src/server/api/endpoints/channels/followed.ts index 9e4c942af2..0e75f06f47 100644 --- a/packages/backend/src/server/api/endpoints/channels/followed.ts +++ b/packages/backend/src/server/api/endpoints/channels/followed.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../define'; import { Channels, ChannelFollowings } from '@/models/index'; import { makePaginationQuery } from '../../common/make-pagination-query'; @@ -11,21 +9,6 @@ export const meta = { kind: 'read:channels', - params: { - sinceId: { - validator: $.optional.type(ID), - }, - - untilId: { - validator: $.optional.type(ID), - }, - - limit: { - validator: $.optional.num.range(1, 100), - default: 5, - }, - }, - res: { type: 'array', optional: false, nullable: false, @@ -37,13 +20,23 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + sinceId: { type: 'string', format: 'misskey:id' }, + untilId: { type: 'string', format: 'misskey:id' }, + limit: { type: 'integer', minimum: 1, maximum: 100, default: 5 }, + }, + required: [], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, me) => { +export default define(meta, paramDef, async (ps, me) => { const query = makePaginationQuery(ChannelFollowings.createQueryBuilder(), ps.sinceId, ps.untilId) .andWhere({ followerId: me.id }); const followings = await query - .take(ps.limit!) + .take(ps.limit) .getMany(); return await Promise.all(followings.map(x => Channels.pack(x.followeeId, me))); diff --git a/packages/backend/src/server/api/endpoints/channels/owned.ts b/packages/backend/src/server/api/endpoints/channels/owned.ts index 5473636a85..f8acae836b 100644 --- a/packages/backend/src/server/api/endpoints/channels/owned.ts +++ b/packages/backend/src/server/api/endpoints/channels/owned.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../define'; import { Channels } from '@/models/index'; import { makePaginationQuery } from '../../common/make-pagination-query'; @@ -11,21 +9,6 @@ export const meta = { kind: 'read:channels', - params: { - sinceId: { - validator: $.optional.type(ID), - }, - - untilId: { - validator: $.optional.type(ID), - }, - - limit: { - validator: $.optional.num.range(1, 100), - default: 5, - }, - }, - res: { type: 'array', optional: false, nullable: false, @@ -37,13 +20,23 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + sinceId: { type: 'string', format: 'misskey:id' }, + untilId: { type: 'string', format: 'misskey:id' }, + limit: { type: 'integer', minimum: 1, maximum: 100, default: 5 }, + }, + required: [], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, me) => { +export default define(meta, paramDef, async (ps, me) => { const query = makePaginationQuery(Channels.createQueryBuilder(), ps.sinceId, ps.untilId) .andWhere({ userId: me.id }); const channels = await query - .take(ps.limit!) + .take(ps.limit) .getMany(); return await Promise.all(channels.map(x => Channels.pack(x, me))); diff --git a/packages/backend/src/server/api/endpoints/channels/show.ts b/packages/backend/src/server/api/endpoints/channels/show.ts index 598a87ec4e..fb2c7b5adf 100644 --- a/packages/backend/src/server/api/endpoints/channels/show.ts +++ b/packages/backend/src/server/api/endpoints/channels/show.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../define'; import { ApiError } from '../../error'; import { Channels } from '@/models/index'; @@ -9,12 +7,6 @@ export const meta = { requireCredential: false, - params: { - channelId: { - validator: $.type(ID), - }, - }, - res: { type: 'object', optional: false, nullable: false, @@ -30,8 +22,16 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + channelId: { type: 'string', format: 'misskey:id' }, + }, + required: ['channelId'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, me) => { +export default define(meta, paramDef, async (ps, me) => { const channel = await Channels.findOne({ id: ps.channelId, }); diff --git a/packages/backend/src/server/api/endpoints/channels/timeline.ts b/packages/backend/src/server/api/endpoints/channels/timeline.ts index 927ce7c741..a870ecccbb 100644 --- a/packages/backend/src/server/api/endpoints/channels/timeline.ts +++ b/packages/backend/src/server/api/endpoints/channels/timeline.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../define'; import { ApiError } from '../../error'; import { Notes, Channels } from '@/models/index'; @@ -11,33 +9,6 @@ export const meta = { requireCredential: false, - params: { - channelId: { - validator: $.type(ID), - }, - - limit: { - validator: $.optional.num.range(1, 100), - default: 10, - }, - - sinceId: { - validator: $.optional.type(ID), - }, - - untilId: { - validator: $.optional.type(ID), - }, - - sinceDate: { - validator: $.optional.num, - }, - - untilDate: { - validator: $.optional.num, - }, - }, - res: { type: 'array', optional: false, nullable: false, @@ -57,8 +28,21 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + channelId: { 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' }, + sinceDate: { type: 'integer' }, + untilDate: { type: 'integer' }, + }, + required: ['channelId'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const channel = await Channels.findOne({ id: ps.channelId, }); @@ -78,9 +62,9 @@ export default define(meta, async (ps, user) => { .leftJoinAndSelect('note.channel', 'channel'); //#endregion - const timeline = await query.take(ps.limit!).getMany(); + const timeline = await query.take(ps.limit).getMany(); - if (user) activeUsersChart.update(user); + if (user) activeUsersChart.read(user); return await Notes.packMany(timeline, user); }); diff --git a/packages/backend/src/server/api/endpoints/channels/unfollow.ts b/packages/backend/src/server/api/endpoints/channels/unfollow.ts index ada0cb29fd..42a0eb0a5a 100644 --- a/packages/backend/src/server/api/endpoints/channels/unfollow.ts +++ b/packages/backend/src/server/api/endpoints/channels/unfollow.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../define'; import { ApiError } from '../../error'; import { Channels, ChannelFollowings } from '@/models/index'; @@ -12,12 +10,6 @@ export const meta = { kind: 'write:channels', - params: { - channelId: { - validator: $.type(ID), - }, - }, - errors: { noSuchChannel: { message: 'No such channel.', @@ -27,8 +19,16 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + channelId: { type: 'string', format: 'misskey:id' }, + }, + required: ['channelId'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const channel = await Channels.findOne({ id: ps.channelId, }); diff --git a/packages/backend/src/server/api/endpoints/channels/update.ts b/packages/backend/src/server/api/endpoints/channels/update.ts index 1f7108a1cb..5a593c62e0 100644 --- a/packages/backend/src/server/api/endpoints/channels/update.ts +++ b/packages/backend/src/server/api/endpoints/channels/update.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../define'; import { ApiError } from '../../error'; import { Channels, DriveFiles } from '@/models/index'; @@ -11,24 +9,6 @@ export const meta = { kind: 'write:channels', - params: { - channelId: { - validator: $.type(ID), - }, - - name: { - validator: $.optional.str.range(1, 128), - }, - - description: { - validator: $.nullable.optional.str.range(1, 2048), - }, - - bannerId: { - validator: $.nullable.optional.type(ID), - }, - }, - res: { type: 'object', optional: false, nullable: false, @@ -56,8 +36,19 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + channelId: { type: 'string', format: 'misskey:id' }, + name: { type: 'string', minLength: 1, maxLength: 128 }, + description: { type: 'string', nullable: true, minLength: 1, maxLength: 2048 }, + bannerId: { type: 'string', format: 'misskey:id', nullable: true }, + }, + required: ['channelId'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, me) => { +export default define(meta, paramDef, async (ps, me) => { const channel = await Channels.findOne({ id: ps.channelId, }); diff --git a/packages/backend/src/server/api/endpoints/charts/active-users.ts b/packages/backend/src/server/api/endpoints/charts/active-users.ts index f7eadc7089..a99269da0c 100644 --- a/packages/backend/src/server/api/endpoints/charts/active-users.ts +++ b/packages/backend/src/server/api/endpoints/charts/active-users.ts @@ -1,31 +1,24 @@ -import $ from 'cafy'; import define from '../../define'; -import { convertLog } from '@/services/chart/core'; +import { getJsonSchema } from '@/services/chart/core'; import { activeUsersChart } from '@/services/chart/index'; export const meta = { tags: ['charts', 'users'], - params: { - span: { - validator: $.str.or(['day', 'hour']), - }, + res: getJsonSchema(activeUsersChart.schema), +} as const; - limit: { - validator: $.optional.num.range(1, 500), - default: 30, - }, - - offset: { - validator: $.optional.nullable.num, - default: null, - }, +const paramDef = { + type: 'object', + properties: { + span: { type: 'string', enum: ['day', 'hour'] }, + limit: { type: 'integer', minimum: 1, maximum: 500, default: 30 }, + offset: { type: 'integer', nullable: true, default: null }, }, - - res: convertLog(activeUsersChart.schema), + required: ['span'], } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps) => { - return await activeUsersChart.getChart(ps.span as any, ps.limit!, ps.offset ? new Date(ps.offset) : null); +export default define(meta, paramDef, async (ps) => { + return await activeUsersChart.getChart(ps.span, ps.limit, ps.offset ? new Date(ps.offset) : null); }); diff --git a/packages/backend/src/server/api/endpoints/charts/ap-request.ts b/packages/backend/src/server/api/endpoints/charts/ap-request.ts new file mode 100644 index 0000000000..403a933807 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/charts/ap-request.ts @@ -0,0 +1,24 @@ +import define from '../../define'; +import { getJsonSchema } from '@/services/chart/core'; +import { apRequestChart } from '@/services/chart/index'; + +export const meta = { + tags: ['charts'], + + res: getJsonSchema(apRequestChart.schema), +} as const; + +const paramDef = { + type: 'object', + properties: { + span: { type: 'string', enum: ['day', 'hour'] }, + limit: { type: 'integer', minimum: 1, maximum: 500, default: 30 }, + offset: { type: 'integer', nullable: true, default: null }, + }, + required: ['span'], +} as const; + +// eslint-disable-next-line import/no-default-export +export default define(meta, paramDef, async (ps) => { + return await apRequestChart.getChart(ps.span, ps.limit, ps.offset ? new Date(ps.offset) : null); +}); diff --git a/packages/backend/src/server/api/endpoints/charts/drive.ts b/packages/backend/src/server/api/endpoints/charts/drive.ts index 364279da95..2c815367f6 100644 --- a/packages/backend/src/server/api/endpoints/charts/drive.ts +++ b/packages/backend/src/server/api/endpoints/charts/drive.ts @@ -1,31 +1,24 @@ -import $ from 'cafy'; import define from '../../define'; -import { convertLog } from '@/services/chart/core'; +import { getJsonSchema } from '@/services/chart/core'; import { driveChart } from '@/services/chart/index'; export const meta = { tags: ['charts', 'drive'], - params: { - span: { - validator: $.str.or(['day', 'hour']), - }, + res: getJsonSchema(driveChart.schema), +} as const; - limit: { - validator: $.optional.num.range(1, 500), - default: 30, - }, - - offset: { - validator: $.optional.nullable.num, - default: null, - }, +const paramDef = { + type: 'object', + properties: { + span: { type: 'string', enum: ['day', 'hour'] }, + limit: { type: 'integer', minimum: 1, maximum: 500, default: 30 }, + offset: { type: 'integer', nullable: true, default: null }, }, - - res: convertLog(driveChart.schema), + required: ['span'], } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps) => { - return await driveChart.getChart(ps.span as any, ps.limit!, ps.offset ? new Date(ps.offset) : null); +export default define(meta, paramDef, async (ps) => { + return await driveChart.getChart(ps.span, ps.limit, ps.offset ? new Date(ps.offset) : null); }); diff --git a/packages/backend/src/server/api/endpoints/charts/federation.ts b/packages/backend/src/server/api/endpoints/charts/federation.ts index 6feb82b6d9..c48613ffac 100644 --- a/packages/backend/src/server/api/endpoints/charts/federation.ts +++ b/packages/backend/src/server/api/endpoints/charts/federation.ts @@ -1,31 +1,24 @@ -import $ from 'cafy'; import define from '../../define'; -import { convertLog } from '@/services/chart/core'; +import { getJsonSchema } from '@/services/chart/core'; import { federationChart } from '@/services/chart/index'; export const meta = { tags: ['charts'], - params: { - span: { - validator: $.str.or(['day', 'hour']), - }, + res: getJsonSchema(federationChart.schema), +} as const; - limit: { - validator: $.optional.num.range(1, 500), - default: 30, - }, - - offset: { - validator: $.optional.nullable.num, - default: null, - }, +const paramDef = { + type: 'object', + properties: { + span: { type: 'string', enum: ['day', 'hour'] }, + limit: { type: 'integer', minimum: 1, maximum: 500, default: 30 }, + offset: { type: 'integer', nullable: true, default: null }, }, - - res: convertLog(federationChart.schema), + required: ['span'], } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps) => { - return await federationChart.getChart(ps.span as any, ps.limit!, ps.offset ? new Date(ps.offset) : null); +export default define(meta, paramDef, async (ps) => { + return await federationChart.getChart(ps.span, ps.limit, ps.offset ? new Date(ps.offset) : null); }); diff --git a/packages/backend/src/server/api/endpoints/charts/hashtag.ts b/packages/backend/src/server/api/endpoints/charts/hashtag.ts index 99dc77998e..3cb0784ba4 100644 --- a/packages/backend/src/server/api/endpoints/charts/hashtag.ts +++ b/packages/backend/src/server/api/endpoints/charts/hashtag.ts @@ -1,35 +1,25 @@ -import $ from 'cafy'; import define from '../../define'; -import { convertLog } from '@/services/chart/core'; +import { getJsonSchema } from '@/services/chart/core'; import { hashtagChart } from '@/services/chart/index'; export const meta = { tags: ['charts', 'hashtags'], - params: { - span: { - validator: $.str.or(['day', 'hour']), - }, + res: getJsonSchema(hashtagChart.schema), +} as const; - limit: { - validator: $.optional.num.range(1, 500), - default: 30, - }, - - offset: { - validator: $.optional.nullable.num, - default: null, - }, - - tag: { - validator: $.str, - }, +const paramDef = { + type: 'object', + properties: { + span: { type: 'string', enum: ['day', 'hour'] }, + limit: { type: 'integer', minimum: 1, maximum: 500, default: 30 }, + offset: { type: 'integer', nullable: true, default: null }, + tag: { type: 'string' }, }, - - res: convertLog(hashtagChart.schema), + required: ['span', 'tag'], } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps) => { - return await hashtagChart.getChart(ps.span as any, ps.limit!, ps.offset ? new Date(ps.offset) : null, ps.tag); +export default define(meta, paramDef, async (ps) => { + return await hashtagChart.getChart(ps.span, ps.limit, ps.offset ? new Date(ps.offset) : null, ps.tag); }); diff --git a/packages/backend/src/server/api/endpoints/charts/instance.ts b/packages/backend/src/server/api/endpoints/charts/instance.ts index 23e6fbf2b0..2da3cd3c3a 100644 --- a/packages/backend/src/server/api/endpoints/charts/instance.ts +++ b/packages/backend/src/server/api/endpoints/charts/instance.ts @@ -1,35 +1,25 @@ -import $ from 'cafy'; import define from '../../define'; -import { convertLog } from '@/services/chart/core'; +import { getJsonSchema } from '@/services/chart/core'; import { instanceChart } from '@/services/chart/index'; export const meta = { tags: ['charts'], - params: { - span: { - validator: $.str.or(['day', 'hour']), - }, + res: getJsonSchema(instanceChart.schema), +} as const; - limit: { - validator: $.optional.num.range(1, 500), - default: 30, - }, - - offset: { - validator: $.optional.nullable.num, - default: null, - }, - - host: { - validator: $.str, - }, +const paramDef = { + type: 'object', + properties: { + span: { type: 'string', enum: ['day', 'hour'] }, + limit: { type: 'integer', minimum: 1, maximum: 500, default: 30 }, + offset: { type: 'integer', nullable: true, default: null }, + host: { type: 'string' }, }, - - res: convertLog(instanceChart.schema), + required: ['span', 'host'], } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps) => { - return await instanceChart.getChart(ps.span as any, ps.limit!, ps.offset ? new Date(ps.offset) : null, ps.host); +export default define(meta, paramDef, async (ps) => { + return await instanceChart.getChart(ps.span, ps.limit, ps.offset ? new Date(ps.offset) : null, ps.host); }); diff --git a/packages/backend/src/server/api/endpoints/charts/network.ts b/packages/backend/src/server/api/endpoints/charts/network.ts deleted file mode 100644 index c5a39bbd76..0000000000 --- a/packages/backend/src/server/api/endpoints/charts/network.ts +++ /dev/null @@ -1,31 +0,0 @@ -import $ from 'cafy'; -import define from '../../define'; -import { convertLog } from '@/services/chart/core'; -import { networkChart } from '@/services/chart/index'; - -export const meta = { - tags: ['charts'], - - params: { - span: { - validator: $.str.or(['day', 'hour']), - }, - - limit: { - validator: $.optional.num.range(1, 500), - default: 30, - }, - - offset: { - validator: $.optional.nullable.num, - default: null, - }, - }, - - res: convertLog(networkChart.schema), -} as const; - -// eslint-disable-next-line import/no-default-export -export default define(meta, async (ps) => { - return await networkChart.getChart(ps.span as any, ps.limit!, ps.offset ? new Date(ps.offset) : null); -}); diff --git a/packages/backend/src/server/api/endpoints/charts/notes.ts b/packages/backend/src/server/api/endpoints/charts/notes.ts index dcbd80c3e9..c48bc67e1c 100644 --- a/packages/backend/src/server/api/endpoints/charts/notes.ts +++ b/packages/backend/src/server/api/endpoints/charts/notes.ts @@ -1,31 +1,24 @@ -import $ from 'cafy'; import define from '../../define'; -import { convertLog } from '@/services/chart/core'; +import { getJsonSchema } from '@/services/chart/core'; import { notesChart } from '@/services/chart/index'; export const meta = { tags: ['charts', 'notes'], - params: { - span: { - validator: $.str.or(['day', 'hour']), - }, + res: getJsonSchema(notesChart.schema), +} as const; - limit: { - validator: $.optional.num.range(1, 500), - default: 30, - }, - - offset: { - validator: $.optional.nullable.num, - default: null, - }, +const paramDef = { + type: 'object', + properties: { + span: { type: 'string', enum: ['day', 'hour'] }, + limit: { type: 'integer', minimum: 1, maximum: 500, default: 30 }, + offset: { type: 'integer', nullable: true, default: null }, }, - - res: convertLog(notesChart.schema), + required: ['span'], } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps) => { - return await notesChart.getChart(ps.span as any, ps.limit!, ps.offset ? new Date(ps.offset) : null); +export default define(meta, paramDef, async (ps) => { + return await notesChart.getChart(ps.span, ps.limit, ps.offset ? new Date(ps.offset) : null); }); diff --git a/packages/backend/src/server/api/endpoints/charts/user/drive.ts b/packages/backend/src/server/api/endpoints/charts/user/drive.ts index 94787b4a57..62f7afa435 100644 --- a/packages/backend/src/server/api/endpoints/charts/user/drive.ts +++ b/packages/backend/src/server/api/endpoints/charts/user/drive.ts @@ -1,36 +1,25 @@ -import $ from 'cafy'; import define from '../../../define'; -import { ID } from '@/misc/cafy-id'; -import { convertLog } from '@/services/chart/core'; +import { getJsonSchema } from '@/services/chart/core'; import { perUserDriveChart } from '@/services/chart/index'; export const meta = { tags: ['charts', 'drive', 'users'], - params: { - span: { - validator: $.str.or(['day', 'hour']), - }, + res: getJsonSchema(perUserDriveChart.schema), +} as const; - limit: { - validator: $.optional.num.range(1, 500), - default: 30, - }, - - offset: { - validator: $.optional.nullable.num, - default: null, - }, - - userId: { - validator: $.type(ID), - }, +const paramDef = { + type: 'object', + properties: { + span: { type: 'string', enum: ['day', 'hour'] }, + limit: { type: 'integer', minimum: 1, maximum: 500, default: 30 }, + offset: { type: 'integer', nullable: true, default: null }, + userId: { type: 'string', format: 'misskey:id' }, }, - - res: convertLog(perUserDriveChart.schema), + required: ['span', 'userId'], } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps) => { - return await perUserDriveChart.getChart(ps.span as any, ps.limit!, ps.offset ? new Date(ps.offset) : null, ps.userId); +export default define(meta, paramDef, async (ps) => { + return await perUserDriveChart.getChart(ps.span, ps.limit, ps.offset ? new Date(ps.offset) : null, ps.userId); }); diff --git a/packages/backend/src/server/api/endpoints/charts/user/following.ts b/packages/backend/src/server/api/endpoints/charts/user/following.ts index effe0c54b9..d6d2d5e3ee 100644 --- a/packages/backend/src/server/api/endpoints/charts/user/following.ts +++ b/packages/backend/src/server/api/endpoints/charts/user/following.ts @@ -1,36 +1,25 @@ -import $ from 'cafy'; import define from '../../../define'; -import { ID } from '@/misc/cafy-id'; -import { convertLog } from '@/services/chart/core'; +import { getJsonSchema } from '@/services/chart/core'; import { perUserFollowingChart } from '@/services/chart/index'; export const meta = { tags: ['charts', 'users', 'following'], - params: { - span: { - validator: $.str.or(['day', 'hour']), - }, + res: getJsonSchema(perUserFollowingChart.schema), +} as const; - limit: { - validator: $.optional.num.range(1, 500), - default: 30, - }, - - offset: { - validator: $.optional.nullable.num, - default: null, - }, - - userId: { - validator: $.type(ID), - }, +const paramDef = { + type: 'object', + properties: { + span: { type: 'string', enum: ['day', 'hour'] }, + limit: { type: 'integer', minimum: 1, maximum: 500, default: 30 }, + offset: { type: 'integer', nullable: true, default: null }, + userId: { type: 'string', format: 'misskey:id' }, }, - - res: convertLog(perUserFollowingChart.schema), + required: ['span', 'userId'], } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps) => { - return await perUserFollowingChart.getChart(ps.span as any, ps.limit!, ps.offset ? new Date(ps.offset) : null, ps.userId); +export default define(meta, paramDef, async (ps) => { + return await perUserFollowingChart.getChart(ps.span, ps.limit, ps.offset ? new Date(ps.offset) : null, ps.userId); }); diff --git a/packages/backend/src/server/api/endpoints/charts/user/notes.ts b/packages/backend/src/server/api/endpoints/charts/user/notes.ts index df68a5fe52..becde8b1df 100644 --- a/packages/backend/src/server/api/endpoints/charts/user/notes.ts +++ b/packages/backend/src/server/api/endpoints/charts/user/notes.ts @@ -1,36 +1,25 @@ -import $ from 'cafy'; import define from '../../../define'; -import { ID } from '@/misc/cafy-id'; -import { convertLog } from '@/services/chart/core'; +import { getJsonSchema } from '@/services/chart/core'; import { perUserNotesChart } from '@/services/chart/index'; export const meta = { tags: ['charts', 'users', 'notes'], - params: { - span: { - validator: $.str.or(['day', 'hour']), - }, + res: getJsonSchema(perUserNotesChart.schema), +} as const; - limit: { - validator: $.optional.num.range(1, 500), - default: 30, - }, - - offset: { - validator: $.optional.nullable.num, - default: null, - }, - - userId: { - validator: $.type(ID), - }, +const paramDef = { + type: 'object', + properties: { + span: { type: 'string', enum: ['day', 'hour'] }, + limit: { type: 'integer', minimum: 1, maximum: 500, default: 30 }, + offset: { type: 'integer', nullable: true, default: null }, + userId: { type: 'string', format: 'misskey:id' }, }, - - res: convertLog(perUserNotesChart.schema), + required: ['span', 'userId'], } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps) => { - return await perUserNotesChart.getChart(ps.span as any, ps.limit!, ps.offset ? new Date(ps.offset) : null, ps.userId); +export default define(meta, paramDef, async (ps) => { + return await perUserNotesChart.getChart(ps.span, ps.limit, ps.offset ? new Date(ps.offset) : null, ps.userId); }); diff --git a/packages/backend/src/server/api/endpoints/charts/user/reactions.ts b/packages/backend/src/server/api/endpoints/charts/user/reactions.ts index dcd067305f..8f4d9a812a 100644 --- a/packages/backend/src/server/api/endpoints/charts/user/reactions.ts +++ b/packages/backend/src/server/api/endpoints/charts/user/reactions.ts @@ -1,36 +1,25 @@ -import $ from 'cafy'; import define from '../../../define'; -import { ID } from '@/misc/cafy-id'; -import { convertLog } from '@/services/chart/core'; +import { getJsonSchema } from '@/services/chart/core'; import { perUserReactionsChart } from '@/services/chart/index'; export const meta = { tags: ['charts', 'users', 'reactions'], - params: { - span: { - validator: $.str.or(['day', 'hour']), - }, + res: getJsonSchema(perUserReactionsChart.schema), +} as const; - limit: { - validator: $.optional.num.range(1, 500), - default: 30, - }, - - offset: { - validator: $.optional.nullable.num, - default: null, - }, - - userId: { - validator: $.type(ID), - }, +const paramDef = { + type: 'object', + properties: { + span: { type: 'string', enum: ['day', 'hour'] }, + limit: { type: 'integer', minimum: 1, maximum: 500, default: 30 }, + offset: { type: 'integer', nullable: true, default: null }, + userId: { type: 'string', format: 'misskey:id' }, }, - - res: convertLog(perUserReactionsChart.schema), + required: ['span', 'userId'], } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps) => { - return await perUserReactionsChart.getChart(ps.span as any, ps.limit!, ps.offset ? new Date(ps.offset) : null, ps.userId); +export default define(meta, paramDef, async (ps) => { + return await perUserReactionsChart.getChart(ps.span, ps.limit, ps.offset ? new Date(ps.offset) : null, ps.userId); }); diff --git a/packages/backend/src/server/api/endpoints/charts/users.ts b/packages/backend/src/server/api/endpoints/charts/users.ts index d32e14ad61..855fc6cd07 100644 --- a/packages/backend/src/server/api/endpoints/charts/users.ts +++ b/packages/backend/src/server/api/endpoints/charts/users.ts @@ -1,31 +1,24 @@ -import $ from 'cafy'; import define from '../../define'; -import { convertLog } from '@/services/chart/core'; +import { getJsonSchema } from '@/services/chart/core'; import { usersChart } from '@/services/chart/index'; export const meta = { tags: ['charts', 'users'], - params: { - span: { - validator: $.str.or(['day', 'hour']), - }, + res: getJsonSchema(usersChart.schema), +} as const; - limit: { - validator: $.optional.num.range(1, 500), - default: 30, - }, - - offset: { - validator: $.optional.nullable.num, - default: null, - }, +const paramDef = { + type: 'object', + properties: { + span: { type: 'string', enum: ['day', 'hour'] }, + limit: { type: 'integer', minimum: 1, maximum: 500, default: 30 }, + offset: { type: 'integer', nullable: true, default: null }, }, - - res: convertLog(usersChart.schema), + required: ['span'], } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps) => { - return await usersChart.getChart(ps.span as any, ps.limit!, ps.offset ? new Date(ps.offset) : null); +export default define(meta, paramDef, async (ps) => { + return await usersChart.getChart(ps.span, ps.limit, ps.offset ? new Date(ps.offset) : null); }); 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 4a740b6cfe..e7dc25fe40 100644 --- a/packages/backend/src/server/api/endpoints/clips/add-note.ts +++ b/packages/backend/src/server/api/endpoints/clips/add-note.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../define'; import { ClipNotes, Clips } from '@/models/index'; import { ApiError } from '../../error'; @@ -13,16 +11,6 @@ export const meta = { kind: 'write:account', - params: { - clipId: { - validator: $.type(ID), - }, - - noteId: { - validator: $.type(ID), - }, - }, - errors: { noSuchClip: { message: 'No such clip.', @@ -44,8 +32,17 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + clipId: { type: 'string', format: 'misskey:id' }, + noteId: { type: 'string', format: 'misskey:id' }, + }, + required: ['clipId', 'noteId'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const clip = await Clips.findOne({ id: ps.clipId, userId: user.id, diff --git a/packages/backend/src/server/api/endpoints/clips/create.ts b/packages/backend/src/server/api/endpoints/clips/create.ts index 852e66c9e4..b6ebf866b8 100644 --- a/packages/backend/src/server/api/endpoints/clips/create.ts +++ b/packages/backend/src/server/api/endpoints/clips/create.ts @@ -1,4 +1,3 @@ -import $ from 'cafy'; import define from '../../define'; import { genId } from '@/misc/gen-id'; import { Clips } from '@/models/index'; @@ -10,20 +9,6 @@ export const meta = { kind: 'write:account', - params: { - name: { - validator: $.str.range(1, 100), - }, - - isPublic: { - validator: $.optional.bool, - }, - - description: { - validator: $.optional.nullable.str.range(1, 2048), - }, - }, - res: { type: 'object', optional: false, nullable: false, @@ -31,8 +16,18 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + name: { type: 'string', minLength: 1, maxLength: 100 }, + isPublic: { type: 'boolean' }, + description: { type: 'string', nullable: true, minLength: 1, maxLength: 2048 }, + }, + required: ['name'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const clip = await Clips.insert({ id: genId(), createdAt: new Date(), diff --git a/packages/backend/src/server/api/endpoints/clips/delete.ts b/packages/backend/src/server/api/endpoints/clips/delete.ts index 85c64a115d..a263e10521 100644 --- a/packages/backend/src/server/api/endpoints/clips/delete.ts +++ b/packages/backend/src/server/api/endpoints/clips/delete.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../define'; import { ApiError } from '../../error'; import { Clips } from '@/models/index'; @@ -11,12 +9,6 @@ export const meta = { kind: 'write:account', - params: { - clipId: { - validator: $.type(ID), - }, - }, - errors: { noSuchClip: { message: 'No such clip.', @@ -26,8 +18,16 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + clipId: { type: 'string', format: 'misskey:id' }, + }, + required: ['clipId'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const clip = await Clips.findOne({ id: ps.clipId, userId: user.id, diff --git a/packages/backend/src/server/api/endpoints/clips/list.ts b/packages/backend/src/server/api/endpoints/clips/list.ts index d88897d164..959b60e6f9 100644 --- a/packages/backend/src/server/api/endpoints/clips/list.ts +++ b/packages/backend/src/server/api/endpoints/clips/list.ts @@ -19,8 +19,14 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: {}, + required: [], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, me) => { +export default define(meta, paramDef, async (ps, me) => { const clips = await Clips.find({ userId: me.id, }); diff --git a/packages/backend/src/server/api/endpoints/clips/notes.ts b/packages/backend/src/server/api/endpoints/clips/notes.ts index eeb20631c1..dd76e396bf 100644 --- a/packages/backend/src/server/api/endpoints/clips/notes.ts +++ b/packages/backend/src/server/api/endpoints/clips/notes.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../define'; import { ClipNotes, Clips, Notes } from '@/models/index'; import { makePaginationQuery } from '../../common/make-pagination-query'; @@ -15,25 +13,6 @@ export const meta = { kind: 'read:account', - params: { - clipId: { - validator: $.type(ID), - }, - - limit: { - validator: $.optional.num.range(1, 100), - default: 10, - }, - - sinceId: { - validator: $.optional.type(ID), - }, - - untilId: { - validator: $.optional.type(ID), - }, - }, - errors: { noSuchClip: { message: 'No such clip.', @@ -53,8 +32,19 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + clipId: { 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' }, + }, + required: ['clipId'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const clip = await Clips.findOne({ id: ps.clipId, }); @@ -87,7 +77,7 @@ export default define(meta, async (ps, user) => { } const notes = await query - .take(ps.limit!) + .take(ps.limit) .getMany(); return await Notes.packMany(notes, user); diff --git a/packages/backend/src/server/api/endpoints/clips/show.ts b/packages/backend/src/server/api/endpoints/clips/show.ts index 0a45672019..7fe827a967 100644 --- a/packages/backend/src/server/api/endpoints/clips/show.ts +++ b/packages/backend/src/server/api/endpoints/clips/show.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../define'; import { ApiError } from '../../error'; import { Clips } from '@/models/index'; @@ -11,12 +9,6 @@ export const meta = { kind: 'read:account', - params: { - clipId: { - validator: $.type(ID), - }, - }, - errors: { noSuchClip: { message: 'No such clip.', @@ -32,8 +24,16 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + clipId: { type: 'string', format: 'misskey:id' }, + }, + required: ['clipId'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, me) => { +export default define(meta, paramDef, async (ps, me) => { // Fetch the clip const clip = await Clips.findOne({ id: ps.clipId, diff --git a/packages/backend/src/server/api/endpoints/clips/update.ts b/packages/backend/src/server/api/endpoints/clips/update.ts index 795483d5b2..b864497349 100644 --- a/packages/backend/src/server/api/endpoints/clips/update.ts +++ b/packages/backend/src/server/api/endpoints/clips/update.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../define'; import { ApiError } from '../../error'; import { Clips } from '@/models/index'; @@ -11,24 +9,6 @@ export const meta = { kind: 'write:account', - params: { - clipId: { - validator: $.type(ID), - }, - - name: { - validator: $.str.range(1, 100), - }, - - isPublic: { - validator: $.optional.bool, - }, - - description: { - validator: $.optional.nullable.str.range(1, 2048), - }, - }, - errors: { noSuchClip: { message: 'No such clip.', @@ -44,8 +24,19 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + clipId: { type: 'string', format: 'misskey:id' }, + name: { type: 'string', minLength: 1, maxLength: 100 }, + isPublic: { type: 'boolean' }, + description: { type: 'string', nullable: true, minLength: 1, maxLength: 2048 }, + }, + required: ['clipId', 'name'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { // Fetch the clip const clip = await Clips.findOne({ id: ps.clipId, diff --git a/packages/backend/src/server/api/endpoints/drive.ts b/packages/backend/src/server/api/endpoints/drive.ts index d9ab9883ca..5fc1f64cf0 100644 --- a/packages/backend/src/server/api/endpoints/drive.ts +++ b/packages/backend/src/server/api/endpoints/drive.ts @@ -25,8 +25,14 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: {}, + required: [], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const instance = await fetchMeta(true); // Calculate drive usage diff --git a/packages/backend/src/server/api/endpoints/drive/files.ts b/packages/backend/src/server/api/endpoints/drive/files.ts index a5c0a626a1..8ca9daed4d 100644 --- a/packages/backend/src/server/api/endpoints/drive/files.ts +++ b/packages/backend/src/server/api/endpoints/drive/files.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../define'; import { DriveFiles } from '@/models/index'; import { makePaginationQuery } from '../../common/make-pagination-query'; @@ -11,30 +9,6 @@ export const meta = { kind: 'read:drive', - params: { - limit: { - validator: $.optional.num.range(1, 100), - default: 10, - }, - - sinceId: { - validator: $.optional.type(ID), - }, - - untilId: { - validator: $.optional.type(ID), - }, - - folderId: { - validator: $.optional.nullable.type(ID), - default: null, - }, - - type: { - validator: $.optional.nullable.str.match(/^[a-zA-Z\/\-*]+$/), - }, - }, - res: { type: 'array', optional: false, nullable: false, @@ -46,8 +20,20 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, + sinceId: { type: 'string', format: 'misskey:id' }, + untilId: { type: 'string', format: 'misskey:id' }, + folderId: { type: 'string', format: 'misskey:id', nullable: true, default: null }, + type: { type: 'string', nullable: true, pattern: /^[a-zA-Z\/\-*]+$/.toString().slice(1, -1) }, + }, + required: [], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const query = makePaginationQuery(DriveFiles.createQueryBuilder('file'), ps.sinceId, ps.untilId) .andWhere('file.userId = :userId', { userId: user.id }); @@ -65,7 +51,7 @@ export default define(meta, async (ps, user) => { } } - const files = await query.take(ps.limit!).getMany(); + const files = await query.take(ps.limit).getMany(); return await DriveFiles.packMany(files, { detail: false, self: true }); }); diff --git a/packages/backend/src/server/api/endpoints/drive/files/attached-notes.ts b/packages/backend/src/server/api/endpoints/drive/files/attached-notes.ts index 835dde8058..20238c1b96 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/attached-notes.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/attached-notes.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../../define'; import { ApiError } from '../../../error'; import { DriveFiles, Notes } from '@/models/index'; @@ -11,12 +9,6 @@ export const meta = { kind: 'read:drive', - params: { - fileId: { - validator: $.type(ID), - }, - }, - res: { type: 'array', optional: false, nullable: false, @@ -36,8 +28,16 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + fileId: { type: 'string', format: 'misskey:id' }, + }, + required: ['fileId'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { // Fetch file const file = await DriveFiles.findOne({ id: ps.fileId, 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 a45d357ee8..8b7af3e54a 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 @@ -1,4 +1,3 @@ -import $ from 'cafy'; import define from '../../../define'; import { DriveFiles } from '@/models/index'; @@ -9,20 +8,22 @@ export const meta = { kind: 'read:drive', - params: { - md5: { - validator: $.str, - }, - }, - res: { type: 'boolean', optional: false, nullable: false, }, } as const; +const paramDef = { + type: 'object', + properties: { + md5: { type: 'string' }, + }, + required: ['md5'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const file = await DriveFiles.findOne({ md5: ps.md5, userId: user.id, diff --git a/packages/backend/src/server/api/endpoints/drive/files/create.ts b/packages/backend/src/server/api/endpoints/drive/files/create.ts index dd65ab0611..f5791f6ec7 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/create.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/create.ts @@ -1,6 +1,4 @@ import ms from 'ms'; -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import { addFile } from '@/services/drive/add-file'; import define from '../../../define'; import { apiLogger } from '../../../logger'; @@ -22,35 +20,6 @@ export const meta = { kind: 'write:drive', - params: { - folderId: { - validator: $.optional.nullable.type(ID), - default: null, - }, - - name: { - validator: $.optional.nullable.str, - default: null, - }, - - comment: { - validator: $.optional.nullable.str.max(DB_MAX_IMAGE_COMMENT_LENGTH), - default: null, - }, - - isSensitive: { - validator: $.optional.either($.bool, $.str), - default: false, - transform: (v: any): boolean => v === true || v === 'true', - }, - - force: { - validator: $.optional.either($.bool, $.str), - default: false, - transform: (v: any): boolean => v === true || v === 'true', - }, - }, - res: { type: 'object', optional: false, nullable: false, @@ -66,8 +35,20 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + folderId: { type: 'string', format: 'misskey:id', nullable: true, default: null }, + name: { type: 'string', nullable: true, default: null }, + comment: { type: 'string', nullable: true, maxLength: DB_MAX_IMAGE_COMMENT_LENGTH, default: null }, + isSensitive: { type: 'boolean', default: false }, + force: { type: 'boolean', default: false }, + }, + required: [], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user, _, file, cleanup) => { +export default define(meta, paramDef, async (ps, user, _, file, cleanup) => { // Get 'name' parameter let name = ps.name || file.originalname; if (name !== undefined && name !== null) { @@ -88,7 +69,9 @@ export default define(meta, async (ps, user, _, file, cleanup) => { const driveFile = await addFile({ user, path: file.path, name, comment: ps.comment, folderId: ps.folderId, force: ps.force, sensitive: ps.isSensitive }); return await DriveFiles.pack(driveFile, { self: true }); } catch (e) { - apiLogger.error(e); + if (e instanceof Error || typeof e === 'string') { + apiLogger.error(e); + } throw new ApiError(); } finally { cleanup!(); diff --git a/packages/backend/src/server/api/endpoints/drive/files/delete.ts b/packages/backend/src/server/api/endpoints/drive/files/delete.ts index 308beb58a4..664637a68f 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/delete.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/delete.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import { deleteFile } from '@/services/drive/delete-file'; import { publishDriveStream } from '@/services/stream'; import define from '../../../define'; @@ -13,12 +11,6 @@ export const meta = { kind: 'write:drive', - params: { - fileId: { - validator: $.type(ID), - }, - }, - errors: { noSuchFile: { message: 'No such file.', @@ -34,8 +26,16 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + fileId: { type: 'string', format: 'misskey:id' }, + }, + required: ['fileId'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const file = await DriveFiles.findOne(ps.fileId); if (file == null) { diff --git a/packages/backend/src/server/api/endpoints/drive/files/find-by-hash.ts b/packages/backend/src/server/api/endpoints/drive/files/find-by-hash.ts index dc74dcb7e6..98de790c0d 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/find-by-hash.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/find-by-hash.ts @@ -1,4 +1,3 @@ -import $ from 'cafy'; import define from '../../../define'; import { DriveFiles } from '@/models/index'; @@ -9,12 +8,6 @@ export const meta = { kind: 'read:drive', - params: { - md5: { - validator: $.str, - }, - }, - res: { type: 'array', optional: false, nullable: false, @@ -26,8 +19,16 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + md5: { type: 'string' }, + }, + required: ['md5'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const files = await DriveFiles.find({ md5: ps.md5, userId: user.id, diff --git a/packages/backend/src/server/api/endpoints/drive/files/find.ts b/packages/backend/src/server/api/endpoints/drive/files/find.ts index 2244df13cd..8d30c29da6 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/find.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/find.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../../define'; import { DriveFiles } from '@/models/index'; @@ -10,17 +8,6 @@ export const meta = { kind: 'read:drive', - params: { - name: { - validator: $.str, - }, - - folderId: { - validator: $.optional.nullable.type(ID), - default: null, - }, - }, - res: { type: 'array', optional: false, nullable: false, @@ -32,8 +19,17 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + name: { type: 'string' }, + folderId: { type: 'string', format: 'misskey:id', nullable: true, default: null }, + }, + required: ['name'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const files = await DriveFiles.find({ name: ps.name, userId: user.id, diff --git a/packages/backend/src/server/api/endpoints/drive/files/show.ts b/packages/backend/src/server/api/endpoints/drive/files/show.ts index 18b17c4653..8edb8785bc 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/show.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/show.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../../define'; import { ApiError } from '../../../error'; import { DriveFile } from '@/models/entities/drive-file'; @@ -12,16 +10,6 @@ export const meta = { kind: 'read:drive', - params: { - fileId: { - validator: $.optional.type(ID), - }, - - url: { - validator: $.optional.str, - }, - }, - res: { type: 'object', optional: false, nullable: false, @@ -49,8 +37,17 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + fileId: { type: 'string', format: 'misskey:id' }, + url: { type: 'string' }, + }, + required: [], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { let file: DriveFile | undefined; if (ps.fileId) { 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 b7ca80e83c..d77deea7db 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/update.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/update.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import { publishDriveStream } from '@/services/stream'; import define from '../../../define'; import { ApiError } from '../../../error'; @@ -13,33 +11,13 @@ export const meta = { kind: 'write:drive', - params: { - fileId: { - validator: $.type(ID), - }, - - folderId: { - validator: $.optional.nullable.type(ID), - default: undefined as any, - }, - - name: { - validator: $.optional.str.pipe(DriveFiles.validateFileName), - default: undefined as any, - }, - - isSensitive: { - validator: $.optional.bool, - default: undefined as any, - }, - - comment: { - validator: $.optional.nullable.str.max(DB_MAX_IMAGE_COMMENT_LENGTH), - default: undefined as any, - }, - }, - errors: { + invalidFileName: { + message: 'Invalid file name.', + code: 'INVALID_FILE_NAME', + id: '395e7156-f9f0-475e-af89-53c3c23080c2', + }, + noSuchFile: { message: 'No such file.', code: 'NO_SUCH_FILE', @@ -66,8 +44,20 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + fileId: { type: 'string', format: 'misskey:id' }, + folderId: { type: 'string', format: 'misskey:id', nullable: true }, + name: { type: 'string' }, + isSensitive: { type: 'boolean' }, + comment: { type: 'string', nullable: true, maxLength: 512 }, + }, + required: ['fileId'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const file = await DriveFiles.findOne(ps.fileId); if (file == null) { @@ -79,6 +69,9 @@ export default define(meta, async (ps, user) => { } if (ps.name) file.name = ps.name; + if (!DriveFiles.validateFileName(file.name)) { + throw new ApiError(meta.errors.invalidFileName); + } if (ps.comment !== undefined) file.comment = ps.comment; diff --git a/packages/backend/src/server/api/endpoints/drive/files/upload-from-url.ts b/packages/backend/src/server/api/endpoints/drive/files/upload-from-url.ts index 40da1a4fb4..a4619ebf36 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/upload-from-url.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/upload-from-url.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import ms from 'ms'; import { uploadFromUrl } from '@/services/drive/upload-from-url'; import define from '../../../define'; @@ -18,42 +16,23 @@ export const meta = { requireCredential: true, kind: 'write:drive', +} as const; - params: { - url: { - // TODO: Validate this url - validator: $.str, - }, - - folderId: { - validator: $.optional.nullable.type(ID), - default: null, - }, - - isSensitive: { - validator: $.optional.bool, - default: false, - }, - - comment: { - validator: $.optional.nullable.str.max(DB_MAX_IMAGE_COMMENT_LENGTH), - default: null, - }, - - marker: { - validator: $.optional.nullable.str, - default: null, - }, - - force: { - validator: $.optional.bool, - default: false, - }, +const paramDef = { + type: 'object', + properties: { + url: { type: 'string' }, + folderId: { type: 'string', format: 'misskey:id', nullable: true, default: null }, + isSensitive: { type: 'boolean', default: false }, + comment: { type: 'string', nullable: true, maxLength: 512, default: null }, + marker: { type: 'string', nullable: true, default: null }, + force: { type: 'boolean', default: false }, }, + required: ['url'], } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { uploadFromUrl({ url: ps.url, user, folderId: ps.folderId, sensitive: ps.isSensitive, force: ps.force, comment: ps.comment }).then(file => { DriveFiles.pack(file, { self: true }).then(packedFile => { publishMainStream(user.id, 'urlUploadFinished', { diff --git a/packages/backend/src/server/api/endpoints/drive/folders.ts b/packages/backend/src/server/api/endpoints/drive/folders.ts index 8f8d1d2c0a..a543ae27df 100644 --- a/packages/backend/src/server/api/endpoints/drive/folders.ts +++ b/packages/backend/src/server/api/endpoints/drive/folders.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../define'; import { DriveFolders } from '@/models/index'; import { makePaginationQuery } from '../../common/make-pagination-query'; @@ -11,26 +9,6 @@ export const meta = { kind: 'read:drive', - params: { - limit: { - validator: $.optional.num.range(1, 100), - default: 10, - }, - - sinceId: { - validator: $.optional.type(ID), - }, - - untilId: { - validator: $.optional.type(ID), - }, - - folderId: { - validator: $.optional.nullable.type(ID), - default: null, - }, - }, - res: { type: 'array', optional: false, nullable: false, @@ -42,8 +20,19 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, + sinceId: { type: 'string', format: 'misskey:id' }, + untilId: { type: 'string', format: 'misskey:id' }, + folderId: { type: 'string', format: 'misskey:id', nullable: true, default: null }, + }, + required: [], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const query = makePaginationQuery(DriveFolders.createQueryBuilder('folder'), ps.sinceId, ps.untilId) .andWhere('folder.userId = :userId', { userId: user.id }); @@ -53,7 +42,7 @@ export default define(meta, async (ps, user) => { query.andWhere('folder.parentId IS NULL'); } - const folders = await query.take(ps.limit!).getMany(); + const folders = await query.take(ps.limit).getMany(); return await Promise.all(folders.map(folder => DriveFolders.pack(folder))); }); diff --git a/packages/backend/src/server/api/endpoints/drive/folders/create.ts b/packages/backend/src/server/api/endpoints/drive/folders/create.ts index 38ed17e0e5..ab48d5599e 100644 --- a/packages/backend/src/server/api/endpoints/drive/folders/create.ts +++ b/packages/backend/src/server/api/endpoints/drive/folders/create.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import { publishDriveStream } from '@/services/stream'; import define from '../../../define'; import { ApiError } from '../../../error'; @@ -13,17 +11,6 @@ export const meta = { kind: 'write:drive', - params: { - name: { - validator: $.optional.str.pipe(DriveFolders.validateFolderName), - default: 'Untitled', - }, - - parentId: { - validator: $.optional.nullable.type(ID), - }, - }, - errors: { noSuchFolder: { message: 'No such folder.', @@ -39,8 +26,17 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + name: { type: 'string', default: "Untitled", maxLength: 200 }, + parentId: { type: 'string', format: 'misskey:id', nullable: true }, + }, + required: [], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { // If the parent folder is specified let parent = null; if (ps.parentId) { diff --git a/packages/backend/src/server/api/endpoints/drive/folders/delete.ts b/packages/backend/src/server/api/endpoints/drive/folders/delete.ts index 13716fccea..51c0340529 100644 --- a/packages/backend/src/server/api/endpoints/drive/folders/delete.ts +++ b/packages/backend/src/server/api/endpoints/drive/folders/delete.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../../define'; import { publishDriveStream } from '@/services/stream'; import { ApiError } from '../../../error'; @@ -12,12 +10,6 @@ export const meta = { kind: 'write:drive', - params: { - folderId: { - validator: $.type(ID), - }, - }, - errors: { noSuchFolder: { message: 'No such folder.', @@ -33,8 +25,16 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + folderId: { type: 'string', format: 'misskey:id' }, + }, + required: ['folderId'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { // Get folder const folder = await DriveFolders.findOne({ id: ps.folderId, diff --git a/packages/backend/src/server/api/endpoints/drive/folders/find.ts b/packages/backend/src/server/api/endpoints/drive/folders/find.ts index 911f51d78b..057f40b1cc 100644 --- a/packages/backend/src/server/api/endpoints/drive/folders/find.ts +++ b/packages/backend/src/server/api/endpoints/drive/folders/find.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../../define'; import { DriveFolders } from '@/models/index'; @@ -10,17 +8,6 @@ export const meta = { kind: 'read:drive', - params: { - name: { - validator: $.str, - }, - - parentId: { - validator: $.optional.nullable.type(ID), - default: null, - }, - }, - res: { type: 'array', optional: false, nullable: false, @@ -32,8 +19,17 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + name: { type: 'string' }, + parentId: { type: 'string', format: 'misskey:id', nullable: true, default: null }, + }, + required: ['name'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const folders = await DriveFolders.find({ name: ps.name, userId: user.id, diff --git a/packages/backend/src/server/api/endpoints/drive/folders/show.ts b/packages/backend/src/server/api/endpoints/drive/folders/show.ts index 58a6dd3c06..d31d7a4276 100644 --- a/packages/backend/src/server/api/endpoints/drive/folders/show.ts +++ b/packages/backend/src/server/api/endpoints/drive/folders/show.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../../define'; import { ApiError } from '../../../error'; import { DriveFolders } from '@/models/index'; @@ -11,12 +9,6 @@ export const meta = { kind: 'read:drive', - params: { - folderId: { - validator: $.type(ID), - }, - }, - res: { type: 'object', optional: false, nullable: false, @@ -32,8 +24,16 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + folderId: { type: 'string', format: 'misskey:id' }, + }, + required: ['folderId'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { // Get folder const folder = await DriveFolders.findOne({ id: ps.folderId, diff --git a/packages/backend/src/server/api/endpoints/drive/folders/update.ts b/packages/backend/src/server/api/endpoints/drive/folders/update.ts index 5b0cccd1c6..c70b5a0d54 100644 --- a/packages/backend/src/server/api/endpoints/drive/folders/update.ts +++ b/packages/backend/src/server/api/endpoints/drive/folders/update.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import { publishDriveStream } from '@/services/stream'; import define from '../../../define'; import { ApiError } from '../../../error'; @@ -12,20 +10,6 @@ export const meta = { kind: 'write:drive', - params: { - folderId: { - validator: $.type(ID), - }, - - name: { - validator: $.optional.str.pipe(DriveFolders.validateFolderName), - }, - - parentId: { - validator: $.optional.nullable.type(ID), - }, - }, - errors: { noSuchFolder: { message: 'No such folder.', @@ -53,8 +37,18 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + folderId: { type: 'string', format: 'misskey:id' }, + name: { type: 'string', maxLength: 200 }, + parentId: { type: 'string', format: 'misskey:id', nullable: true }, + }, + required: ['folderId'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { // Fetch folder const folder = await DriveFolders.findOne({ id: ps.folderId, diff --git a/packages/backend/src/server/api/endpoints/drive/stream.ts b/packages/backend/src/server/api/endpoints/drive/stream.ts index 9ba7804946..921ae4c221 100644 --- a/packages/backend/src/server/api/endpoints/drive/stream.ts +++ b/packages/backend/src/server/api/endpoints/drive/stream.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../define'; import { DriveFiles } from '@/models/index'; import { makePaginationQuery } from '../../common/make-pagination-query'; @@ -11,25 +9,6 @@ export const meta = { kind: 'read:drive', - params: { - limit: { - validator: $.optional.num.range(1, 100), - default: 10, - }, - - sinceId: { - validator: $.optional.type(ID), - }, - - untilId: { - validator: $.optional.type(ID), - }, - - type: { - validator: $.optional.str.match(/^[a-zA-Z\/\-*]+$/), - }, - }, - res: { type: 'array', optional: false, nullable: false, @@ -41,8 +20,19 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, + sinceId: { type: 'string', format: 'misskey:id' }, + untilId: { type: 'string', format: 'misskey:id' }, + type: { type: 'string', pattern: /^[a-zA-Z\/\-*]+$/.toString().slice(1, -1) }, + }, + required: [], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const query = makePaginationQuery(DriveFiles.createQueryBuilder('file'), ps.sinceId, ps.untilId) .andWhere('file.userId = :userId', { userId: user.id }); @@ -54,7 +44,7 @@ export default define(meta, async (ps, user) => { } } - const files = await query.take(ps.limit!).getMany(); + const files = await query.take(ps.limit).getMany(); return await DriveFiles.packMany(files, { detail: false, self: true }); }); diff --git a/packages/backend/src/server/api/endpoints/email-address/available.ts b/packages/backend/src/server/api/endpoints/email-address/available.ts index 19f9b7ccdc..cb740ffae7 100644 --- a/packages/backend/src/server/api/endpoints/email-address/available.ts +++ b/packages/backend/src/server/api/endpoints/email-address/available.ts @@ -1,4 +1,3 @@ -import $ from 'cafy'; import define from '../../define'; import { validateEmailForAccount } from '@/services/validate-email-for-account'; @@ -7,12 +6,6 @@ export const meta = { requireCredential: false, - params: { - emailAddress: { - validator: $.str, - }, - }, - res: { type: 'object', optional: false, nullable: false, @@ -29,7 +22,15 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + emailAddress: { type: 'string' }, + }, + required: ['emailAddress'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps) => { +export default define(meta, paramDef, async (ps) => { return await validateEmailForAccount(ps.emailAddress); }); diff --git a/packages/backend/src/server/api/endpoints/endpoint.ts b/packages/backend/src/server/api/endpoints/endpoint.ts index 42fd468838..b8fe030e8c 100644 --- a/packages/backend/src/server/api/endpoints/endpoint.ts +++ b/packages/backend/src/server/api/endpoints/endpoint.ts @@ -1,4 +1,3 @@ -import $ from 'cafy'; import define from '../define'; import endpoints from '../endpoints'; @@ -6,16 +5,18 @@ export const meta = { requireCredential: false, tags: ['meta'], +} as const; - params: { - endpoint: { - validator: $.str, - }, +const paramDef = { + type: 'object', + properties: { + endpoint: { type: 'string' }, }, + required: ['endpoint'], } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps) => { +export default define(meta, paramDef, async (ps) => { const ep = endpoints.find(x => x.name === ps.endpoint); if (ep == null) return null; return { diff --git a/packages/backend/src/server/api/endpoints/endpoints.ts b/packages/backend/src/server/api/endpoints/endpoints.ts index ebb78de337..b090e0dced 100644 --- a/packages/backend/src/server/api/endpoints/endpoints.ts +++ b/packages/backend/src/server/api/endpoints/endpoints.ts @@ -6,9 +6,6 @@ export const meta = { tags: ['meta'], - params: { - }, - res: { type: 'array', optional: false, nullable: false, @@ -25,7 +22,13 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: {}, + required: [], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async () => { +export default define(meta, paramDef, async () => { return endpoints.map(x => x.name); }); diff --git a/packages/backend/src/server/api/endpoints/export-custom-emojis.ts b/packages/backend/src/server/api/endpoints/export-custom-emojis.ts index 24c9f56aa6..f0ab63f74b 100644 --- a/packages/backend/src/server/api/endpoints/export-custom-emojis.ts +++ b/packages/backend/src/server/api/endpoints/export-custom-emojis.ts @@ -1,4 +1,3 @@ -import $ from 'cafy'; import define from '../define'; import { createExportCustomEmojisJob } from '@/queue/index'; import ms from 'ms'; @@ -12,7 +11,13 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: {}, + required: [], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { createExportCustomEmojisJob(user); }); diff --git a/packages/backend/src/server/api/endpoints/federation/followers.ts b/packages/backend/src/server/api/endpoints/federation/followers.ts index c0a85f166c..5960e482c0 100644 --- a/packages/backend/src/server/api/endpoints/federation/followers.ts +++ b/packages/backend/src/server/api/endpoints/federation/followers.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../define'; import { Followings } from '@/models/index'; import { makePaginationQuery } from '../../common/make-pagination-query'; @@ -9,25 +7,6 @@ export const meta = { requireCredential: false, - params: { - host: { - validator: $.str, - }, - - sinceId: { - validator: $.optional.type(ID), - }, - - untilId: { - validator: $.optional.type(ID), - }, - - limit: { - validator: $.optional.num.range(1, 100), - default: 10, - }, - }, - res: { type: 'array', optional: false, nullable: false, @@ -39,13 +18,24 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + host: { type: 'string' }, + sinceId: { type: 'string', format: 'misskey:id' }, + untilId: { type: 'string', format: 'misskey:id' }, + limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, + }, + required: ['host'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, me) => { +export default define(meta, paramDef, async (ps, me) => { const query = makePaginationQuery(Followings.createQueryBuilder('following'), ps.sinceId, ps.untilId) .andWhere(`following.followeeHost = :host`, { host: ps.host }); const followings = await query - .take(ps.limit!) + .take(ps.limit) .getMany(); return await Followings.packMany(followings, me, { populateFollowee: true }); diff --git a/packages/backend/src/server/api/endpoints/federation/following.ts b/packages/backend/src/server/api/endpoints/federation/following.ts index 147f0aedb2..4f139b0c45 100644 --- a/packages/backend/src/server/api/endpoints/federation/following.ts +++ b/packages/backend/src/server/api/endpoints/federation/following.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../define'; import { Followings } from '@/models/index'; import { makePaginationQuery } from '../../common/make-pagination-query'; @@ -9,25 +7,6 @@ export const meta = { requireCredential: false, - params: { - host: { - validator: $.str, - }, - - sinceId: { - validator: $.optional.type(ID), - }, - - untilId: { - validator: $.optional.type(ID), - }, - - limit: { - validator: $.optional.num.range(1, 100), - default: 10, - }, - }, - res: { type: 'array', optional: false, nullable: false, @@ -39,13 +18,24 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + host: { type: 'string' }, + sinceId: { type: 'string', format: 'misskey:id' }, + untilId: { type: 'string', format: 'misskey:id' }, + limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, + }, + required: ['host'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, me) => { +export default define(meta, paramDef, async (ps, me) => { const query = makePaginationQuery(Followings.createQueryBuilder('following'), ps.sinceId, ps.untilId) .andWhere(`following.followerHost = :host`, { host: ps.host }); const followings = await query - .take(ps.limit!) + .take(ps.limit) .getMany(); return await Followings.packMany(followings, me, { populateFollowee: true }); diff --git a/packages/backend/src/server/api/endpoints/federation/instances.ts b/packages/backend/src/server/api/endpoints/federation/instances.ts index 11df7ed6b6..494b5120ab 100644 --- a/packages/backend/src/server/api/endpoints/federation/instances.ts +++ b/packages/backend/src/server/api/endpoints/federation/instances.ts @@ -1,4 +1,3 @@ -import $ from 'cafy'; import config from '@/config/index'; import define from '../../define'; import { Instances } from '@/models/index'; @@ -9,50 +8,6 @@ export const meta = { requireCredential: false, - params: { - host: { - validator: $.optional.nullable.str, - }, - - blocked: { - validator: $.optional.nullable.bool, - }, - - notResponding: { - validator: $.optional.nullable.bool, - }, - - suspended: { - validator: $.optional.nullable.bool, - }, - - federating: { - validator: $.optional.nullable.bool, - }, - - subscribing: { - validator: $.optional.nullable.bool, - }, - - publishing: { - validator: $.optional.nullable.bool, - }, - - limit: { - validator: $.optional.num.range(1, 100), - default: 30, - }, - - offset: { - validator: $.optional.num.min(0), - default: 0, - }, - - sort: { - validator: $.optional.str, - }, - }, - res: { type: 'array', optional: false, nullable: false, @@ -64,8 +19,25 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + host: { type: 'string', nullable: true }, + blocked: { type: 'boolean', nullable: true }, + notResponding: { type: 'boolean', nullable: true }, + suspended: { type: 'boolean', nullable: true }, + federating: { type: 'boolean', nullable: true }, + subscribing: { type: 'boolean', nullable: true }, + publishing: { type: 'boolean', nullable: true }, + limit: { type: 'integer', minimum: 1, maximum: 100, default: 30 }, + offset: { type: 'integer', default: 0 }, + sort: { type: 'string' }, + }, + required: [], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, me) => { +export default define(meta, paramDef, async (ps, me) => { const query = Instances.createQueryBuilder('instance'); switch (ps.sort) { @@ -144,7 +116,7 @@ export default define(meta, async (ps, me) => { query.andWhere('instance.host like :host', { host: '%' + ps.host.toLowerCase() + '%' }); } - const instances = await query.take(ps.limit!).skip(ps.offset).getMany(); + const instances = await query.take(ps.limit).skip(ps.offset).getMany(); - return instances; + return await Instances.packMany(instances); }); diff --git a/packages/backend/src/server/api/endpoints/federation/show-instance.ts b/packages/backend/src/server/api/endpoints/federation/show-instance.ts index 6f13b28cae..cead515d81 100644 --- a/packages/backend/src/server/api/endpoints/federation/show-instance.ts +++ b/packages/backend/src/server/api/endpoints/federation/show-instance.ts @@ -1,4 +1,3 @@ -import $ from 'cafy'; import define from '../../define'; import { Instances } from '@/models/index'; import { toPuny } from '@/misc/convert-host'; @@ -8,23 +7,28 @@ export const meta = { requireCredential: false, - params: { - host: { - validator: $.str, - }, - }, - res: { - type: 'object', - optional: true, nullable: false, - ref: 'FederationInstance', + oneOf: [{ + type: 'object', + ref: 'FederationInstance', + }, { + type: 'null', + }], }, } as const; +const paramDef = { + type: 'object', + properties: { + host: { type: 'string' }, + }, + required: ['host'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, me) => { +export default define(meta, paramDef, async (ps, me) => { const instance = await Instances .findOne({ host: toPuny(ps.host) }); - return instance; + return instance ? await Instances.pack(instance) : null; }); diff --git a/packages/backend/src/server/api/endpoints/federation/update-remote-user.ts b/packages/backend/src/server/api/endpoints/federation/update-remote-user.ts index 092f805bc2..ebdc54d0a6 100644 --- a/packages/backend/src/server/api/endpoints/federation/update-remote-user.ts +++ b/packages/backend/src/server/api/endpoints/federation/update-remote-user.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../define'; import { getRemoteUser } from '../../common/getters'; import { updatePerson } from '@/remote/activitypub/models/person'; @@ -8,16 +6,18 @@ export const meta = { tags: ['federation'], requireCredential: true, +} as const; - params: { - userId: { - validator: $.type(ID), - }, +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, async (ps) => { +export default define(meta, paramDef, async (ps) => { const user = await getRemoteUser(ps.userId); await updatePerson(user.uri!); }); diff --git a/packages/backend/src/server/api/endpoints/federation/users.ts b/packages/backend/src/server/api/endpoints/federation/users.ts index 9a8f749936..294894cd92 100644 --- a/packages/backend/src/server/api/endpoints/federation/users.ts +++ b/packages/backend/src/server/api/endpoints/federation/users.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../define'; import { Users } from '@/models/index'; import { makePaginationQuery } from '../../common/make-pagination-query'; @@ -9,25 +7,6 @@ export const meta = { requireCredential: false, - params: { - host: { - validator: $.str, - }, - - sinceId: { - validator: $.optional.type(ID), - }, - - untilId: { - validator: $.optional.type(ID), - }, - - limit: { - validator: $.optional.num.range(1, 100), - default: 10, - }, - }, - res: { type: 'array', optional: false, nullable: false, @@ -39,13 +18,24 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + host: { type: 'string' }, + sinceId: { type: 'string', format: 'misskey:id' }, + untilId: { type: 'string', format: 'misskey:id' }, + limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, + }, + required: ['host'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, me) => { +export default define(meta, paramDef, async (ps, me) => { const query = makePaginationQuery(Users.createQueryBuilder('user'), ps.sinceId, ps.untilId) .andWhere(`user.host = :host`, { host: ps.host }); const users = await query - .take(ps.limit!) + .take(ps.limit) .getMany(); return await Users.packMany(users, me, { detail: true }); diff --git a/packages/backend/src/server/api/endpoints/following/create.ts b/packages/backend/src/server/api/endpoints/following/create.ts index 96aede4550..1edb3517a0 100644 --- a/packages/backend/src/server/api/endpoints/following/create.ts +++ b/packages/backend/src/server/api/endpoints/following/create.ts @@ -1,11 +1,10 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import ms from 'ms'; import create from '@/services/following/create'; import define from '../../define'; import { ApiError } from '../../error'; import { getUser } from '../../common/getters'; import { Followings, Users } from '@/models/index'; +import { IdentifiableError } from '@/misc/identifiable-error'; export const meta = { tags: ['following', 'users'], @@ -19,12 +18,6 @@ export const meta = { kind: 'write:following', - params: { - userId: { - validator: $.type(ID), - }, - }, - errors: { noSuchUser: { message: 'No such user.', @@ -64,8 +57,16 @@ export const meta = { }, } as const; +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, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const follower = user; // č‡Ē分č‡ĒčēĢ @@ -92,8 +93,10 @@ export default define(meta, async (ps, user) => { try { await create(follower, followee); } catch (e) { - if (e.id === '710e8fb0-b8c3-4922-be49-d5d93d8e6a6e') throw new ApiError(meta.errors.blocking); - if (e.id === '3338392a-f764-498d-8855-db939dcf8c48') throw new ApiError(meta.errors.blocked); + if (e instanceof IdentifiableError) { + if (e.id === '710e8fb0-b8c3-4922-be49-d5d93d8e6a6e') throw new ApiError(meta.errors.blocking); + if (e.id === '3338392a-f764-498d-8855-db939dcf8c48') throw new ApiError(meta.errors.blocked); + } throw e; } diff --git a/packages/backend/src/server/api/endpoints/following/delete.ts b/packages/backend/src/server/api/endpoints/following/delete.ts index 4cd0c49452..2eb8636816 100644 --- a/packages/backend/src/server/api/endpoints/following/delete.ts +++ b/packages/backend/src/server/api/endpoints/following/delete.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import ms from 'ms'; import deleteFollowing from '@/services/following/delete'; import define from '../../define'; @@ -19,12 +17,6 @@ export const meta = { kind: 'write:following', - params: { - userId: { - validator: $.type(ID), - }, - }, - errors: { noSuchUser: { message: 'No such user.', @@ -52,8 +44,16 @@ export const meta = { }, } as const; +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, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const follower = user; // Check if the followee is yourself diff --git a/packages/backend/src/server/api/endpoints/following/invalidate.ts b/packages/backend/src/server/api/endpoints/following/invalidate.ts index 92e887e00b..8a1da3d299 100644 --- a/packages/backend/src/server/api/endpoints/following/invalidate.ts +++ b/packages/backend/src/server/api/endpoints/following/invalidate.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import * as ms from 'ms'; import deleteFollowing from '@/services/following/delete'; import define from '../../define'; @@ -19,12 +17,6 @@ export const meta = { kind: 'write:following', - params: { - userId: { - validator: $.type(ID), - }, - }, - errors: { noSuchUser: { message: 'No such user.', @@ -52,8 +44,16 @@ export const meta = { }, } as const; +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, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const followee = user; // Check if the follower is yourself diff --git a/packages/backend/src/server/api/endpoints/following/requests/accept.ts b/packages/backend/src/server/api/endpoints/following/requests/accept.ts index 7e7c056f55..7040efcc80 100644 --- a/packages/backend/src/server/api/endpoints/following/requests/accept.ts +++ b/packages/backend/src/server/api/endpoints/following/requests/accept.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import acceptFollowRequest from '@/services/following/requests/accept'; import define from '../../../define'; import { ApiError } from '../../../error'; @@ -12,12 +10,6 @@ export const meta = { kind: 'write:following', - params: { - userId: { - validator: $.type(ID), - }, - }, - errors: { noSuchUser: { message: 'No such user.', @@ -32,8 +24,16 @@ export const meta = { }, } as const; +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, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { // Fetch follower const follower = await getUser(ps.userId).catch(e => { if (e.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); diff --git a/packages/backend/src/server/api/endpoints/following/requests/cancel.ts b/packages/backend/src/server/api/endpoints/following/requests/cancel.ts index 19ed02c152..4204f8526a 100644 --- a/packages/backend/src/server/api/endpoints/following/requests/cancel.ts +++ b/packages/backend/src/server/api/endpoints/following/requests/cancel.ts @@ -1,10 +1,9 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import cancelFollowRequest from '@/services/following/requests/cancel'; import define from '../../../define'; import { ApiError } from '../../../error'; import { getUser } from '../../../common/getters'; import { Users } from '@/models/index'; +import { IdentifiableError } from '@/misc/identifiable-error'; export const meta = { tags: ['following', 'account'], @@ -13,12 +12,6 @@ export const meta = { kind: 'write:following', - params: { - userId: { - validator: $.type(ID), - }, - }, - errors: { noSuchUser: { message: 'No such user.', @@ -40,8 +33,16 @@ export const meta = { }, } as const; +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, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { // Fetch followee const followee = await getUser(ps.userId).catch(e => { if (e.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); @@ -51,7 +52,9 @@ export default define(meta, async (ps, user) => { try { await cancelFollowRequest(followee, user); } catch (e) { - if (e.id === '17447091-ce07-46dd-b331-c1fd4f15b1e7') throw new ApiError(meta.errors.followRequestNotFound); + if (e instanceof IdentifiableError) { + if (e.id === '17447091-ce07-46dd-b331-c1fd4f15b1e7') throw new ApiError(meta.errors.followRequestNotFound); + } throw e; } diff --git a/packages/backend/src/server/api/endpoints/following/requests/list.ts b/packages/backend/src/server/api/endpoints/following/requests/list.ts index ec0c76502c..17d576d1c0 100644 --- a/packages/backend/src/server/api/endpoints/following/requests/list.ts +++ b/packages/backend/src/server/api/endpoints/following/requests/list.ts @@ -35,8 +35,14 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: {}, + required: [], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const reqs = await FollowRequests.find({ followeeId: user.id, }); diff --git a/packages/backend/src/server/api/endpoints/following/requests/reject.ts b/packages/backend/src/server/api/endpoints/following/requests/reject.ts index a5ce1e7c77..9e345aa15f 100644 --- a/packages/backend/src/server/api/endpoints/following/requests/reject.ts +++ b/packages/backend/src/server/api/endpoints/following/requests/reject.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import { rejectFollowRequest } from '@/services/following/reject'; import define from '../../../define'; import { ApiError } from '../../../error'; @@ -12,12 +10,6 @@ export const meta = { kind: 'write:following', - params: { - userId: { - validator: $.type(ID), - }, - }, - errors: { noSuchUser: { message: 'No such user.', @@ -27,8 +19,16 @@ export const meta = { }, } as const; +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, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { // Fetch follower const follower = await getUser(ps.userId).catch(e => { if (e.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); diff --git a/packages/backend/src/server/api/endpoints/gallery/featured.ts b/packages/backend/src/server/api/endpoints/gallery/featured.ts index ff7c16889f..6b347f9dea 100644 --- a/packages/backend/src/server/api/endpoints/gallery/featured.ts +++ b/packages/backend/src/server/api/endpoints/gallery/featured.ts @@ -17,8 +17,14 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: {}, + required: [], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, me) => { +export default define(meta, paramDef, async (ps, me) => { const query = GalleryPosts.createQueryBuilder('post') .andWhere('post.createdAt > :date', { date: new Date(Date.now() - (1000 * 60 * 60 * 24 * 3)) }) .andWhere('post.likedCount > 0') diff --git a/packages/backend/src/server/api/endpoints/gallery/popular.ts b/packages/backend/src/server/api/endpoints/gallery/popular.ts index 2c3368a19d..57ead79826 100644 --- a/packages/backend/src/server/api/endpoints/gallery/popular.ts +++ b/packages/backend/src/server/api/endpoints/gallery/popular.ts @@ -17,8 +17,14 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: {}, + required: [], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, me) => { +export default define(meta, paramDef, async (ps, me) => { const query = GalleryPosts.createQueryBuilder('post') .andWhere('post.likedCount > 0') .orderBy('post.likedCount', 'DESC'); diff --git a/packages/backend/src/server/api/endpoints/gallery/posts.ts b/packages/backend/src/server/api/endpoints/gallery/posts.ts index 9d2601c7e9..c36e80b83c 100644 --- a/packages/backend/src/server/api/endpoints/gallery/posts.ts +++ b/packages/backend/src/server/api/endpoints/gallery/posts.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../define'; import { makePaginationQuery } from '../../common/make-pagination-query'; import { GalleryPosts } from '@/models/index'; @@ -7,21 +5,6 @@ import { GalleryPosts } from '@/models/index'; export const meta = { tags: ['gallery'], - params: { - limit: { - validator: $.optional.num.range(1, 100), - default: 10, - }, - - sinceId: { - validator: $.optional.type(ID), - }, - - untilId: { - validator: $.optional.type(ID), - }, - }, - res: { type: 'array', optional: false, nullable: false, @@ -33,12 +16,22 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, + 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, async (ps, me) => { +export default define(meta, paramDef, async (ps, me) => { const query = makePaginationQuery(GalleryPosts.createQueryBuilder('post'), ps.sinceId, ps.untilId) .innerJoinAndSelect('post.user', 'user'); - const posts = await query.take(ps.limit!).getMany(); + const posts = await query.take(ps.limit).getMany(); return await GalleryPosts.packMany(posts, me); }); diff --git a/packages/backend/src/server/api/endpoints/gallery/posts/create.ts b/packages/backend/src/server/api/endpoints/gallery/posts/create.ts index e9d5df1ab6..d64937d0bc 100644 --- a/packages/backend/src/server/api/endpoints/gallery/posts/create.ts +++ b/packages/backend/src/server/api/endpoints/gallery/posts/create.ts @@ -1,7 +1,5 @@ -import $ from 'cafy'; import ms from 'ms'; import define from '../../../define'; -import { ID } from '../../../../../misc/cafy-id'; import { DriveFiles, GalleryPosts } from '@/models/index'; import { genId } from '../../../../../misc/gen-id'; import { GalleryPost } from '@/models/entities/gallery-post'; @@ -20,25 +18,6 @@ export const meta = { max: 300, }, - params: { - title: { - validator: $.str.min(1), - }, - - description: { - validator: $.optional.nullable.str, - }, - - fileIds: { - validator: $.arr($.type(ID)).unique().range(1, 32), - }, - - isSensitive: { - validator: $.optional.bool, - default: false, - }, - }, - res: { type: 'object', optional: false, nullable: false, @@ -50,8 +29,21 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + title: { type: 'string', minLength: 1 }, + description: { type: 'string', nullable: true }, + fileIds: { type: 'array', uniqueItems: true, minItems: 1, maxItems: 32, items: { + type: 'string', format: 'misskey:id', + } }, + isSensitive: { type: 'boolean', default: false }, + }, + required: ['title', 'fileIds'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const files = (await Promise.all(ps.fileIds.map(fileId => DriveFiles.findOne({ id: fileId, diff --git a/packages/backend/src/server/api/endpoints/gallery/posts/delete.ts b/packages/backend/src/server/api/endpoints/gallery/posts/delete.ts index 2a13b9ed58..82ffd62d60 100644 --- a/packages/backend/src/server/api/endpoints/gallery/posts/delete.ts +++ b/packages/backend/src/server/api/endpoints/gallery/posts/delete.ts @@ -1,8 +1,6 @@ -import $ from 'cafy'; import define from '../../../define'; import { ApiError } from '../../../error'; import { GalleryPosts } from '@/models/index'; -import { ID } from '@/misc/cafy-id'; export const meta = { tags: ['gallery'], @@ -11,12 +9,6 @@ export const meta = { kind: 'write:gallery', - params: { - postId: { - validator: $.type(ID), - }, - }, - errors: { noSuchPost: { message: 'No such post.', @@ -26,8 +18,16 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + postId: { type: 'string', format: 'misskey:id' }, + }, + required: ['postId'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const post = await GalleryPosts.findOne({ id: ps.postId, userId: user.id, 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 0fb408fa5f..73d5a0ea99 100644 --- a/packages/backend/src/server/api/endpoints/gallery/posts/like.ts +++ b/packages/backend/src/server/api/endpoints/gallery/posts/like.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../../define'; import { ApiError } from '../../../error'; import { GalleryPosts, GalleryLikes } from '@/models/index'; @@ -12,12 +10,6 @@ export const meta = { kind: 'write:gallery-likes', - params: { - postId: { - validator: $.type(ID), - }, - }, - errors: { noSuchPost: { message: 'No such post.', @@ -39,8 +31,16 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + postId: { type: 'string', format: 'misskey:id' }, + }, + required: ['postId'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const post = await GalleryPosts.findOne(ps.postId); if (post == null) { throw new ApiError(meta.errors.noSuchPost); diff --git a/packages/backend/src/server/api/endpoints/gallery/posts/show.ts b/packages/backend/src/server/api/endpoints/gallery/posts/show.ts index 4325d2ad37..819d360b14 100644 --- a/packages/backend/src/server/api/endpoints/gallery/posts/show.ts +++ b/packages/backend/src/server/api/endpoints/gallery/posts/show.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../../define'; import { ApiError } from '../../../error'; import { GalleryPosts } from '@/models/index'; @@ -9,12 +7,6 @@ export const meta = { requireCredential: false, - params: { - postId: { - validator: $.type(ID), - }, - }, - errors: { noSuchPost: { message: 'No such post.', @@ -30,8 +22,16 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + postId: { type: 'string', format: 'misskey:id' }, + }, + required: ['postId'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, me) => { +export default define(meta, paramDef, async (ps, me) => { const post = await GalleryPosts.findOne({ id: ps.postId, }); 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 9cca09bddc..fc38174810 100644 --- a/packages/backend/src/server/api/endpoints/gallery/posts/unlike.ts +++ b/packages/backend/src/server/api/endpoints/gallery/posts/unlike.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../../define'; import { ApiError } from '../../../error'; import { GalleryPosts, GalleryLikes } from '@/models/index'; @@ -11,12 +9,6 @@ export const meta = { kind: 'write:gallery-likes', - params: { - postId: { - validator: $.type(ID), - }, - }, - errors: { noSuchPost: { message: 'No such post.', @@ -32,8 +24,16 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + postId: { type: 'string', format: 'misskey:id' }, + }, + required: ['postId'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const post = await GalleryPosts.findOne(ps.postId); if (post == null) { throw new ApiError(meta.errors.noSuchPost); diff --git a/packages/backend/src/server/api/endpoints/gallery/posts/update.ts b/packages/backend/src/server/api/endpoints/gallery/posts/update.ts index c35e1bbf58..1a86b56bc6 100644 --- a/packages/backend/src/server/api/endpoints/gallery/posts/update.ts +++ b/packages/backend/src/server/api/endpoints/gallery/posts/update.ts @@ -1,7 +1,5 @@ -import $ from 'cafy'; import ms from 'ms'; import define from '../../../define'; -import { ID } from '../../../../../misc/cafy-id'; import { DriveFiles, GalleryPosts } from '@/models/index'; import { GalleryPost } from '@/models/entities/gallery-post'; import { ApiError } from '../../../error'; @@ -19,29 +17,6 @@ export const meta = { max: 300, }, - params: { - postId: { - validator: $.type(ID), - }, - - title: { - validator: $.str.min(1), - }, - - description: { - validator: $.optional.nullable.str, - }, - - fileIds: { - validator: $.arr($.type(ID)).unique().range(1, 32), - }, - - isSensitive: { - validator: $.optional.bool, - default: false, - }, - }, - res: { type: 'object', optional: false, nullable: false, @@ -53,8 +28,22 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + postId: { type: 'string', format: 'misskey:id' }, + title: { type: 'string', minLength: 1 }, + description: { type: 'string', nullable: true }, + fileIds: { type: 'array', uniqueItems: true, minItems: 1, maxItems: 32, items: { + type: 'string', format: 'misskey:id', + } }, + isSensitive: { type: 'boolean', default: false }, + }, + required: ['postId', 'title', 'fileIds'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const files = (await Promise.all(ps.fileIds.map(fileId => DriveFiles.findOne({ id: fileId, diff --git a/packages/backend/src/server/api/endpoints/get-online-users-count.ts b/packages/backend/src/server/api/endpoints/get-online-users-count.ts index 5b13d5a3b8..855b9fc342 100644 --- a/packages/backend/src/server/api/endpoints/get-online-users-count.ts +++ b/packages/backend/src/server/api/endpoints/get-online-users-count.ts @@ -7,13 +7,16 @@ export const meta = { tags: ['meta'], requireCredential: false, +} as const; - params: { - }, +const paramDef = { + type: 'object', + properties: {}, + required: [], } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, async () => { +export default define(meta, paramDef, async () => { const count = await Users.count({ lastActiveDate: MoreThan(new Date(Date.now() - USER_ONLINE_THRESHOLD)), }); diff --git a/packages/backend/src/server/api/endpoints/hashtags/list.ts b/packages/backend/src/server/api/endpoints/hashtags/list.ts index 9fa9b3edc6..68f2b2bf57 100644 --- a/packages/backend/src/server/api/endpoints/hashtags/list.ts +++ b/packages/backend/src/server/api/endpoints/hashtags/list.ts @@ -1,4 +1,3 @@ -import $ from 'cafy'; import define from '../../define'; import { Hashtags } from '@/models/index'; @@ -7,45 +6,6 @@ export const meta = { requireCredential: false, - params: { - limit: { - validator: $.optional.num.range(1, 100), - default: 10, - }, - - attachedToUserOnly: { - validator: $.optional.bool, - default: false, - }, - - attachedToLocalUserOnly: { - validator: $.optional.bool, - default: false, - }, - - attachedToRemoteUserOnly: { - validator: $.optional.bool, - default: false, - }, - - sort: { - validator: $.str.or([ - '+mentionedUsers', - '-mentionedUsers', - '+mentionedLocalUsers', - '-mentionedLocalUsers', - '+mentionedRemoteUsers', - '-mentionedRemoteUsers', - '+attachedUsers', - '-attachedUsers', - '+attachedLocalUsers', - '-attachedLocalUsers', - '+attachedRemoteUsers', - '-attachedRemoteUsers', - ]), - }, - }, - res: { type: 'array', optional: false, nullable: false, @@ -57,8 +17,20 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, + attachedToUserOnly: { type: 'boolean', default: false }, + attachedToLocalUserOnly: { type: 'boolean', default: false }, + attachedToRemoteUserOnly: { type: 'boolean', default: false }, + sort: { type: 'string', enum: ['+mentionedUsers', '-mentionedUsers', '+mentionedLocalUsers', '-mentionedLocalUsers', '+mentionedRemoteUsers', '-mentionedRemoteUsers', '+attachedUsers', '-attachedUsers', '+attachedLocalUsers', '-attachedLocalUsers', '+attachedRemoteUsers', '-attachedRemoteUsers'] }, + }, + required: ['sort'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, me) => { +export default define(meta, paramDef, async (ps, me) => { const query = Hashtags.createQueryBuilder('tag'); if (ps.attachedToUserOnly) query.andWhere('tag.attachedUsersCount != 0'); @@ -90,7 +62,7 @@ export default define(meta, async (ps, me) => { 'tag.attachedRemoteUsersCount', ]); - const tags = await query.take(ps.limit!).getMany(); + const tags = await query.take(ps.limit).getMany(); return Hashtags.packMany(tags); }); diff --git a/packages/backend/src/server/api/endpoints/hashtags/search.ts b/packages/backend/src/server/api/endpoints/hashtags/search.ts index 0d646c64f5..9aa08ed28e 100644 --- a/packages/backend/src/server/api/endpoints/hashtags/search.ts +++ b/packages/backend/src/server/api/endpoints/hashtags/search.ts @@ -1,4 +1,3 @@ -import $ from 'cafy'; import define from '../../define'; import { Hashtags } from '@/models/index'; @@ -7,22 +6,6 @@ export const meta = { requireCredential: false, - params: { - limit: { - validator: $.optional.num.range(1, 100), - default: 10, - }, - - query: { - validator: $.str, - }, - - offset: { - validator: $.optional.num.min(0), - default: 0, - }, - }, - res: { type: 'array', optional: false, nullable: false, @@ -33,13 +16,23 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, + query: { type: 'string' }, + offset: { type: 'integer', default: 0 }, + }, + required: ['query'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps) => { +export default define(meta, paramDef, async (ps) => { const hashtags = await Hashtags.createQueryBuilder('tag') .where('tag.name like :q', { q: ps.query.toLowerCase() + '%' }) .orderBy('tag.count', 'DESC') .groupBy('tag.id') - .take(ps.limit!) + .take(ps.limit) .skip(ps.offset) .getMany(); diff --git a/packages/backend/src/server/api/endpoints/hashtags/show.ts b/packages/backend/src/server/api/endpoints/hashtags/show.ts index 242cef99d4..79b9613086 100644 --- a/packages/backend/src/server/api/endpoints/hashtags/show.ts +++ b/packages/backend/src/server/api/endpoints/hashtags/show.ts @@ -1,4 +1,3 @@ -import $ from 'cafy'; import define from '../../define'; import { ApiError } from '../../error'; import { Hashtags } from '@/models/index'; @@ -9,12 +8,6 @@ export const meta = { requireCredential: false, - params: { - tag: { - validator: $.str, - }, - }, - res: { type: 'object', optional: false, nullable: false, @@ -30,8 +23,16 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + tag: { type: 'string' }, + }, + required: ['tag'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const hashtag = await Hashtags.findOne({ name: normalizeForSearch(ps.tag) }); if (hashtag == null) { throw new ApiError(meta.errors.noSuchHashtag); diff --git a/packages/backend/src/server/api/endpoints/hashtags/trend.ts b/packages/backend/src/server/api/endpoints/hashtags/trend.ts index be964ad639..de3e453c3e 100644 --- a/packages/backend/src/server/api/endpoints/hashtags/trend.ts +++ b/packages/backend/src/server/api/endpoints/hashtags/trend.ts @@ -53,8 +53,14 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: {}, + required: [], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async () => { +export default define(meta, paramDef, async () => { const instance = await fetchMeta(true); const hiddenTags = instance.hiddenTags.map(t => normalizeForSearch(t)); diff --git a/packages/backend/src/server/api/endpoints/hashtags/users.ts b/packages/backend/src/server/api/endpoints/hashtags/users.ts index 2158dc4349..1760732c1a 100644 --- a/packages/backend/src/server/api/endpoints/hashtags/users.ts +++ b/packages/backend/src/server/api/endpoints/hashtags/users.ts @@ -1,4 +1,3 @@ -import $ from 'cafy'; import define from '../../define'; import { Users } from '@/models/index'; import { normalizeForSearch } from '@/misc/normalize-for-search'; @@ -8,45 +7,6 @@ export const meta = { tags: ['hashtags', 'users'], - params: { - tag: { - validator: $.str, - }, - - limit: { - validator: $.optional.num.range(1, 100), - default: 10, - }, - - sort: { - validator: $.str.or([ - '+follower', - '-follower', - '+createdAt', - '-createdAt', - '+updatedAt', - '-updatedAt', - ]), - }, - - state: { - validator: $.optional.str.or([ - 'all', - 'alive', - ]), - default: 'all', - }, - - origin: { - validator: $.optional.str.or([ - 'combined', - 'local', - 'remote', - ]), - default: 'local', - }, - }, - res: { type: 'array', optional: false, nullable: false, @@ -58,8 +18,20 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + tag: { type: 'string' }, + limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, + sort: { type: 'string', enum: ['+follower', '-follower', '+createdAt', '-createdAt', '+updatedAt', '-updatedAt'] }, + state: { type: 'string', enum: ['all', 'alive'], default: "all" }, + origin: { type: 'string', enum: ['combined', 'local', 'remote'], default: "local" }, + }, + required: ['tag', 'sort'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, me) => { +export default define(meta, paramDef, async (ps, me) => { const query = Users.createQueryBuilder('user') .where(':tag = ANY(user.tags)', { tag: normalizeForSearch(ps.tag) }); @@ -84,7 +56,7 @@ export default define(meta, async (ps, me) => { case '-updatedAt': query.orderBy('user.updatedAt', 'ASC'); break; } - const users = await query.take(ps.limit!).getMany(); + const users = await query.take(ps.limit).getMany(); return await Users.packMany(users, me, { detail: true }); }); diff --git a/packages/backend/src/server/api/endpoints/i.ts b/packages/backend/src/server/api/endpoints/i.ts index d69c118cfc..091276ea27 100644 --- a/packages/backend/src/server/api/endpoints/i.ts +++ b/packages/backend/src/server/api/endpoints/i.ts @@ -6,8 +6,6 @@ export const meta = { requireCredential: true, - params: {}, - res: { type: 'object', optional: false, nullable: false, @@ -15,8 +13,14 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: {}, + required: [], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user, token) => { +export default define(meta, paramDef, async (ps, user, token) => { const isSecure = token == null; // ã“ã“ã§æ¸ĄãŖãĻきãĻいる user はキãƒŖãƒƒã‚ˇãƒĨされãĻいãĻ古い可čƒŊ性もあるぎで id ã ã‘æ¸Ąã™ 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 4853908693..8d82ab3886 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/done.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/done.ts @@ -1,4 +1,3 @@ -import $ from 'cafy'; import * as speakeasy from 'speakeasy'; import define from '../../../define'; import { UserProfiles } from '@/models/index'; @@ -7,16 +6,18 @@ export const meta = { requireCredential: true, secure: true, +} as const; - params: { - token: { - validator: $.str, - }, +const paramDef = { + type: 'object', + properties: { + token: { type: 'string' }, }, + required: ['token'], } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const token = ps.token.replace(/\s/g, ''); const profile = await UserProfiles.findOneOrFail(user.id); 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 26e9a60886..5052afda97 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 $ from 'cafy'; import * as bcrypt from 'bcryptjs'; import { promisify } from 'util'; import * as cbor from 'cbor'; @@ -14,35 +13,28 @@ import { procedures, hash } from '../../../2fa'; import { publishMainStream } from '@/services/stream'; const cborDecodeFirst = promisify(cbor.decodeFirst) as any; +const rpIdHashReal = hash(Buffer.from(config.hostname, 'utf-8')); export const meta = { requireCredential: true, secure: true, - - params: { - clientDataJSON: { - validator: $.str, - }, - attestationObject: { - validator: $.str, - }, - password: { - validator: $.str, - }, - challengeId: { - validator: $.str, - }, - name: { - validator: $.str, - }, - }, } as const; -const rpIdHashReal = hash(Buffer.from(config.hostname, 'utf-8')); +const paramDef = { + type: 'object', + properties: { + clientDataJSON: { type: 'string' }, + attestationObject: { type: 'string' }, + password: { type: 'string' }, + challengeId: { type: 'string' }, + name: { type: 'string' }, + }, + required: ['clientDataJSON', 'attestationObject', 'password', 'challengeId', 'name'], +} as const; // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const profile = await UserProfiles.findOneOrFail(user.id); // Compare 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 854848a434..2830f8994a 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,4 +1,3 @@ -import $ from 'cafy'; import define from '../../../define'; import { UserProfiles } from '@/models/index'; @@ -6,16 +5,18 @@ export const meta = { requireCredential: true, secure: true, +} as const; - params: { - value: { - validator: $.boolean, - }, +const paramDef = { + type: 'object', + properties: { + value: { type: 'boolean' }, }, + required: ['value'], } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { await UserProfiles.update(user.id, { usePasswordLessLogin: ps.value, }); 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 057e54c69b..da3ff421a7 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,4 +1,3 @@ -import $ from 'cafy'; import * as bcrypt from 'bcryptjs'; import define from '../../../define'; import { UserProfiles, AttestationChallenges } from '@/models/index'; @@ -13,16 +12,18 @@ export const meta = { requireCredential: true, secure: true, +} as const; - params: { - password: { - validator: $.str, - }, +const paramDef = { + type: 'object', + properties: { + password: { type: 'string' }, }, + required: ['password'], } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const profile = await UserProfiles.findOneOrFail(user.id); // Compare password 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 c5cfb9dfad..32acc838b7 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/register.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/register.ts @@ -1,4 +1,3 @@ -import $ from 'cafy'; import * as bcrypt from 'bcryptjs'; import * as speakeasy from 'speakeasy'; import * as QRCode from 'qrcode'; @@ -10,16 +9,18 @@ export const meta = { requireCredential: true, secure: true, +} as const; - params: { - password: { - validator: $.str, - }, +const paramDef = { + type: 'object', + properties: { + password: { type: 'string' }, }, + required: ['password'], } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const profile = await UserProfiles.findOneOrFail(user.id); // Compare password 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 03e1d0434d..3004ce36a1 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,3 @@ -import $ from 'cafy'; import * as bcrypt from 'bcryptjs'; import define from '../../../define'; import { UserProfiles, UserSecurityKeys, Users } from '@/models/index'; @@ -8,19 +7,19 @@ export const meta = { requireCredential: true, secure: true, +} as const; - params: { - password: { - validator: $.str, - }, - credentialId: { - validator: $.str, - }, +const paramDef = { + type: 'object', + properties: { + password: { type: 'string' }, + credentialId: { type: 'string' }, }, + required: ['password', 'credentialId'], } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const profile = await UserProfiles.findOneOrFail(user.id); // Compare password 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 a19ad6810d..b8e89bac72 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/unregister.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/unregister.ts @@ -1,4 +1,3 @@ -import $ from 'cafy'; import * as bcrypt from 'bcryptjs'; import define from '../../../define'; import { UserProfiles } from '@/models/index'; @@ -7,16 +6,18 @@ export const meta = { requireCredential: true, secure: true, +} as const; - params: { - password: { - validator: $.str, - }, +const paramDef = { + type: 'object', + properties: { + password: { type: 'string' }, }, + required: ['password'], } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const profile = await UserProfiles.findOneOrFail(user.id); // Compare password diff --git a/packages/backend/src/server/api/endpoints/i/apps.ts b/packages/backend/src/server/api/endpoints/i/apps.ts index 63999b0981..f45b6745b8 100644 --- a/packages/backend/src/server/api/endpoints/i/apps.ts +++ b/packages/backend/src/server/api/endpoints/i/apps.ts @@ -1,4 +1,3 @@ -import $ from 'cafy'; import define from '../../define'; import { AccessTokens } from '@/models/index'; @@ -6,21 +5,18 @@ export const meta = { requireCredential: true, secure: true, +} as const; - params: { - sort: { - validator: $.optional.str.or([ - '+createdAt', - '-createdAt', - '+lastUsedAt', - '-lastUsedAt', - ]), - }, +const paramDef = { + type: 'object', + properties: { + sort: { type: 'string', enum: ['+createdAt', '-createdAt', '+lastUsedAt', '-lastUsedAt'] }, }, + required: [], } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const query = AccessTokens.createQueryBuilder('token') .where('token.userId = :userId', { userId: user.id }); diff --git a/packages/backend/src/server/api/endpoints/i/authorized-apps.ts b/packages/backend/src/server/api/endpoints/i/authorized-apps.ts index 52122b851b..e417f3e81a 100644 --- a/packages/backend/src/server/api/endpoints/i/authorized-apps.ts +++ b/packages/backend/src/server/api/endpoints/i/authorized-apps.ts @@ -1,4 +1,3 @@ -import $ from 'cafy'; import define from '../../define'; import { AccessTokens, Apps } from '@/models/index'; @@ -6,33 +5,26 @@ export const meta = { requireCredential: true, secure: true, +} as const; - params: { - limit: { - validator: $.optional.num.range(1, 100), - default: 10, - }, - - offset: { - validator: $.optional.num.min(0), - default: 0, - }, - - sort: { - validator: $.optional.str.or('desc|asc'), - default: 'desc', - }, +const paramDef = { + type: 'object', + properties: { + limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, + offset: { type: 'integer', default: 0 }, + sort: { type: 'string', enum: ['desc', 'asc'], default: "desc" }, }, + required: [], } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { // Get tokens const tokens = await AccessTokens.find({ where: { userId: user.id, }, - take: ps.limit!, + take: ps.limit, skip: ps.offset, order: { id: ps.sort == 'asc' ? 1 : -1, 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 7b6c137737..eac60ee183 100644 --- a/packages/backend/src/server/api/endpoints/i/change-password.ts +++ b/packages/backend/src/server/api/endpoints/i/change-password.ts @@ -1,4 +1,3 @@ -import $ from 'cafy'; import * as bcrypt from 'bcryptjs'; import define from '../../define'; import { UserProfiles } from '@/models/index'; @@ -7,20 +6,19 @@ export const meta = { requireCredential: true, secure: true, +} as const; - params: { - currentPassword: { - validator: $.str, - }, - - newPassword: { - validator: $.str, - }, +const paramDef = { + type: 'object', + properties: { + currentPassword: { type: 'string' }, + newPassword: { type: 'string', minLength: 1 }, }, + required: ['currentPassword', 'newPassword'], } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const profile = await UserProfiles.findOneOrFail(user.id); // Compare password 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 e1eee949fc..6f689c0b4e 100644 --- a/packages/backend/src/server/api/endpoints/i/delete-account.ts +++ b/packages/backend/src/server/api/endpoints/i/delete-account.ts @@ -1,4 +1,3 @@ -import $ from 'cafy'; import * as bcrypt from 'bcryptjs'; import define from '../../define'; import { UserProfiles, Users } from '@/models/index'; @@ -10,16 +9,18 @@ export const meta = { requireCredential: true, secure: true, +} as const; - params: { - password: { - validator: $.str, - }, +const paramDef = { + type: 'object', + properties: { + password: { type: 'string' }, }, + required: ['password'], } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const profile = await UserProfiles.findOneOrFail(user.id); const userDetailed = await Users.findOneOrFail(user.id); if (userDetailed.isDeleted) { diff --git a/packages/backend/src/server/api/endpoints/i/export-blocking.ts b/packages/backend/src/server/api/endpoints/i/export-blocking.ts index 44d8a1cb38..afa6f2fc8e 100644 --- a/packages/backend/src/server/api/endpoints/i/export-blocking.ts +++ b/packages/backend/src/server/api/endpoints/i/export-blocking.ts @@ -11,7 +11,13 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: {}, + required: [], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { createExportBlockingJob(user); }); diff --git a/packages/backend/src/server/api/endpoints/i/export-following.ts b/packages/backend/src/server/api/endpoints/i/export-following.ts index 5d1617d57b..4dd8cda398 100644 --- a/packages/backend/src/server/api/endpoints/i/export-following.ts +++ b/packages/backend/src/server/api/endpoints/i/export-following.ts @@ -1,4 +1,3 @@ -import $ from 'cafy'; import define from '../../define'; import { createExportFollowingJob } from '@/queue/index'; import ms from 'ms'; @@ -10,19 +9,18 @@ export const meta = { duration: ms('1hour'), max: 1, }, - params: { - excludeMuting: { - validator: $.optional.bool, - default: false, - }, - excludeInactive: { - validator: $.optional.bool, - default: false, - }, +} as const; + +const paramDef = { + type: 'object', + properties: { + excludeMuting: { type: 'boolean', default: false }, + excludeInactive: { type: 'boolean', default: false }, }, + required: [], } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { createExportFollowingJob(user, ps.excludeMuting, ps.excludeInactive); }); diff --git a/packages/backend/src/server/api/endpoints/i/export-mute.ts b/packages/backend/src/server/api/endpoints/i/export-mute.ts index 27ce8f0b29..ca8a7b997c 100644 --- a/packages/backend/src/server/api/endpoints/i/export-mute.ts +++ b/packages/backend/src/server/api/endpoints/i/export-mute.ts @@ -11,7 +11,13 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: {}, + required: [], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { createExportMuteJob(user); }); diff --git a/packages/backend/src/server/api/endpoints/i/export-notes.ts b/packages/backend/src/server/api/endpoints/i/export-notes.ts index 25b1849e80..fc155d9d0f 100644 --- a/packages/backend/src/server/api/endpoints/i/export-notes.ts +++ b/packages/backend/src/server/api/endpoints/i/export-notes.ts @@ -11,7 +11,13 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: {}, + required: [], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { createExportNotesJob(user); }); diff --git a/packages/backend/src/server/api/endpoints/i/export-user-lists.ts b/packages/backend/src/server/api/endpoints/i/export-user-lists.ts index d28b699c5a..22cbf973f8 100644 --- a/packages/backend/src/server/api/endpoints/i/export-user-lists.ts +++ b/packages/backend/src/server/api/endpoints/i/export-user-lists.ts @@ -11,7 +11,13 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: {}, + required: [], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { createExportUserListsJob(user); }); diff --git a/packages/backend/src/server/api/endpoints/i/favorites.ts b/packages/backend/src/server/api/endpoints/i/favorites.ts index 92c767876b..45f5d99cce 100644 --- a/packages/backend/src/server/api/endpoints/i/favorites.ts +++ b/packages/backend/src/server/api/endpoints/i/favorites.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../define'; import { NoteFavorites } from '@/models/index'; import { makePaginationQuery } from '../../common/make-pagination-query'; @@ -11,21 +9,6 @@ export const meta = { kind: 'read:favorites', - params: { - limit: { - validator: $.optional.num.range(1, 100), - default: 10, - }, - - sinceId: { - validator: $.optional.type(ID), - }, - - untilId: { - validator: $.optional.type(ID), - }, - }, - res: { type: 'array', optional: false, nullable: false, @@ -37,14 +20,24 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, + 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, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const query = makePaginationQuery(NoteFavorites.createQueryBuilder('favorite'), ps.sinceId, ps.untilId) .andWhere(`favorite.userId = :meId`, { meId: user.id }) .leftJoinAndSelect('favorite.note', 'note'); const favorites = await query - .take(ps.limit!) + .take(ps.limit) .getMany(); return await NoteFavorites.packMany(favorites, user); diff --git a/packages/backend/src/server/api/endpoints/i/gallery/likes.ts b/packages/backend/src/server/api/endpoints/i/gallery/likes.ts index f1c5763593..dc862a6b05 100644 --- a/packages/backend/src/server/api/endpoints/i/gallery/likes.ts +++ b/packages/backend/src/server/api/endpoints/i/gallery/likes.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../../define'; import { GalleryLikes } from '@/models/index'; import { makePaginationQuery } from '../../../common/make-pagination-query'; @@ -11,21 +9,6 @@ export const meta = { kind: 'read:gallery-likes', - params: { - limit: { - validator: $.optional.num.range(1, 100), - default: 10, - }, - - sinceId: { - validator: $.optional.type(ID), - }, - - untilId: { - validator: $.optional.type(ID), - }, - }, - res: { type: 'object', optional: false, nullable: false, @@ -44,14 +27,24 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, + 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, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const query = makePaginationQuery(GalleryLikes.createQueryBuilder('like'), ps.sinceId, ps.untilId) .andWhere(`like.userId = :meId`, { meId: user.id }) .leftJoinAndSelect('like.post', 'post'); const likes = await query - .take(ps.limit!) + .take(ps.limit) .getMany(); return await GalleryLikes.packMany(likes, user); diff --git a/packages/backend/src/server/api/endpoints/i/gallery/posts.ts b/packages/backend/src/server/api/endpoints/i/gallery/posts.ts index d46d42f633..fbab8795ca 100644 --- a/packages/backend/src/server/api/endpoints/i/gallery/posts.ts +++ b/packages/backend/src/server/api/endpoints/i/gallery/posts.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../../define'; import { GalleryPosts } from '@/models/index'; import { makePaginationQuery } from '../../../common/make-pagination-query'; @@ -11,21 +9,6 @@ export const meta = { kind: 'read:gallery', - params: { - limit: { - validator: $.optional.num.range(1, 100), - default: 10, - }, - - sinceId: { - validator: $.optional.type(ID), - }, - - untilId: { - validator: $.optional.type(ID), - }, - }, - res: { type: 'array', optional: false, nullable: false, @@ -37,13 +20,23 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, + 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, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const query = makePaginationQuery(GalleryPosts.createQueryBuilder('post'), ps.sinceId, ps.untilId) .andWhere(`post.userId = :meId`, { meId: user.id }); const posts = await query - .take(ps.limit!) + .take(ps.limit) .getMany(); return await GalleryPosts.packMany(posts, user); diff --git a/packages/backend/src/server/api/endpoints/i/get-word-muted-notes-count.ts b/packages/backend/src/server/api/endpoints/i/get-word-muted-notes-count.ts index 4e1a4d3db9..2b31b89ff1 100644 --- a/packages/backend/src/server/api/endpoints/i/get-word-muted-notes-count.ts +++ b/packages/backend/src/server/api/endpoints/i/get-word-muted-notes-count.ts @@ -8,9 +8,6 @@ export const meta = { kind: 'read:account', - params: { - }, - res: { type: 'object', optional: false, nullable: false, @@ -23,8 +20,14 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: {}, + required: [], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { return { count: await MutedNotes.count({ userId: user.id, diff --git a/packages/backend/src/server/api/endpoints/i/import-blocking.ts b/packages/backend/src/server/api/endpoints/i/import-blocking.ts index acc5797420..b90bfb1402 100644 --- a/packages/backend/src/server/api/endpoints/i/import-blocking.ts +++ b/packages/backend/src/server/api/endpoints/i/import-blocking.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../define'; import { createImportBlockingJob } from '@/queue/index'; import ms from 'ms'; @@ -15,12 +13,6 @@ export const meta = { max: 1, }, - params: { - fileId: { - validator: $.type(ID), - }, - }, - errors: { noSuchFile: { message: 'No such file.', @@ -48,8 +40,16 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + fileId: { type: 'string', format: 'misskey:id' }, + }, + required: ['fileId'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const file = await DriveFiles.findOne(ps.fileId); if (file == null) throw new ApiError(meta.errors.noSuchFile); diff --git a/packages/backend/src/server/api/endpoints/i/import-following.ts b/packages/backend/src/server/api/endpoints/i/import-following.ts index 35006746fb..75388502a5 100644 --- a/packages/backend/src/server/api/endpoints/i/import-following.ts +++ b/packages/backend/src/server/api/endpoints/i/import-following.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../define'; import { createImportFollowingJob } from '@/queue/index'; import ms from 'ms'; @@ -14,12 +12,6 @@ export const meta = { max: 1, }, - params: { - fileId: { - validator: $.type(ID), - }, - }, - errors: { noSuchFile: { message: 'No such file.', @@ -47,8 +39,16 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + fileId: { type: 'string', format: 'misskey:id' }, + }, + required: ['fileId'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const file = await DriveFiles.findOne(ps.fileId); if (file == null) throw new ApiError(meta.errors.noSuchFile); diff --git a/packages/backend/src/server/api/endpoints/i/import-muting.ts b/packages/backend/src/server/api/endpoints/i/import-muting.ts index 7bbb2e008e..e1c3df33e1 100644 --- a/packages/backend/src/server/api/endpoints/i/import-muting.ts +++ b/packages/backend/src/server/api/endpoints/i/import-muting.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../define'; import { createImportMutingJob } from '@/queue/index'; import ms from 'ms'; @@ -15,12 +13,6 @@ export const meta = { max: 1, }, - params: { - fileId: { - validator: $.type(ID), - }, - }, - errors: { noSuchFile: { message: 'No such file.', @@ -48,8 +40,16 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + fileId: { type: 'string', format: 'misskey:id' }, + }, + required: ['fileId'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const file = await DriveFiles.findOne(ps.fileId); if (file == null) throw new ApiError(meta.errors.noSuchFile); diff --git a/packages/backend/src/server/api/endpoints/i/import-user-lists.ts b/packages/backend/src/server/api/endpoints/i/import-user-lists.ts index 759d41b6cd..eb1302a98c 100644 --- a/packages/backend/src/server/api/endpoints/i/import-user-lists.ts +++ b/packages/backend/src/server/api/endpoints/i/import-user-lists.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../define'; import { createImportUserListsJob } from '@/queue/index'; import ms from 'ms'; @@ -14,12 +12,6 @@ export const meta = { max: 1, }, - params: { - fileId: { - validator: $.type(ID), - }, - }, - errors: { noSuchFile: { message: 'No such file.', @@ -47,8 +39,16 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + fileId: { type: 'string', format: 'misskey:id' }, + }, + required: ['fileId'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const file = await DriveFiles.findOne(ps.fileId); if (file == null) throw new ApiError(meta.errors.noSuchFile); diff --git a/packages/backend/src/server/api/endpoints/i/notifications.ts b/packages/backend/src/server/api/endpoints/i/notifications.ts index 59efd32bb2..bb1d6a49b3 100644 --- a/packages/backend/src/server/api/endpoints/i/notifications.ts +++ b/packages/backend/src/server/api/endpoints/i/notifications.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import { readNotification } from '../../common/read-notification'; import define from '../../define'; import { makePaginationQuery } from '../../common/make-pagination-query'; @@ -16,44 +14,6 @@ export const meta = { kind: 'read:notifications', - params: { - limit: { - validator: $.optional.num.range(1, 100), - default: 10, - }, - - sinceId: { - validator: $.optional.type(ID), - }, - - untilId: { - validator: $.optional.type(ID), - }, - - following: { - validator: $.optional.bool, - default: false, - }, - - unreadOnly: { - validator: $.optional.bool, - default: false, - }, - - markAsRead: { - validator: $.optional.bool, - default: true, - }, - - includeTypes: { - validator: $.optional.arr($.str.or(notificationTypes as unknown as string[])), - }, - - excludeTypes: { - validator: $.optional.arr($.str.or(notificationTypes as unknown as string[])), - }, - }, - res: { type: 'array', optional: false, nullable: false, @@ -65,8 +25,27 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, + sinceId: { type: 'string', format: 'misskey:id' }, + untilId: { type: 'string', format: 'misskey:id' }, + following: { type: 'boolean', default: false }, + unreadOnly: { type: 'boolean', default: false }, + markAsRead: { type: 'boolean', default: true }, + includeTypes: { type: 'array', items: { + type: 'string', enum: notificationTypes, + } }, + excludeTypes: { type: 'array', items: { + type: 'string', enum: notificationTypes, + } }, + }, + required: [], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { // includeTypes がįŠēぎ場合はクエãƒĒしãĒい if (ps.includeTypes && ps.includeTypes.length === 0) { return []; @@ -125,7 +104,7 @@ export default define(meta, async (ps, user) => { query.andWhere(`notification.isRead = false`); } - const notifications = await query.take(ps.limit!).getMany(); + const notifications = await query.take(ps.limit).getMany(); // Mark all as read if (notifications.length > 0 && ps.markAsRead) { diff --git a/packages/backend/src/server/api/endpoints/i/page-likes.ts b/packages/backend/src/server/api/endpoints/i/page-likes.ts index 59239c7446..e66bc616c2 100644 --- a/packages/backend/src/server/api/endpoints/i/page-likes.ts +++ b/packages/backend/src/server/api/endpoints/i/page-likes.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../define'; import { PageLikes } from '@/models/index'; import { makePaginationQuery } from '../../common/make-pagination-query'; @@ -11,21 +9,6 @@ export const meta = { kind: 'read:page-likes', - params: { - limit: { - validator: $.optional.num.range(1, 100), - default: 10, - }, - - sinceId: { - validator: $.optional.type(ID), - }, - - untilId: { - validator: $.optional.type(ID), - }, - }, - res: { type: 'object', optional: false, nullable: false, @@ -44,14 +27,24 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, + 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, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const query = makePaginationQuery(PageLikes.createQueryBuilder('like'), ps.sinceId, ps.untilId) .andWhere(`like.userId = :meId`, { meId: user.id }) .leftJoinAndSelect('like.page', 'page'); const likes = await query - .take(ps.limit!) + .take(ps.limit) .getMany(); return await PageLikes.packMany(likes, user); diff --git a/packages/backend/src/server/api/endpoints/i/pages.ts b/packages/backend/src/server/api/endpoints/i/pages.ts index bef775d063..dbc58021b9 100644 --- a/packages/backend/src/server/api/endpoints/i/pages.ts +++ b/packages/backend/src/server/api/endpoints/i/pages.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../define'; import { Pages } from '@/models/index'; import { makePaginationQuery } from '../../common/make-pagination-query'; @@ -11,21 +9,6 @@ export const meta = { kind: 'read:pages', - params: { - limit: { - validator: $.optional.num.range(1, 100), - default: 10, - }, - - sinceId: { - validator: $.optional.type(ID), - }, - - untilId: { - validator: $.optional.type(ID), - }, - }, - res: { type: 'array', optional: false, nullable: false, @@ -37,13 +20,23 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, + 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, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const query = makePaginationQuery(Pages.createQueryBuilder('page'), ps.sinceId, ps.untilId) .andWhere(`page.userId = :meId`, { meId: user.id }); const pages = await query - .take(ps.limit!) + .take(ps.limit) .getMany(); return await Pages.packMany(pages); diff --git a/packages/backend/src/server/api/endpoints/i/pin.ts b/packages/backend/src/server/api/endpoints/i/pin.ts index a940d1b99b..a09618ebe8 100644 --- a/packages/backend/src/server/api/endpoints/i/pin.ts +++ b/packages/backend/src/server/api/endpoints/i/pin.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import { addPinned } from '@/services/i/pin'; import define from '../../define'; import { ApiError } from '../../error'; @@ -12,12 +10,6 @@ export const meta = { kind: 'write:account', - params: { - noteId: { - validator: $.type(ID), - }, - }, - errors: { noSuchNote: { message: 'No such note.', @@ -45,8 +37,16 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + noteId: { type: 'string', format: 'misskey:id' }, + }, + required: ['noteId'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { await addPinned(user, ps.noteId).catch(e => { if (e.id === '70c4e51f-5bea-449c-a030-53bee3cce202') throw new ApiError(meta.errors.noSuchNote); if (e.id === '15a018eb-58e5-4da1-93be-330fcc5e4e1a') throw new ApiError(meta.errors.pinLimitExceeded); diff --git a/packages/backend/src/server/api/endpoints/i/read-all-messaging-messages.ts b/packages/backend/src/server/api/endpoints/i/read-all-messaging-messages.ts index 4e4fb3840f..b0185a3e37 100644 --- a/packages/backend/src/server/api/endpoints/i/read-all-messaging-messages.ts +++ b/packages/backend/src/server/api/endpoints/i/read-all-messaging-messages.ts @@ -8,13 +8,16 @@ export const meta = { requireCredential: true, kind: 'write:account', +} as const; - params: { - }, +const paramDef = { + type: 'object', + properties: {}, + required: [], } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { // Update documents await MessagingMessages.update({ recipientId: user.id, diff --git a/packages/backend/src/server/api/endpoints/i/read-all-unread-notes.ts b/packages/backend/src/server/api/endpoints/i/read-all-unread-notes.ts index 99f17ddfc9..45638c2579 100644 --- a/packages/backend/src/server/api/endpoints/i/read-all-unread-notes.ts +++ b/packages/backend/src/server/api/endpoints/i/read-all-unread-notes.ts @@ -8,13 +8,16 @@ export const meta = { requireCredential: true, kind: 'write:account', +} as const; - params: { - }, +const paramDef = { + type: 'object', + properties: {}, + required: [], } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { // Remove documents await NoteUnreads.delete({ userId: user.id, 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 e9bb66264b..f3cedddb35 100644 --- a/packages/backend/src/server/api/endpoints/i/read-announcement.ts +++ b/packages/backend/src/server/api/endpoints/i/read-announcement.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../define'; import { ApiError } from '../../error'; import { genId } from '@/misc/gen-id'; @@ -13,12 +11,6 @@ export const meta = { kind: 'write:account', - params: { - announcementId: { - validator: $.type(ID), - }, - }, - errors: { noSuchAnnouncement: { message: 'No such announcement.', @@ -28,8 +20,16 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + announcementId: { type: 'string', format: 'misskey:id' }, + }, + required: ['announcementId'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { // Check if announcement exists const announcement = await Announcements.findOne(ps.announcementId); 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 a20719363b..71a3ea08af 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 $ from 'cafy'; import * as bcrypt from 'bcryptjs'; import { publishMainStream, publishUserEvent } from '@/services/stream'; import generateUserToken from '../../common/generate-native-user-token'; @@ -9,16 +8,18 @@ export const meta = { requireCredential: true, secure: true, +} as const; - params: { - password: { - validator: $.str, - }, +const paramDef = { + type: 'object', + properties: { + password: { type: 'string' }, }, + required: ['password'], } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const profile = await UserProfiles.findOneOrFail(user.id); // Compare password diff --git a/packages/backend/src/server/api/endpoints/i/registry/get-all.ts b/packages/backend/src/server/api/endpoints/i/registry/get-all.ts index 2941b441e2..0429cd283a 100644 --- a/packages/backend/src/server/api/endpoints/i/registry/get-all.ts +++ b/packages/backend/src/server/api/endpoints/i/registry/get-all.ts @@ -1,4 +1,3 @@ -import $ from 'cafy'; import define from '../../../define'; import { RegistryItems } from '@/models/index'; @@ -6,17 +5,20 @@ export const meta = { requireCredential: true, secure: true, +} as const; - params: { - scope: { - validator: $.optional.arr($.str.match(/^[a-zA-Z0-9_]+$/)), - default: [], - }, +const paramDef = { + type: 'object', + properties: { + scope: { type: 'array', default: [], items: { + type: 'string', pattern: /^[a-zA-Z0-9_]+$/.toString().slice(1, -1), + } }, }, + required: [], } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { 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/registry/get-detail.ts b/packages/backend/src/server/api/endpoints/i/registry/get-detail.ts index 51371353c9..194a922718 100644 --- a/packages/backend/src/server/api/endpoints/i/registry/get-detail.ts +++ b/packages/backend/src/server/api/endpoints/i/registry/get-detail.ts @@ -1,4 +1,3 @@ -import $ from 'cafy'; import define from '../../../define'; import { RegistryItems } from '@/models/index'; import { ApiError } from '../../../error'; @@ -8,17 +7,6 @@ export const meta = { secure: true, - params: { - key: { - validator: $.str, - }, - - scope: { - validator: $.optional.arr($.str.match(/^[a-zA-Z0-9_]+$/)), - default: [], - }, - }, - errors: { noSuchKey: { message: 'No such key.', @@ -28,8 +16,19 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + key: { type: 'string' }, + scope: { type: 'array', default: [], items: { + type: 'string', pattern: /^[a-zA-Z0-9_]+$/.toString().slice(1, -1), + } }, + }, + required: ['key'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { 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/registry/get.ts b/packages/backend/src/server/api/endpoints/i/registry/get.ts index ac617defb0..7d413e3528 100644 --- a/packages/backend/src/server/api/endpoints/i/registry/get.ts +++ b/packages/backend/src/server/api/endpoints/i/registry/get.ts @@ -1,4 +1,3 @@ -import $ from 'cafy'; import define from '../../../define'; import { RegistryItems } from '@/models/index'; import { ApiError } from '../../../error'; @@ -8,17 +7,6 @@ export const meta = { secure: true, - params: { - key: { - validator: $.str, - }, - - scope: { - validator: $.optional.arr($.str.match(/^[a-zA-Z0-9_]+$/)), - default: [], - }, - }, - errors: { noSuchKey: { message: 'No such key.', @@ -28,8 +16,19 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + key: { type: 'string' }, + scope: { type: 'array', default: [], items: { + type: 'string', pattern: /^[a-zA-Z0-9_]+$/.toString().slice(1, -1), + } }, + }, + required: ['key'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { 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/registry/keys-with-type.ts b/packages/backend/src/server/api/endpoints/i/registry/keys-with-type.ts index 0445922188..52e0d6fac5 100644 --- a/packages/backend/src/server/api/endpoints/i/registry/keys-with-type.ts +++ b/packages/backend/src/server/api/endpoints/i/registry/keys-with-type.ts @@ -1,4 +1,3 @@ -import $ from 'cafy'; import define from '../../../define'; import { RegistryItems } from '@/models/index'; @@ -6,17 +5,20 @@ export const meta = { requireCredential: true, secure: true, +} as const; - params: { - scope: { - validator: $.optional.arr($.str.match(/^[a-zA-Z0-9_]+$/)), - default: [], - }, +const paramDef = { + type: 'object', + properties: { + scope: { type: 'array', default: [], items: { + type: 'string', pattern: /^[a-zA-Z0-9_]+$/.toString().slice(1, -1), + } }, }, + required: [], } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { 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/registry/keys.ts b/packages/backend/src/server/api/endpoints/i/registry/keys.ts index a3c9d0e5ee..c494ed8f5c 100644 --- a/packages/backend/src/server/api/endpoints/i/registry/keys.ts +++ b/packages/backend/src/server/api/endpoints/i/registry/keys.ts @@ -1,4 +1,3 @@ -import $ from 'cafy'; import define from '../../../define'; import { RegistryItems } from '@/models/index'; @@ -6,17 +5,20 @@ export const meta = { requireCredential: true, secure: true, +} as const; - params: { - scope: { - validator: $.optional.arr($.str.match(/^[a-zA-Z0-9_]+$/)), - default: [], - }, +const paramDef = { + type: 'object', + properties: { + scope: { type: 'array', default: [], items: { + type: 'string', pattern: /^[a-zA-Z0-9_]+$/.toString().slice(1, -1), + } }, }, + required: [], } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const query = RegistryItems.createQueryBuilder('item') .select('item.key') .where('item.domain IS NULL') diff --git a/packages/backend/src/server/api/endpoints/i/registry/remove.ts b/packages/backend/src/server/api/endpoints/i/registry/remove.ts index 08185f224b..5bc55bd297 100644 --- a/packages/backend/src/server/api/endpoints/i/registry/remove.ts +++ b/packages/backend/src/server/api/endpoints/i/registry/remove.ts @@ -1,4 +1,3 @@ -import $ from 'cafy'; import define from '../../../define'; import { RegistryItems } from '@/models/index'; import { ApiError } from '../../../error'; @@ -8,17 +7,6 @@ export const meta = { secure: true, - params: { - key: { - validator: $.str, - }, - - scope: { - validator: $.optional.arr($.str.match(/^[a-zA-Z0-9_]+$/)), - default: [], - }, - }, - errors: { noSuchKey: { message: 'No such key.', @@ -28,8 +16,19 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + key: { type: 'string' }, + scope: { type: 'array', default: [], items: { + type: 'string', pattern: /^[a-zA-Z0-9_]+$/.toString().slice(1, -1), + } }, + }, + required: ['key'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { 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/registry/scopes.ts b/packages/backend/src/server/api/endpoints/i/registry/scopes.ts index 9de68ac6e8..e04d123a1f 100644 --- a/packages/backend/src/server/api/endpoints/i/registry/scopes.ts +++ b/packages/backend/src/server/api/endpoints/i/registry/scopes.ts @@ -5,13 +5,16 @@ export const meta = { requireCredential: true, secure: true, +} as const; - params: { - }, +const paramDef = { + type: 'object', + properties: {}, + required: [], } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const query = RegistryItems.createQueryBuilder('item') .select('item.scope') .where('item.domain IS NULL') diff --git a/packages/backend/src/server/api/endpoints/i/registry/set.ts b/packages/backend/src/server/api/endpoints/i/registry/set.ts index 27884046b4..c78f4383be 100644 --- a/packages/backend/src/server/api/endpoints/i/registry/set.ts +++ b/packages/backend/src/server/api/endpoints/i/registry/set.ts @@ -1,4 +1,3 @@ -import $ from 'cafy'; import { publishMainStream } from '@/services/stream'; import define from '../../../define'; import { RegistryItems } from '@/models/index'; @@ -8,25 +7,22 @@ export const meta = { requireCredential: true, secure: true, +} as const; - params: { - key: { - validator: $.str.min(1), - }, - - value: { - validator: $.nullable.any, - }, - - scope: { - validator: $.optional.arr($.str.match(/^[a-zA-Z0-9_]+$/)), - default: [], - }, +const paramDef = { + type: 'object', + properties: { + key: { type: 'string', minLength: 1 }, + value: {}, + scope: { type: 'array', default: [], items: { + type: 'string', pattern: /^[a-zA-Z0-9_]+$/.toString().slice(1, -1), + } }, }, + required: ['key', 'value'], } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { 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 51721c5b58..14800346c1 100644 --- a/packages/backend/src/server/api/endpoints/i/revoke-token.ts +++ b/packages/backend/src/server/api/endpoints/i/revoke-token.ts @@ -1,23 +1,23 @@ -import $ from 'cafy'; import define from '../../define'; import { AccessTokens } from '@/models/index'; -import { ID } from '@/misc/cafy-id'; import { publishUserEvent } from '@/services/stream'; export const meta = { requireCredential: true, secure: true, +} as const; - params: { - tokenId: { - validator: $.type(ID), - }, +const paramDef = { + type: 'object', + properties: { + tokenId: { type: 'string', format: 'misskey:id' }, }, + required: ['tokenId'], } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const token = await AccessTokens.findOne(ps.tokenId); if (token) { diff --git a/packages/backend/src/server/api/endpoints/i/signin-history.ts b/packages/backend/src/server/api/endpoints/i/signin-history.ts index 796e2ec309..468972c239 100644 --- a/packages/backend/src/server/api/endpoints/i/signin-history.ts +++ b/packages/backend/src/server/api/endpoints/i/signin-history.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../define'; import { Signins } from '@/models/index'; import { makePaginationQuery } from '../../common/make-pagination-query'; @@ -8,29 +6,24 @@ export const meta = { requireCredential: true, secure: true, +} as const; - params: { - limit: { - validator: $.optional.num.range(1, 100), - default: 10, - }, - - sinceId: { - validator: $.optional.type(ID), - }, - - untilId: { - validator: $.optional.type(ID), - }, +const paramDef = { + type: 'object', + properties: { + limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, + 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, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const query = makePaginationQuery(Signins.createQueryBuilder('signin'), ps.sinceId, ps.untilId) .andWhere(`signin.userId = :meId`, { meId: user.id }); - const history = await query.take(ps.limit!).getMany(); + const history = await query.take(ps.limit).getMany(); return await Promise.all(history.map(record => Signins.pack(record))); }); diff --git a/packages/backend/src/server/api/endpoints/i/unpin.ts b/packages/backend/src/server/api/endpoints/i/unpin.ts index 9c82b74960..895bfd0c28 100644 --- a/packages/backend/src/server/api/endpoints/i/unpin.ts +++ b/packages/backend/src/server/api/endpoints/i/unpin.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import { removePinned } from '@/services/i/pin'; import define from '../../define'; import { ApiError } from '../../error'; @@ -12,12 +10,6 @@ export const meta = { kind: 'write:account', - params: { - noteId: { - validator: $.type(ID), - }, - }, - errors: { noSuchNote: { message: 'No such note.', @@ -33,8 +25,16 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + noteId: { type: 'string', format: 'misskey:id' }, + }, + required: ['noteId'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { await removePinned(user, ps.noteId).catch(e => { if (e.id === 'b302d4cf-c050-400a-bbb3-be208681f40c') throw new ApiError(meta.errors.noSuchNote); throw e; 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 b4479aa50d..1450eabc2a 100644 --- a/packages/backend/src/server/api/endpoints/i/update-email.ts +++ b/packages/backend/src/server/api/endpoints/i/update-email.ts @@ -1,4 +1,3 @@ -import $ from 'cafy'; import { publishMainStream } from '@/services/stream'; import define from '../../define'; import rndstr from 'rndstr'; @@ -20,16 +19,6 @@ export const meta = { max: 3, }, - params: { - password: { - validator: $.str, - }, - - email: { - validator: $.optional.nullable.str, - }, - }, - errors: { incorrectPassword: { message: 'Incorrect password.', @@ -45,8 +34,17 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + password: { type: 'string' }, + email: { type: 'string', nullable: true }, + }, + required: ['password'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const profile = await UserProfiles.findOneOrFail(user.id); // Compare password diff --git a/packages/backend/src/server/api/endpoints/i/update.ts b/packages/backend/src/server/api/endpoints/i/update.ts index 6b7e53aa1f..0a5188609b 100644 --- a/packages/backend/src/server/api/endpoints/i/update.ts +++ b/packages/backend/src/server/api/endpoints/i/update.ts @@ -1,6 +1,5 @@ -import $ from 'cafy'; +const RE2 = require('re2'); import * as mfm from 'mfm-js'; -import { ID } from '@/misc/cafy-id'; import { publishMainStream, publishUserEvent } from '@/services/stream'; import acceptAllFollowRequests from '@/services/following/requests/accept-all'; import { publishToFollowers } from '@/services/i/update'; @@ -23,112 +22,6 @@ export const meta = { kind: 'write:account', - params: { - name: { - validator: $.optional.nullable.use(Users.validateName), - }, - - description: { - validator: $.optional.nullable.use(Users.validateDescription), - }, - - lang: { - validator: $.optional.nullable.str.or(Object.keys(langmap)), - }, - - location: { - validator: $.optional.nullable.use(Users.validateLocation), - }, - - birthday: { - validator: $.optional.nullable.use(Users.validateBirthday), - }, - - avatarId: { - validator: $.optional.nullable.type(ID), - }, - - bannerId: { - validator: $.optional.nullable.type(ID), - }, - - fields: { - validator: $.optional.arr($.object()).range(1, 4), - }, - - isLocked: { - validator: $.optional.bool, - }, - - isExplorable: { - validator: $.optional.bool, - }, - - hideOnlineStatus: { - validator: $.optional.bool, - }, - - publicReactions: { - validator: $.optional.bool, - }, - - ffVisibility: { - validator: $.optional.str, - }, - - carefulBot: { - validator: $.optional.bool, - }, - - autoAcceptFollowed: { - validator: $.optional.bool, - }, - - noCrawle: { - validator: $.optional.bool, - }, - - isBot: { - validator: $.optional.bool, - }, - - isCat: { - validator: $.optional.bool, - }, - - injectFeaturedNote: { - validator: $.optional.bool, - }, - - receiveAnnouncementEmail: { - validator: $.optional.bool, - }, - - alwaysMarkNsfw: { - validator: $.optional.bool, - }, - - pinnedPageId: { - validator: $.optional.nullable.type(ID), - }, - - mutedWords: { - validator: $.optional.arr($.arr($.str)), - }, - - mutedInstances: { - validator: $.optional.arr($.str), - }, - - mutingNotificationTypes: { - validator: $.optional.arr($.str.or(notificationTypes as unknown as string[])), - }, - - emailNotificationTypes: { - validator: $.optional.arr($.str), - }, - }, - errors: { noSuchAvatar: { message: 'No such avatar file.', @@ -159,6 +52,12 @@ export const meta = { code: 'NO_SUCH_PAGE', id: '8e01b590-7eb9-431b-a239-860e086c408e', }, + + invalidRegexp: { + message: 'Invalid Regular Expression.', + code: 'INVALID_REGEXP', + id: '0d786918-10df-41cd-8f33-8dec7d9a89a5', + } }, res: { @@ -168,8 +67,60 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + name: { ...Users.nameSchema, nullable: true }, + description: { ...Users.descriptionSchema, nullable: true }, + location: { ...Users.locationSchema, nullable: true }, + birthday: { ...Users.birthdaySchema, nullable: true }, + lang: { type: 'string', enum: Object.keys(langmap), nullable: true }, + avatarId: { type: 'string', format: 'misskey:id', nullable: true }, + bannerId: { type: 'string', format: 'misskey:id', nullable: true }, + fields: { type: 'array', + minItems: 0, + maxItems: 8, + items: { + type: 'object', + properties: { + name: { type: 'string' }, + value: { type: 'string' }, + }, + required: ['name', 'value'], + }, + }, + isLocked: { type: 'boolean' }, + isExplorable: { type: 'boolean' }, + hideOnlineStatus: { type: 'boolean' }, + publicReactions: { type: 'boolean' }, + carefulBot: { type: 'boolean' }, + autoAcceptFollowed: { type: 'boolean' }, + noCrawle: { type: 'boolean' }, + isBot: { type: 'boolean' }, + isCat: { type: 'boolean' }, + showTimelineReplies: { type: 'boolean' }, + injectFeaturedNote: { type: 'boolean' }, + receiveAnnouncementEmail: { type: 'boolean' }, + alwaysMarkNsfw: { type: 'boolean' }, + ffVisibility: { type: 'string', enum: ['public', 'followers', 'private'] }, + pinnedPageId: { type: 'array', items: { + type: 'string', format: 'misskey:id', + } }, + mutedWords: { type: 'array' }, + mutedInstances: { type: 'array', items: { + type: 'string', + } }, + mutingNotificationTypes: { type: 'array', items: { + type: 'string', enum: notificationTypes, + } }, + emailNotificationTypes: { type: 'array', items: { + type: 'string', + } }, + }, +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, _user, token) => { +export default define(meta, paramDef, async (ps, _user, token) => { const user = await Users.findOneOrFail(_user.id); const isSecure = token == null; @@ -187,6 +138,18 @@ export default define(meta, async (ps, _user, token) => { if (ps.avatarId !== undefined) updates.avatarId = ps.avatarId; if (ps.bannerId !== undefined) updates.bannerId = ps.bannerId; if (ps.mutedWords !== undefined) { + // validate regular expression syntax + ps.mutedWords.filter(x => !Array.isArray(x)).forEach(x => { + const regexp = x.match(/^\/(.+)\/(.*)$/); + if (!regexp) throw new ApiError(meta.errors.invalidRegexp); + + try { + new RE2(regexp[1], regexp[2]); + } catch (err) { + throw new ApiError(meta.errors.invalidRegexp); + } + }); + profileUpdates.mutedWords = ps.mutedWords; profileUpdates.enableWordMute = ps.mutedWords.length > 0; } @@ -197,6 +160,7 @@ export default define(meta, async (ps, _user, token) => { if (typeof ps.hideOnlineStatus === 'boolean') updates.hideOnlineStatus = ps.hideOnlineStatus; 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; diff --git a/packages/backend/src/server/api/endpoints/i/user-group-invites.ts b/packages/backend/src/server/api/endpoints/i/user-group-invites.ts index 76a3131e6d..6c38240913 100644 --- a/packages/backend/src/server/api/endpoints/i/user-group-invites.ts +++ b/packages/backend/src/server/api/endpoints/i/user-group-invites.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../define'; import { UserGroupInvitations } from '@/models/index'; import { makePaginationQuery } from '../../common/make-pagination-query'; @@ -11,21 +9,6 @@ export const meta = { kind: 'read:user-groups', - params: { - limit: { - validator: $.optional.num.range(1, 100), - default: 10, - }, - - sinceId: { - validator: $.optional.type(ID), - }, - - untilId: { - validator: $.optional.type(ID), - }, - }, - res: { type: 'array', optional: false, nullable: false, @@ -48,14 +31,24 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, + 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, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const query = makePaginationQuery(UserGroupInvitations.createQueryBuilder('invitation'), ps.sinceId, ps.untilId) .andWhere(`invitation.userId = :meId`, { meId: user.id }) .leftJoinAndSelect('invitation.userGroup', 'user_group'); const invitations = await query - .take(ps.limit!) + .take(ps.limit) .getMany(); return await UserGroupInvitations.packMany(invitations); diff --git a/packages/backend/src/server/api/endpoints/messaging/history.ts b/packages/backend/src/server/api/endpoints/messaging/history.ts index 5ac49cf96b..5c1c040cc3 100644 --- a/packages/backend/src/server/api/endpoints/messaging/history.ts +++ b/packages/backend/src/server/api/endpoints/messaging/history.ts @@ -1,4 +1,3 @@ -import $ from 'cafy'; import define from '../../define'; import { MessagingMessage } from '@/models/entities/messaging-message'; import { MessagingMessages, Mutings, UserGroupJoinings } from '@/models/index'; @@ -11,18 +10,6 @@ export const meta = { kind: 'read:messaging', - params: { - limit: { - validator: $.optional.num.range(1, 100), - default: 10, - }, - - group: { - validator: $.optional.bool, - default: false, - }, - }, - res: { type: 'array', optional: false, nullable: false, @@ -34,8 +21,17 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, + group: { type: 'boolean', default: false }, + }, + required: [], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const mute = await Mutings.find({ muterId: user.id, }); @@ -50,7 +46,7 @@ export default define(meta, async (ps, user) => { const history: MessagingMessage[] = []; - for (let i = 0; i < ps.limit!; i++) { + for (let i = 0; i < ps.limit; i++) { const found = ps.group ? history.map(m => m.groupId!) : history.map(m => (m.userId === user.id) ? m.recipientId! : m.userId!); diff --git a/packages/backend/src/server/api/endpoints/messaging/messages.ts b/packages/backend/src/server/api/endpoints/messaging/messages.ts index 7dbddd80e2..ce477feac4 100644 --- a/packages/backend/src/server/api/endpoints/messaging/messages.ts +++ b/packages/backend/src/server/api/endpoints/messaging/messages.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../define'; import { ApiError } from '../../error'; import { getUser } from '../../common/getters'; @@ -15,34 +13,6 @@ export const meta = { kind: 'read:messaging', - params: { - userId: { - validator: $.optional.type(ID), - }, - - groupId: { - validator: $.optional.type(ID), - }, - - limit: { - validator: $.optional.num.range(1, 100), - default: 10, - }, - - sinceId: { - validator: $.optional.type(ID), - }, - - untilId: { - validator: $.optional.type(ID), - }, - - markAsRead: { - validator: $.optional.bool, - default: true, - }, - }, - res: { type: 'array', optional: false, nullable: false, @@ -74,8 +44,21 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + userId: { type: 'string', format: 'misskey:id' }, + groupId: { 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' }, + markAsRead: { type: 'boolean', default: true }, + }, + required: [], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { if (ps.userId != null) { // Fetch recipient (user) const recipient = await getUser(ps.userId).catch(e => { @@ -97,7 +80,7 @@ export default define(meta, async (ps, user) => { .setParameter('meId', user.id) .setParameter('recipientId', recipient.id); - const messages = await query.take(ps.limit!).getMany(); + const messages = await query.take(ps.limit).getMany(); // Mark all as read if (ps.markAsRead) { @@ -133,7 +116,7 @@ export default define(meta, async (ps, user) => { const query = makePaginationQuery(MessagingMessages.createQueryBuilder('message'), ps.sinceId, ps.untilId) .andWhere(`message.groupId = :groupId`, { groupId: recipientGroup.id }); - const messages = await query.take(ps.limit!).getMany(); + const messages = await query.take(ps.limit).getMany(); // Mark all as read if (ps.markAsRead) { 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 5ec16f5e5a..60b1e2cdce 100644 --- a/packages/backend/src/server/api/endpoints/messaging/messages/create.ts +++ b/packages/backend/src/server/api/endpoints/messaging/messages/create.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../../define'; import { ApiError } from '../../../error'; import { getUser } from '../../../common/getters'; @@ -15,24 +13,6 @@ export const meta = { kind: 'write:messaging', - params: { - userId: { - validator: $.optional.type(ID), - }, - - groupId: { - validator: $.optional.type(ID), - }, - - text: { - validator: $.optional.str.pipe(MessagingMessages.validateText), - }, - - fileId: { - validator: $.optional.type(ID), - }, - }, - res: { type: 'object', optional: false, nullable: false, @@ -84,8 +64,19 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + userId: { type: 'string', format: 'misskey:id' }, + groupId: { type: 'string', format: 'misskey:id' }, + text: { type: 'string', nullable: true, maxLength: 3000 }, + fileId: { type: 'string', format: 'misskey:id' }, + }, + required: [], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { let recipientUser: User | undefined; let recipientGroup: UserGroup | undefined; diff --git a/packages/backend/src/server/api/endpoints/messaging/messages/delete.ts b/packages/backend/src/server/api/endpoints/messaging/messages/delete.ts index 2975419cef..67ecd4a1e6 100644 --- a/packages/backend/src/server/api/endpoints/messaging/messages/delete.ts +++ b/packages/backend/src/server/api/endpoints/messaging/messages/delete.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../../define'; import ms from 'ms'; import { ApiError } from '../../../error'; @@ -19,12 +17,6 @@ export const meta = { minInterval: ms('1sec'), }, - params: { - messageId: { - validator: $.type(ID), - }, - }, - errors: { noSuchMessage: { message: 'No such message.', @@ -34,8 +26,16 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + messageId: { type: 'string', format: 'misskey:id' }, + }, + required: ['messageId'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const message = await MessagingMessages.findOne({ id: ps.messageId, userId: user.id, diff --git a/packages/backend/src/server/api/endpoints/messaging/messages/read.ts b/packages/backend/src/server/api/endpoints/messaging/messages/read.ts index 42c3f49f6f..1d0ddaf98f 100644 --- a/packages/backend/src/server/api/endpoints/messaging/messages/read.ts +++ b/packages/backend/src/server/api/endpoints/messaging/messages/read.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../../define'; import { ApiError } from '../../../error'; import { MessagingMessages } from '@/models/index'; @@ -12,12 +10,6 @@ export const meta = { kind: 'write:messaging', - params: { - messageId: { - validator: $.type(ID), - }, - }, - errors: { noSuchMessage: { message: 'No such message.', @@ -27,8 +19,16 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + messageId: { type: 'string', format: 'misskey:id' }, + }, + required: ['messageId'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const message = await MessagingMessages.findOne(ps.messageId); if (message == null) { diff --git a/packages/backend/src/server/api/endpoints/meta.ts b/packages/backend/src/server/api/endpoints/meta.ts index 693a7a04ec..fdd1586ca5 100644 --- a/packages/backend/src/server/api/endpoints/meta.ts +++ b/packages/backend/src/server/api/endpoints/meta.ts @@ -1,4 +1,3 @@ -import $ from 'cafy'; import config from '@/config/index'; import define from '../define'; import { fetchMeta } from '@/misc/fetch-meta'; @@ -11,13 +10,6 @@ export const meta = { requireCredential: false, - params: { - detail: { - validator: $.optional.bool, - default: true, - }, - }, - res: { type: 'object', optional: false, nullable: false, @@ -448,8 +440,16 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + detail: { type: 'boolean', default: true }, + }, + required: [], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, me) => { +export default define(meta, paramDef, async (ps, me) => { const instance = await fetchMeta(true); const emojis = await Emojis.find({ @@ -499,6 +499,7 @@ export default define(meta, async (ps, me) => { enableRecaptcha: instance.enableRecaptcha, recaptchaSiteKey: instance.recaptchaSiteKey, swPublickey: instance.swPublicKey, + themeColor: instance.themeColor, mascotImageUrl: instance.mascotImageUrl, bannerUrl: instance.bannerUrl, errorImageUrl: instance.errorImageUrl, diff --git a/packages/backend/src/server/api/endpoints/miauth/gen-token.ts b/packages/backend/src/server/api/endpoints/miauth/gen-token.ts index 158c8877e9..e61c78754f 100644 --- a/packages/backend/src/server/api/endpoints/miauth/gen-token.ts +++ b/packages/backend/src/server/api/endpoints/miauth/gen-token.ts @@ -1,4 +1,3 @@ -import $ from 'cafy'; import define from '../../define'; import { AccessTokens } from '@/models/index'; import { genId } from '@/misc/gen-id'; @@ -11,28 +10,6 @@ export const meta = { secure: true, - params: { - session: { - validator: $.nullable.str, - }, - - name: { - validator: $.nullable.optional.str, - }, - - description: { - validator: $.nullable.optional.str, - }, - - iconUrl: { - validator: $.nullable.optional.str, - }, - - permission: { - validator: $.arr($.str).unique(), - }, - }, - res: { type: 'object', optional: false, nullable: false, @@ -45,8 +22,22 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + session: { type: 'string', nullable: true }, + name: { type: 'string', nullable: true }, + description: { type: 'string', nullable: true }, + iconUrl: { type: 'string', nullable: true }, + permission: { type: 'array', uniqueItems: true, items: { + type: 'string', + } }, + }, + required: ['session', 'permission'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { // Generate access token const accessToken = secureRndstr(32, true); diff --git a/packages/backend/src/server/api/endpoints/mute/create.ts b/packages/backend/src/server/api/endpoints/mute/create.ts index 6ba5a453c3..3ef97a1491 100644 --- a/packages/backend/src/server/api/endpoints/mute/create.ts +++ b/packages/backend/src/server/api/endpoints/mute/create.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../define'; import { ApiError } from '../../error'; import { getUser } from '../../common/getters'; @@ -15,12 +13,6 @@ export const meta = { kind: 'write:mutes', - params: { - userId: { - validator: $.type(ID), - }, - }, - errors: { noSuchUser: { message: 'No such user.', @@ -42,8 +34,16 @@ export const meta = { }, } as const; +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, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const muter = user; // č‡Ē分č‡ĒčēĢ diff --git a/packages/backend/src/server/api/endpoints/mute/delete.ts b/packages/backend/src/server/api/endpoints/mute/delete.ts index 21948dc3d1..8a88e13039 100644 --- a/packages/backend/src/server/api/endpoints/mute/delete.ts +++ b/packages/backend/src/server/api/endpoints/mute/delete.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../define'; import { ApiError } from '../../error'; import { getUser } from '../../common/getters'; @@ -13,12 +11,6 @@ export const meta = { kind: 'write:mutes', - params: { - userId: { - validator: $.type(ID), - }, - }, - errors: { noSuchUser: { message: 'No such user.', @@ -40,8 +32,16 @@ export const meta = { }, } as const; +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, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const muter = user; // Check if the mutee is yourself diff --git a/packages/backend/src/server/api/endpoints/mute/list.ts b/packages/backend/src/server/api/endpoints/mute/list.ts index 4c6a81b63c..fd8443e2b7 100644 --- a/packages/backend/src/server/api/endpoints/mute/list.ts +++ b/packages/backend/src/server/api/endpoints/mute/list.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../define'; import { makePaginationQuery } from '../../common/make-pagination-query'; import { Mutings } from '@/models/index'; @@ -11,21 +9,6 @@ export const meta = { kind: 'read:mutes', - params: { - limit: { - validator: $.optional.num.range(1, 100), - default: 30, - }, - - sinceId: { - validator: $.optional.type(ID), - }, - - untilId: { - validator: $.optional.type(ID), - }, - }, - res: { type: 'array', optional: false, nullable: false, @@ -37,13 +20,23 @@ export const meta = { }, } as const; +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, async (ps, me) => { +export default define(meta, paramDef, async (ps, me) => { const query = makePaginationQuery(Mutings.createQueryBuilder('muting'), ps.sinceId, ps.untilId) .andWhere(`muting.muterId = :meId`, { meId: me.id }); const mutings = await query - .take(ps.limit!) + .take(ps.limit) .getMany(); return await Mutings.packMany(mutings, me); diff --git a/packages/backend/src/server/api/endpoints/my/apps.ts b/packages/backend/src/server/api/endpoints/my/apps.ts index 42bd5c5f75..60e5014110 100644 --- a/packages/backend/src/server/api/endpoints/my/apps.ts +++ b/packages/backend/src/server/api/endpoints/my/apps.ts @@ -1,4 +1,3 @@ -import $ from 'cafy'; import define from '../../define'; import { Apps } from '@/models/index'; @@ -7,18 +6,6 @@ export const meta = { requireCredential: true, - params: { - limit: { - validator: $.optional.num.range(1, 100), - default: 10, - }, - - offset: { - validator: $.optional.num.min(0), - default: 0, - }, - }, - res: { type: 'array', optional: false, nullable: false, @@ -69,15 +56,24 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, + offset: { type: 'integer', default: 0 }, + }, + required: [], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const query = { userId: user.id, }; const apps = await Apps.find({ where: query, - take: ps.limit!, + take: ps.limit, skip: ps.offset, }); diff --git a/packages/backend/src/server/api/endpoints/notes.ts b/packages/backend/src/server/api/endpoints/notes.ts index 9edc6cb11c..0106bac516 100644 --- a/packages/backend/src/server/api/endpoints/notes.ts +++ b/packages/backend/src/server/api/endpoints/notes.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../define'; import { makePaginationQuery } from '../common/make-pagination-query'; import { Notes } from '@/models/index'; @@ -7,41 +5,6 @@ import { Notes } from '@/models/index'; export const meta = { tags: ['notes'], - params: { - local: { - validator: $.optional.bool, - }, - - reply: { - validator: $.optional.bool, - }, - - renote: { - validator: $.optional.bool, - }, - - withFiles: { - validator: $.optional.bool, - }, - - poll: { - validator: $.optional.bool, - }, - - limit: { - validator: $.optional.num.range(1, 100), - default: 10, - }, - - sinceId: { - validator: $.optional.type(ID), - }, - - untilId: { - validator: $.optional.type(ID), - }, - }, - res: { type: 'array', optional: false, nullable: false, @@ -53,8 +16,23 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + local: { type: 'boolean' }, + reply: { type: 'boolean' }, + renote: { type: 'boolean' }, + withFiles: { type: 'boolean' }, + poll: { type: 'boolean' }, + limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, + 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, async (ps) => { +export default define(meta, paramDef, async (ps) => { const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId) .andWhere(`note.visibility = 'public'`) .andWhere(`note.localOnly = FALSE`) @@ -89,7 +67,7 @@ export default define(meta, async (ps) => { // query.isBot = bot; //} - const notes = await query.take(ps.limit!).getMany(); + const notes = await query.take(ps.limit).getMany(); return await Notes.packMany(notes); }); diff --git a/packages/backend/src/server/api/endpoints/notes/children.ts b/packages/backend/src/server/api/endpoints/notes/children.ts index 088ef65e96..ea2c166b75 100644 --- a/packages/backend/src/server/api/endpoints/notes/children.ts +++ b/packages/backend/src/server/api/endpoints/notes/children.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../define'; import { makePaginationQuery } from '../../common/make-pagination-query'; import { generateVisibilityQuery } from '../../common/generate-visibility-query'; @@ -14,25 +12,6 @@ export const meta = { requireCredential: false, - params: { - noteId: { - validator: $.type(ID), - }, - - limit: { - validator: $.optional.num.range(1, 100), - default: 10, - }, - - sinceId: { - validator: $.optional.type(ID), - }, - - untilId: { - validator: $.optional.type(ID), - }, - }, - res: { type: 'array', optional: false, nullable: false, @@ -44,8 +23,19 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + noteId: { 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' }, + }, + required: ['noteId'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId) .andWhere(new Brackets(qb => { qb .where(`note.replyId = :noteId`, { noteId: ps.noteId }) @@ -69,7 +59,7 @@ export default define(meta, async (ps, user) => { if (user) generateBlockedUserQuery(query, user); if (user) generateMutedInstanceQuery(query, user); - const notes = await query.take(ps.limit!).getMany(); + const notes = await query.take(ps.limit).getMany(); return await Notes.packMany(notes, user); }); diff --git a/packages/backend/src/server/api/endpoints/notes/clips.ts b/packages/backend/src/server/api/endpoints/notes/clips.ts index b89c6db4a8..2caa3a499f 100644 --- a/packages/backend/src/server/api/endpoints/notes/clips.ts +++ b/packages/backend/src/server/api/endpoints/notes/clips.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../define'; import { ClipNotes, Clips } from '@/models/index'; import { getNote } from '../../common/getters'; @@ -11,12 +9,6 @@ export const meta = { requireCredential: false, - params: { - noteId: { - validator: $.type(ID), - }, - }, - res: { type: 'array', optional: false, nullable: false, @@ -36,8 +28,16 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + noteId: { type: 'string', format: 'misskey:id' }, + }, + required: ['noteId'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, me) => { +export default define(meta, paramDef, async (ps, me) => { const note = await getNote(ps.noteId).catch(e => { if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); throw e; diff --git a/packages/backend/src/server/api/endpoints/notes/conversation.ts b/packages/backend/src/server/api/endpoints/notes/conversation.ts index 4bd89c32e7..c199a3f831 100644 --- a/packages/backend/src/server/api/endpoints/notes/conversation.ts +++ b/packages/backend/src/server/api/endpoints/notes/conversation.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../define'; import { ApiError } from '../../error'; import { getNote } from '../../common/getters'; @@ -11,22 +9,6 @@ export const meta = { requireCredential: false, - params: { - noteId: { - validator: $.type(ID), - }, - - limit: { - validator: $.optional.num.range(1, 100), - default: 10, - }, - - offset: { - validator: $.optional.num.min(0), - default: 0, - }, - }, - res: { type: 'array', optional: false, nullable: false, @@ -46,8 +28,18 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + noteId: { type: 'string', format: 'misskey:id' }, + limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, + offset: { type: 'integer', default: 0 }, + }, + required: ['noteId'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const note = await getNote(ps.noteId).catch(e => { if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); throw e; @@ -65,7 +57,7 @@ export default define(meta, async (ps, user) => { conversation.push(p); } - if (conversation.length == ps.limit!) { + if (conversation.length == ps.limit) { return; } diff --git a/packages/backend/src/server/api/endpoints/notes/create.ts b/packages/backend/src/server/api/endpoints/notes/create.ts index 4efa76b248..7d81abc3a7 100644 --- a/packages/backend/src/server/api/endpoints/notes/create.ts +++ b/packages/backend/src/server/api/endpoints/notes/create.ts @@ -1,11 +1,9 @@ -import $ from 'cafy'; import ms from 'ms'; import { length } from 'stringz'; import create from '@/services/note/create'; import define from '../../define'; import { fetchMeta } from '@/misc/fetch-meta'; import { ApiError } from '../../error'; -import { ID } from '@/misc/cafy-id'; import { User } from '@/models/entities/user'; import { Users, DriveFiles, Notes, Channels, Blockings } from '@/models/index'; import { DriveFile } from '@/models/entities/drive-file'; @@ -34,84 +32,6 @@ export const meta = { kind: 'write:notes', - params: { - visibility: { - validator: $.optional.str.or(noteVisibilities as unknown as string[]), - default: 'public', - }, - - visibleUserIds: { - validator: $.optional.arr($.type(ID)).unique().min(0), - }, - - text: { - validator: $.optional.nullable.str.pipe(text => - text.trim() != '' - && length(text.trim()) <= maxNoteTextLength - && Array.from(text.trim()).length <= DB_MAX_NOTE_TEXT_LENGTH, // DB limit - ), - default: null, - }, - - cw: { - validator: $.optional.nullable.str.pipe(Notes.validateCw), - }, - - localOnly: { - validator: $.optional.bool, - default: false, - }, - - noExtractMentions: { - validator: $.optional.bool, - default: false, - }, - - noExtractHashtags: { - validator: $.optional.bool, - default: false, - }, - - noExtractEmojis: { - validator: $.optional.bool, - default: false, - }, - - fileIds: { - validator: $.optional.arr($.type(ID)).unique().range(1, 16), - }, - - mediaIds: { - validator: $.optional.arr($.type(ID)).unique().range(1, 16), - deprecated: true, - }, - - replyId: { - validator: $.optional.nullable.type(ID), - }, - - renoteId: { - validator: $.optional.nullable.type(ID), - }, - - channelId: { - validator: $.optional.nullable.type(ID), - }, - - poll: { - validator: $.optional.nullable.obj({ - choices: $.arr($.str) - .unique() - .range(2, 10) - .each(c => c.length > 0 && c.length < 50), - multiple: $.optional.bool, - expiresAt: $.optional.nullable.num.int(), - expiredAfter: $.optional.nullable.num.int().min(1), - }).strict(), - ref: 'poll', - }, - }, - res: { type: 'object', optional: false, nullable: false, @@ -175,8 +95,49 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + visibility: { type: 'string', enum: ['public', 'home', 'followers', 'specified'], default: "public" }, + visibleUserIds: { type: 'array', uniqueItems: true, items: { + type: 'string', format: 'misskey:id', + } }, + text: { type: 'string', nullable: true, maxLength: 3000, default: null }, + cw: { type: 'string', nullable: true, maxLength: 100 }, + 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: { 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'], + }, + }, + required: [], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { let visibleUsers: User[] = []; if (ps.visibleUserIds) { visibleUsers = (await Promise.all(ps.visibleUserIds.map(id => Users.findOne(id)))) diff --git a/packages/backend/src/server/api/endpoints/notes/delete.ts b/packages/backend/src/server/api/endpoints/notes/delete.ts index 9e080d9e99..62a2a98469 100644 --- a/packages/backend/src/server/api/endpoints/notes/delete.ts +++ b/packages/backend/src/server/api/endpoints/notes/delete.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import deleteNote from '@/services/note/delete'; import define from '../../define'; import ms from 'ms'; @@ -20,12 +18,6 @@ export const meta = { minInterval: ms('1sec'), }, - params: { - noteId: { - validator: $.type(ID), - }, - }, - errors: { noSuchNote: { message: 'No such note.', @@ -41,8 +33,16 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + noteId: { type: 'string', format: 'misskey:id' }, + }, + required: ['noteId'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const note = await getNote(ps.noteId).catch(e => { if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); throw e; 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 78da6a3b00..570e2354ba 100644 --- a/packages/backend/src/server/api/endpoints/notes/favorites/create.ts +++ b/packages/backend/src/server/api/endpoints/notes/favorites/create.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../../define'; import { ApiError } from '../../../error'; import { getNote } from '../../../common/getters'; @@ -13,12 +11,6 @@ export const meta = { kind: 'write:favorites', - params: { - noteId: { - validator: $.type(ID), - }, - }, - errors: { noSuchNote: { message: 'No such note.', @@ -34,8 +26,16 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + noteId: { type: 'string', format: 'misskey:id' }, + }, + required: ['noteId'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { // Get favoritee const note = await getNote(ps.noteId).catch(e => { if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); 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 3f3d50f0d5..b33a5f148b 100644 --- a/packages/backend/src/server/api/endpoints/notes/favorites/delete.ts +++ b/packages/backend/src/server/api/endpoints/notes/favorites/delete.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../../define'; import { ApiError } from '../../../error'; import { getNote } from '../../../common/getters'; @@ -12,12 +10,6 @@ export const meta = { kind: 'write:favorites', - params: { - noteId: { - validator: $.type(ID), - }, - }, - errors: { noSuchNote: { message: 'No such note.', @@ -33,8 +25,16 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + noteId: { type: 'string', format: 'misskey:id' }, + }, + required: ['noteId'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { // Get favoritee const note = await getNote(ps.noteId).catch(e => { if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); diff --git a/packages/backend/src/server/api/endpoints/notes/featured.ts b/packages/backend/src/server/api/endpoints/notes/featured.ts index 5a47fb9e08..8a4297de62 100644 --- a/packages/backend/src/server/api/endpoints/notes/featured.ts +++ b/packages/backend/src/server/api/endpoints/notes/featured.ts @@ -1,4 +1,3 @@ -import $ from 'cafy'; import define from '../../define'; import { generateMutedUserQuery } from '../../common/generate-muted-user-query'; import { Notes } from '@/models/index'; @@ -9,18 +8,6 @@ export const meta = { requireCredential: false, - params: { - limit: { - validator: $.optional.num.range(1, 100), - default: 10, - }, - - offset: { - validator: $.optional.num.min(0), - default: 0, - }, - }, - res: { type: 'array', optional: false, nullable: false, @@ -32,8 +19,17 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, + offset: { type: 'integer', default: 0 }, + }, + required: [], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const max = 30; const day = 1000 * 60 * 60 * 24 * 3; // 3æ—Ĩ前ぞで 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 cac8b7d8a9..23ca8752c9 100644 --- a/packages/backend/src/server/api/endpoints/notes/global-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/global-timeline.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../define'; import { fetchMeta } from '@/misc/fetch-meta'; import { ApiError } from '../../error'; @@ -15,33 +13,6 @@ import { generateBlockedUserQuery } from '../../common/generate-block-query'; export const meta = { tags: ['notes'], - params: { - withFiles: { - validator: $.optional.bool, - }, - - limit: { - validator: $.optional.num.range(1, 100), - default: 10, - }, - - sinceId: { - validator: $.optional.type(ID), - }, - - untilId: { - validator: $.optional.type(ID), - }, - - sinceDate: { - validator: $.optional.num, - }, - - untilDate: { - validator: $.optional.num, - }, - }, - res: { type: 'array', optional: false, nullable: false, @@ -61,8 +32,21 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + withFiles: { type: 'boolean' }, + limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, + sinceId: { type: 'string', format: 'misskey:id' }, + untilId: { type: 'string', format: 'misskey:id' }, + sinceDate: { type: 'integer' }, + untilDate: { type: 'integer' }, + }, + required: [], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const m = await fetchMeta(); if (m.disableGlobalTimeline) { if (user == null || (!user.isAdmin && !user.isModerator)) { @@ -92,11 +76,11 @@ export default define(meta, async (ps, user) => { } //#endregion - const timeline = await query.take(ps.limit!).getMany(); + const timeline = await query.take(ps.limit).getMany(); process.nextTick(() => { if (user) { - activeUsersChart.update(user); + activeUsersChart.read(user); } }); 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 9683df4611..ba6f3b5927 100644 --- a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../define'; import { fetchMeta } from '@/misc/fetch-meta'; import { ApiError } from '../../error'; @@ -20,48 +18,6 @@ export const meta = { requireCredential: true, - params: { - limit: { - validator: $.optional.num.range(1, 100), - default: 10, - }, - - sinceId: { - validator: $.optional.type(ID), - }, - - untilId: { - validator: $.optional.type(ID), - }, - - sinceDate: { - validator: $.optional.num, - }, - - untilDate: { - validator: $.optional.num, - }, - - includeMyRenotes: { - validator: $.optional.bool, - default: true, - }, - - includeRenotedMyNotes: { - validator: $.optional.bool, - default: true, - }, - - includeLocalRenotes: { - validator: $.optional.bool, - default: true, - }, - - withFiles: { - validator: $.optional.bool, - }, - }, - res: { type: 'array', optional: false, nullable: false, @@ -81,8 +37,24 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, + sinceId: { type: 'string', format: 'misskey:id' }, + untilId: { type: 'string', format: 'misskey:id' }, + sinceDate: { type: 'integer' }, + untilDate: { type: 'integer' }, + includeMyRenotes: { type: 'boolean', default: true }, + includeRenotedMyNotes: { type: 'boolean', default: true }, + includeLocalRenotes: { type: 'boolean', default: true }, + withFiles: { type: 'boolean' }, + }, + required: [], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const m = await fetchMeta(); if (m.disableLocalTimeline && !user.isAdmin && !user.isModerator) { throw new ApiError(meta.errors.stlDisabled); @@ -149,11 +121,11 @@ export default define(meta, async (ps, user) => { } //#endregion - const timeline = await query.take(ps.limit!).getMany(); + const timeline = await query.take(ps.limit).getMany(); process.nextTick(() => { if (user) { - activeUsersChart.update(user); + activeUsersChart.read(user); } }); 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 7776644124..7c118317d6 100644 --- a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../define'; import { fetchMeta } from '@/misc/fetch-meta'; import { ApiError } from '../../error'; @@ -17,42 +15,6 @@ import { generateBlockedUserQuery } from '../../common/generate-block-query'; export const meta = { tags: ['notes'], - params: { - withFiles: { - validator: $.optional.bool, - }, - - fileType: { - validator: $.optional.arr($.str), - }, - - excludeNsfw: { - validator: $.optional.bool, - default: false, - }, - - limit: { - validator: $.optional.num.range(1, 100), - default: 10, - }, - - sinceId: { - validator: $.optional.type(ID), - }, - - untilId: { - validator: $.optional.type(ID), - }, - - sinceDate: { - validator: $.optional.num, - }, - - untilDate: { - validator: $.optional.num, - }, - }, - res: { type: 'array', optional: false, nullable: false, @@ -72,8 +34,25 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + withFiles: { type: 'boolean' }, + fileType: { type: 'array', items: { + type: 'string', + } }, + excludeNsfw: { type: 'boolean', default: false }, + limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, + sinceId: { type: 'string', format: 'misskey:id' }, + untilId: { type: 'string', format: 'misskey:id' }, + sinceDate: { type: 'integer' }, + untilDate: { type: 'integer' }, + }, + required: [], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const m = await fetchMeta(); if (m.disableLocalTimeline) { if (user == null || (!user.isAdmin && !user.isModerator)) { @@ -118,11 +97,11 @@ export default define(meta, async (ps, user) => { } //#endregion - const timeline = await query.take(ps.limit!).getMany(); + const timeline = await query.take(ps.limit).getMany(); process.nextTick(() => { if (user) { - activeUsersChart.update(user); + activeUsersChart.read(user); } }); diff --git a/packages/backend/src/server/api/endpoints/notes/mentions.ts b/packages/backend/src/server/api/endpoints/notes/mentions.ts index 81b3844365..265882fa70 100644 --- a/packages/backend/src/server/api/endpoints/notes/mentions.ts +++ b/packages/backend/src/server/api/endpoints/notes/mentions.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../define'; import read from '@/services/note/read'; import { Notes, Followings } from '@/models/index'; @@ -15,30 +13,6 @@ export const meta = { requireCredential: true, - params: { - following: { - validator: $.optional.bool, - default: false, - }, - - limit: { - validator: $.optional.num.range(1, 100), - default: 10, - }, - - sinceId: { - validator: $.optional.type(ID), - }, - - untilId: { - validator: $.optional.type(ID), - }, - - visibility: { - validator: $.optional.str, - }, - }, - res: { type: 'array', optional: false, nullable: false, @@ -50,8 +24,20 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + following: { type: 'boolean', default: false }, + limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, + sinceId: { type: 'string', format: 'misskey:id' }, + untilId: { type: 'string', format: 'misskey:id' }, + visibility: { type: 'string' }, + }, + required: [], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const followingQuery = Followings.createQueryBuilder('following') .select('following.followeeId') .where('following.followerId = :followerId', { followerId: user.id }); @@ -81,7 +67,7 @@ export default define(meta, async (ps, user) => { query.setParameters(followingQuery.getParameters()); } - const mentions = await query.take(ps.limit!).getMany(); + const mentions = await query.take(ps.limit).getMany(); read(user.id, mentions); diff --git a/packages/backend/src/server/api/endpoints/notes/polls/recommendation.ts b/packages/backend/src/server/api/endpoints/notes/polls/recommendation.ts index 79b558e65e..4030694b58 100644 --- a/packages/backend/src/server/api/endpoints/notes/polls/recommendation.ts +++ b/packages/backend/src/server/api/endpoints/notes/polls/recommendation.ts @@ -1,4 +1,3 @@ -import $ from 'cafy'; import define from '../../../define'; import { Polls, Mutings, Notes, PollVotes } from '@/models/index'; import { Brackets, In } from 'typeorm'; @@ -8,18 +7,6 @@ export const meta = { requireCredential: true, - params: { - limit: { - validator: $.optional.num.range(1, 100), - default: 10, - }, - - offset: { - validator: $.optional.num.min(0), - default: 0, - }, - }, - res: { type: 'array', optional: false, nullable: false, @@ -31,8 +18,17 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, + offset: { type: 'integer', default: 0 }, + }, + required: [], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const query = Polls.createQueryBuilder('poll') .where('poll.userHost IS NULL') .andWhere(`poll.userId != :meId`, { meId: user.id }) @@ -64,7 +60,7 @@ export default define(meta, async (ps, user) => { query.setParameters(mutingQuery.getParameters()); //#endregion - const polls = await query.take(ps.limit!).skip(ps.offset).getMany(); + const polls = await query.take(ps.limit).skip(ps.offset).getMany(); if (polls.length === 0) return []; 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 77387cacb2..739df8a03c 100644 --- a/packages/backend/src/server/api/endpoints/notes/polls/vote.ts +++ b/packages/backend/src/server/api/endpoints/notes/polls/vote.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import { publishNoteStream } from '@/services/stream'; import { createNotification } from '@/services/create-notification'; import define from '../../../define'; @@ -21,16 +19,6 @@ export const meta = { kind: 'write:votes', - params: { - noteId: { - validator: $.type(ID), - }, - - choice: { - validator: $.num, - }, - }, - errors: { noSuchNote: { message: 'No such note.', @@ -70,8 +58,17 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + noteId: { type: 'string', format: 'misskey:id' }, + choice: { type: 'integer' }, + }, + required: ['noteId', 'choice'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const createdAt = new Date(); // Get votee diff --git a/packages/backend/src/server/api/endpoints/notes/reactions.ts b/packages/backend/src/server/api/endpoints/notes/reactions.ts index 5205a78171..fdcaed64e1 100644 --- a/packages/backend/src/server/api/endpoints/notes/reactions.ts +++ b/packages/backend/src/server/api/endpoints/notes/reactions.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../define'; import { getNote } from '../../common/getters'; import { ApiError } from '../../error'; @@ -12,34 +10,6 @@ export const meta = { requireCredential: false, - params: { - noteId: { - validator: $.type(ID), - }, - - type: { - validator: $.optional.nullable.str, - }, - - limit: { - validator: $.optional.num.range(1, 100), - default: 10, - }, - - offset: { - validator: $.optional.num, - default: 0, - }, - - sinceId: { - validator: $.optional.type(ID), - }, - - untilId: { - validator: $.optional.type(ID), - }, - }, - res: { type: 'array', optional: false, nullable: false, @@ -59,8 +29,21 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + noteId: { type: 'string', format: 'misskey:id' }, + type: { type: 'string', nullable: true }, + limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, + offset: { type: 'integer', default: 0 }, + sinceId: { type: 'string', format: 'misskey:id' }, + untilId: { type: 'string', format: 'misskey:id' }, + }, + required: ['noteId'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const note = await getNote(ps.noteId).catch(e => { if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); throw e; @@ -80,7 +63,7 @@ export default define(meta, async (ps, user) => { const reactions = await NoteReactions.find({ where: query, - take: ps.limit!, + take: ps.limit, skip: ps.offset, order: { id: -1, diff --git a/packages/backend/src/server/api/endpoints/notes/reactions/create.ts b/packages/backend/src/server/api/endpoints/notes/reactions/create.ts index 1b42781ceb..a898b3249a 100644 --- a/packages/backend/src/server/api/endpoints/notes/reactions/create.ts +++ b/packages/backend/src/server/api/endpoints/notes/reactions/create.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import createReaction from '@/services/note/reaction/create'; import define from '../../../define'; import { getNote } from '../../../common/getters'; @@ -12,16 +10,6 @@ export const meta = { kind: 'write:reactions', - params: { - noteId: { - validator: $.type(ID), - }, - - reaction: { - validator: $.str, - }, - }, - errors: { noSuchNote: { message: 'No such note.', @@ -43,8 +31,17 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + noteId: { type: 'string', format: 'misskey:id' }, + reaction: { type: 'string' }, + }, + required: ['noteId', 'reaction'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const note = await getNote(ps.noteId).catch(e => { if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); throw e; diff --git a/packages/backend/src/server/api/endpoints/notes/reactions/delete.ts b/packages/backend/src/server/api/endpoints/notes/reactions/delete.ts index 1d686b5971..53cadac7c9 100644 --- a/packages/backend/src/server/api/endpoints/notes/reactions/delete.ts +++ b/packages/backend/src/server/api/endpoints/notes/reactions/delete.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../../define'; import ms from 'ms'; import deleteReaction from '@/services/note/reaction/delete'; @@ -19,12 +17,6 @@ export const meta = { minInterval: ms('3sec'), }, - params: { - noteId: { - validator: $.type(ID), - }, - }, - errors: { noSuchNote: { message: 'No such note.', @@ -40,8 +32,16 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + noteId: { type: 'string', format: 'misskey:id' }, + }, + required: ['noteId'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const note = await getNote(ps.noteId).catch(e => { if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); throw e; diff --git a/packages/backend/src/server/api/endpoints/notes/renotes.ts b/packages/backend/src/server/api/endpoints/notes/renotes.ts index f71d23146a..aa5c2c0a45 100644 --- a/packages/backend/src/server/api/endpoints/notes/renotes.ts +++ b/packages/backend/src/server/api/endpoints/notes/renotes.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../define'; import { getNote } from '../../common/getters'; import { ApiError } from '../../error'; @@ -14,25 +12,6 @@ export const meta = { requireCredential: false, - params: { - noteId: { - validator: $.type(ID), - }, - - limit: { - validator: $.optional.num.range(1, 100), - default: 10, - }, - - sinceId: { - validator: $.optional.type(ID), - }, - - untilId: { - validator: $.optional.type(ID), - }, - }, - res: { type: 'array', optional: false, nullable: false, @@ -52,8 +31,19 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + noteId: { 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' }, + }, + required: ['noteId'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const note = await getNote(ps.noteId).catch(e => { if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); throw e; @@ -71,7 +61,7 @@ export default define(meta, async (ps, user) => { if (user) generateMutedUserQuery(query, user); if (user) generateBlockedUserQuery(query, user); - const renotes = await query.take(ps.limit!).getMany(); + const renotes = await query.take(ps.limit).getMany(); return await Notes.packMany(renotes, user); }); diff --git a/packages/backend/src/server/api/endpoints/notes/replies.ts b/packages/backend/src/server/api/endpoints/notes/replies.ts index 62c56534e1..b88e3d7168 100644 --- a/packages/backend/src/server/api/endpoints/notes/replies.ts +++ b/packages/backend/src/server/api/endpoints/notes/replies.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../define'; import { Notes } from '@/models/index'; import { makePaginationQuery } from '../../common/make-pagination-query'; @@ -12,25 +10,6 @@ export const meta = { requireCredential: false, - params: { - noteId: { - validator: $.type(ID), - }, - - sinceId: { - validator: $.optional.type(ID), - }, - - untilId: { - validator: $.optional.type(ID), - }, - - limit: { - validator: $.optional.num.range(1, 100), - default: 10, - }, - }, - res: { type: 'array', optional: false, nullable: false, @@ -42,8 +21,19 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + noteId: { type: 'string', format: 'misskey:id' }, + sinceId: { type: 'string', format: 'misskey:id' }, + untilId: { type: 'string', format: 'misskey:id' }, + limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, + }, + required: ['noteId'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId) .andWhere('note.replyId = :replyId', { replyId: ps.noteId }) .innerJoinAndSelect('note.user', 'user') @@ -56,7 +46,7 @@ export default define(meta, async (ps, user) => { if (user) generateMutedUserQuery(query, user); if (user) generateBlockedUserQuery(query, user); - const timeline = await query.take(ps.limit!).getMany(); + const timeline = await query.take(ps.limit).getMany(); return await Notes.packMany(timeline, user); }); 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 87eaffe2f1..bf18a20b97 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 @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../define'; import { makePaginationQuery } from '../../common/make-pagination-query'; import { Notes } from '@/models/index'; @@ -13,48 +11,6 @@ import { generateBlockedUserQuery } from '../../common/generate-block-query'; export const meta = { tags: ['notes', 'hashtags'], - params: { - tag: { - validator: $.optional.str, - }, - - query: { - validator: $.optional.arr($.arr($.str)), - }, - - reply: { - validator: $.optional.nullable.bool, - default: null, - }, - - renote: { - validator: $.optional.nullable.bool, - default: null, - }, - - withFiles: { - validator: $.optional.bool, - }, - - poll: { - validator: $.optional.nullable.bool, - default: null, - }, - - sinceId: { - validator: $.optional.type(ID), - }, - - untilId: { - validator: $.optional.type(ID), - }, - - limit: { - validator: $.optional.num.range(1, 100), - default: 10, - }, - }, - res: { type: 'array', optional: false, nullable: false, @@ -66,8 +22,28 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + tag: { type: 'string' }, + query: { type: 'array', items: { + type: 'array', items: { + type: 'string', + }, + } }, + reply: { type: 'boolean', nullable: true, default: null }, + renote: { type: 'boolean', nullable: true, default: null }, + withFiles: { type: 'boolean' }, + poll: { type: 'boolean', nullable: true, default: null }, + sinceId: { type: 'string', format: 'misskey:id' }, + untilId: { type: 'string', format: 'misskey:id' }, + limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, + }, + required: [], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, me) => { +export default define(meta, paramDef, async (ps, me) => { const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId) .innerJoinAndSelect('note.user', 'user') .leftJoinAndSelect('note.reply', 'reply') @@ -129,7 +105,7 @@ export default define(meta, async (ps, me) => { } // Search notes - const notes = await query.take(ps.limit!).getMany(); + const notes = await query.take(ps.limit).getMany(); return await Notes.packMany(notes, me); }); diff --git a/packages/backend/src/server/api/endpoints/notes/search.ts b/packages/backend/src/server/api/endpoints/notes/search.ts index e75212b14b..bcb9c2b8a1 100644 --- a/packages/backend/src/server/api/endpoints/notes/search.ts +++ b/packages/backend/src/server/api/endpoints/notes/search.ts @@ -1,9 +1,7 @@ -import $ from 'cafy'; import es from '../../../../db/elasticsearch'; import define from '../../define'; import { Notes } from '@/models/index'; import { In } from 'typeorm'; -import { ID } from '@/misc/cafy-id'; import config from '@/config/index'; import { makePaginationQuery } from '../../common/make-pagination-query'; import { generateVisibilityQuery } from '../../common/generate-visibility-query'; @@ -15,40 +13,6 @@ export const meta = { requireCredential: false, - params: { - query: { - validator: $.str, - }, - - sinceId: { - validator: $.optional.type(ID), - }, - - untilId: { - validator: $.optional.type(ID), - }, - - limit: { - validator: $.optional.num.range(1, 100), - default: 10, - }, - - host: { - validator: $.optional.nullable.str, - default: undefined, - }, - - userId: { - validator: $.optional.nullable.type(ID), - default: null, - }, - - channelId: { - validator: $.optional.nullable.type(ID), - default: null, - }, - }, - res: { type: 'array', optional: false, nullable: false, @@ -63,8 +27,23 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + query: { type: 'string' }, + sinceId: { type: 'string', format: 'misskey:id' }, + untilId: { type: 'string', format: 'misskey:id' }, + limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, + offset: { type: 'integer', default: 0 }, + host: { type: 'string', nullable: true }, + userId: { type: 'string', format: 'misskey:id', nullable: true, default: null }, + channelId: { type: 'string', format: 'misskey:id', nullable: true, default: null }, + }, + required: ['query'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, me) => { +export default define(meta, paramDef, async (ps, me) => { if (es == null) { const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId); @@ -86,7 +65,7 @@ export default define(meta, async (ps, me) => { if (me) generateMutedUserQuery(query, me); if (me) generateBlockedUserQuery(query, me); - const notes = await query.take(ps.limit!).getMany(); + const notes = await query.take(ps.limit).getMany(); return await Notes.packMany(notes, me); } else { @@ -115,7 +94,7 @@ export default define(meta, async (ps, me) => { const result = await es.search({ index: config.elasticsearch.index || 'misskey_note', body: { - size: ps.limit!, + size: ps.limit, from: ps.offset, query: { bool: { diff --git a/packages/backend/src/server/api/endpoints/notes/show.ts b/packages/backend/src/server/api/endpoints/notes/show.ts index feb94be1a1..8f75eeb5e8 100644 --- a/packages/backend/src/server/api/endpoints/notes/show.ts +++ b/packages/backend/src/server/api/endpoints/notes/show.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../define'; import { getNote } from '../../common/getters'; import { ApiError } from '../../error'; @@ -10,12 +8,6 @@ export const meta = { requireCredential: false, - params: { - noteId: { - validator: $.type(ID), - }, - }, - res: { type: 'object', optional: false, nullable: false, @@ -31,8 +23,16 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + noteId: { type: 'string', format: 'misskey:id' }, + }, + required: ['noteId'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const note = await getNote(ps.noteId).catch(e => { if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); throw e; diff --git a/packages/backend/src/server/api/endpoints/notes/state.ts b/packages/backend/src/server/api/endpoints/notes/state.ts index c3e9090bbf..cf5f08ea9a 100644 --- a/packages/backend/src/server/api/endpoints/notes/state.ts +++ b/packages/backend/src/server/api/endpoints/notes/state.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../define'; import { NoteFavorites, Notes, NoteThreadMutings, NoteWatchings } from '@/models/index'; @@ -8,12 +6,6 @@ export const meta = { requireCredential: true, - params: { - noteId: { - validator: $.type(ID), - }, - }, - res: { type: 'object', optional: false, nullable: false, @@ -34,8 +26,16 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + noteId: { type: 'string', format: 'misskey:id' }, + }, + required: ['noteId'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const note = await Notes.findOneOrFail(ps.noteId); const [favorite, watching, threadMuting] = await Promise.all([ diff --git a/packages/backend/src/server/api/endpoints/notes/thread-muting/create.ts b/packages/backend/src/server/api/endpoints/notes/thread-muting/create.ts index a8b50d90f6..4068a8a571 100644 --- a/packages/backend/src/server/api/endpoints/notes/thread-muting/create.ts +++ b/packages/backend/src/server/api/endpoints/notes/thread-muting/create.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../../define'; import { getNote } from '../../../common/getters'; import { ApiError } from '../../../error'; @@ -14,12 +12,6 @@ export const meta = { kind: 'write:account', - params: { - noteId: { - validator: $.type(ID), - }, - }, - errors: { noSuchNote: { message: 'No such note.', @@ -29,8 +21,16 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + noteId: { type: 'string', format: 'misskey:id' }, + }, + required: ['noteId'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const note = await getNote(ps.noteId).catch(e => { if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); throw e; diff --git a/packages/backend/src/server/api/endpoints/notes/thread-muting/delete.ts b/packages/backend/src/server/api/endpoints/notes/thread-muting/delete.ts index f76b526ce1..09b64f7277 100644 --- a/packages/backend/src/server/api/endpoints/notes/thread-muting/delete.ts +++ b/packages/backend/src/server/api/endpoints/notes/thread-muting/delete.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../../define'; import { getNote } from '../../../common/getters'; import { ApiError } from '../../../error'; @@ -12,12 +10,6 @@ export const meta = { kind: 'write:account', - params: { - noteId: { - validator: $.type(ID), - }, - }, - errors: { noSuchNote: { message: 'No such note.', @@ -27,8 +19,16 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + noteId: { type: 'string', format: 'misskey:id' }, + }, + required: ['noteId'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const note = await getNote(ps.noteId).catch(e => { if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); throw e; diff --git a/packages/backend/src/server/api/endpoints/notes/timeline.ts b/packages/backend/src/server/api/endpoints/notes/timeline.ts index 8be2861aec..c06a98feaa 100644 --- a/packages/backend/src/server/api/endpoints/notes/timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/timeline.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../define'; import { makePaginationQuery } from '../../common/make-pagination-query'; import { Notes, Followings } from '@/models/index'; @@ -18,48 +16,6 @@ export const meta = { requireCredential: true, - params: { - limit: { - validator: $.optional.num.range(1, 100), - default: 10, - }, - - sinceId: { - validator: $.optional.type(ID), - }, - - untilId: { - validator: $.optional.type(ID), - }, - - sinceDate: { - validator: $.optional.num, - }, - - untilDate: { - validator: $.optional.num, - }, - - includeMyRenotes: { - validator: $.optional.bool, - default: true, - }, - - includeRenotedMyNotes: { - validator: $.optional.bool, - default: true, - }, - - includeLocalRenotes: { - validator: $.optional.bool, - default: true, - }, - - withFiles: { - validator: $.optional.bool, - }, - }, - res: { type: 'array', optional: false, nullable: false, @@ -71,8 +27,24 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, + sinceId: { type: 'string', format: 'misskey:id' }, + untilId: { type: 'string', format: 'misskey:id' }, + sinceDate: { type: 'integer' }, + untilDate: { type: 'integer' }, + includeMyRenotes: { type: 'boolean', default: true }, + includeRenotedMyNotes: { type: 'boolean', default: true }, + includeLocalRenotes: { type: 'boolean', default: true }, + withFiles: { type: 'boolean' }, + }, + required: [], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const hasFollowing = (await Followings.count({ where: { followerId: user.id, @@ -141,11 +113,11 @@ export default define(meta, async (ps, user) => { } //#endregion - const timeline = await query.take(ps.limit!).getMany(); + const timeline = await query.take(ps.limit).getMany(); process.nextTick(() => { if (user) { - activeUsersChart.update(user); + activeUsersChart.read(user); } }); diff --git a/packages/backend/src/server/api/endpoints/notes/translate.ts b/packages/backend/src/server/api/endpoints/notes/translate.ts index ed069cb75a..d85c8b96f9 100644 --- a/packages/backend/src/server/api/endpoints/notes/translate.ts +++ b/packages/backend/src/server/api/endpoints/notes/translate.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../define'; import { getNote } from '../../common/getters'; import { ApiError } from '../../error'; @@ -15,15 +13,6 @@ export const meta = { requireCredential: false, - params: { - noteId: { - validator: $.type(ID), - }, - targetLang: { - validator: $.str, - }, - }, - res: { type: 'object', optional: false, nullable: false, @@ -38,8 +27,17 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + noteId: { type: 'string', format: 'misskey:id' }, + targetLang: { type: 'string' }, + }, + required: ['noteId', 'targetLang'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const note = await getNote(ps.noteId).catch(e => { if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); throw e; diff --git a/packages/backend/src/server/api/endpoints/notes/unrenote.ts b/packages/backend/src/server/api/endpoints/notes/unrenote.ts index 8db543d328..c10d277de1 100644 --- a/packages/backend/src/server/api/endpoints/notes/unrenote.ts +++ b/packages/backend/src/server/api/endpoints/notes/unrenote.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import deleteNote from '@/services/note/delete'; import define from '../../define'; import ms from 'ms'; @@ -20,12 +18,6 @@ export const meta = { minInterval: ms('1sec'), }, - params: { - noteId: { - validator: $.type(ID), - }, - }, - errors: { noSuchNote: { message: 'No such note.', @@ -35,8 +27,16 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + noteId: { type: 'string', format: 'misskey:id' }, + }, + required: ['noteId'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const note = await getNote(ps.noteId).catch(e => { if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); throw e; 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 89de73fb9d..018e3e06a8 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 @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../define'; import { ApiError } from '../../error'; import { UserLists, UserListJoinings, Notes } from '@/models/index'; @@ -13,52 +11,6 @@ export const meta = { requireCredential: true, - params: { - listId: { - validator: $.type(ID), - }, - - limit: { - validator: $.optional.num.range(1, 100), - default: 10, - }, - - sinceId: { - validator: $.optional.type(ID), - }, - - untilId: { - validator: $.optional.type(ID), - }, - - sinceDate: { - validator: $.optional.num, - }, - - untilDate: { - validator: $.optional.num, - }, - - includeMyRenotes: { - validator: $.optional.bool, - default: true, - }, - - includeRenotedMyNotes: { - validator: $.optional.bool, - default: true, - }, - - includeLocalRenotes: { - validator: $.optional.bool, - default: true, - }, - - withFiles: { - validator: $.optional.bool, - }, - }, - res: { type: 'array', optional: false, nullable: false, @@ -78,8 +30,25 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + listId: { 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' }, + sinceDate: { type: 'integer' }, + untilDate: { type: 'integer' }, + includeMyRenotes: { type: 'boolean', default: true }, + includeRenotedMyNotes: { type: 'boolean', default: true }, + includeLocalRenotes: { type: 'boolean', default: true }, + withFiles: { type: 'boolean' }, + }, + required: ['listId'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const list = await UserLists.findOne({ id: ps.listId, userId: user.id, @@ -140,9 +109,9 @@ export default define(meta, async (ps, user) => { } //#endregion - const timeline = await query.take(ps.limit!).getMany(); + const timeline = await query.take(ps.limit).getMany(); - activeUsersChart.update(user); + activeUsersChart.read(user); return await Notes.packMany(timeline, user); }); diff --git a/packages/backend/src/server/api/endpoints/notes/watching/create.ts b/packages/backend/src/server/api/endpoints/notes/watching/create.ts index 6433c6bc2a..b19d370027 100644 --- a/packages/backend/src/server/api/endpoints/notes/watching/create.ts +++ b/packages/backend/src/server/api/endpoints/notes/watching/create.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../../define'; import watch from '@/services/note/watch'; import { getNote } from '../../../common/getters'; @@ -12,12 +10,6 @@ export const meta = { kind: 'write:account', - params: { - noteId: { - validator: $.type(ID), - }, - }, - errors: { noSuchNote: { message: 'No such note.', @@ -27,8 +19,16 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + noteId: { type: 'string', format: 'misskey:id' }, + }, + required: ['noteId'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const note = await getNote(ps.noteId).catch(e => { if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); throw e; diff --git a/packages/backend/src/server/api/endpoints/notes/watching/delete.ts b/packages/backend/src/server/api/endpoints/notes/watching/delete.ts index 3e9faa2b23..f9ceb49278 100644 --- a/packages/backend/src/server/api/endpoints/notes/watching/delete.ts +++ b/packages/backend/src/server/api/endpoints/notes/watching/delete.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../../define'; import unwatch from '@/services/note/unwatch'; import { getNote } from '../../../common/getters'; @@ -12,12 +10,6 @@ export const meta = { kind: 'write:account', - params: { - noteId: { - validator: $.type(ID), - }, - }, - errors: { noSuchNote: { message: 'No such note.', @@ -27,8 +19,16 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + noteId: { type: 'string', format: 'misskey:id' }, + }, + required: ['noteId'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const note = await getNote(ps.noteId).catch(e => { if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); throw e; diff --git a/packages/backend/src/server/api/endpoints/notifications/create.ts b/packages/backend/src/server/api/endpoints/notifications/create.ts index bd8a7ba1b7..b5bfb34f4b 100644 --- a/packages/backend/src/server/api/endpoints/notifications/create.ts +++ b/packages/backend/src/server/api/endpoints/notifications/create.ts @@ -1,4 +1,3 @@ -import $ from 'cafy'; import define from '../../define'; import { createNotification } from '@/services/create-notification'; @@ -9,26 +8,22 @@ export const meta = { kind: 'write:notifications', - params: { - body: { - validator: $.str, - }, - - header: { - validator: $.optional.nullable.str, - }, - - icon: { - validator: $.optional.nullable.str, - }, - }, - errors: { }, } as const; +const paramDef = { + type: 'object', + properties: { + body: { type: 'string' }, + header: { type: 'string', nullable: true }, + icon: { type: 'string', nullable: true }, + }, + required: ['body'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user, token) => { +export default define(meta, paramDef, async (ps, user, token) => { createNotification(user.id, 'app', { appAccessTokenId: token ? token.id : null, customBody: ps.body, diff --git a/packages/backend/src/server/api/endpoints/notifications/mark-all-as-read.ts b/packages/backend/src/server/api/endpoints/notifications/mark-all-as-read.ts index 4cec38a95d..12dc43de16 100644 --- a/packages/backend/src/server/api/endpoints/notifications/mark-all-as-read.ts +++ b/packages/backend/src/server/api/endpoints/notifications/mark-all-as-read.ts @@ -10,8 +10,14 @@ export const meta = { kind: 'write:notifications', } as const; +const paramDef = { + type: 'object', + properties: {}, + required: [], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { // Update documents await Notifications.update({ notifieeId: user.id, diff --git a/packages/backend/src/server/api/endpoints/notifications/read.ts b/packages/backend/src/server/api/endpoints/notifications/read.ts index 95bac775f3..659cc1e033 100644 --- a/packages/backend/src/server/api/endpoints/notifications/read.ts +++ b/packages/backend/src/server/api/endpoints/notifications/read.ts @@ -1,8 +1,5 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../define'; import { readNotification } from '../../common/read-notification'; -import { ApiError } from '../../error'; export const meta = { desc: { @@ -16,27 +13,36 @@ export const meta = { kind: 'write:notifications', - params: { - notificationId: { - validator: $.optional.type(ID), - desc: { - 'ja-JP': 'å¯žčąĄãŽé€šįŸĨぎID', - 'en-US': 'Target notification ID.' - } + errors: { + noSuchNotification: { + message: 'No such notification.', + code: 'NO_SUCH_NOTIFICATION', + id: 'efa929d5-05b5-47d1-beec-e6a4dbed011e', }, - - notificationIds: { - validator: $.optional.arr($.type(ID)), - desc: { - 'ja-JP': 'å¯žčąĄãŽé€šįŸĨぎIDぎ配列', - 'en-US': 'Target notification IDs.' - } - } }, } as const; +const paramDef = { + oneOf: [ + { + type: 'object', + properties: { + notificationId: { type: 'string', format: 'misskey:id' }, + }, + required: ['notificationId'], + }, + { + type: 'object', + properties: { + notificationIds: { type: 'array', items: { type: 'string', format: 'misskey:id' } }, + }, + required: ['notificationIds'], + }, + ], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { let notificationIds = [] as string[]; if (ps.notificationId) notificationIds.push(ps.notificationId); diff --git a/packages/backend/src/server/api/endpoints/page-push.ts b/packages/backend/src/server/api/endpoints/page-push.ts index 61c0160f83..20334f33fc 100644 --- a/packages/backend/src/server/api/endpoints/page-push.ts +++ b/packages/backend/src/server/api/endpoints/page-push.ts @@ -1,6 +1,4 @@ -import $ from 'cafy'; import define from '../define'; -import { ID } from '@/misc/cafy-id'; import { publishMainStream } from '@/services/stream'; import { Users, Pages } from '@/models/index'; import { ApiError } from '../error'; @@ -9,20 +7,6 @@ export const meta = { requireCredential: true, secure: true, - params: { - pageId: { - validator: $.type(ID), - }, - - event: { - validator: $.str, - }, - - var: { - validator: $.optional.nullable.any, - }, - }, - errors: { noSuchPage: { message: 'No such page.', @@ -32,8 +16,18 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + pageId: { type: 'string', format: 'misskey:id' }, + event: { type: 'string' }, + var: {}, + }, + required: ['pageId', 'event'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const page = await Pages.findOne(ps.pageId); if (page == null) { throw new ApiError(meta.errors.noSuchPage); diff --git a/packages/backend/src/server/api/endpoints/pages/create.ts b/packages/backend/src/server/api/endpoints/pages/create.ts index 7ee50fbdfa..6c00a2f779 100644 --- a/packages/backend/src/server/api/endpoints/pages/create.ts +++ b/packages/backend/src/server/api/endpoints/pages/create.ts @@ -1,7 +1,5 @@ -import $ from 'cafy'; import ms from 'ms'; import define from '../../define'; -import { ID } from '@/misc/cafy-id'; import { Pages, DriveFiles } from '@/models/index'; import { genId } from '@/misc/gen-id'; import { Page } from '@/models/entities/page'; @@ -19,51 +17,6 @@ export const meta = { max: 300, }, - params: { - title: { - validator: $.str, - }, - - name: { - validator: $.str.min(1), - }, - - summary: { - validator: $.optional.nullable.str, - }, - - content: { - validator: $.arr($.obj()), - }, - - variables: { - validator: $.arr($.obj()), - }, - - script: { - validator: $.str, - }, - - eyeCatchingImageId: { - validator: $.optional.nullable.type(ID), - }, - - font: { - validator: $.optional.str.or(['serif', 'sans-serif']), - default: 'sans-serif', - }, - - alignCenter: { - validator: $.optional.bool, - default: false, - }, - - hideTitleWhenPinned: { - validator: $.optional.bool, - default: false, - }, - }, - res: { type: 'object', optional: false, nullable: false, @@ -84,8 +37,29 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + title: { type: 'string' }, + name: { type: 'string', minLength: 1 }, + summary: { type: 'string', nullable: true }, + content: { type: 'array', items: { + type: 'object', additionalProperties: true, + } }, + variables: { type: 'array', items: { + type: 'object', additionalProperties: true, + } }, + script: { type: 'string' }, + eyeCatchingImageId: { type: 'string', format: 'misskey:id', nullable: true }, + font: { type: 'string', enum: ['serif', 'sans-serif'], default: "sans-serif" }, + alignCenter: { type: 'boolean', default: false }, + hideTitleWhenPinned: { type: 'boolean', default: false }, + }, + required: ['title', 'name', 'content', 'variables', 'script'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { let eyeCatchingImage = null; if (ps.eyeCatchingImageId != null) { eyeCatchingImage = await DriveFiles.findOne({ diff --git a/packages/backend/src/server/api/endpoints/pages/delete.ts b/packages/backend/src/server/api/endpoints/pages/delete.ts index aeda823e52..c41373bb7c 100644 --- a/packages/backend/src/server/api/endpoints/pages/delete.ts +++ b/packages/backend/src/server/api/endpoints/pages/delete.ts @@ -1,8 +1,6 @@ -import $ from 'cafy'; import define from '../../define'; import { ApiError } from '../../error'; import { Pages } from '@/models/index'; -import { ID } from '@/misc/cafy-id'; export const meta = { tags: ['pages'], @@ -11,12 +9,6 @@ export const meta = { kind: 'write:pages', - params: { - pageId: { - validator: $.type(ID), - }, - }, - errors: { noSuchPage: { message: 'No such page.', @@ -32,8 +24,16 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + pageId: { type: 'string', format: 'misskey:id' }, + }, + required: ['pageId'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const page = await Pages.findOne(ps.pageId); if (page == null) { throw new ApiError(meta.errors.noSuchPage); diff --git a/packages/backend/src/server/api/endpoints/pages/featured.ts b/packages/backend/src/server/api/endpoints/pages/featured.ts index 7f0d58b350..38aa4dfacc 100644 --- a/packages/backend/src/server/api/endpoints/pages/featured.ts +++ b/packages/backend/src/server/api/endpoints/pages/featured.ts @@ -17,8 +17,14 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: {}, + required: [], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, me) => { +export default define(meta, paramDef, async (ps, me) => { const query = Pages.createQueryBuilder('page') .where('page.visibility = \'public\'') .andWhere('page.likedCount > 0') diff --git a/packages/backend/src/server/api/endpoints/pages/like.ts b/packages/backend/src/server/api/endpoints/pages/like.ts index c479f637a9..d286fd2bfd 100644 --- a/packages/backend/src/server/api/endpoints/pages/like.ts +++ b/packages/backend/src/server/api/endpoints/pages/like.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../define'; import { ApiError } from '../../error'; import { Pages, PageLikes } from '@/models/index'; @@ -12,12 +10,6 @@ export const meta = { kind: 'write:page-likes', - params: { - pageId: { - validator: $.type(ID), - }, - }, - errors: { noSuchPage: { message: 'No such page.', @@ -39,8 +31,16 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + pageId: { type: 'string', format: 'misskey:id' }, + }, + required: ['pageId'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const page = await Pages.findOne(ps.pageId); if (page == null) { throw new ApiError(meta.errors.noSuchPage); diff --git a/packages/backend/src/server/api/endpoints/pages/show.ts b/packages/backend/src/server/api/endpoints/pages/show.ts index 5cda5386d5..95243e411f 100644 --- a/packages/backend/src/server/api/endpoints/pages/show.ts +++ b/packages/backend/src/server/api/endpoints/pages/show.ts @@ -1,8 +1,6 @@ -import $ from 'cafy'; import define from '../../define'; import { ApiError } from '../../error'; import { Pages, Users } from '@/models/index'; -import { ID } from '@/misc/cafy-id'; import { Page } from '@/models/entities/page'; export const meta = { @@ -10,20 +8,6 @@ export const meta = { requireCredential: false, - params: { - pageId: { - validator: $.optional.type(ID), - }, - - name: { - validator: $.optional.str, - }, - - username: { - validator: $.optional.str, - }, - }, - res: { type: 'object', optional: false, nullable: false, @@ -39,8 +23,18 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + pageId: { type: 'string', format: 'misskey:id' }, + name: { type: 'string' }, + username: { type: 'string' }, + }, + required: [], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { let page: Page | undefined; if (ps.pageId) { diff --git a/packages/backend/src/server/api/endpoints/pages/unlike.ts b/packages/backend/src/server/api/endpoints/pages/unlike.ts index cca5e5b5a9..582c924d91 100644 --- a/packages/backend/src/server/api/endpoints/pages/unlike.ts +++ b/packages/backend/src/server/api/endpoints/pages/unlike.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../define'; import { ApiError } from '../../error'; import { Pages, PageLikes } from '@/models/index'; @@ -11,12 +9,6 @@ export const meta = { kind: 'write:page-likes', - params: { - pageId: { - validator: $.type(ID), - }, - }, - errors: { noSuchPage: { message: 'No such page.', @@ -32,8 +24,16 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + pageId: { type: 'string', format: 'misskey:id' }, + }, + required: ['pageId'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const page = await Pages.findOne(ps.pageId); if (page == null) { throw new ApiError(meta.errors.noSuchPage); diff --git a/packages/backend/src/server/api/endpoints/pages/update.ts b/packages/backend/src/server/api/endpoints/pages/update.ts index 991085ee09..686271cf46 100644 --- a/packages/backend/src/server/api/endpoints/pages/update.ts +++ b/packages/backend/src/server/api/endpoints/pages/update.ts @@ -1,9 +1,7 @@ -import $ from 'cafy'; import ms from 'ms'; import define from '../../define'; import { ApiError } from '../../error'; import { Pages, DriveFiles } from '@/models/index'; -import { ID } from '@/misc/cafy-id'; import { Not } from 'typeorm'; export const meta = { @@ -18,52 +16,6 @@ export const meta = { max: 300, }, - params: { - pageId: { - validator: $.type(ID), - }, - - title: { - validator: $.str, - }, - - name: { - validator: $.str.min(1), - }, - - summary: { - validator: $.optional.nullable.str, - }, - - content: { - validator: $.arr($.obj()), - }, - - variables: { - validator: $.arr($.obj()), - }, - - script: { - validator: $.str, - }, - - eyeCatchingImageId: { - validator: $.optional.nullable.type(ID), - }, - - font: { - validator: $.optional.str.or(['serif', 'sans-serif']), - }, - - alignCenter: { - validator: $.optional.bool, - }, - - hideTitleWhenPinned: { - validator: $.optional.bool, - }, - }, - errors: { noSuchPage: { message: 'No such page.', @@ -90,8 +42,30 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + pageId: { type: 'string', format: 'misskey:id' }, + title: { type: 'string' }, + name: { type: 'string', minLength: 1 }, + summary: { type: 'string', nullable: true }, + content: { type: 'array', items: { + type: 'object', additionalProperties: true, + } }, + variables: { type: 'array', items: { + type: 'object', additionalProperties: true, + } }, + script: { type: 'string' }, + eyeCatchingImageId: { type: 'string', format: 'misskey:id', nullable: true }, + font: { type: 'string', enum: ['serif', 'sans-serif'] }, + alignCenter: { type: 'boolean' }, + hideTitleWhenPinned: { type: 'boolean' }, + }, + required: ['pageId', 'title', 'name', 'content', 'variables', 'script'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const page = await Pages.findOne(ps.pageId); if (page == null) { throw new ApiError(meta.errors.noSuchPage); diff --git a/packages/backend/src/server/api/endpoints/ping.ts b/packages/backend/src/server/api/endpoints/ping.ts index 3eab70ae2e..964401e19b 100644 --- a/packages/backend/src/server/api/endpoints/ping.ts +++ b/packages/backend/src/server/api/endpoints/ping.ts @@ -5,9 +5,6 @@ export const meta = { tags: ['meta'], - params: { - }, - res: { type: 'object', optional: false, nullable: false, @@ -20,8 +17,14 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: {}, + required: [], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async () => { +export default define(meta, paramDef, async () => { return { pong: Date.now(), }; diff --git a/packages/backend/src/server/api/endpoints/pinned-users.ts b/packages/backend/src/server/api/endpoints/pinned-users.ts index ff0e22555f..83657f8bf9 100644 --- a/packages/backend/src/server/api/endpoints/pinned-users.ts +++ b/packages/backend/src/server/api/endpoints/pinned-users.ts @@ -9,9 +9,6 @@ export const meta = { requireCredential: false, - params: { - }, - res: { type: 'array', optional: false, nullable: false, @@ -23,8 +20,14 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: {}, + required: [], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, me) => { +export default define(meta, paramDef, async (ps, me) => { const meta = await fetchMeta(); const users = await Promise.all(meta.pinnedUsers.map(acct => Users.findOne(Acct.parse(acct)))); diff --git a/packages/backend/src/server/api/endpoints/promo/read.ts b/packages/backend/src/server/api/endpoints/promo/read.ts index 8d8c60d755..99be33fb0c 100644 --- a/packages/backend/src/server/api/endpoints/promo/read.ts +++ b/packages/backend/src/server/api/endpoints/promo/read.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../define'; import { ApiError } from '../../error'; import { getNote } from '../../common/getters'; @@ -11,12 +9,6 @@ export const meta = { requireCredential: true, - params: { - noteId: { - validator: $.type(ID), - }, - }, - errors: { noSuchNote: { message: 'No such note.', @@ -26,8 +18,16 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + noteId: { type: 'string', format: 'misskey:id' }, + }, + required: ['noteId'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const note = await getNote(ps.noteId).catch(e => { if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); throw e; diff --git a/packages/backend/src/server/api/endpoints/request-reset-password.ts b/packages/backend/src/server/api/endpoints/request-reset-password.ts index af1aeb4311..193c3ee79d 100644 --- a/packages/backend/src/server/api/endpoints/request-reset-password.ts +++ b/packages/backend/src/server/api/endpoints/request-reset-password.ts @@ -1,4 +1,3 @@ -import $ from 'cafy'; import { publishMainStream } from '@/services/stream'; import define from '../define'; import rndstr from 'rndstr'; @@ -18,23 +17,22 @@ export const meta = { max: 3, }, - params: { - username: { - validator: $.str, - }, - - email: { - validator: $.str, - }, - }, - errors: { }, } as const; +const paramDef = { + type: 'object', + properties: { + username: { type: 'string' }, + email: { type: 'string' }, + }, + required: ['username', 'email'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps) => { +export default define(meta, paramDef, async (ps) => { const user = await Users.findOne({ usernameLower: ps.username.toLowerCase(), host: IsNull(), diff --git a/packages/backend/src/server/api/endpoints/reset-db.ts b/packages/backend/src/server/api/endpoints/reset-db.ts index e99dc9db15..1c1cb3f136 100644 --- a/packages/backend/src/server/api/endpoints/reset-db.ts +++ b/packages/backend/src/server/api/endpoints/reset-db.ts @@ -1,4 +1,3 @@ -import $ from 'cafy'; import define from '../define'; import { ApiError } from '../error'; import { resetDb } from '@/db/postgre'; @@ -6,16 +5,19 @@ import { resetDb } from '@/db/postgre'; export const meta = { requireCredential: false, - params: { - }, - errors: { }, } as const; +const paramDef = { + type: 'object', + properties: {}, + required: [], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { if (process.env.NODE_ENV !== 'test') throw 'NODE_ENV is not a test'; await resetDb(); diff --git a/packages/backend/src/server/api/endpoints/reset-password.ts b/packages/backend/src/server/api/endpoints/reset-password.ts index a7366584b1..04048b9ef1 100644 --- a/packages/backend/src/server/api/endpoints/reset-password.ts +++ b/packages/backend/src/server/api/endpoints/reset-password.ts @@ -1,4 +1,3 @@ -import $ from 'cafy'; import * as bcrypt from 'bcryptjs'; import { publishMainStream } from '@/services/stream'; import define from '../define'; @@ -8,23 +7,22 @@ import { ApiError } from '../error'; export const meta = { requireCredential: false, - params: { - token: { - validator: $.str, - }, - - password: { - validator: $.str, - }, - }, - errors: { }, } as const; +const paramDef = { + type: 'object', + properties: { + token: { type: 'string' }, + password: { type: 'string' }, + }, + required: ['token', 'password'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const req = await PasswordResetRequests.findOneOrFail({ token: ps.token, }); diff --git a/packages/backend/src/server/api/endpoints/server-info.ts b/packages/backend/src/server/api/endpoints/server-info.ts index 1ad2c54ab5..4a5bc6a86c 100644 --- a/packages/backend/src/server/api/endpoints/server-info.ts +++ b/packages/backend/src/server/api/endpoints/server-info.ts @@ -5,17 +5,17 @@ import define from '../define'; export const meta = { requireCredential: false, - desc: { - }, - tags: ['meta'], +} as const; - params: { - }, +const paramDef = { + type: 'object', + properties: {}, + required: [], } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, async () => { +export default define(meta, paramDef, async () => { const memStats = await si.mem(); const fsStats = await si.fsSize(); diff --git a/packages/backend/src/server/api/endpoints/stats.ts b/packages/backend/src/server/api/endpoints/stats.ts index 9879ef2adf..8abae921ac 100644 --- a/packages/backend/src/server/api/endpoints/stats.ts +++ b/packages/backend/src/server/api/endpoints/stats.ts @@ -1,15 +1,12 @@ import define from '../define'; -import { NoteReactions, Notes, Users } from '@/models/index'; -import { federationChart, driveChart } from '@/services/chart/index'; +import { Instances, NoteReactions, Notes, Users } from '@/models/index'; +import { } from '@/services/chart/index'; export const meta = { requireCredential: false, tags: ['meta'], - params: { - }, - res: { type: 'object', optional: false, nullable: false, @@ -46,8 +43,14 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: {}, + required: [], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async () => { +export default define(meta, paramDef, async () => { const [ notesCount, originalNotesCount, @@ -56,8 +59,6 @@ export default define(meta, async () => { reactionsCount, //originalReactionsCount, instances, - driveUsageLocal, - driveUsageRemote, ] = await Promise.all([ Notes.count({ cache: 3600000 }), // 1 hour Notes.count({ where: { userHost: null }, cache: 3600000 }), @@ -65,9 +66,7 @@ export default define(meta, async () => { Users.count({ where: { host: null }, cache: 3600000 }), NoteReactions.count({ cache: 3600000 }), // 1 hour //NoteReactions.count({ where: { userHost: null }, cache: 3600000 }), - federationChart.getChart('hour', 1, null).then(chart => chart.instance.total[0]), - driveChart.getChart('hour', 1, null).then(chart => chart.local.totalSize[0]), - driveChart.getChart('hour', 1, null).then(chart => chart.remote.totalSize[0]), + Instances.count({ cache: 3600000 }), ]); return { @@ -78,7 +77,7 @@ export default define(meta, async () => { reactionsCount, //originalReactionsCount, instances, - driveUsageLocal, - driveUsageRemote, + driveUsageLocal: 0, + driveUsageRemote: 0, }; }); diff --git a/packages/backend/src/server/api/endpoints/sw/register.ts b/packages/backend/src/server/api/endpoints/sw/register.ts index ae3e9ce77a..9091c9481a 100644 --- a/packages/backend/src/server/api/endpoints/sw/register.ts +++ b/packages/backend/src/server/api/endpoints/sw/register.ts @@ -1,4 +1,3 @@ -import $ from 'cafy'; import define from '../../define'; import { fetchMeta } from '@/misc/fetch-meta'; import { genId } from '@/misc/gen-id'; @@ -9,20 +8,6 @@ export const meta = { requireCredential: true, - params: { - endpoint: { - validator: $.str, - }, - - auth: { - validator: $.str, - }, - - publickey: { - validator: $.str, - }, - }, - res: { type: 'object', optional: false, nullable: false, @@ -40,8 +25,18 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + endpoint: { type: 'string' }, + auth: { type: 'string' }, + publickey: { type: 'string' }, + }, + required: ['endpoint', 'auth', 'publickey'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { // if already subscribed const exist = await SwSubscriptions.findOne({ userId: user.id, diff --git a/packages/backend/src/server/api/endpoints/sw/unregister.ts b/packages/backend/src/server/api/endpoints/sw/unregister.ts index 6f569e9417..ec994c37c2 100644 --- a/packages/backend/src/server/api/endpoints/sw/unregister.ts +++ b/packages/backend/src/server/api/endpoints/sw/unregister.ts @@ -1,4 +1,3 @@ -import $ from 'cafy'; import define from '../../define'; import { SwSubscriptions } from '../../../../models'; @@ -6,16 +5,18 @@ export const meta = { tags: ['account'], requireCredential: true, +} as const; - params: { - endpoint: { - validator: $.str, - }, +const paramDef = { + type: 'object', + properties: { + endpoint: { type: 'string' }, }, + required: ['endpoint'], } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { await SwSubscriptions.delete({ userId: user.id, endpoint: ps.endpoint, diff --git a/packages/backend/src/server/api/endpoints/test.ts b/packages/backend/src/server/api/endpoints/test.ts new file mode 100644 index 0000000000..820448e677 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/test.ts @@ -0,0 +1,22 @@ +import define from '../define'; + +export const meta = { + requireCredential: false, +} as const; + +const paramDef = { + type: 'object', + properties: { + required: { type: 'boolean' }, + string: { type: 'string' }, + default: { type: 'string', default: 'hello' }, + nullableDefault: { type: 'string', nullable: true, default: 'hello' }, + id: { type: 'string', format: 'misskey:id' }, + }, + required: ['required'], +} as const; + +// eslint-disable-next-line import/no-default-export +export default define(meta, paramDef, async (ps, me) => { + return ps; +}); diff --git a/packages/backend/src/server/api/endpoints/username/available.ts b/packages/backend/src/server/api/endpoints/username/available.ts index 74120fc406..6e38d4d310 100644 --- a/packages/backend/src/server/api/endpoints/username/available.ts +++ b/packages/backend/src/server/api/endpoints/username/available.ts @@ -1,4 +1,3 @@ -import $ from 'cafy'; import define from '../../define'; import { Users, UsedUsernames } from '@/models/index'; @@ -7,12 +6,6 @@ export const meta = { requireCredential: false, - params: { - username: { - validator: $.use(Users.validateLocalUsername), - }, - }, - res: { type: 'object', optional: false, nullable: false, @@ -25,8 +18,16 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + username: Users.localUsernameSchema, + }, + required: ['username'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps) => { +export default define(meta, paramDef, async (ps) => { // Get exist const exist = await Users.count({ host: null, diff --git a/packages/backend/src/server/api/endpoints/users.ts b/packages/backend/src/server/api/endpoints/users.ts index 6b11ec0f01..0c03ac05c3 100644 --- a/packages/backend/src/server/api/endpoints/users.ts +++ b/packages/backend/src/server/api/endpoints/users.ts @@ -1,4 +1,3 @@ -import $ from 'cafy'; import define from '../define'; import { Users } from '@/models/index'; import { generateMutedUserQueryForUsers } from '../common/generate-muted-user-query'; @@ -9,49 +8,6 @@ export const meta = { requireCredential: false, - params: { - limit: { - validator: $.optional.num.range(1, 100), - default: 10, - }, - - offset: { - validator: $.optional.num.min(0), - default: 0, - }, - - sort: { - validator: $.optional.str.or([ - '+follower', - '-follower', - '+createdAt', - '-createdAt', - '+updatedAt', - '-updatedAt', - ]), - }, - - state: { - validator: $.optional.str.or([ - 'all', - 'admin', - 'moderator', - 'adminOrModerator', - 'alive', - ]), - default: 'all', - }, - - origin: { - validator: $.optional.str.or([ - 'combined', - 'local', - 'remote', - ]), - default: 'local', - }, - }, - res: { type: 'array', optional: false, nullable: false, @@ -63,8 +19,20 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, + offset: { type: 'integer', default: 0 }, + sort: { type: 'string', enum: ['+follower', '-follower', '+createdAt', '-createdAt', '+updatedAt', '-updatedAt'] }, + state: { type: 'string', enum: ['all', 'admin', 'moderator', 'adminOrModerator', 'alive'], default: "all" }, + origin: { type: 'string', enum: ['combined', 'local', 'remote'], default: "local" }, + }, + required: [], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, me) => { +export default define(meta, paramDef, async (ps, me) => { const query = Users.createQueryBuilder('user'); query.where('user.isExplorable = TRUE'); @@ -93,7 +61,7 @@ export default define(meta, async (ps, me) => { if (me) generateMutedUserQueryForUsers(query, me); if (me) generateBlockQueryForUsers(query, me); - query.take(ps.limit!); + query.take(ps.limit); query.skip(ps.offset); const users = await query.getMany(); diff --git a/packages/backend/src/server/api/endpoints/users/clips.ts b/packages/backend/src/server/api/endpoints/users/clips.ts index d4152fbf50..e224bdf603 100644 --- a/packages/backend/src/server/api/endpoints/users/clips.ts +++ b/packages/backend/src/server/api/endpoints/users/clips.ts @@ -1,40 +1,30 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../define'; import { Clips } from '@/models/index'; import { makePaginationQuery } from '../../common/make-pagination-query'; export const meta = { tags: ['users', 'clips'], +} as const; - params: { - userId: { - validator: $.type(ID), - }, - - limit: { - validator: $.optional.num.range(1, 100), - default: 10, - }, - - sinceId: { - validator: $.optional.type(ID), - }, - - untilId: { - validator: $.optional.type(ID), - }, +const paramDef = { + type: 'object', + properties: { + 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' }, }, + required: ['userId'], } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const query = makePaginationQuery(Clips.createQueryBuilder('clip'), ps.sinceId, ps.untilId) .andWhere(`clip.userId = :userId`, { userId: ps.userId }) .andWhere('clip.isPublic = true'); const clips = await query - .take(ps.limit!) + .take(ps.limit) .getMany(); return await Clips.packMany(clips); diff --git a/packages/backend/src/server/api/endpoints/users/followers.ts b/packages/backend/src/server/api/endpoints/users/followers.ts index 6214ab40ba..5607b4cc73 100644 --- a/packages/backend/src/server/api/endpoints/users/followers.ts +++ b/packages/backend/src/server/api/endpoints/users/followers.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../define'; import { ApiError } from '../../error'; import { Users, Followings, UserProfiles } from '@/models/index'; @@ -11,33 +9,6 @@ export const meta = { requireCredential: false, - params: { - userId: { - validator: $.optional.type(ID), - }, - - username: { - validator: $.optional.str, - }, - - host: { - validator: $.optional.nullable.str, - }, - - sinceId: { - validator: $.optional.type(ID), - }, - - untilId: { - validator: $.optional.type(ID), - }, - - limit: { - validator: $.optional.num.range(1, 100), - default: 10, - }, - }, - res: { type: 'array', optional: false, nullable: false, @@ -63,8 +34,21 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + userId: { type: 'string', format: 'misskey:id' }, + username: { type: 'string' }, + host: { type: 'string', nullable: true }, + sinceId: { type: 'string', format: 'misskey:id' }, + untilId: { type: 'string', format: 'misskey:id' }, + limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, + }, + required: [], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, me) => { +export default define(meta, paramDef, async (ps, me) => { const user = await Users.findOne(ps.userId != null ? { id: ps.userId } : { usernameLower: ps.username!.toLowerCase(), host: toPunyNullable(ps.host) }); @@ -98,7 +82,7 @@ export default define(meta, async (ps, me) => { .innerJoinAndSelect('following.follower', 'follower'); const followings = await query - .take(ps.limit!) + .take(ps.limit) .getMany(); return await Followings.packMany(followings, me, { populateFollower: true }); diff --git a/packages/backend/src/server/api/endpoints/users/following.ts b/packages/backend/src/server/api/endpoints/users/following.ts index 76112eab25..36943232d1 100644 --- a/packages/backend/src/server/api/endpoints/users/following.ts +++ b/packages/backend/src/server/api/endpoints/users/following.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../define'; import { ApiError } from '../../error'; import { Users, Followings, UserProfiles } from '@/models/index'; @@ -11,33 +9,6 @@ export const meta = { requireCredential: false, - params: { - userId: { - validator: $.optional.type(ID), - }, - - username: { - validator: $.optional.str, - }, - - host: { - validator: $.optional.nullable.str, - }, - - sinceId: { - validator: $.optional.type(ID), - }, - - untilId: { - validator: $.optional.type(ID), - }, - - limit: { - validator: $.optional.num.range(1, 100), - default: 10, - }, - }, - res: { type: 'array', optional: false, nullable: false, @@ -63,8 +34,21 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + userId: { type: 'string', format: 'misskey:id' }, + username: { type: 'string' }, + host: { type: 'string', nullable: true }, + sinceId: { type: 'string', format: 'misskey:id' }, + untilId: { type: 'string', format: 'misskey:id' }, + limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, + }, + required: [], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, me) => { +export default define(meta, paramDef, async (ps, me) => { const user = await Users.findOne(ps.userId != null ? { id: ps.userId } : { usernameLower: ps.username!.toLowerCase(), host: toPunyNullable(ps.host) }); @@ -98,7 +82,7 @@ export default define(meta, async (ps, me) => { .innerJoinAndSelect('following.followee', 'followee'); const followings = await query - .take(ps.limit!) + .take(ps.limit) .getMany(); return await Followings.packMany(followings, me, { populateFollowee: true }); diff --git a/packages/backend/src/server/api/endpoints/users/gallery/posts.ts b/packages/backend/src/server/api/endpoints/users/gallery/posts.ts index c5f08b4c94..e874a54c77 100644 --- a/packages/backend/src/server/api/endpoints/users/gallery/posts.ts +++ b/packages/backend/src/server/api/endpoints/users/gallery/posts.ts @@ -1,39 +1,29 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../../define'; import { GalleryPosts } from '@/models/index'; import { makePaginationQuery } from '../../../common/make-pagination-query'; export const meta = { tags: ['users', 'gallery'], +} as const; - params: { - userId: { - validator: $.type(ID), - }, - - limit: { - validator: $.optional.num.range(1, 100), - default: 10, - }, - - sinceId: { - validator: $.optional.type(ID), - }, - - untilId: { - validator: $.optional.type(ID), - }, +const paramDef = { + type: 'object', + properties: { + 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' }, }, + required: ['userId'], } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const query = makePaginationQuery(GalleryPosts.createQueryBuilder('post'), ps.sinceId, ps.untilId) .andWhere(`post.userId = :userId`, { userId: ps.userId }); const posts = await query - .take(ps.limit!) + .take(ps.limit) .getMany(); return await GalleryPosts.packMany(posts, user); diff --git a/packages/backend/src/server/api/endpoints/users/get-frequently-replied-users.ts b/packages/backend/src/server/api/endpoints/users/get-frequently-replied-users.ts index d886d3355a..a08587cd16 100644 --- a/packages/backend/src/server/api/endpoints/users/get-frequently-replied-users.ts +++ b/packages/backend/src/server/api/endpoints/users/get-frequently-replied-users.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../define'; import { maximum } from '@/prelude/array'; import { ApiError } from '../../error'; @@ -12,17 +10,6 @@ export const meta = { requireCredential: false, - params: { - userId: { - validator: $.type(ID), - }, - - limit: { - validator: $.optional.num.range(1, 100), - default: 10, - }, - }, - res: { type: 'array', optional: false, nullable: false, @@ -52,8 +39,17 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + userId: { type: 'string', format: 'misskey:id' }, + limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, + }, + required: ['userId'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, me) => { +export default define(meta, paramDef, async (ps, me) => { // Lookup user const user = await getUser(ps.userId).catch(e => { if (e.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); @@ -104,7 +100,7 @@ export default define(meta, async (ps, me) => { const repliedUsersSorted = Object.keys(repliedUsers).sort((a, b) => repliedUsers[b] - repliedUsers[a]); // Extract top replied users - const topRepliedUsers = repliedUsersSorted.slice(0, ps.limit!); + const topRepliedUsers = repliedUsersSorted.slice(0, ps.limit); // Make replies object (includes weights) const repliesObj = await Promise.all(topRepliedUsers.map(async (user) => ({ diff --git a/packages/backend/src/server/api/endpoints/users/groups/create.ts b/packages/backend/src/server/api/endpoints/users/groups/create.ts index 25e29de01c..e7bc95791f 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/create.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/create.ts @@ -1,4 +1,3 @@ -import $ from 'cafy'; import define from '../../../define'; import { UserGroups, UserGroupJoinings } from '@/models/index'; import { genId } from '@/misc/gen-id'; @@ -12,12 +11,6 @@ export const meta = { kind: 'write:user-groups', - params: { - name: { - validator: $.str.range(1, 100), - }, - }, - res: { type: 'object', optional: false, nullable: false, @@ -25,8 +18,16 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + name: { type: 'string', minLength: 1, maxLength: 100 }, + }, + required: ['name'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const userGroup = await UserGroups.insert({ id: genId(), createdAt: new Date(), diff --git a/packages/backend/src/server/api/endpoints/users/groups/delete.ts b/packages/backend/src/server/api/endpoints/users/groups/delete.ts index f30ab78ca0..cb30b4d813 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/delete.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/delete.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../../define'; import { ApiError } from '../../../error'; import { UserGroups } from '@/models/index'; @@ -11,12 +9,6 @@ export const meta = { kind: 'write:user-groups', - params: { - groupId: { - validator: $.type(ID), - }, - }, - errors: { noSuchGroup: { message: 'No such group.', @@ -26,8 +18,16 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + groupId: { type: 'string', format: 'misskey:id' }, + }, + required: ['groupId'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const userGroup = await UserGroups.findOne({ id: ps.groupId, userId: user.id, diff --git a/packages/backend/src/server/api/endpoints/users/groups/invitations/accept.ts b/packages/backend/src/server/api/endpoints/users/groups/invitations/accept.ts index 7061db538b..7a6a620cb3 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/invitations/accept.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/invitations/accept.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../../../define'; import { ApiError } from '../../../../error'; import { UserGroupJoinings, UserGroupInvitations } from '@/models/index'; @@ -13,12 +11,6 @@ export const meta = { kind: 'write:user-groups', - params: { - invitationId: { - validator: $.type(ID), - }, - }, - errors: { noSuchInvitation: { message: 'No such invitation.', @@ -28,8 +20,16 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + invitationId: { type: 'string', format: 'misskey:id' }, + }, + required: ['invitationId'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { // Fetch the invitation const invitation = await UserGroupInvitations.findOne({ id: ps.invitationId, diff --git a/packages/backend/src/server/api/endpoints/users/groups/invitations/reject.ts b/packages/backend/src/server/api/endpoints/users/groups/invitations/reject.ts index f5ca3dec8b..b5c7f0908a 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/invitations/reject.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/invitations/reject.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../../../define'; import { ApiError } from '../../../../error'; import { UserGroupInvitations } from '@/models/index'; @@ -11,12 +9,6 @@ export const meta = { kind: 'write:user-groups', - params: { - invitationId: { - validator: $.type(ID), - }, - }, - errors: { noSuchInvitation: { message: 'No such invitation.', @@ -26,8 +18,16 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + invitationId: { type: 'string', format: 'misskey:id' }, + }, + required: ['invitationId'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { // Fetch the invitation const invitation = await UserGroupInvitations.findOne({ id: ps.invitationId, diff --git a/packages/backend/src/server/api/endpoints/users/groups/invite.ts b/packages/backend/src/server/api/endpoints/users/groups/invite.ts index 3b7a4edb81..ba78a28a38 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/invite.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/invite.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../../define'; import { ApiError } from '../../../error'; import { getUser } from '../../../common/getters'; @@ -15,16 +13,6 @@ export const meta = { kind: 'write:user-groups', - params: { - groupId: { - validator: $.type(ID), - }, - - userId: { - validator: $.type(ID), - }, - }, - errors: { noSuchGroup: { message: 'No such group.', @@ -52,8 +40,17 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + groupId: { type: 'string', format: 'misskey:id' }, + userId: { type: 'string', format: 'misskey:id' }, + }, + required: ['groupId', 'userId'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, me) => { +export default define(meta, paramDef, async (ps, me) => { // Fetch the group const userGroup = await UserGroups.findOne({ id: ps.groupId, diff --git a/packages/backend/src/server/api/endpoints/users/groups/joined.ts b/packages/backend/src/server/api/endpoints/users/groups/joined.ts index ab48b1910d..192a52a66b 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/joined.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/joined.ts @@ -20,8 +20,14 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: {}, + required: [], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, me) => { +export default define(meta, paramDef, async (ps, me) => { const ownedGroups = await UserGroups.find({ userId: me.id, }); diff --git a/packages/backend/src/server/api/endpoints/users/groups/leave.ts b/packages/backend/src/server/api/endpoints/users/groups/leave.ts index d2fcdab301..b7c7b328fd 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/leave.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/leave.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../../define'; import { ApiError } from '../../../error'; import { UserGroups, UserGroupJoinings } from '@/models/index'; @@ -11,12 +9,6 @@ export const meta = { kind: 'write:user-groups', - params: { - groupId: { - validator: $.type(ID), - }, - }, - errors: { noSuchGroup: { message: 'No such group.', @@ -32,8 +24,16 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + groupId: { type: 'string', format: 'misskey:id' }, + }, + required: ['groupId'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, me) => { +export default define(meta, paramDef, async (ps, me) => { // Fetch the group const userGroup = await UserGroups.findOne({ id: ps.groupId, diff --git a/packages/backend/src/server/api/endpoints/users/groups/owned.ts b/packages/backend/src/server/api/endpoints/users/groups/owned.ts index 6193a71019..4592fc437f 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/owned.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/owned.ts @@ -19,8 +19,14 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: {}, + required: [], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, me) => { +export default define(meta, paramDef, async (ps, me) => { const userGroups = await UserGroups.find({ userId: me.id, }); diff --git a/packages/backend/src/server/api/endpoints/users/groups/pull.ts b/packages/backend/src/server/api/endpoints/users/groups/pull.ts index 785bea140d..d50f61b159 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/pull.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/pull.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../../define'; import { ApiError } from '../../../error'; import { getUser } from '../../../common/getters'; @@ -12,16 +10,6 @@ export const meta = { kind: 'write:user-groups', - params: { - groupId: { - validator: $.type(ID), - }, - - userId: { - validator: $.type(ID), - }, - }, - errors: { noSuchGroup: { message: 'No such group.', @@ -43,8 +31,17 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + groupId: { type: 'string', format: 'misskey:id' }, + userId: { type: 'string', format: 'misskey:id' }, + }, + required: ['groupId', 'userId'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, me) => { +export default define(meta, paramDef, async (ps, me) => { // Fetch the group const userGroup = await UserGroups.findOne({ id: ps.groupId, diff --git a/packages/backend/src/server/api/endpoints/users/groups/show.ts b/packages/backend/src/server/api/endpoints/users/groups/show.ts index eb26eac2a8..230254060d 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/show.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/show.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../../define'; import { ApiError } from '../../../error'; import { UserGroups, UserGroupJoinings } from '@/models/index'; @@ -11,12 +9,6 @@ export const meta = { kind: 'read:user-groups', - params: { - groupId: { - validator: $.type(ID), - }, - }, - res: { type: 'object', optional: false, nullable: false, @@ -32,8 +24,16 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + groupId: { type: 'string', format: 'misskey:id' }, + }, + required: ['groupId'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, me) => { +export default define(meta, paramDef, async (ps, me) => { // Fetch the group const userGroup = await UserGroups.findOne({ id: ps.groupId, diff --git a/packages/backend/src/server/api/endpoints/users/groups/transfer.ts b/packages/backend/src/server/api/endpoints/users/groups/transfer.ts index 4b1c8fbbdb..4b5206fa18 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/transfer.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/transfer.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../../define'; import { ApiError } from '../../../error'; import { getUser } from '../../../common/getters'; @@ -12,16 +10,6 @@ export const meta = { kind: 'write:user-groups', - params: { - groupId: { - validator: $.type(ID), - }, - - userId: { - validator: $.type(ID), - }, - }, - res: { type: 'object', optional: false, nullable: false, @@ -49,8 +37,17 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + groupId: { type: 'string', format: 'misskey:id' }, + userId: { type: 'string', format: 'misskey:id' }, + }, + required: ['groupId', 'userId'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, me) => { +export default define(meta, paramDef, async (ps, me) => { // Fetch the group const userGroup = await UserGroups.findOne({ id: ps.groupId, diff --git a/packages/backend/src/server/api/endpoints/users/groups/update.ts b/packages/backend/src/server/api/endpoints/users/groups/update.ts index 6caf903555..0714fb4bd4 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/update.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/update.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../../define'; import { ApiError } from '../../../error'; import { UserGroups } from '@/models/index'; @@ -11,16 +9,6 @@ export const meta = { kind: 'write:user-groups', - params: { - groupId: { - validator: $.type(ID), - }, - - name: { - validator: $.str.range(1, 100), - }, - }, - res: { type: 'object', optional: false, nullable: false, @@ -36,8 +24,17 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + groupId: { type: 'string', format: 'misskey:id' }, + name: { type: 'string', minLength: 1, maxLength: 100 }, + }, + required: ['groupId', 'name'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, me) => { +export default define(meta, paramDef, async (ps, me) => { // Fetch the group const userGroup = await UserGroups.findOne({ id: ps.groupId, diff --git a/packages/backend/src/server/api/endpoints/users/lists/create.ts b/packages/backend/src/server/api/endpoints/users/lists/create.ts index 945b511628..0a74424e0e 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/create.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/create.ts @@ -1,4 +1,3 @@ -import $ from 'cafy'; import define from '../../../define'; import { UserLists } from '@/models/index'; import { genId } from '@/misc/gen-id'; @@ -11,12 +10,6 @@ export const meta = { kind: 'write:account', - params: { - name: { - validator: $.str.range(1, 100), - }, - }, - res: { type: 'object', optional: false, nullable: false, @@ -24,8 +17,16 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + name: { type: 'string', minLength: 1, maxLength: 100 }, + }, + required: ['name'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const userList = await UserLists.insert({ id: genId(), createdAt: new Date(), diff --git a/packages/backend/src/server/api/endpoints/users/lists/delete.ts b/packages/backend/src/server/api/endpoints/users/lists/delete.ts index 3183d2a09c..6795a227dc 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/delete.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/delete.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../../define'; import { ApiError } from '../../../error'; import { UserLists } from '@/models/index'; @@ -11,12 +9,6 @@ export const meta = { kind: 'write:account', - params: { - listId: { - validator: $.type(ID), - }, - }, - errors: { noSuchList: { message: 'No such list.', @@ -26,8 +18,16 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + listId: { type: 'string', format: 'misskey:id' }, + }, + required: ['listId'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const userList = await UserLists.findOne({ id: ps.listId, userId: user.id, diff --git a/packages/backend/src/server/api/endpoints/users/lists/list.ts b/packages/backend/src/server/api/endpoints/users/lists/list.ts index ae66b0aacc..d439723403 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/list.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/list.ts @@ -19,8 +19,14 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: {}, + required: [], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, me) => { +export default define(meta, paramDef, async (ps, me) => { const userLists = await UserLists.find({ userId: me.id, }); diff --git a/packages/backend/src/server/api/endpoints/users/lists/pull.ts b/packages/backend/src/server/api/endpoints/users/lists/pull.ts index 4c74aefa8a..5eca5a999d 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/pull.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/pull.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import { publishUserListStream } from '@/services/stream'; import define from '../../../define'; import { ApiError } from '../../../error'; @@ -13,16 +11,6 @@ export const meta = { kind: 'write:account', - params: { - listId: { - validator: $.type(ID), - }, - - userId: { - validator: $.type(ID), - }, - }, - errors: { noSuchList: { message: 'No such list.', @@ -38,8 +26,17 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + listId: { type: 'string', format: 'misskey:id' }, + userId: { type: 'string', format: 'misskey:id' }, + }, + required: ['listId', 'userId'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, me) => { +export default define(meta, paramDef, async (ps, me) => { // Fetch the list const userList = await UserLists.findOne({ id: ps.listId, 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 8b50c475b0..68a1f00774 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/push.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/push.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../../define'; import { ApiError } from '../../../error'; import { getUser } from '../../../common/getters'; @@ -13,16 +11,6 @@ export const meta = { kind: 'write:account', - params: { - listId: { - validator: $.type(ID), - }, - - userId: { - validator: $.type(ID), - }, - }, - errors: { noSuchList: { message: 'No such list.', @@ -50,8 +38,17 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + listId: { type: 'string', format: 'misskey:id' }, + userId: { type: 'string', format: 'misskey:id' }, + }, + required: ['listId', 'userId'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, me) => { +export default define(meta, paramDef, async (ps, me) => { // Fetch the list const userList = await UserLists.findOne({ id: ps.listId, 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 06555c1a88..3e9aa836ba 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/show.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/show.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../../define'; import { ApiError } from '../../../error'; import { UserLists } from '@/models/index'; @@ -11,12 +9,6 @@ export const meta = { kind: 'read:account', - params: { - listId: { - validator: $.type(ID), - }, - }, - res: { type: 'object', optional: false, nullable: false, @@ -32,8 +24,16 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + listId: { type: 'string', format: 'misskey:id' }, + }, + required: ['listId'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, me) => { +export default define(meta, paramDef, async (ps, me) => { // Fetch the list const userList = await UserLists.findOne({ id: ps.listId, diff --git a/packages/backend/src/server/api/endpoints/users/lists/update.ts b/packages/backend/src/server/api/endpoints/users/lists/update.ts index 02b0d5fe18..ee1489fc6a 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/update.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/update.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../../define'; import { ApiError } from '../../../error'; import { UserLists } from '@/models/index'; @@ -11,16 +9,6 @@ export const meta = { kind: 'write:account', - params: { - listId: { - validator: $.type(ID), - }, - - name: { - validator: $.str.range(1, 100), - }, - }, - res: { type: 'object', optional: false, nullable: false, @@ -36,8 +24,17 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + listId: { type: 'string', format: 'misskey:id' }, + name: { type: 'string', minLength: 1, maxLength: 100 }, + }, + required: ['listId', 'name'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { // Fetch the list const userList = await UserLists.findOne({ id: ps.listId, diff --git a/packages/backend/src/server/api/endpoints/users/notes.ts b/packages/backend/src/server/api/endpoints/users/notes.ts index 99158fb0ae..956d90b7ad 100644 --- a/packages/backend/src/server/api/endpoints/users/notes.ts +++ b/packages/backend/src/server/api/endpoints/users/notes.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../define'; import { ApiError } from '../../error'; import { getUser } from '../../common/getters'; @@ -14,57 +12,6 @@ import { generateMutedInstanceQuery } from '../../common/generate-muted-instance export const meta = { tags: ['users', 'notes'], - params: { - userId: { - validator: $.type(ID), - }, - - includeReplies: { - validator: $.optional.bool, - default: true, - }, - - limit: { - validator: $.optional.num.range(1, 100), - default: 10, - }, - - sinceId: { - validator: $.optional.type(ID), - }, - - untilId: { - validator: $.optional.type(ID), - }, - - sinceDate: { - validator: $.optional.num, - }, - - untilDate: { - validator: $.optional.num, - }, - - includeMyRenotes: { - validator: $.optional.bool, - default: true, - }, - - withFiles: { - validator: $.optional.bool, - default: false, - }, - - fileType: { - validator: $.optional.arr($.str), - }, - - excludeNsfw: { - validator: $.optional.bool, - default: false, - }, - }, - res: { type: 'array', optional: false, nullable: false, @@ -84,8 +31,28 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + userId: { type: 'string', format: 'misskey:id' }, + includeReplies: { type: 'boolean', default: true }, + limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, + sinceId: { type: 'string', format: 'misskey:id' }, + untilId: { type: 'string', format: 'misskey:id' }, + sinceDate: { type: 'integer' }, + untilDate: { type: 'integer' }, + includeMyRenotes: { type: 'boolean', default: true }, + withFiles: { type: 'boolean', default: false }, + fileType: { type: 'array', items: { + type: 'string', + } }, + excludeNsfw: { type: 'boolean', default: false }, + }, + required: ['userId'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, me) => { +export default define(meta, paramDef, async (ps, me) => { // Lookup user const user = await getUser(ps.userId).catch(e => { if (e.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); @@ -141,7 +108,7 @@ export default define(meta, async (ps, me) => { //#endregion - const timeline = await query.take(ps.limit!).getMany(); + const timeline = await query.take(ps.limit).getMany(); return await Notes.packMany(timeline, me); }); diff --git a/packages/backend/src/server/api/endpoints/users/pages.ts b/packages/backend/src/server/api/endpoints/users/pages.ts index 6e003dd1af..873b5e80fa 100644 --- a/packages/backend/src/server/api/endpoints/users/pages.ts +++ b/packages/backend/src/server/api/endpoints/users/pages.ts @@ -1,40 +1,30 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../define'; import { Pages } from '@/models/index'; import { makePaginationQuery } from '../../common/make-pagination-query'; export const meta = { tags: ['users', 'pages'], +} as const; - params: { - userId: { - validator: $.type(ID), - }, - - limit: { - validator: $.optional.num.range(1, 100), - default: 10, - }, - - sinceId: { - validator: $.optional.type(ID), - }, - - untilId: { - validator: $.optional.type(ID), - }, +const paramDef = { + type: 'object', + properties: { + 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' }, }, + required: ['userId'], } as const; // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, user) => { +export default define(meta, paramDef, async (ps, user) => { const query = makePaginationQuery(Pages.createQueryBuilder('page'), ps.sinceId, ps.untilId) .andWhere(`page.userId = :userId`, { userId: ps.userId }) .andWhere('page.visibility = \'public\''); const pages = await query - .take(ps.limit!) + .take(ps.limit) .getMany(); return await Pages.packMany(pages); diff --git a/packages/backend/src/server/api/endpoints/users/reactions.ts b/packages/backend/src/server/api/endpoints/users/reactions.ts index 312d4dbf23..8d968210bc 100644 --- a/packages/backend/src/server/api/endpoints/users/reactions.ts +++ b/packages/backend/src/server/api/endpoints/users/reactions.ts @@ -1,5 +1,3 @@ -import $ from 'cafy'; -import { ID } from '@/misc/cafy-id'; import define from '../../define'; import { NoteReactions, UserProfiles } from '@/models/index'; import { makePaginationQuery } from '../../common/make-pagination-query'; @@ -11,33 +9,6 @@ export const meta = { requireCredential: false, - params: { - userId: { - validator: $.type(ID), - }, - - limit: { - validator: $.optional.num.range(1, 100), - default: 10, - }, - - sinceId: { - validator: $.optional.type(ID), - }, - - untilId: { - validator: $.optional.type(ID), - }, - - sinceDate: { - validator: $.optional.num, - }, - - untilDate: { - validator: $.optional.num, - }, - }, - res: { type: 'array', optional: false, nullable: false, @@ -57,8 +28,21 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + 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' }, + sinceDate: { type: 'integer' }, + untilDate: { type: 'integer' }, + }, + required: ['userId'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, me) => { +export default define(meta, paramDef, async (ps, me) => { const profile = await UserProfiles.findOneOrFail(ps.userId); if (me == null || (me.id !== ps.userId && !profile.publicReactions)) { @@ -73,7 +57,7 @@ export default define(meta, async (ps, me) => { generateVisibilityQuery(query, me); const reactions = await query - .take(ps.limit!) + .take(ps.limit) .getMany(); return await Promise.all(reactions.map(reaction => NoteReactions.pack(reaction, me, { withNote: true }))); diff --git a/packages/backend/src/server/api/endpoints/users/recommendation.ts b/packages/backend/src/server/api/endpoints/users/recommendation.ts index 9ea39eb2dd..4a2d8d7e4a 100644 --- a/packages/backend/src/server/api/endpoints/users/recommendation.ts +++ b/packages/backend/src/server/api/endpoints/users/recommendation.ts @@ -1,5 +1,4 @@ import ms from 'ms'; -import $ from 'cafy'; import define from '../../define'; import { Users, Followings } from '@/models/index'; import { generateMutedUserQueryForUsers } from '../../common/generate-muted-user-query'; @@ -12,18 +11,6 @@ export const meta = { kind: 'read:account', - params: { - limit: { - validator: $.optional.num.range(1, 100), - default: 10, - }, - - offset: { - validator: $.optional.num.min(0), - default: 0, - }, - }, - res: { type: 'array', optional: false, nullable: false, @@ -35,8 +22,17 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, + offset: { type: 'integer', default: 0 }, + }, + required: [], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, me) => { +export default define(meta, paramDef, async (ps, me) => { const query = Users.createQueryBuilder('user') .where('user.isLocked = FALSE') .andWhere('user.isExplorable = TRUE') @@ -58,7 +54,7 @@ export default define(meta, async (ps, me) => { query.setParameters(followingQuery.getParameters()); - const users = await query.take(ps.limit!).skip(ps.offset).getMany(); + const users = await query.take(ps.limit).skip(ps.offset).getMany(); return await Users.packMany(users, me, { detail: true }); }); diff --git a/packages/backend/src/server/api/endpoints/users/relation.ts b/packages/backend/src/server/api/endpoints/users/relation.ts index 7e319ca105..fac1a4b92d 100644 --- a/packages/backend/src/server/api/endpoints/users/relation.ts +++ b/packages/backend/src/server/api/endpoints/users/relation.ts @@ -1,6 +1,4 @@ -import $ from 'cafy'; import define from '../../define'; -import { ID } from '@/misc/cafy-id'; import { Users } from '@/models/index'; export const meta = { @@ -8,12 +6,6 @@ export const meta = { requireCredential: true, - params: { - userId: { - validator: $.either($.type(ID), $.arr($.type(ID)).unique()), - }, - }, - res: { optional: false, nullable: false, oneOf: [ @@ -101,8 +93,24 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + userId: { + anyOf: [ + { type: 'string', format: 'misskey:id' }, + { + type: 'array', + items: { type: 'string', format: 'misskey:id' }, + }, + ], + }, + }, + required: ['userId'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, me) => { +export default define(meta, paramDef, async (ps, me) => { const ids = Array.isArray(ps.userId) ? ps.userId : [ps.userId]; const relations = await Promise.all(ids.map(id => Users.getRelation(me.id, id))); diff --git a/packages/backend/src/server/api/endpoints/users/report-abuse.ts b/packages/backend/src/server/api/endpoints/users/report-abuse.ts index ed2aa7bb26..1df5e1573f 100644 --- a/packages/backend/src/server/api/endpoints/users/report-abuse.ts +++ b/packages/backend/src/server/api/endpoints/users/report-abuse.ts @@ -1,6 +1,4 @@ -import $ from 'cafy'; import * as sanitizeHtml from 'sanitize-html'; -import { ID } from '@/misc/cafy-id'; import define from '../../define'; import { publishAdminStream } from '@/services/stream'; import { ApiError } from '../../error'; @@ -15,16 +13,6 @@ export const meta = { requireCredential: true, - params: { - userId: { - validator: $.type(ID), - }, - - comment: { - validator: $.str.range(1, 2048), - }, - }, - errors: { noSuchUser: { message: 'No such user.', @@ -46,8 +34,17 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + userId: { type: 'string', format: 'misskey:id' }, + comment: { type: 'string', minLength: 1, maxLength: 2048 }, + }, + required: ['userId', 'comment'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, me) => { +export default define(meta, paramDef, async (ps, me) => { // Lookup user const user = await getUser(ps.userId).catch(e => { if (e.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); 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 d67625e624..433779ae2d 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 @@ -1,4 +1,3 @@ -import $ from 'cafy'; import define from '../../define'; import { Followings, Users } from '@/models/index'; import { Brackets } from 'typeorm'; @@ -10,26 +9,6 @@ export const meta = { requireCredential: false, - params: { - username: { - validator: $.optional.nullable.str, - }, - - host: { - validator: $.optional.nullable.str, - }, - - limit: { - validator: $.optional.num.range(1, 100), - default: 10, - }, - - detail: { - validator: $.optional.bool, - default: true, - }, - }, - res: { type: 'array', optional: false, nullable: false, @@ -41,8 +20,19 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + username: { type: 'string', nullable: true }, + host: { type: 'string', nullable: true }, + limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, + detail: { type: 'boolean', default: true }, + }, + required: [], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, me) => { +export default define(meta, paramDef, async (ps, me) => { const activeThreshold = new Date(Date.now() - (1000 * 60 * 60 * 24 * 30)); // 30æ—Ĩ if (ps.host) { @@ -57,7 +47,7 @@ export default define(meta, async (ps, me) => { q.andWhere('user.updatedAt IS NOT NULL'); q.orderBy('user.updatedAt', 'DESC'); - const users = await q.take(ps.limit!).getMany(); + const users = await q.take(ps.limit).getMany(); return await Users.packMany(users, me, { detail: ps.detail }); } else if (ps.username) { @@ -82,10 +72,10 @@ export default define(meta, async (ps, me) => { users = await query .orderBy('user.usernameLower', 'ASC') - .take(ps.limit!) + .take(ps.limit) .getMany(); - if (users.length < ps.limit!) { + if (users.length < ps.limit) { const otherQuery = await Users.createQueryBuilder('user') .where(`user.id NOT IN (${ followingQuery.getQuery() })`) .andWhere(`user.id != :meId`, { meId: me.id }) @@ -97,7 +87,7 @@ export default define(meta, async (ps, me) => { const otherUsers = await otherQuery .orderBy('user.updatedAt', 'DESC') - .take(ps.limit! - users.length) + .take(ps.limit - users.length) .getMany(); users = users.concat(otherUsers); @@ -108,10 +98,12 @@ export default define(meta, async (ps, me) => { .andWhere('user.usernameLower LIKE :username', { username: ps.username.toLowerCase() + '%' }) .andWhere('user.updatedAt IS NOT NULL') .orderBy('user.updatedAt', 'DESC') - .take(ps.limit! - users.length) + .take(ps.limit - users.length) .getMany(); } return await Users.packMany(users, me, { detail: !!ps.detail }); } + + return []; }); diff --git a/packages/backend/src/server/api/endpoints/users/search.ts b/packages/backend/src/server/api/endpoints/users/search.ts index 26f818afcc..8c28e7c3a6 100644 --- a/packages/backend/src/server/api/endpoints/users/search.ts +++ b/packages/backend/src/server/api/endpoints/users/search.ts @@ -1,4 +1,3 @@ -import $ from 'cafy'; import define from '../../define'; import { UserProfiles, Users } from '@/models/index'; import { User } from '@/models/entities/user'; @@ -9,32 +8,6 @@ export const meta = { requireCredential: false, - params: { - query: { - validator: $.str, - }, - - offset: { - validator: $.optional.num.min(0), - default: 0, - }, - - limit: { - validator: $.optional.num.range(1, 100), - default: 10, - }, - - origin: { - validator: $.optional.str.or(['local', 'remote', 'combined']), - default: 'combined', - }, - - detail: { - validator: $.optional.bool, - default: true, - }, - }, - res: { type: 'array', optional: false, nullable: false, @@ -46,8 +19,20 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + query: { type: 'string' }, + offset: { type: 'integer', default: 0 }, + limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, + origin: { type: 'string', enum: ['local', 'remote', 'combined'], default: "combined" }, + detail: { type: 'boolean', default: true }, + }, + required: ['query'], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, me) => { +export default define(meta, paramDef, async (ps, me) => { const activeThreshold = new Date(Date.now() - (1000 * 60 * 60 * 24 * 30)); // 30æ—Ĩ const isUsername = ps.query.startsWith('@'); @@ -71,7 +56,7 @@ export default define(meta, async (ps, me) => { users = await usernameQuery .orderBy('user.updatedAt', 'DESC', 'NULLS LAST') - .take(ps.limit!) + .take(ps.limit) .skip(ps.offset) .getMany(); } else { @@ -91,11 +76,11 @@ export default define(meta, async (ps, me) => { users = await nameQuery .orderBy('user.updatedAt', 'DESC', 'NULLS LAST') - .take(ps.limit!) + .take(ps.limit) .skip(ps.offset) .getMany(); - if (users.length < ps.limit!) { + if (users.length < ps.limit) { const profQuery = UserProfiles.createQueryBuilder('prof') .select('prof.userId') .where('prof.description ILIKE :query', { query: '%' + ps.query + '%' }); @@ -117,7 +102,7 @@ export default define(meta, async (ps, me) => { users = users.concat(await query .orderBy('user.updatedAt', 'DESC', 'NULLS LAST') - .take(ps.limit!) + .take(ps.limit) .skip(ps.offset) .getMany() ); diff --git a/packages/backend/src/server/api/endpoints/users/show.ts b/packages/backend/src/server/api/endpoints/users/show.ts index 92910e9ed8..29c90963ad 100644 --- a/packages/backend/src/server/api/endpoints/users/show.ts +++ b/packages/backend/src/server/api/endpoints/users/show.ts @@ -1,9 +1,7 @@ -import $ from 'cafy'; import { resolveUser } from '@/remote/resolve-user'; import define from '../../define'; import { apiLogger } from '../../logger'; import { ApiError } from '../../error'; -import { ID } from '@/misc/cafy-id'; import { Users } from '@/models/index'; import { In } from 'typeorm'; import { User } from '@/models/entities/user'; @@ -13,24 +11,6 @@ export const meta = { requireCredential: false, - params: { - userId: { - validator: $.optional.type(ID), - }, - - userIds: { - validator: $.optional.arr($.type(ID)).unique(), - }, - - username: { - validator: $.optional.str, - }, - - host: { - validator: $.optional.nullable.str, - }, - }, - res: { optional: false, nullable: false, oneOf: [ @@ -64,8 +44,21 @@ export const meta = { }, } as const; +const paramDef = { + type: 'object', + properties: { + userId: { type: 'string', format: 'misskey:id' }, + userIds: { type: 'array', uniqueItems: true, items: { + type: 'string', format: 'misskey:id', + } }, + username: { type: 'string' }, + host: { type: 'string', nullable: true }, + }, + required: [], +} as const; + // eslint-disable-next-line import/no-default-export -export default define(meta, async (ps, me) => { +export default define(meta, paramDef, async (ps, me) => { let user; const isAdminOrModerator = me && (me.isAdmin || me.isModerator); diff --git a/packages/backend/src/server/api/endpoints/users/stats.ts b/packages/backend/src/server/api/endpoints/users/stats.ts index 381e433479..29dd8fdf93 100644 --- a/packages/backend/src/server/api/endpoints/users/stats.ts +++ b/packages/backend/src/server/api/endpoints/users/stats.ts @@ -1,7 +1,5 @@ -import $ from 'cafy'; import define from '../../define'; import { ApiError } from '../../error'; -import { ID } from '@/misc/cafy-id'; import { DriveFiles, Followings, NoteFavorites, NoteReactions, Notes, PageLikes, PollVotes, Users } from '@/models/index'; export const meta = { @@ -9,12 +7,6 @@ export const meta = { requireCredential: false, - params: { - userId: { - validator: $.type(ID), - }, - }, - errors: { noSuchUser: { message: 'No such user.', @@ -24,8 +16,16 @@ export const meta = { }, } as const; +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, async (ps, me) => { +export default define(meta, paramDef, async (ps, me) => { const user = await Users.findOne(ps.userId); if (user == null) { throw new ApiError(meta.errors.noSuchUser); diff --git a/packages/backend/src/server/api/openapi/gen-spec.ts b/packages/backend/src/server/api/openapi/gen-spec.ts index 1efef8d26d..fe5a4715e8 100644 --- a/packages/backend/src/server/api/openapi/gen-spec.ts +++ b/packages/backend/src/server/api/openapi/gen-spec.ts @@ -1,5 +1,4 @@ import endpoints from '../endpoints'; -import { Context } from 'cafy'; import config from '@/config/index'; import { errors as basicErrors } from './errors'; import { schemas, convertSchemaToOpenApiSchema } from './schemas'; @@ -38,47 +37,7 @@ export function genOpenapiSpec(lang = 'ja-JP') { }, }; - function genProps(props: { [key: string]: Context; }) { - const properties = {} as any; - - for (const [k, v] of Object.entries(props)) { - properties[k] = genProp(v); - } - - return properties; - } - - function genProp(param: Context): any { - const required = param.name === 'Object' ? (param as any).props ? Object.entries((param as any).props).filter(([k, v]: any) => !v.isOptional).map(([k, v]) => k) : [] : []; - return { - description: (param.data || {}).desc, - default: (param.data || {}).default, - deprecated: (param.data || {}).deprecated, - ...((param.data || {}).default ? { default: (param.data || {}).default } : {}), - type: param.name === 'ID' ? 'string' : param.name.toLowerCase(), - ...(param.name === 'ID' ? { example: 'xxxxxxxxxx', format: 'id' } : {}), - nullable: param.isNullable, - ...(param.name === 'String' ? { - ...((param as any).enum ? { enum: (param as any).enum } : {}), - ...((param as any).minLength ? { minLength: (param as any).minLength } : {}), - ...((param as any).maxLength ? { maxLength: (param as any).maxLength } : {}), - } : {}), - ...(param.name === 'Number' ? { - ...((param as any).minimum ? { minimum: (param as any).minimum } : {}), - ...((param as any).maximum ? { maximum: (param as any).maximum } : {}), - } : {}), - ...(param.name === 'Object' ? { - ...(required.length > 0 ? { required } : {}), - properties: (param as any).props ? genProps((param as any).props) : {}, - } : {}), - ...(param.name === 'Array' ? { - items: (param as any).ctx ? genProp((param as any).ctx) : {}, - } : {}), - }; - } - for (const endpoint of endpoints.filter(ep => !ep.meta.secure)) { - const porops = {} as any; const errors = {} as any; if (endpoint.meta.errors) { @@ -91,21 +50,9 @@ export function genOpenapiSpec(lang = 'ja-JP') { } } - if (endpoint.meta.params) { - for (const [k, v] of Object.entries(endpoint.meta.params)) { - if (v.validator.data == null) v.validator.data = {}; - if (v.desc) v.validator.data.desc = v.desc[lang]; - if (v.deprecated) v.validator.data.deprecated = v.deprecated; - if (v.default) v.validator.data.default = v.default; - porops[k] = v.validator; - } - } - - const required = endpoint.meta.params ? Object.entries(endpoint.meta.params).filter(([k, v]) => !v.validator.isOptional).map(([k, v]) => k) : []; - const resSchema = endpoint.meta.res ? convertSchemaToOpenApiSchema(endpoint.meta.res) : {}; - let desc = (endpoint.meta.desc ? endpoint.meta.desc[lang] : 'No description provided.') + '\n\n'; + let desc = (endpoint.meta.description ? endpoint.meta.description : 'No description provided.') + '\n\n'; desc += `**Credential required**: *${endpoint.meta.requireCredential ? 'Yes' : 'No'}*`; if (endpoint.meta.kind) { const kind = endpoint.meta.kind; @@ -132,11 +79,7 @@ export function genOpenapiSpec(lang = 'ja-JP') { required: true, content: { 'application/json': { - schema: { - type: 'object', - ...(required.length > 0 ? { required } : {}), - properties: endpoint.meta.params ? genProps(porops) : {}, - }, + schema: endpoint.params, }, }, }, diff --git a/packages/backend/src/server/api/openapi/schemas.ts b/packages/backend/src/server/api/openapi/schemas.ts index eb42667fd5..5d062168e7 100644 --- a/packages/backend/src/server/api/openapi/schemas.ts +++ b/packages/backend/src/server/api/openapi/schemas.ts @@ -1,6 +1,6 @@ -import { refs, MinimumSchema } from '@/misc/schema'; +import { refs, Schema } from '@/misc/schema'; -export function convertSchemaToOpenApiSchema(schema: MinimumSchema) { +export function convertSchemaToOpenApiSchema(schema: Schema) { const res: any = schema; if (schema.type === 'object' && schema.properties) { diff --git a/packages/backend/src/server/api/private/signup.ts b/packages/backend/src/server/api/private/signup.ts index eaab8e7111..d42c270bb0 100644 --- a/packages/backend/src/server/api/private/signup.ts +++ b/packages/backend/src/server/api/private/signup.ts @@ -38,7 +38,7 @@ export default async (ctx: Koa.Context) => { const emailAddress = body['emailAddress']; if (instance.emailRequiredForSignup) { - if (emailAddress == null || typeof emailAddress != 'string') { + if (emailAddress == null || typeof emailAddress !== 'string') { ctx.status = 400; return; } @@ -51,7 +51,7 @@ export default async (ctx: Koa.Context) => { } if (instance.disableRegistration) { - if (invitationCode == null || typeof invitationCode != 'string') { + if (invitationCode == null || typeof invitationCode !== 'string') { ctx.status = 400; return; } diff --git a/packages/backend/src/server/api/service/discord.ts b/packages/backend/src/server/api/service/discord.ts index f574fe3878..dd731c422e 100644 --- a/packages/backend/src/server/api/service/discord.ts +++ b/packages/backend/src/server/api/service/discord.ts @@ -11,18 +11,18 @@ import { fetchMeta } from '@/misc/fetch-meta'; import { Users, UserProfiles } from '@/models/index'; import { ILocalUser } from '@/models/entities/user'; -function getUserToken(ctx: Koa.Context) { +function getUserToken(ctx: Koa.BaseContext): string | null { return ((ctx.headers['cookie'] || '').match(/igi=(\w+)/) || [null, null])[1]; } -function compareOrigin(ctx: Koa.Context) { - function normalizeUrl(url: string) { +function compareOrigin(ctx: Koa.BaseContext): boolean { + function normalizeUrl(url?: string): string { return url ? url.endsWith('/') ? url.substr(0, url.length - 1) : url : ''; } const referer = ctx.headers['referer']; - return (normalizeUrl(referer) == normalizeUrl(config.url)); + return (normalizeUrl(referer) === normalizeUrl(config.url)); } // Init router diff --git a/packages/backend/src/server/api/service/github.ts b/packages/backend/src/server/api/service/github.ts index 5e0839df93..b23219986a 100644 --- a/packages/backend/src/server/api/service/github.ts +++ b/packages/backend/src/server/api/service/github.ts @@ -11,18 +11,18 @@ import { fetchMeta } from '@/misc/fetch-meta'; import { Users, UserProfiles } from '@/models/index'; import { ILocalUser } from '@/models/entities/user'; -function getUserToken(ctx: Koa.Context) { +function getUserToken(ctx: Koa.BaseContext): string | null { return ((ctx.headers['cookie'] || '').match(/igi=(\w+)/) || [null, null])[1]; } -function compareOrigin(ctx: Koa.Context) { - function normalizeUrl(url: string) { +function compareOrigin(ctx: Koa.BaseContext): boolean { + function normalizeUrl(url?: string): string { return url ? url.endsWith('/') ? url.substr(0, url.length - 1) : url : ''; } const referer = ctx.headers['referer']; - return (normalizeUrl(referer) == normalizeUrl(config.url)); + return (normalizeUrl(referer) === normalizeUrl(config.url)); } // Init router diff --git a/packages/backend/src/server/api/service/twitter.ts b/packages/backend/src/server/api/service/twitter.ts index 8659b82cbe..bca00b7924 100644 --- a/packages/backend/src/server/api/service/twitter.ts +++ b/packages/backend/src/server/api/service/twitter.ts @@ -10,18 +10,18 @@ import { fetchMeta } from '@/misc/fetch-meta'; import { Users, UserProfiles } from '@/models/index'; import { ILocalUser } from '@/models/entities/user'; -function getUserToken(ctx: Koa.Context) { +function getUserToken(ctx: Koa.BaseContext): string | null { return ((ctx.headers['cookie'] || '').match(/igi=(\w+)/) || [null, null])[1]; } -function compareOrigin(ctx: Koa.Context) { - function normalizeUrl(url: string) { - return url.endsWith('/') ? url.substr(0, url.length - 1) : url; +function compareOrigin(ctx: Koa.BaseContext): boolean { + function normalizeUrl(url?: string): string { + return url == null ? '' : url.endsWith('/') ? url.substr(0, url.length - 1) : url; } const referer = ctx.headers['referer']; - return (normalizeUrl(referer) == normalizeUrl(config.url)); + return (normalizeUrl(referer) === normalizeUrl(config.url)); } // Init router 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 f14f597aac..ecd87d093d 100644 --- a/packages/backend/src/server/api/stream/channels/global-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/global-timeline.ts @@ -43,7 +43,7 @@ export default class extends Channel { } // é–ĸäŋ‚ãĒいčŋ”äŋĄã¯é™¤å¤– - if (note.reply) { + if (note.reply && !this.user!.showTimelineReplies) { const reply = note.reply; // 「チãƒŖãƒŗネãƒĢæŽĨįļšä¸ģへぎčŋ”äŋĄã€ã§ã‚‚ãĒければ、「チãƒŖãƒŗネãƒĢæŽĨįļšä¸ģãŒčĄŒãŖたčŋ”äŋĄã€ã§ã‚‚ãĒければ、「投į¨ŋč€…ãŽæŠ•į¨ŋ者č‡ĒčēĢへぎčŋ”äŋĄã€ã§ã‚‚ãĒい場合 if (reply.userId !== this.user!.id && note.userId !== this.user!.id && reply.userId !== note.userId) return; 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 3bd491421d..445db5c382 100644 --- a/packages/backend/src/server/api/stream/channels/home-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/home-timeline.ts @@ -54,7 +54,7 @@ export default class extends Channel { } // é–ĸäŋ‚ãĒいčŋ”äŋĄã¯é™¤å¤– - if (note.reply) { + if (note.reply && !this.user!.showTimelineReplies) { const reply = note.reply; // 「チãƒŖãƒŗネãƒĢæŽĨįļšä¸ģへぎčŋ”äŋĄã€ã§ã‚‚ãĒければ、「チãƒŖãƒŗネãƒĢæŽĨįļšä¸ģãŒčĄŒãŖたčŋ”äŋĄã€ã§ã‚‚ãĒければ、「投į¨ŋč€…ãŽæŠ•į¨ŋ者č‡ĒčēĢへぎčŋ”äŋĄã€ã§ã‚‚ãĒい場合 if (reply.userId !== this.user!.id && note.userId !== this.user!.id && reply.userId !== note.userId) 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 0ae19aa7ce..c0be71fe2d 100644 --- a/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts @@ -62,7 +62,7 @@ export default class extends Channel { if (isInstanceMuted(note, new Set(this.userProfile?.mutedInstances ?? []))) return; // é–ĸäŋ‚ãĒいčŋ”äŋĄã¯é™¤å¤– - if (note.reply) { + if (note.reply && !this.user!.showTimelineReplies) { const reply = note.reply; // 「チãƒŖãƒŗネãƒĢæŽĨįļšä¸ģへぎčŋ”äŋĄã€ã§ã‚‚ãĒければ、「チãƒŖãƒŗネãƒĢæŽĨįļšä¸ģãŒčĄŒãŖたčŋ”äŋĄã€ã§ã‚‚ãĒければ、「投į¨ŋč€…ãŽæŠ•į¨ŋ者č‡ĒčēĢへぎčŋ”äŋĄã€ã§ã‚‚ãĒい場合 if (reply.userId !== this.user!.id && note.userId !== this.user!.id && reply.userId !== note.userId) 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 3178b1d511..ae8f62ba61 100644 --- a/packages/backend/src/server/api/stream/channels/local-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/local-timeline.ts @@ -43,7 +43,7 @@ export default class extends Channel { } // é–ĸäŋ‚ãĒいčŋ”äŋĄã¯é™¤å¤– - if (note.reply) { + if (note.reply && !this.user!.showTimelineReplies) { const reply = note.reply; // 「チãƒŖãƒŗネãƒĢæŽĨįļšä¸ģへぎčŋ”äŋĄã€ã§ã‚‚ãĒければ、「チãƒŖãƒŗネãƒĢæŽĨįļšä¸ģãŒčĄŒãŖたčŋ”äŋĄã€ã§ã‚‚ãĒければ、「投į¨ŋč€…ãŽæŠ•į¨ŋ者č‡ĒčēĢへぎčŋ”äŋĄã€ã§ã‚‚ãĒい場合 if (reply.userId !== this.user!.id && note.userId !== this.user!.id && reply.userId !== note.userId) return; diff --git a/packages/backend/src/server/api/stream/index.ts b/packages/backend/src/server/api/stream/index.ts index 84689bca1a..e0bb8033a2 100644 --- a/packages/backend/src/server/api/stream/index.ts +++ b/packages/backend/src/server/api/stream/index.ts @@ -109,7 +109,8 @@ export default class Connection { * クナイã‚ĸãƒŗãƒˆã‹ã‚‰ãƒĄãƒƒã‚ģãƒŧジ受äŋĄæ™‚ */ @autobind - private async onWsConnectionMessage(data: websocket.IMessage) { + private async onWsConnectionMessage(data: websocket.Message) { + if (data.type !== 'utf8') return; if (data.utf8Data == null) return; let obj: Record; diff --git a/packages/backend/src/server/api/stream/types.ts b/packages/backend/src/server/api/stream/types.ts index e70c26f5e5..e2f1c6fc9c 100644 --- a/packages/backend/src/server/api/stream/types.ts +++ b/packages/backend/src/server/api/stream/types.ts @@ -105,7 +105,10 @@ export interface NoteStreamTypes { }; reacted: { reaction: string; - emoji?: Emoji; + emoji?: { + name: string; + url: string; + } | null; userId: User['id']; }; unreacted: { diff --git a/packages/backend/src/server/api/streaming.ts b/packages/backend/src/server/api/streaming.ts index ad87311064..b706b1b8df 100644 --- a/packages/backend/src/server/api/streaming.ts +++ b/packages/backend/src/server/api/streaming.ts @@ -59,7 +59,7 @@ module.exports = (server: http.Server) => { }); connection.on('message', async (data) => { - if (data.utf8Data === 'ping') { + if (data.type === 'utf8' && data.utf8Data === 'ping') { connection.send('pong'); } }); diff --git a/packages/backend/src/server/file/index.ts b/packages/backend/src/server/file/index.ts index a455acd1cf..6fe6110dc9 100644 --- a/packages/backend/src/server/file/index.ts +++ b/packages/backend/src/server/file/index.ts @@ -18,7 +18,7 @@ const _dirname = dirname(_filename); const app = new Koa(); app.use(cors()); app.use(async (ctx, next) => { - ctx.set('Content-Security-Policy', `default-src 'none'; style-src 'unsafe-inline'`); + ctx.set('Content-Security-Policy', `default-src 'none'; img-src 'self'; media-src 'self'; style-src 'unsafe-inline'`); await next(); }); diff --git a/packages/backend/src/server/index.ts b/packages/backend/src/server/index.ts index 764306c7d8..4d6b402e64 100644 --- a/packages/backend/src/server/index.ts +++ b/packages/backend/src/server/index.ts @@ -10,7 +10,6 @@ import * as Koa from 'koa'; import * as Router from '@koa/router'; import * as mount from 'koa-mount'; import * as koaLogger from 'koa-logger'; -import * as requestStats from 'request-stats'; import * as slow from 'koa-slow'; import activityPub from './activitypub'; @@ -18,11 +17,9 @@ import nodeinfo from './nodeinfo'; import wellKnown from './well-known'; import config from '@/config/index'; import apiServer from './api/index'; -import { sum } from '@/prelude/array'; import Logger from '@/services/logger'; import { envOption } from '../env'; import { UserProfiles, Users } from '@/models/index'; -import { networkChart } from '@/services/chart/index'; import { genIdenticon } from '@/misc/gen-identicon'; import { createTemp } from '@/misc/create-temp'; import { publishMainStream } from '@/services/stream'; @@ -153,27 +150,4 @@ export default () => new Promise(resolve => { // Listen server.listen(config.port, resolve); - - //#region Network stats - let queue: any[] = []; - - requestStats(server, (stats: any) => { - if (stats.ok) { - queue.push(stats); - } - }); - - // Bulk write - setInterval(() => { - if (queue.length === 0) return; - - const requests = queue.length; - const time = sum(queue.map(x => x.time)); - const incomingBytes = sum(queue.map(x => x.req.byets)); - const outgoingBytes = sum(queue.map(x => x.res.byets)); - queue = []; - - networkChart.update(requests, time, incomingBytes, outgoingBytes); - }, 5000); - //#endregion }); diff --git a/packages/backend/src/server/nodeinfo.ts b/packages/backend/src/server/nodeinfo.ts index 44f32bf882..4209fc7f14 100644 --- a/packages/backend/src/server/nodeinfo.ts +++ b/packages/backend/src/server/nodeinfo.ts @@ -2,7 +2,7 @@ import * as Router from '@koa/router'; import config from '@/config/index'; import { fetchMeta } from '@/misc/fetch-meta'; import { Users, Notes } from '@/models/index'; -import { Not, IsNull, MoreThan } from 'typeorm'; +import { MoreThan } from 'typeorm'; const router = new Router(); @@ -25,14 +25,12 @@ const nodeinfo2 = async () => { activeHalfyear, activeMonth, localPosts, - localComments, ] = await Promise.all([ fetchMeta(true), Users.count({ where: { host: null } }), - Users.count({ where: { host: null, updatedAt: MoreThan(new Date(now - 15552000000)) } }), - Users.count({ where: { host: null, updatedAt: MoreThan(new Date(now - 2592000000)) } }), - Notes.count({ where: { userHost: null, replyId: null } }), - Notes.count({ where: { userHost: null, replyId: Not(IsNull()) } }), + Users.count({ where: { host: null, lastActiveDate: MoreThan(new Date(now - 15552000000)) } }), + Users.count({ where: { host: null, lastActiveDate: MoreThan(new Date(now - 2592000000)) } }), + Notes.count({ where: { userHost: null } }), ]); const proxyAccount = meta.proxyAccountId ? await Users.pack(meta.proxyAccountId).catch(() => null) : null; @@ -52,7 +50,7 @@ const nodeinfo2 = async () => { usage: { users: { total, activeHalfyear, activeMonth }, localPosts, - localComments, + localComments: 0, }, metadata: { nodeName: meta.name, diff --git a/packages/backend/src/server/proxy/index.ts b/packages/backend/src/server/proxy/index.ts index b8993f19f8..7a3094311c 100644 --- a/packages/backend/src/server/proxy/index.ts +++ b/packages/backend/src/server/proxy/index.ts @@ -11,7 +11,7 @@ import { proxyMedia } from './proxy-media'; const app = new Koa(); app.use(cors()); app.use(async (ctx, next) => { - ctx.set('Content-Security-Policy', `default-src 'none'; style-src 'unsafe-inline'`); + ctx.set('Content-Security-Policy', `default-src 'none'; img-src 'self'; media-src 'self'; style-src 'unsafe-inline'`); await next(); }); diff --git a/packages/backend/src/server/proxy/proxy-media.ts b/packages/backend/src/server/proxy/proxy-media.ts index c234b70c55..b7dcd0292b 100644 --- a/packages/backend/src/server/proxy/proxy-media.ts +++ b/packages/backend/src/server/proxy/proxy-media.ts @@ -11,6 +11,11 @@ import { FILE_TYPE_BROWSERSAFE } from '@/const'; export async function proxyMedia(ctx: Koa.Context) { const url = 'url' in ctx.query ? ctx.query.url : 'https://' + ctx.params.url; + if (typeof url !== 'string') { + ctx.status = 400; + return; + } + // Create temp file const [path, cleanup] = await createTemp(); diff --git a/packages/backend/src/server/web/index.ts b/packages/backend/src/server/web/index.ts index e95a115aec..325121bbaf 100644 --- a/packages/backend/src/server/web/index.ts +++ b/packages/backend/src/server/web/index.ts @@ -200,6 +200,7 @@ router.get(['/@:user', '/@:user/:sub'], async (ctx, next) => { sub: ctx.params.sub, instanceName: meta.name || 'Misskey', icon: meta.iconUrl, + themeColor: meta.themeColor, }); ctx.set('Cache-Control', 'public, max-age=30'); } else { @@ -239,6 +240,7 @@ router.get('/notes/:note', async (ctx, next) => { summary: getNoteSummary(_note), instanceName: meta.name || 'Misskey', icon: meta.iconUrl, + themeColor: meta.themeColor, }); if (['public', 'home'].includes(note.visibility)) { @@ -276,6 +278,8 @@ router.get('/@:user/pages/:page', async (ctx, next) => { page: _page, profile, instanceName: meta.name || 'Misskey', + icon: meta.iconUrl, + themeColor: meta.themeColor, }); if (['public'].includes(page.visibility)) { @@ -305,6 +309,8 @@ router.get('/clips/:clip', async (ctx, next) => { clip: _clip, profile, instanceName: meta.name || 'Misskey', + icon: meta.iconUrl, + themeColor: meta.themeColor, }); ctx.set('Cache-Control', 'public, max-age=180'); @@ -328,6 +334,7 @@ router.get('/gallery/:post', async (ctx, next) => { profile, instanceName: meta.name || 'Misskey', icon: meta.iconUrl, + themeColor: meta.themeColor, }); ctx.set('Cache-Control', 'public, max-age=180'); @@ -350,6 +357,8 @@ router.get('/channels/:channel', async (ctx, next) => { await ctx.render('channel', { channel: _channel, instanceName: meta.name || 'Misskey', + icon: meta.iconUrl, + themeColor: meta.themeColor, }); ctx.set('Cache-Control', 'public, max-age=180'); @@ -409,6 +418,7 @@ router.get('(.*)', async ctx => { instanceName: meta.name || 'Misskey', desc: meta.description, icon: meta.iconUrl, + themeColor: meta.themeColor, }); ctx.set('Cache-Control', 'public, max-age=300'); }); diff --git a/packages/backend/src/server/web/manifest.ts b/packages/backend/src/server/web/manifest.ts index 918fe27c03..464b893d6b 100644 --- a/packages/backend/src/server/web/manifest.ts +++ b/packages/backend/src/server/web/manifest.ts @@ -9,6 +9,7 @@ module.exports = async (ctx: Koa.Context) => { json.short_name = instance.name || 'Misskey'; json.name = instance.name || 'Misskey'; + if (instance.themeColor) json.theme_color = instance.themeColor; ctx.set('Cache-Control', 'max-age=300'); ctx.body = json; diff --git a/packages/backend/src/server/web/url-preview.ts b/packages/backend/src/server/web/url-preview.ts index 71465c8083..26fffbea88 100644 --- a/packages/backend/src/server/web/url-preview.ts +++ b/packages/backend/src/server/web/url-preview.ts @@ -9,22 +9,34 @@ import { getJson } from '@/misc/fetch'; const logger = new Logger('url-preview'); module.exports = async (ctx: Koa.Context) => { + const url = ctx.query.url; + if (typeof url !== 'string') { + ctx.status = 400; + return; + } + + const lang = ctx.query.lang; + if (Array.isArray(lang)) { + ctx.status = 400; + return; + } + const meta = await fetchMeta(); logger.info(meta.summalyProxy - ? `(Proxy) Getting preview of ${ctx.query.url}@${ctx.query.lang} ...` - : `Getting preview of ${ctx.query.url}@${ctx.query.lang} ...`); + ? `(Proxy) Getting preview of ${url}@${lang} ...` + : `Getting preview of ${url}@${lang} ...`); try { const summary = meta.summalyProxy ? await getJson(`${meta.summalyProxy}?${query({ - url: ctx.query.url, - lang: ctx.query.lang || 'ja-JP', - })}`) : await summaly(ctx.query.url, { + url: url, + lang: lang ?? 'ja-JP', + })}`) : await summaly(url, { followRedirects: false, - lang: ctx.query.lang || 'ja-JP', + lang: lang ?? 'ja-JP', }); - logger.succ(`Got preview of ${ctx.query.url}: ${summary.title}`); + logger.succ(`Got preview of ${url}: ${summary.title}`); summary.icon = wrap(summary.icon); summary.thumbnail = wrap(summary.thumbnail); @@ -33,8 +45,8 @@ module.exports = async (ctx: Koa.Context) => { ctx.set('Cache-Control', 'max-age=604800, immutable'); ctx.body = summary; - } catch (e) { - logger.warn(`Failed to get preview of ${ctx.query.url}: ${e}`); + } catch (err) { + logger.warn(`Failed to get preview of ${url}: ${err}`); ctx.status = 200; ctx.set('Cache-Control', 'max-age=86400, immutable'); ctx.body = '{}'; diff --git a/packages/backend/src/server/web/views/base.pug b/packages/backend/src/server/web/views/base.pug index 42c068c403..e1cb2cfa93 100644 --- a/packages/backend/src/server/web/views/base.pug +++ b/packages/backend/src/server/web/views/base.pug @@ -19,8 +19,9 @@ html meta(charset='utf-8') meta(name='application-name' content='Misskey') meta(name='referrer' content='origin') - meta(name='theme-color' content='#86b300') - meta(name='theme-color-orig' content='#86b300') + meta(name='theme-color' content= themeColor || '#86b300') + meta(name='theme-color-orig' content= themeColor || '#86b300') + meta(property='twitter:card' content='summary') meta(property='og:site_name' content= instanceName || 'Misskey') meta(name='viewport' content='width=device-width, initial-scale=1') link(rel='icon' href= icon || '/favicon.ico') @@ -29,8 +30,8 @@ html link(rel='prefetch' href='https://xn--931a.moe/assets/info.jpg') link(rel='prefetch' href='https://xn--931a.moe/assets/not-found.jpg') link(rel='prefetch' href='https://xn--931a.moe/assets/error.jpg') - link(rel='preload' href='https://use.fontawesome.com/releases/v5.15.3/css/all.css' as='style') - link(rel='stylesheet' href='https://use.fontawesome.com/releases/v5.15.3/css/all.css') + link(rel='preload' href='/assets/fontawesome/css/all.css' as='style') + link(rel='stylesheet' href='/assets/fontawesome/css/all.css') title block title @@ -42,7 +43,9 @@ html block meta block og - meta(property='og:image' content=img) + meta(property='og:title' content= title || 'Misskey') + meta(property='og:description' content= desc || '✨🌎✨ A interplanetary communication platform ✨🚀✨') + meta(property='og:image' content= img) style include ../style.css diff --git a/packages/backend/src/server/web/views/channel.pug b/packages/backend/src/server/web/views/channel.pug index 273632f0e0..486f0ecc47 100644 --- a/packages/backend/src/server/web/views/channel.pug +++ b/packages/backend/src/server/web/views/channel.pug @@ -16,6 +16,3 @@ block og meta(property='og:description' content= channel.description) meta(property='og:url' content= url) meta(property='og:image' content= channel.bannerUrl) - -block meta - meta(name='twitter:card' content='summary') diff --git a/packages/backend/src/server/web/views/clip.pug b/packages/backend/src/server/web/views/clip.pug index 8de53f19d6..7a84d50f6c 100644 --- a/packages/backend/src/server/web/views/clip.pug +++ b/packages/backend/src/server/web/views/clip.pug @@ -26,8 +26,6 @@ block meta meta(name='misskey:user-id' content=user.id) meta(name='misskey:clip-id' content=clip.id) - meta(name='twitter:card' content='summary') - // todo if user.twitter meta(name='twitter:creator' content=`@${user.twitter.screenName}`) diff --git a/packages/backend/src/server/web/views/gallery-post.pug b/packages/backend/src/server/web/views/gallery-post.pug index 95bbb2437c..ca0663a481 100644 --- a/packages/backend/src/server/web/views/gallery-post.pug +++ b/packages/backend/src/server/web/views/gallery-post.pug @@ -25,8 +25,6 @@ block meta meta(name='misskey:user-username' content=user.username) meta(name='misskey:user-id' content=user.id) - meta(name='twitter:card' content='summary') - // todo if user.twitter meta(name='twitter:creator' content=`@${user.twitter.screenName}`) diff --git a/packages/backend/src/server/web/views/note.pug b/packages/backend/src/server/web/views/note.pug index fce91bdabe..34b03f9833 100644 --- a/packages/backend/src/server/web/views/note.pug +++ b/packages/backend/src/server/web/views/note.pug @@ -26,9 +26,7 @@ block meta meta(name='misskey:user-username' content=user.username) meta(name='misskey:user-id' content=user.id) meta(name='misskey:note-id' content=note.id) - - meta(name='twitter:card' content='summary') - + // todo if user.twitter meta(name='twitter:creator' content=`@${user.twitter.screenName}`) diff --git a/packages/backend/src/server/web/views/page.pug b/packages/backend/src/server/web/views/page.pug index cb9e1039e1..b6c9548025 100644 --- a/packages/backend/src/server/web/views/page.pug +++ b/packages/backend/src/server/web/views/page.pug @@ -26,8 +26,6 @@ block meta meta(name='misskey:user-id' content=user.id) meta(name='misskey:page-id' content=page.id) - meta(name='twitter:card' content='summary') - // todo if user.twitter meta(name='twitter:creator' content=`@${user.twitter.screenName}`) diff --git a/packages/backend/src/server/web/views/user.pug b/packages/backend/src/server/web/views/user.pug index 1a8a6b4413..2adec0f889 100644 --- a/packages/backend/src/server/web/views/user.pug +++ b/packages/backend/src/server/web/views/user.pug @@ -25,8 +25,6 @@ block meta meta(name='misskey:user-username' content=user.username) meta(name='misskey:user-id' content=user.id) - meta(name='twitter:card' content='summary') - if profile.twitter meta(name='twitter:creator' content=`@${profile.twitter.screenName}`) diff --git a/packages/backend/src/services/chart/charts/active-users.ts b/packages/backend/src/services/chart/charts/active-users.ts index 9490101e36..5baf46f772 100644 --- a/packages/backend/src/services/chart/charts/active-users.ts +++ b/packages/backend/src/services/chart/charts/active-users.ts @@ -1,51 +1,49 @@ import autobind from 'autobind-decorator'; -import Chart, { Obj, DeepPartial } from '../core'; +import Chart, { KVs } from '../core'; import { User } from '@/models/entities/user'; -import { SchemaType } from '@/misc/schema'; import { Users } from '@/models/index'; import { name, schema } from './entities/active-users'; -type ActiveUsersLog = SchemaType; +const week = 1000 * 60 * 60 * 24 * 7; +const month = 1000 * 60 * 60 * 24 * 30; +const year = 1000 * 60 * 60 * 24 * 365; /** * ã‚ĸクテã‚ŖブãƒĻãƒŧã‚ļãƒŧãĢé–ĸするチãƒŖãƒŧト */ // eslint-disable-next-line import/no-default-export -export default class ActiveUsersChart extends Chart { +export default class ActiveUsersChart extends Chart { constructor() { super(name, schema); } @autobind - protected genNewLog(latest: ActiveUsersLog): DeepPartial { + protected async tickMajor(): Promise>> { return {}; } @autobind - protected aggregate(logs: ActiveUsersLog[]): ActiveUsersLog { - return { - local: { - users: logs.reduce((a, b) => a.concat(b.local.users), [] as ActiveUsersLog['local']['users']), - }, - remote: { - users: logs.reduce((a, b) => a.concat(b.remote.users), [] as ActiveUsersLog['remote']['users']), - }, - }; - } - - @autobind - protected async fetchActual(): Promise> { + protected async tickMinor(): Promise>> { return {}; } @autobind - public async update(user: { id: User['id'], host: User['host'] }): Promise { - const update: Obj = { - users: [user.id], - }; + public async read(user: { id: User['id'], host: null, createdAt: User['createdAt'] }): Promise { + await this.commit({ + 'read': [user.id], + 'registeredWithinWeek': (Date.now() - user.createdAt.getTime() < week) ? [user.id] : [], + 'registeredWithinMonth': (Date.now() - user.createdAt.getTime() < month) ? [user.id] : [], + 'registeredWithinYear': (Date.now() - user.createdAt.getTime() < year) ? [user.id] : [], + 'registeredOutsideWeek': (Date.now() - user.createdAt.getTime() > week) ? [user.id] : [], + 'registeredOutsideMonth': (Date.now() - user.createdAt.getTime() > month) ? [user.id] : [], + 'registeredOutsideYear': (Date.now() - user.createdAt.getTime() > year) ? [user.id] : [], + }); + } - await this.inc({ - [Users.isLocalUser(user) ? 'local' : 'remote']: update, + @autobind + public async write(user: { id: User['id'], host: null, createdAt: User['createdAt'] }): Promise { + await this.commit({ + 'write': [user.id], }); } } diff --git a/packages/backend/src/services/chart/charts/ap-request.ts b/packages/backend/src/services/chart/charts/ap-request.ts new file mode 100644 index 0000000000..ca763c8847 --- /dev/null +++ b/packages/backend/src/services/chart/charts/ap-request.ts @@ -0,0 +1,44 @@ +import autobind from 'autobind-decorator'; +import Chart, { KVs } from '../core'; +import { name, schema } from './entities/ap-request'; + +/** + * Chart about ActivityPub requests + */ +// eslint-disable-next-line import/no-default-export +export default class ApRequestChart extends Chart { + constructor() { + super(name, schema); + } + + @autobind + protected async tickMajor(): Promise>> { + return {}; + } + + @autobind + protected async tickMinor(): Promise>> { + return {}; + } + + @autobind + public async deliverSucc(): Promise { + await this.commit({ + 'deliverSucceeded': 1, + }); + } + + @autobind + public async deliverFail(): Promise { + await this.commit({ + 'deliverFailed': 1, + }); + } + + @autobind + public async inbox(): Promise { + await this.commit({ + 'inboxReceived': 1, + }); + } +} diff --git a/packages/backend/src/services/chart/charts/drive.ts b/packages/backend/src/services/chart/charts/drive.ts index 06cf7ebeeb..288689784e 100644 --- a/packages/backend/src/services/chart/charts/drive.ts +++ b/packages/backend/src/services/chart/charts/drive.ts @@ -1,95 +1,42 @@ import autobind from 'autobind-decorator'; -import Chart, { Obj, DeepPartial } from '../core'; -import { SchemaType } from '@/misc/schema'; +import Chart, { KVs } from '../core'; import { DriveFiles } from '@/models/index'; import { Not, IsNull } from 'typeorm'; import { DriveFile } from '@/models/entities/drive-file'; import { name, schema } from './entities/drive'; -type DriveLog = SchemaType; - /** * ドナイブãĢé–ĸするチãƒŖãƒŧト */ // eslint-disable-next-line import/no-default-export -export default class DriveChart extends Chart { +export default class DriveChart extends Chart { constructor() { super(name, schema); } @autobind - protected genNewLog(latest: DriveLog): DeepPartial { - return { - local: { - totalCount: latest.local.totalCount, - totalSize: latest.local.totalSize, - }, - remote: { - totalCount: latest.remote.totalCount, - totalSize: latest.remote.totalSize, - }, - }; + protected async tickMajor(): Promise>> { + return {}; } @autobind - protected aggregate(logs: DriveLog[]): DriveLog { - return { - local: { - totalCount: logs[0].local.totalCount, - totalSize: logs[0].local.totalSize, - incCount: logs.reduce((a, b) => a + b.local.incCount, 0), - incSize: logs.reduce((a, b) => a + b.local.incSize, 0), - decCount: logs.reduce((a, b) => a + b.local.decCount, 0), - decSize: logs.reduce((a, b) => a + b.local.decSize, 0), - }, - remote: { - totalCount: logs[0].remote.totalCount, - totalSize: logs[0].remote.totalSize, - incCount: logs.reduce((a, b) => a + b.remote.incCount, 0), - incSize: logs.reduce((a, b) => a + b.remote.incSize, 0), - decCount: logs.reduce((a, b) => a + b.remote.decCount, 0), - decSize: logs.reduce((a, b) => a + b.remote.decSize, 0), - }, - }; - } - - @autobind - protected async fetchActual(): Promise> { - const [localCount, remoteCount, localSize, remoteSize] = await Promise.all([ - DriveFiles.count({ userHost: null }), - DriveFiles.count({ userHost: Not(IsNull()) }), - DriveFiles.calcDriveUsageOfLocal(), - DriveFiles.calcDriveUsageOfRemote(), - ]); - - return { - local: { - totalCount: localCount, - totalSize: localSize, - }, - remote: { - totalCount: remoteCount, - totalSize: remoteSize, - }, - }; + protected async tickMinor(): Promise>> { + return {}; } @autobind public async update(file: DriveFile, isAdditional: boolean): Promise { - const update: Obj = {}; - - update.totalCount = isAdditional ? 1 : -1; - update.totalSize = isAdditional ? file.size : -file.size; - if (isAdditional) { - update.incCount = 1; - update.incSize = file.size; - } else { - update.decCount = 1; - update.decSize = file.size; - } - - await this.inc({ - [file.userHost === null ? 'local' : 'remote']: update, + const fileSizeKb = file.size / 1000; + await this.commit(file.userHost === null ? { + 'local.incCount': isAdditional ? 1 : 0, + 'local.incSize': isAdditional ? fileSizeKb : 0, + 'local.decCount': isAdditional ? 0 : 1, + 'local.decSize': isAdditional ? 0 : fileSizeKb, + } : { + 'remote.incCount': isAdditional ? 1 : 0, + 'remote.incSize': isAdditional ? fileSizeKb : 0, + 'remote.decCount': isAdditional ? 0 : 1, + 'remote.decSize': isAdditional ? 0 : fileSizeKb, }); } } diff --git a/packages/backend/src/services/chart/charts/entities/active-users.ts b/packages/backend/src/services/chart/charts/entities/active-users.ts index d6b49c86c3..843843836d 100644 --- a/packages/backend/src/services/chart/charts/entities/active-users.ts +++ b/packages/backend/src/services/chart/charts/entities/active-users.ts @@ -2,35 +2,16 @@ import Chart from '../../core'; export const name = 'activeUsers'; -const logSchema = { - /** - * ã‚ĸクテã‚ŖブãƒĻãƒŧã‚ļãƒŧ - */ - users: { - type: 'array' as const, - optional: false as const, nullable: false as const, - items: { - type: 'string' as const, - optional: false as const, nullable: false as const, - }, - }, -}; - export const schema = { - type: 'object' as const, - optional: false as const, nullable: false as const, - properties: { - local: { - type: 'object' as const, - optional: false as const, nullable: false as const, - properties: logSchema, - }, - remote: { - type: 'object' as const, - optional: false as const, nullable: false as const, - properties: logSchema, - }, - }, -}; + 'readWrite': { intersection: ['read', 'write'], range: 'small' }, + 'read': { uniqueIncrement: true, range: 'small' }, + 'write': { uniqueIncrement: true, range: 'small' }, + 'registeredWithinWeek': { uniqueIncrement: true, range: 'small' }, + 'registeredWithinMonth': { uniqueIncrement: true, range: 'small' }, + 'registeredWithinYear': { uniqueIncrement: true, range: 'small' }, + 'registeredOutsideWeek': { uniqueIncrement: true, range: 'small' }, + 'registeredOutsideMonth': { uniqueIncrement: true, range: 'small' }, + 'registeredOutsideYear': { uniqueIncrement: true, range: 'small' }, +} as const; export const entity = Chart.schemaToEntity(name, schema); diff --git a/packages/backend/src/services/chart/charts/entities/ap-request.ts b/packages/backend/src/services/chart/charts/entities/ap-request.ts new file mode 100644 index 0000000000..21fb40d138 --- /dev/null +++ b/packages/backend/src/services/chart/charts/entities/ap-request.ts @@ -0,0 +1,11 @@ +import Chart from '../../core'; + +export const name = 'apRequest'; + +export const schema = { + 'deliverFailed': { }, + 'deliverSucceeded': { }, + 'inboxReceived': { }, +} as const; + +export const entity = Chart.schemaToEntity(name, schema); diff --git a/packages/backend/src/services/chart/charts/entities/drive.ts b/packages/backend/src/services/chart/charts/entities/drive.ts index 3362cbd4cb..c5cdfd85bd 100644 --- a/packages/backend/src/services/chart/charts/entities/drive.ts +++ b/packages/backend/src/services/chart/charts/entities/drive.ts @@ -2,71 +2,15 @@ import Chart from '../../core'; export const name = 'drive'; -const logSchema = { - /** - * é›†č¨ˆæœŸé–“æ™‚į‚šã§ãŽã€å…¨ãƒ‰ãƒŠã‚¤ãƒ–ãƒ•ã‚Ąã‚¤ãƒĢ数 - */ - totalCount: { - type: 'number' as const, - optional: false as const, nullable: false as const, - }, - - /** - * é›†č¨ˆæœŸé–“æ™‚į‚šã§ãŽã€å…¨ãƒ‰ãƒŠã‚¤ãƒ–ãƒ•ã‚Ąã‚¤ãƒĢãŽåˆč¨ˆã‚ĩイã‚ē - */ - totalSize: { - type: 'number' as const, - optional: false as const, nullable: false as const, - }, - - /** - * åĸ—åŠ ã—ãŸãƒ‰ãƒŠã‚¤ãƒ–ãƒ•ã‚Ąã‚¤ãƒĢ数 - */ - incCount: { - type: 'number' as const, - optional: false as const, nullable: false as const, - }, - - /** - * åĸ—加したドナイブäŊŋį”¨é‡ - */ - incSize: { - type: 'number' as const, - optional: false as const, nullable: false as const, - }, - - /** - * æ¸›å°‘ã—ãŸãƒ‰ãƒŠã‚¤ãƒ–ãƒ•ã‚Ąã‚¤ãƒĢ数 - */ - decCount: { - type: 'number' as const, - optional: false as const, nullable: false as const, - }, - - /** - * 減少したドナイブäŊŋį”¨é‡ - */ - decSize: { - type: 'number' as const, - optional: false as const, nullable: false as const, - }, -}; - export const schema = { - type: 'object' as const, - optional: false as const, nullable: false as const, - properties: { - local: { - type: 'object' as const, - optional: false as const, nullable: false as const, - properties: logSchema, - }, - remote: { - type: 'object' as const, - optional: false as const, nullable: false as const, - properties: logSchema, - }, - }, -}; + 'local.incCount': {}, + 'local.incSize': {}, // in kilobyte + 'local.decCount': {}, + 'local.decSize': {}, // in kilobyte + 'remote.incCount': {}, + 'remote.incSize': {}, // in kilobyte + 'remote.decCount': {}, + 'remote.decSize': {}, // in kilobyte +} as const; export const entity = Chart.schemaToEntity(name, schema); diff --git a/packages/backend/src/services/chart/charts/entities/federation.ts b/packages/backend/src/services/chart/charts/entities/federation.ts index 836116bd06..6b2089f0b4 100644 --- a/packages/backend/src/services/chart/charts/entities/federation.ts +++ b/packages/backend/src/services/chart/charts/entities/federation.ts @@ -3,28 +3,11 @@ import Chart from '../../core'; export const name = 'federation'; export const schema = { - type: 'object' as const, - optional: false as const, nullable: false as const, - properties: { - instance: { - type: 'object' as const, - optional: false as const, nullable: false as const, - properties: { - total: { - type: 'number' as const, - optional: false as const, nullable: false as const, - }, - inc: { - type: 'number' as const, - optional: false as const, nullable: false as const, - }, - dec: { - type: 'number' as const, - optional: false as const, nullable: false as const, - }, - }, - }, - }, -}; + 'deliveredInstances': { uniqueIncrement: true, range: 'small' }, + 'inboxInstances': { uniqueIncrement: true, range: 'small' }, + 'stalled': { uniqueIncrement: true, range: 'small' }, + 'sub': { accumulate: true, range: 'small' }, + 'pub': { accumulate: true, range: 'small' }, +} as const; export const entity = Chart.schemaToEntity(name, schema); diff --git a/packages/backend/src/services/chart/charts/entities/hashtag.ts b/packages/backend/src/services/chart/charts/entities/hashtag.ts index 43e15456a5..bd2ae38a16 100644 --- a/packages/backend/src/services/chart/charts/entities/hashtag.ts +++ b/packages/backend/src/services/chart/charts/entities/hashtag.ts @@ -2,35 +2,9 @@ import Chart from '../../core'; export const name = 'hashtag'; -const logSchema = { - /** - * 投į¨ŋしたãƒĻãƒŧã‚ļãƒŧ - */ - users: { - type: 'array' as const, - optional: false as const, nullable: false as const, - items: { - type: 'string' as const, - optional: false as const, nullable: false as const, - }, - }, -}; - export const schema = { - type: 'object' as const, - optional: false as const, nullable: false as const, - properties: { - local: { - type: 'object' as const, - optional: false as const, nullable: false as const, - properties: logSchema, - }, - remote: { - type: 'object' as const, - optional: false as const, nullable: false as const, - properties: logSchema, - }, - }, -}; + 'local.users': { uniqueIncrement: true }, + 'remote.users': { uniqueIncrement: true }, +} as const; export const entity = Chart.schemaToEntity(name, schema, true); diff --git a/packages/backend/src/services/chart/charts/entities/instance.ts b/packages/backend/src/services/chart/charts/entities/instance.ts index 9d1f651dbb..b98e1640c8 100644 --- a/packages/backend/src/services/chart/charts/entities/instance.ts +++ b/packages/backend/src/services/chart/charts/entities/instance.ts @@ -3,156 +3,30 @@ import Chart from '../../core'; export const name = 'instance'; export const schema = { - type: 'object' as const, - optional: false as const, nullable: false as const, - properties: { - requests: { - type: 'object' as const, - optional: false as const, nullable: false as const, - properties: { - failed: { - type: 'number' as const, - optional: false as const, nullable: false as const, - }, - succeeded: { - type: 'number' as const, - optional: false as const, nullable: false as const, - }, - received: { - type: 'number' as const, - optional: false as const, nullable: false as const, - }, - }, - }, - - notes: { - type: 'object' as const, - optional: false as const, nullable: false as const, - properties: { - total: { - type: 'number' as const, - optional: false as const, nullable: false as const, - }, - inc: { - type: 'number' as const, - optional: false as const, nullable: false as const, - }, - dec: { - type: 'number' as const, - optional: false as const, nullable: false as const, - }, - - diffs: { - type: 'object' as const, - optional: false as const, nullable: false as const, - properties: { - normal: { - type: 'number' as const, - optional: false as const, nullable: false as const, - }, - - reply: { - type: 'number' as const, - optional: false as const, nullable: false as const, - }, - - renote: { - type: 'number' as const, - optional: false as const, nullable: false as const, - }, - }, - }, - }, - }, - - users: { - type: 'object' as const, - optional: false as const, nullable: false as const, - properties: { - total: { - type: 'number' as const, - optional: false as const, nullable: false as const, - }, - inc: { - type: 'number' as const, - optional: false as const, nullable: false as const, - }, - dec: { - type: 'number' as const, - optional: false as const, nullable: false as const, - }, - }, - }, - - following: { - type: 'object' as const, - optional: false as const, nullable: false as const, - properties: { - total: { - type: 'number' as const, - optional: false as const, nullable: false as const, - }, - inc: { - type: 'number' as const, - optional: false as const, nullable: false as const, - }, - dec: { - type: 'number' as const, - optional: false as const, nullable: false as const, - }, - }, - }, - - followers: { - type: 'object' as const, - optional: false as const, nullable: false as const, - properties: { - total: { - type: 'number' as const, - optional: false as const, nullable: false as const, - }, - inc: { - type: 'number' as const, - optional: false as const, nullable: false as const, - }, - dec: { - type: 'number' as const, - optional: false as const, nullable: false as const, - }, - }, - }, - - drive: { - type: 'object' as const, - optional: false as const, nullable: false as const, - properties: { - totalFiles: { - type: 'number' as const, - optional: false as const, nullable: false as const, - }, - totalUsage: { - type: 'number' as const, - optional: false as const, nullable: false as const, - }, - incFiles: { - type: 'number' as const, - optional: false as const, nullable: false as const, - }, - incUsage: { - type: 'number' as const, - optional: false as const, nullable: false as const, - }, - decFiles: { - type: 'number' as const, - optional: false as const, nullable: false as const, - }, - decUsage: { - type: 'number' as const, - optional: false as const, nullable: false as const, - }, - }, - }, - }, -}; + 'requests.failed': { range: 'small' }, + 'requests.succeeded': { range: 'small' }, + 'requests.received': { range: 'small' }, + 'notes.total': { accumulate: true }, + 'notes.inc': {}, + 'notes.dec': {}, + 'notes.diffs.normal': {}, + 'notes.diffs.reply': {}, + 'notes.diffs.renote': {}, + 'notes.diffs.withFile': {}, + 'users.total': { accumulate: true }, + 'users.inc': { range: 'small' }, + 'users.dec': { range: 'small' }, + 'following.total': { accumulate: true }, + 'following.inc': { range: 'small' }, + 'following.dec': { range: 'small' }, + 'followers.total': { accumulate: true }, + 'followers.inc': { range: 'small' }, + 'followers.dec': { range: 'small' }, + 'drive.totalFiles': { accumulate: true }, + 'drive.incFiles': {}, + 'drive.decFiles': {}, + 'drive.incUsage': {}, // in kilobyte + 'drive.decUsage': {}, // in kilobyte +} as const; export const entity = Chart.schemaToEntity(name, schema, true); diff --git a/packages/backend/src/services/chart/charts/entities/network.ts b/packages/backend/src/services/chart/charts/entities/network.ts deleted file mode 100644 index 3d4fffb855..0000000000 --- a/packages/backend/src/services/chart/charts/entities/network.ts +++ /dev/null @@ -1,32 +0,0 @@ -import Chart from '../../core'; - -export const name = 'network'; - -export const schema = { - type: 'object' as const, - optional: false as const, nullable: false as const, - properties: { - incomingRequests: { - type: 'number' as const, - optional: false as const, nullable: false as const, - }, - outgoingRequests: { - type: 'number' as const, - optional: false as const, nullable: false as const, - }, - totalTime: { // TIP: (totalTime / incomingRequests) ã§ã˛ã¨ã¤ãŽãƒĒクエ゚トãĢåšŗ均でおれくらいぎ時間がかかãŖたかįŸĨれる - type: 'number' as const, - optional: false as const, nullable: false as const, - }, - incomingBytes: { - type: 'number' as const, - optional: false as const, nullable: false as const, - }, - outgoingBytes: { - type: 'number' as const, - optional: false as const, nullable: false as const, - }, - }, -}; - -export const entity = Chart.schemaToEntity(name, schema); diff --git a/packages/backend/src/services/chart/charts/entities/notes.ts b/packages/backend/src/services/chart/charts/entities/notes.ts index 554d3abe12..f9b9b20eed 100644 --- a/packages/backend/src/services/chart/charts/entities/notes.ts +++ b/packages/backend/src/services/chart/charts/entities/notes.ts @@ -2,59 +2,21 @@ import Chart from '../../core'; export const name = 'notes'; -const logSchema = { - total: { - type: 'number' as const, - optional: false as const, nullable: false as const, - }, - - inc: { - type: 'number' as const, - optional: false as const, nullable: false as const, - }, - - dec: { - type: 'number' as const, - optional: false as const, nullable: false as const, - }, - - diffs: { - type: 'object' as const, - optional: false as const, nullable: false as const, - properties: { - normal: { - type: 'number' as const, - optional: false as const, nullable: false as const, - }, - - reply: { - type: 'number' as const, - optional: false as const, nullable: false as const, - }, - - renote: { - type: 'number' as const, - optional: false as const, nullable: false as const, - }, - }, - }, -}; - export const schema = { - type: 'object' as const, - optional: false as const, nullable: false as const, - properties: { - local: { - type: 'object' as const, - optional: false as const, nullable: false as const, - properties: logSchema, - }, - remote: { - type: 'object' as const, - optional: false as const, nullable: false as const, - properties: logSchema, - }, - }, -}; + 'local.total': { accumulate: true }, + 'local.inc': {}, + 'local.dec': {}, + 'local.diffs.normal': {}, + 'local.diffs.reply': {}, + 'local.diffs.renote': {}, + 'local.diffs.withFile': {}, + 'remote.total': { accumulate: true }, + 'remote.inc': {}, + 'remote.dec': {}, + 'remote.diffs.normal': {}, + 'remote.diffs.reply': {}, + 'remote.diffs.renote': {}, + 'remote.diffs.withFile': {}, +} as const; export const entity = Chart.schemaToEntity(name, schema); diff --git a/packages/backend/src/services/chart/charts/entities/per-user-drive.ts b/packages/backend/src/services/chart/charts/entities/per-user-drive.ts index ebf64e733e..00d85b1620 100644 --- a/packages/backend/src/services/chart/charts/entities/per-user-drive.ts +++ b/packages/backend/src/services/chart/charts/entities/per-user-drive.ts @@ -3,57 +3,12 @@ import Chart from '../../core'; export const name = 'perUserDrive'; export const schema = { - type: 'object' as const, - optional: false as const, nullable: false as const, - properties: { - /** - * é›†č¨ˆæœŸé–“æ™‚į‚šã§ãŽã€å…¨ãƒ‰ãƒŠã‚¤ãƒ–ãƒ•ã‚Ąã‚¤ãƒĢ数 - */ - totalCount: { - type: 'number' as const, - optional: false as const, nullable: false as const, - }, - - /** - * é›†č¨ˆæœŸé–“æ™‚į‚šã§ãŽã€å…¨ãƒ‰ãƒŠã‚¤ãƒ–ãƒ•ã‚Ąã‚¤ãƒĢãŽåˆč¨ˆã‚ĩイã‚ē - */ - totalSize: { - type: 'number' as const, - optional: false as const, nullable: false as const, - }, - - /** - * åĸ—åŠ ã—ãŸãƒ‰ãƒŠã‚¤ãƒ–ãƒ•ã‚Ąã‚¤ãƒĢ数 - */ - incCount: { - type: 'number' as const, - optional: false as const, nullable: false as const, - }, - - /** - * åĸ—加したドナイブäŊŋį”¨é‡ - */ - incSize: { - type: 'number' as const, - optional: false as const, nullable: false as const, - }, - - /** - * æ¸›å°‘ã—ãŸãƒ‰ãƒŠã‚¤ãƒ–ãƒ•ã‚Ąã‚¤ãƒĢ数 - */ - decCount: { - type: 'number' as const, - optional: false as const, nullable: false as const, - }, - - /** - * 減少したドナイブäŊŋį”¨é‡ - */ - decSize: { - type: 'number' as const, - optional: false as const, nullable: false as const, - }, - }, -}; + 'totalCount': { accumulate: true }, + 'totalSize': { accumulate: true }, // in kilobyte + 'incCount': { range: 'small' }, + 'incSize': {}, // in kilobyte + 'decCount': { range: 'small' }, + 'decSize': {}, // in kilobyte +} as const; export const entity = Chart.schemaToEntity(name, schema, true); diff --git a/packages/backend/src/services/chart/charts/entities/per-user-following.ts b/packages/backend/src/services/chart/charts/entities/per-user-following.ts index 8016c5fe97..1efd4977fc 100644 --- a/packages/backend/src/services/chart/charts/entities/per-user-following.ts +++ b/packages/backend/src/services/chart/charts/entities/per-user-following.ts @@ -2,89 +2,19 @@ import Chart from '../../core'; export const name = 'perUserFollowing'; -const logSchema = { - /** - * フりロãƒŧしãĻいる - */ - followings: { - type: 'object' as const, - optional: false as const, nullable: false as const, - properties: { - /** - * フりロãƒŧしãĻã„ã‚‹åˆč¨ˆ - */ - total: { - type: 'number' as const, - optional: false as const, nullable: false as const, - }, - - /** - * フりロãƒŧした数 - */ - inc: { - type: 'number' as const, - optional: false as const, nullable: false as const, - }, - - /** - * フりロãƒŧč§Ŗ除した数 - */ - dec: { - type: 'number' as const, - optional: false as const, nullable: false as const, - }, - }, - }, - - /** - * フりロãƒŧされãĻいる - */ - followers: { - type: 'object' as const, - optional: false as const, nullable: false as const, - properties: { - /** - * フりロãƒŧされãĻã„ã‚‹åˆč¨ˆ - */ - total: { - type: 'number' as const, - optional: false as const, nullable: false as const, - }, - - /** - * フりロãƒŧされた数 - */ - inc: { - type: 'number' as const, - optional: false as const, nullable: false as const, - }, - - /** - * フりロãƒŧč§Ŗ除された数 - */ - dec: { - type: 'number' as const, - optional: false as const, nullable: false as const, - }, - }, - }, -}; - export const schema = { - type: 'object' as const, - optional: false as const, nullable: false as const, - properties: { - local: { - type: 'object' as const, - optional: false as const, nullable: false as const, - properties: logSchema, - }, - remote: { - type: 'object' as const, - optional: false as const, nullable: false as const, - properties: logSchema, - }, - }, -}; + 'local.followings.total': { accumulate: true }, + 'local.followings.inc': { range: 'small' }, + 'local.followings.dec': { range: 'small' }, + 'local.followers.total': { accumulate: true }, + 'local.followers.inc': { range: 'small' }, + 'local.followers.dec': { range: 'small' }, + 'remote.followings.total': { accumulate: true }, + 'remote.followings.inc': { range: 'small' }, + 'remote.followings.dec': { range: 'small' }, + 'remote.followers.total': { accumulate: true }, + 'remote.followers.inc': { range: 'small' }, + 'remote.followers.dec': { range: 'small' }, +} as const; export const entity = Chart.schemaToEntity(name, schema, true); diff --git a/packages/backend/src/services/chart/charts/entities/per-user-notes.ts b/packages/backend/src/services/chart/charts/entities/per-user-notes.ts index d8f645b36e..562cde9b00 100644 --- a/packages/backend/src/services/chart/charts/entities/per-user-notes.ts +++ b/packages/backend/src/services/chart/charts/entities/per-user-notes.ts @@ -3,45 +3,13 @@ import Chart from '../../core'; export const name = 'perUserNotes'; export const schema = { - type: 'object' as const, - optional: false as const, nullable: false as const, - properties: { - total: { - type: 'number' as const, - optional: false as const, nullable: false as const, - }, - - inc: { - type: 'number' as const, - optional: false as const, nullable: false as const, - }, - - dec: { - type: 'number' as const, - optional: false as const, nullable: false as const, - }, - - diffs: { - type: 'object' as const, - optional: false as const, nullable: false as const, - properties: { - normal: { - type: 'number' as const, - optional: false as const, nullable: false as const, - }, - - reply: { - type: 'number' as const, - optional: false as const, nullable: false as const, - }, - - renote: { - type: 'number' as const, - optional: false as const, nullable: false as const, - }, - }, - }, - }, -}; + 'total': { accumulate: true }, + 'inc': { range: 'small' }, + 'dec': { range: 'small' }, + 'diffs.normal': { range: 'small' }, + 'diffs.reply': { range: 'small' }, + 'diffs.renote': { range: 'small' }, + 'diffs.withFile': { range: 'small' }, +} as const; export const entity = Chart.schemaToEntity(name, schema, true); diff --git a/packages/backend/src/services/chart/charts/entities/per-user-reactions.ts b/packages/backend/src/services/chart/charts/entities/per-user-reactions.ts index bcb7012661..ab315d24c9 100644 --- a/packages/backend/src/services/chart/charts/entities/per-user-reactions.ts +++ b/packages/backend/src/services/chart/charts/entities/per-user-reactions.ts @@ -2,31 +2,9 @@ import Chart from '../../core'; export const name = 'perUserReaction'; -const logSchema = { - /** - * čĸĢãƒĒã‚ĸã‚¯ã‚ˇãƒ§ãƒŗ数 - */ - count: { - type: 'number' as const, - optional: false as const, nullable: false as const, - }, -}; - export const schema = { - type: 'object' as const, - optional: false as const, nullable: false as const, - properties: { - local: { - type: 'object' as const, - optional: false as const, nullable: false as const, - properties: logSchema, - }, - remote: { - type: 'object' as const, - optional: false as const, nullable: false as const, - properties: logSchema, - }, - }, -}; + 'local.count': { range: 'small' }, + 'remote.count': { range: 'small' }, +} as const; export const entity = Chart.schemaToEntity(name, schema, true); diff --git a/packages/backend/src/services/chart/charts/entities/test-grouped.ts b/packages/backend/src/services/chart/charts/entities/test-grouped.ts index ca1c8c5700..78c2bbd548 100644 --- a/packages/backend/src/services/chart/charts/entities/test-grouped.ts +++ b/packages/backend/src/services/chart/charts/entities/test-grouped.ts @@ -3,30 +3,9 @@ import Chart from '../../core'; export const name = 'testGrouped'; export const schema = { - type: 'object' as const, - optional: false as const, nullable: false as const, - properties: { - foo: { - type: 'object' as const, - optional: false as const, nullable: false as const, - properties: { - total: { - type: 'number' as const, - optional: false as const, nullable: false as const, - }, - - inc: { - type: 'number' as const, - optional: false as const, nullable: false as const, - }, - - dec: { - type: 'number' as const, - optional: false as const, nullable: false as const, - }, - }, - }, - }, -}; + 'foo.total': { accumulate: true }, + 'foo.inc': {}, + 'foo.dec': {}, +} as const; export const entity = Chart.schemaToEntity(name, schema, true); diff --git a/packages/backend/src/services/chart/charts/entities/test-intersection.ts b/packages/backend/src/services/chart/charts/entities/test-intersection.ts new file mode 100644 index 0000000000..dc56eb93f5 --- /dev/null +++ b/packages/backend/src/services/chart/charts/entities/test-intersection.ts @@ -0,0 +1,11 @@ +import Chart from '../../core'; + +export const name = 'testIntersection'; + +export const schema = { + 'a': { uniqueIncrement: true }, + 'b': { uniqueIncrement: true }, + 'aAndB': { intersection: ['a', 'b'] }, +} as const; + +export const entity = Chart.schemaToEntity(name, schema); diff --git a/packages/backend/src/services/chart/charts/entities/test-unique.ts b/packages/backend/src/services/chart/charts/entities/test-unique.ts index 2e917ee9ed..dc7c1520e1 100644 --- a/packages/backend/src/services/chart/charts/entities/test-unique.ts +++ b/packages/backend/src/services/chart/charts/entities/test-unique.ts @@ -3,18 +3,7 @@ import Chart from '../../core'; export const name = 'testUnique'; export const schema = { - type: 'object' as const, - optional: false as const, nullable: false as const, - properties: { - foo: { - type: 'array' as const, - optional: false as const, nullable: false as const, - items: { - type: 'string' as const, - optional: false as const, nullable: false as const, - }, - }, - }, -}; + 'foo': { uniqueIncrement: true }, +} as const; export const entity = Chart.schemaToEntity(name, schema); diff --git a/packages/backend/src/services/chart/charts/entities/test.ts b/packages/backend/src/services/chart/charts/entities/test.ts index fa536ff2cf..edfa4c524b 100644 --- a/packages/backend/src/services/chart/charts/entities/test.ts +++ b/packages/backend/src/services/chart/charts/entities/test.ts @@ -3,30 +3,9 @@ import Chart from '../../core'; export const name = 'test'; export const schema = { - type: 'object' as const, - optional: false as const, nullable: false as const, - properties: { - foo: { - type: 'object' as const, - optional: false as const, nullable: false as const, - properties: { - total: { - type: 'number' as const, - optional: false as const, nullable: false as const, - }, - - inc: { - type: 'number' as const, - optional: false as const, nullable: false as const, - }, - - dec: { - type: 'number' as const, - optional: false as const, nullable: false as const, - }, - }, - }, - }, -}; + 'foo.total': { accumulate: true }, + 'foo.inc': {}, + 'foo.dec': {}, +} as const; export const entity = Chart.schemaToEntity(name, schema); diff --git a/packages/backend/src/services/chart/charts/entities/users.ts b/packages/backend/src/services/chart/charts/entities/users.ts index 08d51c9414..d2cec72497 100644 --- a/packages/backend/src/services/chart/charts/entities/users.ts +++ b/packages/backend/src/services/chart/charts/entities/users.ts @@ -2,47 +2,13 @@ import Chart from '../../core'; export const name = 'users'; -const logSchema = { - /** - * é›†č¨ˆæœŸé–“æ™‚į‚šã§ãŽã€å…¨ãƒĻãƒŧã‚ļãƒŧ数 - */ - total: { - type: 'number' as const, - optional: false as const, nullable: false as const, - }, - - /** - * åĸ—加したãƒĻãƒŧã‚ļãƒŧ数 - */ - inc: { - type: 'number' as const, - optional: false as const, nullable: false as const, - }, - - /** - * 減少したãƒĻãƒŧã‚ļãƒŧ数 - */ - dec: { - type: 'number' as const, - optional: false as const, nullable: false as const, - }, -}; - export const schema = { - type: 'object' as const, - optional: false as const, nullable: false as const, - properties: { - local: { - type: 'object' as const, - optional: false as const, nullable: false as const, - properties: logSchema, - }, - remote: { - type: 'object' as const, - optional: false as const, nullable: false as const, - properties: logSchema, - }, - }, -}; + 'local.total': { accumulate: true }, + 'local.inc': { range: 'small' }, + 'local.dec': { range: 'small' }, + 'remote.total': { accumulate: true }, + 'remote.inc': { range: 'small' }, + 'remote.dec': { range: 'small' }, +} as const; export const entity = Chart.schemaToEntity(name, schema); diff --git a/packages/backend/src/services/chart/charts/federation.ts b/packages/backend/src/services/chart/charts/federation.ts index 8abb18b51f..211ba1debc 100644 --- a/packages/backend/src/services/chart/charts/federation.ts +++ b/packages/backend/src/services/chart/charts/federation.ts @@ -1,66 +1,57 @@ import autobind from 'autobind-decorator'; -import Chart, { Obj, DeepPartial } from '../core'; -import { SchemaType } from '@/misc/schema'; -import { Instances } from '@/models/index'; +import Chart, { KVs } from '../core'; +import { Followings } from '@/models/index'; import { name, schema } from './entities/federation'; -type FederationLog = SchemaType; - /** * フェデãƒŦãƒŧã‚ˇãƒ§ãƒŗãĢé–ĸするチãƒŖãƒŧト */ // eslint-disable-next-line import/no-default-export -export default class FederationChart extends Chart { +export default class FederationChart extends Chart { constructor() { super(name, schema); } @autobind - protected genNewLog(latest: FederationLog): DeepPartial { + protected async tickMajor(): Promise>> { return { - instance: { - total: latest.instance.total, - }, }; } @autobind - protected aggregate(logs: FederationLog[]): FederationLog { - return { - instance: { - total: logs[0].instance.total, - inc: logs.reduce((a, b) => a + b.instance.inc, 0), - dec: logs.reduce((a, b) => a + b.instance.dec, 0), - }, - }; - } - - @autobind - protected async fetchActual(): Promise> { - const [total] = await Promise.all([ - Instances.count({}), + protected async tickMinor(): Promise>> { + const [sub, pub] = await Promise.all([ + Followings.createQueryBuilder('following') + .select('COUNT(DISTINCT following.followeeHost)') + .where('following.followeeHost IS NOT NULL') + .getRawOne() + .then(x => parseInt(x.count, 10)), + Followings.createQueryBuilder('following') + .select('COUNT(DISTINCT following.followerHost)') + .where('following.followerHost IS NOT NULL') + .getRawOne() + .then(x => parseInt(x.count, 10)), ]); return { - instance: { - total: total, - }, + 'sub': sub, + 'pub': pub, }; } @autobind - public async update(isAdditional: boolean): Promise { - const update: Obj = {}; + public async deliverd(host: string, succeeded: boolean): Promise { + await this.commit(succeeded ? { + 'deliveredInstances': [host], + } : { + 'stalled': [host], + }); + } - update.total = isAdditional ? 1 : -1; - if (isAdditional) { - update.inc = 1; - } else { - update.dec = 1; - } - - await this.inc({ - instance: update, + @autobind + public async inbox(host: string): Promise { + await this.commit({ + 'inboxInstances': [host], }); } } diff --git a/packages/backend/src/services/chart/charts/hashtag.ts b/packages/backend/src/services/chart/charts/hashtag.ts index 34e0614643..cbae686833 100644 --- a/packages/backend/src/services/chart/charts/hashtag.ts +++ b/packages/backend/src/services/chart/charts/hashtag.ts @@ -1,51 +1,33 @@ import autobind from 'autobind-decorator'; -import Chart, { Obj, DeepPartial } from '../core'; +import Chart, { KVs } from '../core'; import { User } from '@/models/entities/user'; -import { SchemaType } from '@/misc/schema'; import { Users } from '@/models/index'; import { name, schema } from './entities/hashtag'; -type HashtagLog = SchemaType; - /** * ãƒãƒƒã‚ˇãƒĨã‚ŋグãĢé–ĸするチãƒŖãƒŧト */ // eslint-disable-next-line import/no-default-export -export default class HashtagChart extends Chart { +export default class HashtagChart extends Chart { constructor() { super(name, schema, true); } @autobind - protected genNewLog(latest: HashtagLog): DeepPartial { + protected async tickMajor(): Promise>> { return {}; } @autobind - protected aggregate(logs: HashtagLog[]): HashtagLog { - return { - local: { - users: logs.reduce((a, b) => a.concat(b.local.users), [] as HashtagLog['local']['users']), - }, - remote: { - users: logs.reduce((a, b) => a.concat(b.remote.users), [] as HashtagLog['remote']['users']), - }, - }; - } - - @autobind - protected async fetchActual(): Promise> { + protected async tickMinor(): Promise>> { return {}; } @autobind public async update(hashtag: string, user: { id: User['id'], host: User['host'] }): Promise { - const update: Obj = { - users: [user.id], - }; - - await this.inc({ - [Users.isLocalUser(user) ? 'local' : 'remote']: update, + await this.commit({ + 'local.users': Users.isLocalUser(user) ? [user.id] : [], + 'remote.users': Users.isLocalUser(user) ? [] : [user.id], }, hashtag); } } diff --git a/packages/backend/src/services/chart/charts/instance.ts b/packages/backend/src/services/chart/charts/instance.ts index 7f3419b69c..930ac4729b 100644 --- a/packages/backend/src/services/chart/charts/instance.ts +++ b/packages/backend/src/services/chart/charts/instance.ts @@ -1,221 +1,115 @@ import autobind from 'autobind-decorator'; -import Chart, { Obj, DeepPartial } from '../core'; -import { SchemaType } from '@/misc/schema'; +import Chart, { KVs } from '../core'; import { DriveFiles, Followings, Users, Notes } from '@/models/index'; import { DriveFile } from '@/models/entities/drive-file'; import { Note } from '@/models/entities/note'; import { toPuny } from '@/misc/convert-host'; import { name, schema } from './entities/instance'; -type InstanceLog = SchemaType; - /** * イãƒŗã‚šã‚ŋãƒŗ゚ごとぎチãƒŖãƒŧト */ // eslint-disable-next-line import/no-default-export -export default class InstanceChart extends Chart { +export default class InstanceChart extends Chart { constructor() { super(name, schema, true); } @autobind - protected genNewLog(latest: InstanceLog): DeepPartial { - return { - notes: { - total: latest.notes.total, - }, - users: { - total: latest.users.total, - }, - following: { - total: latest.following.total, - }, - followers: { - total: latest.followers.total, - }, - drive: { - totalFiles: latest.drive.totalFiles, - totalUsage: latest.drive.totalUsage, - }, - }; - } - - @autobind - protected aggregate(logs: InstanceLog[]): InstanceLog { - return { - requests: { - failed: logs.reduce((a, b) => a + b.requests.failed, 0), - succeeded: logs.reduce((a, b) => a + b.requests.succeeded, 0), - received: logs.reduce((a, b) => a + b.requests.received, 0), - }, - notes: { - total: logs[0].notes.total, - inc: logs.reduce((a, b) => a + b.notes.inc, 0), - dec: logs.reduce((a, b) => a + b.notes.dec, 0), - diffs: { - reply: logs.reduce((a, b) => a + b.notes.diffs.reply, 0), - renote: logs.reduce((a, b) => a + b.notes.diffs.renote, 0), - normal: logs.reduce((a, b) => a + b.notes.diffs.normal, 0), - }, - }, - users: { - total: logs[0].users.total, - inc: logs.reduce((a, b) => a + b.users.inc, 0), - dec: logs.reduce((a, b) => a + b.users.dec, 0), - }, - following: { - total: logs[0].following.total, - inc: logs.reduce((a, b) => a + b.following.inc, 0), - dec: logs.reduce((a, b) => a + b.following.dec, 0), - }, - followers: { - total: logs[0].followers.total, - inc: logs.reduce((a, b) => a + b.followers.inc, 0), - dec: logs.reduce((a, b) => a + b.followers.dec, 0), - }, - drive: { - totalFiles: logs[0].drive.totalFiles, - totalUsage: logs[0].drive.totalUsage, - incFiles: logs.reduce((a, b) => a + b.drive.incFiles, 0), - incUsage: logs.reduce((a, b) => a + b.drive.incUsage, 0), - decFiles: logs.reduce((a, b) => a + b.drive.decFiles, 0), - decUsage: logs.reduce((a, b) => a + b.drive.decUsage, 0), - }, - }; - } - - @autobind - protected async fetchActual(group: string): Promise> { + protected async tickMajor(group: string): Promise>> { const [ notesCount, usersCount, followingCount, followersCount, driveFiles, - driveUsage, + //driveUsage, ] = await Promise.all([ Notes.count({ userHost: group }), Users.count({ host: group }), Followings.count({ followerHost: group }), Followings.count({ followeeHost: group }), DriveFiles.count({ userHost: group }), - DriveFiles.calcDriveUsageOfHost(group), + //DriveFiles.calcDriveUsageOfHost(group), ]); return { - notes: { - total: notesCount, - }, - users: { - total: usersCount, - }, - following: { - total: followingCount, - }, - followers: { - total: followersCount, - }, - drive: { - totalFiles: driveFiles, - totalUsage: driveUsage, - }, + 'notes.total': notesCount, + 'users.total': usersCount, + 'following.total': followingCount, + 'followers.total': followersCount, + 'drive.totalFiles': driveFiles, }; } + @autobind + protected async tickMinor(): Promise>> { + return {}; + } + @autobind public async requestReceived(host: string): Promise { - await this.inc({ - requests: { - received: 1, - }, + await this.commit({ + 'requests.received': 1, }, toPuny(host)); } @autobind public async requestSent(host: string, isSucceeded: boolean): Promise { - const update: Obj = {}; - - if (isSucceeded) { - update.succeeded = 1; - } else { - update.failed = 1; - } - - await this.inc({ - requests: update, + await this.commit({ + 'requests.succeeded': isSucceeded ? 1 : 0, + 'requests.failed': isSucceeded ? 0 : 1, }, toPuny(host)); } @autobind public async newUser(host: string): Promise { - await this.inc({ - users: { - total: 1, - inc: 1, - }, + await this.commit({ + 'users.total': 1, + 'users.inc': 1, }, toPuny(host)); } @autobind public async updateNote(host: string, note: Note, isAdditional: boolean): Promise { - const diffs = {} as Record; - - if (note.replyId != null) { - diffs.reply = isAdditional ? 1 : -1; - } else if (note.renoteId != null) { - diffs.renote = isAdditional ? 1 : -1; - } else { - diffs.normal = isAdditional ? 1 : -1; - } - - await this.inc({ - notes: { - total: isAdditional ? 1 : -1, - inc: isAdditional ? 1 : 0, - dec: isAdditional ? 0 : 1, - diffs: diffs, - }, + await this.commit({ + 'notes.total': isAdditional ? 1 : -1, + 'notes.inc': isAdditional ? 1 : 0, + 'notes.dec': isAdditional ? 0 : 1, + 'notes.diffs.normal': note.replyId == null && note.renoteId == null ? (isAdditional ? 1 : -1) : 0, + 'notes.diffs.renote': note.renoteId != null ? (isAdditional ? 1 : -1) : 0, + 'notes.diffs.reply': note.replyId != null ? (isAdditional ? 1 : -1) : 0, + 'notes.diffs.withFile': note.fileIds.length > 0 ? (isAdditional ? 1 : -1) : 0, }, toPuny(host)); } @autobind public async updateFollowing(host: string, isAdditional: boolean): Promise { - await this.inc({ - following: { - total: isAdditional ? 1 : -1, - inc: isAdditional ? 1 : 0, - dec: isAdditional ? 0 : 1, - }, + await this.commit({ + 'following.total': isAdditional ? 1 : -1, + 'following.inc': isAdditional ? 1 : 0, + 'following.dec': isAdditional ? 0 : 1, }, toPuny(host)); } @autobind public async updateFollowers(host: string, isAdditional: boolean): Promise { - await this.inc({ - followers: { - total: isAdditional ? 1 : -1, - inc: isAdditional ? 1 : 0, - dec: isAdditional ? 0 : 1, - }, + await this.commit({ + 'followers.total': isAdditional ? 1 : -1, + 'followers.inc': isAdditional ? 1 : 0, + 'followers.dec': isAdditional ? 0 : 1, }, toPuny(host)); } @autobind public async updateDrive(file: DriveFile, isAdditional: boolean): Promise { - const update: Obj = {}; - - update.totalFiles = isAdditional ? 1 : -1; - update.totalUsage = isAdditional ? file.size : -file.size; - if (isAdditional) { - update.incFiles = 1; - update.incUsage = file.size; - } else { - update.decFiles = 1; - update.decUsage = file.size; - } - - await this.inc({ - drive: update, + const fileSizeKb = file.size / 1000; + await this.commit({ + 'drive.totalFiles': isAdditional ? 1 : -1, + 'drive.incFiles': isAdditional ? 1 : 0, + 'drive.incUsage': isAdditional ? fileSizeKb : 0, + 'drive.decFiles': isAdditional ? 1 : 0, + 'drive.decUsage': isAdditional ? fileSizeKb : 0, }, file.userHost); } } diff --git a/packages/backend/src/services/chart/charts/network.ts b/packages/backend/src/services/chart/charts/network.ts deleted file mode 100644 index 73ea2f7e19..0000000000 --- a/packages/backend/src/services/chart/charts/network.ts +++ /dev/null @@ -1,49 +0,0 @@ -import autobind from 'autobind-decorator'; -import Chart, { DeepPartial } from '../core'; -import { SchemaType } from '@/misc/schema'; -import { name, schema } from './entities/network'; - -type NetworkLog = SchemaType; - -/** - * ネットワãƒŧクãĢé–ĸするチãƒŖãƒŧト - */ -// eslint-disable-next-line import/no-default-export -export default class NetworkChart extends Chart { - constructor() { - super(name, schema); - } - - @autobind - protected genNewLog(latest: NetworkLog): DeepPartial { - return {}; - } - - @autobind - protected aggregate(logs: NetworkLog[]): NetworkLog { - return { - incomingRequests: logs.reduce((a, b) => a + b.incomingRequests, 0), - outgoingRequests: logs.reduce((a, b) => a + b.outgoingRequests, 0), - totalTime: logs.reduce((a, b) => a + b.totalTime, 0), - incomingBytes: logs.reduce((a, b) => a + b.incomingBytes, 0), - outgoingBytes: logs.reduce((a, b) => a + b.outgoingBytes, 0), - }; - } - - @autobind - protected async fetchActual(): Promise> { - return {}; - } - - @autobind - public async update(incomingRequests: number, time: number, incomingBytes: number, outgoingBytes: number): Promise { - const inc: DeepPartial = { - incomingRequests: incomingRequests, - totalTime: time, - incomingBytes: incomingBytes, - outgoingBytes: outgoingBytes, - }; - - await this.inc(inc); - } -} diff --git a/packages/backend/src/services/chart/charts/notes.ts b/packages/backend/src/services/chart/charts/notes.ts index 86cda17225..624ee5db28 100644 --- a/packages/backend/src/services/chart/charts/notes.ts +++ b/packages/backend/src/services/chart/charts/notes.ts @@ -1,101 +1,49 @@ import autobind from 'autobind-decorator'; -import Chart, { Obj, DeepPartial } from '../core'; -import { SchemaType } from '@/misc/schema'; +import Chart, { KVs } from '../core'; import { Notes } from '@/models/index'; import { Not, IsNull } from 'typeorm'; import { Note } from '@/models/entities/note'; import { name, schema } from './entities/notes'; -type NotesLog = SchemaType; - /** * ノãƒŧトãĢé–ĸするチãƒŖãƒŧト */ // eslint-disable-next-line import/no-default-export -export default class NotesChart extends Chart { +export default class NotesChart extends Chart { constructor() { super(name, schema); } @autobind - protected genNewLog(latest: NotesLog): DeepPartial { - return { - local: { - total: latest.local.total, - }, - remote: { - total: latest.remote.total, - }, - }; - } - - @autobind - protected aggregate(logs: NotesLog[]): NotesLog { - return { - local: { - total: logs[0].local.total, - inc: logs.reduce((a, b) => a + b.local.inc, 0), - dec: logs.reduce((a, b) => a + b.local.dec, 0), - diffs: { - reply: logs.reduce((a, b) => a + b.local.diffs.reply, 0), - renote: logs.reduce((a, b) => a + b.local.diffs.renote, 0), - normal: logs.reduce((a, b) => a + b.local.diffs.normal, 0), - }, - }, - remote: { - total: logs[0].remote.total, - inc: logs.reduce((a, b) => a + b.remote.inc, 0), - dec: logs.reduce((a, b) => a + b.remote.dec, 0), - diffs: { - reply: logs.reduce((a, b) => a + b.remote.diffs.reply, 0), - renote: logs.reduce((a, b) => a + b.remote.diffs.renote, 0), - normal: logs.reduce((a, b) => a + b.remote.diffs.normal, 0), - }, - }, - }; - } - - @autobind - protected async fetchActual(): Promise> { + protected async tickMajor(): Promise>> { const [localCount, remoteCount] = await Promise.all([ Notes.count({ userHost: null }), Notes.count({ userHost: Not(IsNull()) }), ]); return { - local: { - total: localCount, - }, - remote: { - total: remoteCount, - }, + 'local.total': localCount, + 'remote.total': remoteCount, }; } + @autobind + protected async tickMinor(): Promise>> { + return {}; + } + @autobind public async update(note: Note, isAdditional: boolean): Promise { - const update: Obj = { - diffs: {}, - }; + const prefix = note.userHost === null ? 'local' : 'remote'; - update.total = isAdditional ? 1 : -1; - - if (isAdditional) { - update.inc = 1; - } else { - update.dec = 1; - } - - if (note.replyId != null) { - update.diffs.reply = isAdditional ? 1 : -1; - } else if (note.renoteId != null) { - update.diffs.renote = isAdditional ? 1 : -1; - } else { - update.diffs.normal = isAdditional ? 1 : -1; - } - - await this.inc({ - [note.userHost === null ? 'local' : 'remote']: update, + await this.commit({ + [`${prefix}.total`]: isAdditional ? 1 : -1, + [`${prefix}.inc`]: isAdditional ? 1 : 0, + [`${prefix}.dec`]: isAdditional ? 0 : 1, + [`${prefix}.diffs.normal`]: note.replyId == null && note.renoteId == null ? (isAdditional ? 1 : -1) : 0, + [`${prefix}.diffs.renote`]: note.renoteId != null ? (isAdditional ? 1 : -1) : 0, + [`${prefix}.diffs.reply`]: note.replyId != null ? (isAdditional ? 1 : -1) : 0, + [`${prefix}.diffs.withFile`]: note.fileIds.length > 0 ? (isAdditional ? 1 : -1) : 0, }); } } diff --git a/packages/backend/src/services/chart/charts/per-user-drive.ts b/packages/backend/src/services/chart/charts/per-user-drive.ts index fff790367f..ae9e8c5694 100644 --- a/packages/backend/src/services/chart/charts/per-user-drive.ts +++ b/packages/backend/src/services/chart/charts/per-user-drive.ts @@ -1,68 +1,46 @@ import autobind from 'autobind-decorator'; -import Chart, { Obj, DeepPartial } from '../core'; -import { SchemaType } from '@/misc/schema'; +import Chart, { KVs } from '../core'; import { DriveFiles } from '@/models/index'; import { DriveFile } from '@/models/entities/drive-file'; import { name, schema } from './entities/per-user-drive'; -type PerUserDriveLog = SchemaType; - /** * ãƒĻãƒŧã‚ļãƒŧごとぎドナイブãĢé–ĸするチãƒŖãƒŧト */ // eslint-disable-next-line import/no-default-export -export default class PerUserDriveChart extends Chart { +export default class PerUserDriveChart extends Chart { constructor() { super(name, schema, true); } @autobind - protected genNewLog(latest: PerUserDriveLog): DeepPartial { - return { - totalCount: latest.totalCount, - totalSize: latest.totalSize, - }; - } - - @autobind - protected aggregate(logs: PerUserDriveLog[]): PerUserDriveLog { - return { - totalCount: logs[0].totalCount, - totalSize: logs[0].totalSize, - incCount: logs.reduce((a, b) => a + b.incCount, 0), - incSize: logs.reduce((a, b) => a + b.incSize, 0), - decCount: logs.reduce((a, b) => a + b.decCount, 0), - decSize: logs.reduce((a, b) => a + b.decSize, 0), - }; - } - - @autobind - protected async fetchActual(group: string): Promise> { + protected async tickMajor(group: string): Promise>> { const [count, size] = await Promise.all([ DriveFiles.count({ userId: group }), DriveFiles.calcDriveUsageOf(group), ]); return { - totalCount: count, - totalSize: size, + 'totalCount': count, + 'totalSize': size, }; } + @autobind + protected async tickMinor(): Promise>> { + return {}; + } + @autobind public async update(file: DriveFile, isAdditional: boolean): Promise { - const update: Obj = {}; - - update.totalCount = isAdditional ? 1 : -1; - update.totalSize = isAdditional ? file.size : -file.size; - if (isAdditional) { - update.incCount = 1; - update.incSize = file.size; - } else { - update.decCount = 1; - update.decSize = file.size; - } - - await this.inc(update, file.userId); + const fileSizeKb = file.size / 1000; + await this.commit({ + 'totalCount': isAdditional ? 1 : -1, + 'totalSize': isAdditional ? fileSizeKb : -fileSizeKb, + 'incCount': isAdditional ? 1 : 0, + 'incSize': isAdditional ? fileSizeKb : 0, + 'decCount': isAdditional ? 0 : 1, + 'decSize': isAdditional ? 0 : fileSizeKb, + }, file.userId); } } diff --git a/packages/backend/src/services/chart/charts/per-user-following.ts b/packages/backend/src/services/chart/charts/per-user-following.ts index d0a80abdaf..0b39881c14 100644 --- a/packages/backend/src/services/chart/charts/per-user-following.ts +++ b/packages/backend/src/services/chart/charts/per-user-following.ts @@ -1,76 +1,21 @@ import autobind from 'autobind-decorator'; -import Chart, { Obj, DeepPartial } from '../core'; -import { SchemaType } from '@/misc/schema'; +import Chart, { KVs } from '../core'; import { Followings, Users } from '@/models/index'; import { Not, IsNull } from 'typeorm'; import { User } from '@/models/entities/user'; import { name, schema } from './entities/per-user-following'; -type PerUserFollowingLog = SchemaType; - /** * ãƒĻãƒŧã‚ļãƒŧごとぎフりロãƒŧãĢé–ĸするチãƒŖãƒŧト */ // eslint-disable-next-line import/no-default-export -export default class PerUserFollowingChart extends Chart { +export default class PerUserFollowingChart extends Chart { constructor() { super(name, schema, true); } @autobind - protected genNewLog(latest: PerUserFollowingLog): DeepPartial { - return { - local: { - followings: { - total: latest.local.followings.total, - }, - followers: { - total: latest.local.followers.total, - }, - }, - remote: { - followings: { - total: latest.remote.followings.total, - }, - followers: { - total: latest.remote.followers.total, - }, - }, - }; - } - - @autobind - protected aggregate(logs: PerUserFollowingLog[]): PerUserFollowingLog { - return { - local: { - followings: { - total: logs[0].local.followings.total, - inc: logs.reduce((a, b) => a + b.local.followings.inc, 0), - dec: logs.reduce((a, b) => a + b.local.followings.dec, 0), - }, - followers: { - total: logs[0].local.followers.total, - inc: logs.reduce((a, b) => a + b.local.followers.inc, 0), - dec: logs.reduce((a, b) => a + b.local.followers.dec, 0), - }, - }, - remote: { - followings: { - total: logs[0].remote.followings.total, - inc: logs.reduce((a, b) => a + b.remote.followings.inc, 0), - dec: logs.reduce((a, b) => a + b.remote.followings.dec, 0), - }, - followers: { - total: logs[0].remote.followers.total, - inc: logs.reduce((a, b) => a + b.remote.followers.inc, 0), - dec: logs.reduce((a, b) => a + b.remote.followers.dec, 0), - }, - }, - }; - } - - @autobind - protected async fetchActual(group: string): Promise> { + protected async tickMajor(group: string): Promise>> { const [ localFollowingsCount, localFollowersCount, @@ -84,42 +29,32 @@ export default class PerUserFollowingChart extends Chart { ]); return { - local: { - followings: { - total: localFollowingsCount, - }, - followers: { - total: localFollowersCount, - }, - }, - remote: { - followings: { - total: remoteFollowingsCount, - }, - followers: { - total: remoteFollowersCount, - }, - }, + 'local.followings.total': localFollowingsCount, + 'local.followers.total': localFollowersCount, + 'remote.followings.total': remoteFollowingsCount, + 'remote.followers.total': remoteFollowersCount, }; } + @autobind + protected async tickMinor(): Promise>> { + return {}; + } + @autobind public async update(follower: { id: User['id']; host: User['host']; }, followee: { id: User['id']; host: User['host']; }, isFollow: boolean): Promise { - const update: Obj = {}; + const prefixFollower = Users.isLocalUser(follower) ? 'local' : 'remote'; + const prefixFollowee = Users.isLocalUser(followee) ? 'local' : 'remote'; - update.total = isFollow ? 1 : -1; - - if (isFollow) { - update.inc = 1; - } else { - update.dec = 1; - } - - this.inc({ - [Users.isLocalUser(follower) ? 'local' : 'remote']: { followings: update }, + this.commit({ + [`${prefixFollower}.followings.total`]: isFollow ? 1 : -1, + [`${prefixFollower}.followings.inc`]: isFollow ? 1 : 0, + [`${prefixFollower}.followings.dec`]: isFollow ? 0 : 1, }, follower.id); - this.inc({ - [Users.isLocalUser(followee) ? 'local' : 'remote']: { followers: update }, + this.commit({ + [`${prefixFollowee}.followers.total`]: isFollow ? 1 : -1, + [`${prefixFollowee}.followers.inc`]: isFollow ? 1 : 0, + [`${prefixFollowee}.followers.dec`]: isFollow ? 0 : 1, }, followee.id); } } diff --git a/packages/backend/src/services/chart/charts/per-user-notes.ts b/packages/backend/src/services/chart/charts/per-user-notes.ts index d048c88885..01a2785158 100644 --- a/packages/backend/src/services/chart/charts/per-user-notes.ts +++ b/packages/backend/src/services/chart/charts/per-user-notes.ts @@ -1,45 +1,21 @@ import autobind from 'autobind-decorator'; -import Chart, { Obj, DeepPartial } from '../core'; +import Chart, { KVs } from '../core'; import { User } from '@/models/entities/user'; -import { SchemaType } from '@/misc/schema'; import { Notes } from '@/models/index'; import { Note } from '@/models/entities/note'; import { name, schema } from './entities/per-user-notes'; -type PerUserNotesLog = SchemaType; - /** * ãƒĻãƒŧã‚ļãƒŧごとぎノãƒŧトãĢé–ĸするチãƒŖãƒŧト */ // eslint-disable-next-line import/no-default-export -export default class PerUserNotesChart extends Chart { +export default class PerUserNotesChart extends Chart { constructor() { super(name, schema, true); } @autobind - protected genNewLog(latest: PerUserNotesLog): DeepPartial { - return { - total: latest.total, - }; - } - - @autobind - protected aggregate(logs: PerUserNotesLog[]): PerUserNotesLog { - return { - total: logs[0].total, - inc: logs.reduce((a, b) => a + b.inc, 0), - dec: logs.reduce((a, b) => a + b.dec, 0), - diffs: { - reply: logs.reduce((a, b) => a + b.diffs.reply, 0), - renote: logs.reduce((a, b) => a + b.diffs.renote, 0), - normal: logs.reduce((a, b) => a + b.diffs.normal, 0), - }, - }; - } - - @autobind - protected async fetchActual(group: string): Promise> { + protected async tickMajor(group: string): Promise>> { const [count] = await Promise.all([ Notes.count({ userId: group }), ]); @@ -49,28 +25,21 @@ export default class PerUserNotesChart extends Chart { }; } + @autobind + protected async tickMinor(): Promise>> { + return {}; + } + @autobind public async update(user: { id: User['id'] }, note: Note, isAdditional: boolean): Promise { - const update: Obj = { - diffs: {}, - }; - - update.total = isAdditional ? 1 : -1; - - if (isAdditional) { - update.inc = 1; - } else { - update.dec = 1; - } - - if (note.replyId != null) { - update.diffs.reply = isAdditional ? 1 : -1; - } else if (note.renoteId != null) { - update.diffs.renote = isAdditional ? 1 : -1; - } else { - update.diffs.normal = isAdditional ? 1 : -1; - } - - await this.inc(update, user.id); + await this.commit({ + 'total': isAdditional ? 1 : -1, + 'inc': isAdditional ? 1 : 0, + 'dec': isAdditional ? 0 : 1, + 'diffs.normal': note.replyId == null && note.renoteId == null ? (isAdditional ? 1 : -1) : 0, + 'diffs.renote': note.renoteId != null ? (isAdditional ? 1 : -1) : 0, + 'diffs.reply': note.replyId != null ? (isAdditional ? 1 : -1) : 0, + 'diffs.withFile': note.fileIds.length > 0 ? (isAdditional ? 1 : -1) : 0, + }, user.id); } } diff --git a/packages/backend/src/services/chart/charts/per-user-reactions.ts b/packages/backend/src/services/chart/charts/per-user-reactions.ts index 2f5353340d..59af0e86c0 100644 --- a/packages/backend/src/services/chart/charts/per-user-reactions.ts +++ b/packages/backend/src/services/chart/charts/per-user-reactions.ts @@ -1,48 +1,34 @@ import autobind from 'autobind-decorator'; -import Chart, { DeepPartial } from '../core'; +import Chart, { KVs } from '../core'; import { User } from '@/models/entities/user'; import { Note } from '@/models/entities/note'; -import { SchemaType } from '@/misc/schema'; import { Users } from '@/models/index'; import { name, schema } from './entities/per-user-reactions'; -type PerUserReactionsLog = SchemaType; - /** * ãƒĻãƒŧã‚ļãƒŧごとぎãƒĒã‚ĸã‚¯ã‚ˇãƒ§ãƒŗãĢé–ĸするチãƒŖãƒŧト */ // eslint-disable-next-line import/no-default-export -export default class PerUserReactionsChart extends Chart { +export default class PerUserReactionsChart extends Chart { constructor() { super(name, schema, true); } @autobind - protected genNewLog(latest: PerUserReactionsLog): DeepPartial { + protected async tickMajor(group: string): Promise>> { return {}; } @autobind - protected aggregate(logs: PerUserReactionsLog[]): PerUserReactionsLog { - return { - local: { - count: logs.reduce((a, b) => a + b.local.count, 0), - }, - remote: { - count: logs.reduce((a, b) => a + b.remote.count, 0), - }, - }; - } - - @autobind - protected async fetchActual(group: string): Promise> { + protected async tickMinor(): Promise>> { return {}; } @autobind public async update(user: { id: User['id'], host: User['host'] }, note: Note): Promise { - this.inc({ - [Users.isLocalUser(user) ? 'local' : 'remote']: { count: 1 }, + const prefix = Users.isLocalUser(user) ? 'local' : 'remote'; + this.commit({ + [`${prefix}.count`]: 1, }, note.userId); } } diff --git a/packages/backend/src/services/chart/charts/test-grouped.ts b/packages/backend/src/services/chart/charts/test-grouped.ts index c851d2df01..19b2135849 100644 --- a/packages/backend/src/services/chart/charts/test-grouped.ts +++ b/packages/backend/src/services/chart/charts/test-grouped.ts @@ -1,15 +1,12 @@ import autobind from 'autobind-decorator'; -import Chart, { Obj, DeepPartial } from '../core'; -import { SchemaType } from '@/misc/schema'; +import Chart, { KVs } from '../core'; import { name, schema } from './entities/test-grouped'; -type TestGroupedLog = SchemaType; - /** * For testing */ // eslint-disable-next-line import/no-default-export -export default class TestGroupedChart extends Chart { +export default class TestGroupedChart extends Chart { private total = {} as Record; constructor() { @@ -17,46 +14,26 @@ export default class TestGroupedChart extends Chart { } @autobind - protected genNewLog(latest: TestGroupedLog): DeepPartial { + protected async tickMajor(group: string): Promise>> { return { - foo: { - total: latest.foo.total, - }, + 'foo.total': this.total[group], }; } @autobind - protected aggregate(logs: TestGroupedLog[]): TestGroupedLog { - return { - foo: { - total: logs[0].foo.total, - inc: logs.reduce((a, b) => a + b.foo.inc, 0), - dec: logs.reduce((a, b) => a + b.foo.dec, 0), - }, - }; - } - - @autobind - protected async fetchActual(group: string): Promise> { - return { - foo: { - total: this.total[group], - }, - }; + protected async tickMinor(): Promise>> { + return {}; } @autobind public async increment(group: string): Promise { if (this.total[group] == null) this.total[group] = 0; - const update: Obj = {}; - - update.total = 1; - update.inc = 1; this.total[group]++; - await this.inc({ - foo: update, + await this.commit({ + 'foo.total': 1, + 'foo.inc': 1, }, group); } } diff --git a/packages/backend/src/services/chart/charts/test-intersection.ts b/packages/backend/src/services/chart/charts/test-intersection.ts new file mode 100644 index 0000000000..6fd780f9b9 --- /dev/null +++ b/packages/backend/src/services/chart/charts/test-intersection.ts @@ -0,0 +1,37 @@ +import autobind from 'autobind-decorator'; +import Chart, { KVs } from '../core'; +import { name, schema } from './entities/test-intersection'; + +/** + * For testing + */ +// eslint-disable-next-line import/no-default-export +export default class TestIntersectionChart extends Chart { + constructor() { + super(name, schema); + } + + @autobind + protected async tickMajor(): Promise>> { + return {}; + } + + @autobind + protected async tickMinor(): Promise>> { + return {}; + } + + @autobind + public async addA(key: string): Promise { + await this.commit({ + a: [key], + }); + } + + @autobind + public async addB(key: string): Promise { + await this.commit({ + b: [key], + }); + } +} diff --git a/packages/backend/src/services/chart/charts/test-unique.ts b/packages/backend/src/services/chart/charts/test-unique.ts index 3564f675ad..2c9cc2fd6a 100644 --- a/packages/backend/src/services/chart/charts/test-unique.ts +++ b/packages/backend/src/services/chart/charts/test-unique.ts @@ -1,39 +1,29 @@ import autobind from 'autobind-decorator'; -import Chart, { DeepPartial } from '../core'; -import { SchemaType } from '@/misc/schema'; +import Chart, { KVs } from '../core'; import { name, schema } from './entities/test-unique'; -type TestUniqueLog = SchemaType; - /** * For testing */ // eslint-disable-next-line import/no-default-export -export default class TestUniqueChart extends Chart { +export default class TestUniqueChart extends Chart { constructor() { super(name, schema); } @autobind - protected genNewLog(latest: TestUniqueLog): DeepPartial { + protected async tickMajor(): Promise>> { return {}; } @autobind - protected aggregate(logs: TestUniqueLog[]): TestUniqueLog { - return { - foo: logs.reduce((a, b) => a.concat(b.foo), [] as TestUniqueLog['foo']), - }; - } - - @autobind - protected async fetchActual(): Promise> { + protected async tickMinor(): Promise>> { return {}; } @autobind public async uniqueIncrement(key: string): Promise { - await this.inc({ + await this.commit({ foo: [key], }); } diff --git a/packages/backend/src/services/chart/charts/test.ts b/packages/backend/src/services/chart/charts/test.ts index 06add7ede9..b539625c10 100644 --- a/packages/backend/src/services/chart/charts/test.ts +++ b/packages/backend/src/services/chart/charts/test.ts @@ -1,15 +1,12 @@ import autobind from 'autobind-decorator'; -import Chart, { Obj, DeepPartial } from '../core'; -import { SchemaType } from '@/misc/schema'; +import Chart, { KVs } from '../core'; import { name, schema } from './entities/test'; -type TestLog = SchemaType; - /** * For testing */ // eslint-disable-next-line import/no-default-export -export default class TestChart extends Chart { +export default class TestChart extends Chart { public total = 0; // publicãĢするぎはテ゚トぎため constructor() { @@ -17,57 +14,34 @@ export default class TestChart extends Chart { } @autobind - protected genNewLog(latest: TestLog): DeepPartial { + protected async tickMajor(): Promise>> { return { - foo: { - total: latest.foo.total, - }, + 'foo.total': this.total, }; } @autobind - protected aggregate(logs: TestLog[]): TestLog { - return { - foo: { - total: logs[0].foo.total, - inc: logs.reduce((a, b) => a + b.foo.inc, 0), - dec: logs.reduce((a, b) => a + b.foo.dec, 0), - }, - }; - } - - @autobind - protected async fetchActual(): Promise> { - return { - foo: { - total: this.total, - }, - }; + protected async tickMinor(): Promise>> { + return {}; } @autobind public async increment(): Promise { - const update: Obj = {}; - - update.total = 1; - update.inc = 1; this.total++; - await this.inc({ - foo: update, + await this.commit({ + 'foo.total': 1, + 'foo.inc': 1, }); } @autobind public async decrement(): Promise { - const update: Obj = {}; - - update.total = -1; - update.dec = 1; this.total--; - await this.inc({ - foo: update, + await this.commit({ + 'foo.total': -1, + 'foo.dec': 1, }); } } diff --git a/packages/backend/src/services/chart/charts/users.ts b/packages/backend/src/services/chart/charts/users.ts index c36c6cd979..70ef89f8cd 100644 --- a/packages/backend/src/services/chart/charts/users.ts +++ b/packages/backend/src/services/chart/charts/users.ts @@ -1,80 +1,45 @@ import autobind from 'autobind-decorator'; -import Chart, { Obj, DeepPartial } from '../core'; -import { SchemaType } from '@/misc/schema'; +import Chart, { KVs } from '../core'; import { Users } from '@/models/index'; import { Not, IsNull } from 'typeorm'; import { User } from '@/models/entities/user'; import { name, schema } from './entities/users'; -type UsersLog = SchemaType; - /** * ãƒĻãƒŧã‚ļãƒŧ数ãĢé–ĸするチãƒŖãƒŧト */ // eslint-disable-next-line import/no-default-export -export default class UsersChart extends Chart { +export default class UsersChart extends Chart { constructor() { super(name, schema); } @autobind - protected genNewLog(latest: UsersLog): DeepPartial { - return { - local: { - total: latest.local.total, - }, - remote: { - total: latest.remote.total, - }, - }; - } - - @autobind - protected aggregate(logs: UsersLog[]): UsersLog { - return { - local: { - total: logs[0].local.total, - inc: logs.reduce((a, b) => a + b.local.inc, 0), - dec: logs.reduce((a, b) => a + b.local.dec, 0), - }, - remote: { - total: logs[0].remote.total, - inc: logs.reduce((a, b) => a + b.remote.inc, 0), - dec: logs.reduce((a, b) => a + b.remote.dec, 0), - }, - }; - } - - @autobind - protected async fetchActual(): Promise> { + protected async tickMajor(): Promise>> { const [localCount, remoteCount] = await Promise.all([ Users.count({ host: null }), Users.count({ host: Not(IsNull()) }), ]); return { - local: { - total: localCount, - }, - remote: { - total: remoteCount, - }, + 'local.total': localCount, + 'remote.total': remoteCount, }; } + @autobind + protected async tickMinor(): Promise>> { + return {}; + } + @autobind public async update(user: { id: User['id'], host: User['host'] }, isAdditional: boolean): Promise { - const update: Obj = {}; + const prefix = Users.isLocalUser(user) ? 'local' : 'remote'; - update.total = isAdditional ? 1 : -1; - if (isAdditional) { - update.inc = 1; - } else { - update.dec = 1; - } - - await this.inc({ - [Users.isLocalUser(user) ? 'local' : 'remote']: update, + await this.commit({ + [`${prefix}.total`]: isAdditional ? 1 : -1, + [`${prefix}.inc`]: isAdditional ? 1 : 0, + [`${prefix}.dec`]: isAdditional ? 0 : 1, }); } } diff --git a/packages/backend/src/services/chart/core.ts b/packages/backend/src/services/chart/core.ts index e406449f4f..c6bea0feea 100644 --- a/packages/backend/src/services/chart/core.ts +++ b/packages/backend/src/services/chart/core.ts @@ -7,24 +7,38 @@ import * as nestedProperty from 'nested-property'; import autobind from 'autobind-decorator'; import Logger from '../logger'; -import { Schema } from '@/misc/schema'; import { EntitySchema, getRepository, Repository, LessThan, Between } from 'typeorm'; import { dateUTC, isTimeSame, isTimeBefore, subtractTime, addTime } from '@/prelude/time'; import { getChartInsertLock } from '@/misc/app-lock'; const logger = new Logger('chart', 'white', process.env.NODE_ENV !== 'test'); -export type Obj = { [key: string]: any }; +const columnPrefix = '___' as const; +const uniqueTempColumnPrefix = 'unique_temp___' as const; +const columnDot = '_' as const; -export type DeepPartial = { - [P in keyof T]?: DeepPartial; +type Schema = Record; + + range?: 'big' | 'small' | 'medium'; + + // previousãĒ値をåŧ•ãįļ™ãã‹ãŠã†ã‹ + accumulate?: boolean; +}>; + +type KeyToColumnName = T extends `${infer R1}.${infer R2}` ? `${R1}${typeof columnDot}${KeyToColumnName}` : T; + +type Columns = { + [K in keyof S as `${typeof columnPrefix}${KeyToColumnName}`]: number; }; -type ArrayValue = { - [P in keyof T]: T[P] extends number ? T[P][] : ArrayValue; +type TempColumnsForUnique = { + [K in keyof S as `${typeof uniqueTempColumnPrefix}${KeyToColumnName}`]: S[K]['uniqueIncrement'] extends true ? string[] : never; }; -type Log = { +type RawRecord = { id: number; /** @@ -36,7 +50,7 @@ type Log = { * 集計æ—Ĩ時ぎUnixã‚ŋイム゚ã‚ŋãƒŗプ(į§’) */ date: number; -}; +} & TempColumnsForUnique & Columns; const camelToSnake = (str: string): string => { return str.replace(/([A-Z])/g, s => '_' + s.charAt(0).toLowerCase()); @@ -44,123 +58,118 @@ const camelToSnake = (str: string): string => { const removeDuplicates = (array: any[]) => Array.from(new Set(array)); +type Commit = { + [K in keyof S]?: S[K]['uniqueIncrement'] extends true ? string[] : number; +}; + +export type KVs = { + [K in keyof S]: number; +}; + +type ChartResult = { + [P in keyof T]: number[]; +}; + +type UnionToIntersection = (T extends any ? (x: T) => any : never) extends (x: infer R) => any ? R : never; + +type UnflattenSingleton = K extends `${infer A}.${infer B}` + ? { [_ in A]: UnflattenSingleton; } + : { [_ in K]: V; }; + +type Unflatten> = UnionToIntersection< + { + [K in Extract]: UnflattenSingleton; + }[Extract] +>; + +type ToJsonSchema = { + type: 'object'; + properties: { + [K in keyof S]: S[K] extends number[] ? { type: 'array'; items: { type: 'number'; }; } : ToJsonSchema; + }, + required: (keyof S)[]; +}; + +export function getJsonSchema(schema: S): ToJsonSchema>> { + const object = {}; + for (const [k, v] of Object.entries(schema)) { + nestedProperty.set(object, k, null); + } + + function f(obj: Record>) { + const jsonSchema = { + type: 'object', + properties: {} as Record, + required: [], + }; + for (const [k, v] of Object.entries(obj)) { + jsonSchema.properties[k] = v === null ? { + type: 'array', + items: { type: 'number' }, + } : f(v as Record>); + } + return jsonSchema; + } + + return f(object) as ToJsonSchema>>; +} + /** * 様々ãĒチãƒŖãƒŧトぎįŽĄį†ã‚’司るクナ゚ */ // eslint-disable-next-line import/no-default-export -export default abstract class Chart> { - private static readonly columnPrefix = '___'; - private static readonly columnDot = '_'; +export default abstract class Chart { + public schema: T; private name: string; private buffer: { - diff: DeepPartial; + diff: Commit; group: string | null; }[] = []; - public schema: Schema; - protected repositoryForHour: Repository; - protected repositoryForDay: Repository; - - protected abstract genNewLog(latest: T): DeepPartial; + // ↓ãĢしたいけおfindOneとかで型エナãƒŧãĢãĒる + //private repositoryForHour: Repository>; + //private repositoryForDay: Repository>; + private repositoryForHour: Repository<{ id: number; group?: string | null; date: number; }>; + private repositoryForDay: Repository<{ id: number; group?: string | null; date: number; }>; /** - * @param logs æ—Ĩ時が新しい斚が先頭 + * 1æ—ĨãĢ一回į¨‹åēĻåŽŸčĄŒã•ã‚Œã‚Œã°č‰¯ã„ã‚ˆã†ãĒ計įŽ—å‡Ļį†ã‚’å…Ĩれる(ä¸ģãĢCASCADE削除ãĒおã‚ĸプãƒĒã‚ąãƒŧã‚ˇãƒ§ãƒŗ側で感įŸĨできãĒい変動ãĢよるã‚ēãƒŦぎäŋŽæ­Ŗį”¨) */ - protected abstract aggregate(logs: T[]): T; + protected abstract tickMajor(group: string | null): Promise>>; - protected abstract fetchActual(group: string | null): Promise>; + /** + * 少ãĒくとも最小゚パãƒŗ内ãĢ1å›žã¯åŽŸčĄŒã•ã‚ŒãĻæŦ˛ã—ã„č¨ˆįŽ—å‡Ļį†ã‚’å…Ĩれる + */ + protected abstract tickMinor(group: string | null): Promise>>; @autobind - private static convertSchemaToFlatColumnDefinitions(schema: Schema) { - const columns = {} as Record; - const flatColumns = (x: Obj, path?: string) => { - for (const [k, v] of Object.entries(x)) { - const p = path ? `${path}${this.columnDot}${k}` : k; - if (v.type === 'object') { - flatColumns(v.properties, p); - } else if (v.type === 'number') { - columns[this.columnPrefix + p] = { - type: 'bigint', - }; - } else if (v.type === 'array' && v.items.type === 'string') { - columns[this.columnPrefix + p] = { - type: 'varchar', - array: true, - }; - } + private static convertSchemaToColumnDefinitions(schema: Schema): Record { + const columns = {} as Record; + for (const [k, v] of Object.entries(schema)) { + const name = k.replaceAll('.', columnDot); + const type = v.range === 'big' ? 'bigint' : v.range === 'small' ? 'smallint' : 'integer'; + if (v.uniqueIncrement) { + columns[uniqueTempColumnPrefix + name] = { + type: 'varchar', + array: true, + default: '{}', + }; + columns[columnPrefix + name] = { + type, + default: 0, + }; + } else { + columns[columnPrefix + name] = { + type, + default: 0, + }; } - }; - flatColumns(schema.properties!); + } return columns; } @autobind - private static convertFlattenColumnsToObject(x: Record): Record { - const obj = {} as Record; - for (const k of Object.keys(x).filter(k => k.startsWith(Chart.columnPrefix))) { - // now k is ___x_y_z - const path = k.substr(Chart.columnPrefix.length).split(Chart.columnDot).join('.'); - nestedProperty.set(obj, path, x[k]); - } - return obj; - } - - @autobind - private static convertObjectToFlattenColumns(x: Record) { - const columns = {} as Record; - const flatten = (x: Obj, path?: string) => { - for (const [k, v] of Object.entries(x)) { - const p = path ? `${path}${this.columnDot}${k}` : k; - if (typeof v === 'object' && !Array.isArray(v)) { - flatten(v, p); - } else { - columns[this.columnPrefix + p] = v; - } - } - }; - flatten(x); - return columns; - } - - @autobind - private static countUniqueFields(x: Record) { - const exec = (x: Obj) => { - const res = {} as Record; - for (const [k, v] of Object.entries(x)) { - if (typeof v === 'object' && !Array.isArray(v)) { - res[k] = exec(v); - } else if (Array.isArray(v)) { - res[k] = Array.from(new Set(v)).length; - } else { - res[k] = v; - } - } - return res; - }; - return exec(x); - } - - @autobind - private static convertQuery(diff: Record) { - const query: Record string> = {}; - - for (const [k, v] of Object.entries(diff)) { - if (typeof v === 'number') { - if (v > 0) query[k] = () => `"${k}" + ${v}`; - if (v < 0) query[k] = () => `"${k}" - ${Math.abs(v)}`; - } else if (Array.isArray(v)) { - // TODO: item が文字列äģĨ外ぎ場合も寞åŋœ - // TODO: item をSQLã‚¨ã‚šã‚ąãƒŧプ - const items = v.map(item => `"${item}"`).join(','); - query[k] = () => `array_cat("${k}", '{${items}}'::varchar[])`; - } - } - - return query; - } - - @autobind - private static dateToTimestamp(x: Date): Log['date'] { + private static dateToTimestamp(x: Date): number { return Math.floor(x.getTime() / 1000); } @@ -207,7 +216,7 @@ export default abstract class Chart> { length: 128, }, } : {}), - ...Chart.convertSchemaToFlatColumnDefinitions(schema), + ...Chart.convertSchemaToColumnDefinitions(schema), }, indices: [{ columns: grouped ? ['date', 'group'] : ['date'], @@ -233,37 +242,39 @@ export default abstract class Chart> { }; } - constructor(name: string, schema: Schema, grouped = false) { + constructor(name: string, schema: T, grouped = false) { this.name = name; this.schema = schema; const { hour, day } = Chart.schemaToEntity(name, schema, grouped); - this.repositoryForHour = getRepository(hour); - this.repositoryForDay = getRepository(day); + this.repositoryForHour = getRepository<{ id: number; group?: string | null; date: number; }>(hour); + this.repositoryForDay = getRepository<{ id: number; group?: string | null; date: number; }>(day); } @autobind - private getNewLog(latest: T | null): T { - const log = latest ? this.genNewLog(latest) : {}; - const flatColumns = (x: Obj, path?: string) => { - for (const [k, v] of Object.entries(x)) { - const p = path ? `${path}.${k}` : k; - if (v.type === 'object') { - flatColumns(v.properties, p); - } else { - if (nestedProperty.get(log, p) == null) { - const emptyValue = v.type === 'number' ? 0 : []; - nestedProperty.set(log, p, emptyValue); - } - } + private convertRawRecord(x: RawRecord): KVs { + const kvs = {} as Record; + for (const k of Object.keys(x).filter((k) => k.startsWith(columnPrefix)) as (keyof Columns)[]) { + kvs[(k as string).substr(columnPrefix.length).split(columnDot).join('.')] = x[k]; + } + return kvs as KVs; + } + + @autobind + private getNewLog(latest: KVs | null): KVs { + const log = {} as Record; + for (const [k, v] of Object.entries(this.schema) as ([keyof typeof this['schema'], this['schema'][string]])[]) { + if (v.accumulate && latest) { + log[k] = latest[k]; + } else { + log[k] = 0; } - }; - flatColumns(this.schema.properties!); - return log as T; + } + return log as KVs; } @autobind - private getLatestLog(group: string | null, span: 'hour' | 'day'): Promise { + private getLatestLog(group: string | null, span: 'hour' | 'day'): Promise | null> { const repository = span === 'hour' ? this.repositoryForHour : span === 'day' ? this.repositoryForDay : @@ -275,14 +286,14 @@ export default abstract class Chart> { order: { date: -1, }, - }).then(x => x || null); + }).then(x => x ?? null) as Promise | null>; } /** * įžåœ¨(=äģŠãŽHour or Day)ぎログをデãƒŧã‚ŋベãƒŧ゚からæŽĸしãĻ、あればそれをčŋ”し、ãĒければäŊœæˆã—ãĻčŋ”しぞす。 */ @autobind - private async claimCurrentLog(group: string | null, span: 'hour' | 'day'): Promise { + private async claimCurrentLog(group: string | null, span: 'hour' | 'day'): Promise> { const [y, m, d, h] = Chart.getCurrentDate(); const current = dateUTC( @@ -299,15 +310,15 @@ export default abstract class Chart> { const currentLog = await repository.findOne({ date: Chart.dateToTimestamp(current), ...(group ? { group: group } : {}), - }); + }) as RawRecord | undefined; // ログがあればそれをčŋ”しãĻįĩ‚äē† if (currentLog != null) { return currentLog; } - let log: Log; - let data: T; + let log: RawRecord; + let data: KVs; // é›†č¨ˆæœŸé–“ãŒå¤‰ã‚ãŖãĻから、初めãĻぎチãƒŖãƒŧト更新ãĒら // 最も最čŋ‘ぎログを持ãŖãĻくる @@ -318,10 +329,8 @@ export default abstract class Chart> { const latest = await this.getLatestLog(group, span); if (latest != null) { - const obj = Chart.convertFlattenColumnsToObject(latest) as T; - // įŠēログデãƒŧã‚ŋをäŊœæˆ - data = this.getNewLog(obj); + data = this.getNewLog(this.convertRawRecord(latest)); } else { // ログが存在しãĒかãŖたら // (Misskeyイãƒŗã‚šã‚ŋãƒŗã‚šã‚’åģēãĻãĻ初めãĻぎチãƒŖãƒŧト更新時ãĒお) @@ -341,17 +350,23 @@ export default abstract class Chart> { const currentLog = await repository.findOne({ date: date, ...(group ? { group: group } : {}), - }); + }) as RawRecord | undefined; // ログがあればそれをčŋ”しãĻįĩ‚äē† if (currentLog != null) return currentLog; + const columns = {} as Record; + for (const [k, v] of Object.entries(data)) { + const name = k.replaceAll('.', columnDot); + columns[columnPrefix + name] = v; + } + // 新čĻãƒ­ã‚°æŒŋå…Ĩ log = await repository.insert({ date: date, ...(group ? { group: group } : {}), - ...Chart.convertObjectToFlattenColumns(data), - }).then(x => repository.findOneOrFail(x.identifiers[0])); + ...columns, + }).then(x => repository.findOneOrFail(x.identifiers[0])) as RawRecord; logger.info(`${this.name + (group ? `:${group}` : '')}(${span}): New commit created`); @@ -362,7 +377,10 @@ export default abstract class Chart> { } @autobind - protected commit(diff: DeepPartial, group: string | null = null): void { + protected commit(diff: Commit, group: string | null = null): void { + for (const [k, v] of Object.entries(diff)) { + if (v == null || v === 0 || (Array.isArray(v) && v.length === 0)) delete diff[k]; + } this.buffer.push({ diff, group, }); @@ -381,37 +399,91 @@ export default abstract class Chart> { // そぎログはæœŦæĨは 01:00~ ぎログとしãĻDBãĢäŋå­˜ã•ã‚ŒãĻæŦ˛ã—いぎãĢ、02:00~ ãŽãƒ­ã‚°æ‰ąã„ãĢãĒãŖãĻしぞう。 // これを回éŋするためぎ原čŖ…ã¯č¤‡é›‘ãĢãĒりそうãĒため、一æ—Ļäŋį•™ã€‚ - const update = async (logHour: Log, logDay: Log): Promise => { - const finalDiffs = {} as Record; + const update = async (logHour: RawRecord, logDay: RawRecord): Promise => { + const finalDiffs = {} as Record; for (const diff of this.buffer.filter(q => q.group == null || (q.group === logHour.group)).map(q => q.diff)) { - const columns = Chart.convertObjectToFlattenColumns(diff); - - for (const [k, v] of Object.entries(columns)) { + for (const [k, v] of Object.entries(diff)) { if (finalDiffs[k] == null) { finalDiffs[k] = v; } else { if (typeof finalDiffs[k] === 'number') { (finalDiffs[k] as number) += v as number; } else { - (finalDiffs[k] as unknown[]) = (finalDiffs[k] as unknown[]).concat(v); + (finalDiffs[k] as string[]) = (finalDiffs[k] as string[]).concat(v); } } } } - const query = Chart.convertQuery(finalDiffs); + const queryForHour: Record, number | (() => string)> = {} as any; + const queryForDay: Record, number | (() => string)> = {} as any; + for (const [k, v] of Object.entries(finalDiffs)) { + if (typeof v === 'number') { + const name = columnPrefix + k.replaceAll('.', columnDot) as keyof Columns; + if (v > 0) queryForHour[name] = () => `"${name}" + ${v}`; + if (v < 0) queryForHour[name] = () => `"${name}" - ${Math.abs(v)}`; + if (v > 0) queryForDay[name] = () => `"${name}" + ${v}`; + if (v < 0) queryForDay[name] = () => `"${name}" - ${Math.abs(v)}`; + } else if (Array.isArray(v) && v.length > 0) { // ãƒĻニãƒŧクイãƒŗクãƒĒãƒĄãƒŗト + const tempColumnName = uniqueTempColumnPrefix + k.replaceAll('.', columnDot) as keyof TempColumnsForUnique; + // TODO: item をSQLã‚¨ã‚šã‚ąãƒŧプ + const itemsForHour = v.filter(item => !logHour[tempColumnName].includes(item)).map(item => `"${item}"`); + const itemsForDay = v.filter(item => !logDay[tempColumnName].includes(item)).map(item => `"${item}"`); + if (itemsForHour.length > 0) queryForHour[tempColumnName] = () => `array_cat("${tempColumnName}", '{${itemsForHour.join(',')}}'::varchar[])`; + if (itemsForDay.length > 0) queryForDay[tempColumnName] = () => `array_cat("${tempColumnName}", '{${itemsForDay.join(',')}}'::varchar[])`; + } + } + + // bake unique count + for (const [k, v] of Object.entries(finalDiffs)) { + if (this.schema[k].uniqueIncrement) { + const name = columnPrefix + k.replaceAll('.', columnDot) as keyof Columns; + const tempColumnName = uniqueTempColumnPrefix + k.replaceAll('.', columnDot) as keyof TempColumnsForUnique; + queryForHour[name] = new Set([...(v as string[]), ...logHour[tempColumnName]]).size; + queryForDay[name] = new Set([...(v as string[]), ...logDay[tempColumnName]]).size; + } + } + + // compute intersection + // TODO: intersectionãĢ指厚されたã‚ĢナムがintersectionだãŖた場合ぎ寞åŋœ + for (const [k, v] of Object.entries(this.schema)) { + const intersection = v.intersection; + if (intersection) { + const name = columnPrefix + k.replaceAll('.', columnDot) as keyof Columns; + const firstKey = intersection[0]; + const firstTempColumnName = uniqueTempColumnPrefix + firstKey.replaceAll('.', columnDot) as keyof TempColumnsForUnique; + const firstValues = finalDiffs[firstKey] as string[] | undefined; + const currentValuesForHour = new Set([...(firstValues ?? []), ...logHour[firstTempColumnName]]); + const currentValuesForDay = new Set([...(firstValues ?? []), ...logDay[firstTempColumnName]]); + for (let i = 1; i < intersection.length; i++) { + const targetKey = intersection[i]; + const targetTempColumnName = uniqueTempColumnPrefix + targetKey.replaceAll('.', columnDot) as keyof TempColumnsForUnique; + const targetValues = finalDiffs[targetKey] as string[] | undefined; + const targetValuesForHour = new Set([...(targetValues ?? []), ...logHour[targetTempColumnName]]); + const targetValuesForDay = new Set([...(targetValues ?? []), ...logDay[targetTempColumnName]]); + currentValuesForHour.forEach(v => { + if (!targetValuesForHour.has(v)) currentValuesForHour.delete(v); + }); + currentValuesForDay.forEach(v => { + if (!targetValuesForDay.has(v)) currentValuesForDay.delete(v); + }); + } + queryForHour[name] = currentValuesForHour.size; + queryForDay[name] = currentValuesForDay.size; + } + } // ログ更新 await Promise.all([ this.repositoryForHour.createQueryBuilder() .update() - .set(query) + .set(queryForHour as any) .where('id = :id', { id: logHour.id }) .execute(), this.repositoryForDay.createQueryBuilder() .update() - .set(query) + .set(queryForDay as any) .where('id = :id', { id: logDay.id }) .execute(), ]); @@ -434,19 +506,29 @@ export default abstract class Chart> { } @autobind - public async resync(group: string | null = null): Promise { - const data = await this.fetchActual(group); + public async tick(major: boolean, group: string | null = null): Promise { + const data = major ? await this.tickMajor(group) : await this.tickMinor(group); - const update = async (logHour: Log, logDay: Log): Promise => { + const columns = {} as Record, number>; + for (const [k, v] of Object.entries(data) as ([keyof typeof data, number])[]) { + const name = columnPrefix + (k as string).replaceAll('.', columnDot) as keyof Columns; + columns[name] = v; + } + + if (Object.keys(columns).length === 0) { + return; + } + + const update = async (logHour: RawRecord, logDay: RawRecord): Promise => { await Promise.all([ this.repositoryForHour.createQueryBuilder() .update() - .set(Chart.convertObjectToFlattenColumns(data)) + .set(columns) .where('id = :id', { id: logHour.id }) .execute(), this.repositoryForDay.createQueryBuilder() .update() - .set(Chart.convertObjectToFlattenColumns(data)) + .set(columns) .where('id = :id', { id: logDay.id }) .execute(), ]); @@ -460,12 +542,48 @@ export default abstract class Chart> { } @autobind - protected async inc(inc: DeepPartial, group: string | null = null): Promise { - await this.commit(inc, group); + public resync(group: string | null = null): Promise { + return this.tick(true, group); } @autobind - public async getChart(span: 'hour' | 'day', amount: number, cursor: Date | null, group: string | null = null): Promise> { + public async clean(): Promise { + const current = dateUTC(Chart.getCurrentDate()); + + // 一æ—ĨäģĨ上前かつ三æ—ĨäģĨ内 + const gt = Chart.dateToTimestamp(current) - (60 * 60 * 24 * 3); + const lt = Chart.dateToTimestamp(current) - (60 * 60 * 24); + + const columns = {} as Record, []>; + for (const [k, v] of Object.entries(this.schema)) { + if (v.uniqueIncrement) { + const name = uniqueTempColumnPrefix + k.replaceAll('.', columnDot) as keyof TempColumnsForUnique; + columns[name] = []; + } + } + + if (Object.keys(columns).length === 0) { + return; + } + + await Promise.all([ + this.repositoryForHour.createQueryBuilder() + .update() + .set(columns) + .where('date > :gt', { gt }) + .andWhere('date < :lt', { lt }) + .execute(), + this.repositoryForDay.createQueryBuilder() + .update() + .set(columns) + .where('date > :gt', { gt }) + .andWhere('date < :lt', { lt }) + .execute(), + ]); + } + + @autobind + public async getChartRaw(span: 'hour' | 'day', amount: number, cursor: Date | null, group: string | null = null): Promise> { const [y, m, d, h, _m, _s, _ms] = cursor ? Chart.parseDate(subtractTime(addTime(cursor, 1, span), 1)) : Chart.getCurrentDate(); const [y2, m2, d2, h2] = cursor ? Chart.parseDate(addTime(cursor, 1, span)) : [] as never; @@ -490,7 +608,7 @@ export default abstract class Chart> { order: { date: -1, }, - }); + }) as RawRecord[]; // čĻæą‚されたį¯„å›˛ãĢãƒ­ã‚°ãŒã˛ã¨ã¤ã‚‚ãĒかãŖたら if (logs.length === 0) { @@ -502,7 +620,7 @@ export default abstract class Chart> { order: { date: -1, }, - }); + }) as RawRecord | undefined; if (recentLog) { logs = [recentLog]; @@ -519,14 +637,14 @@ export default abstract class Chart> { order: { date: -1, }, - }); + }) as RawRecord | undefined; if (outdatedLog) { logs.push(outdatedLog); } } - const chart: T[] = []; + const chart: KVs[] = []; for (let i = (amount - 1); i >= 0; i--) { const current = @@ -537,17 +655,16 @@ export default abstract class Chart> { const log = logs.find(l => isTimeSame(new Date(l.date * 1000), current)); if (log) { - const data = Chart.convertFlattenColumnsToObject(log); - chart.unshift(Chart.countUniqueFields(data) as T); + chart.unshift(this.convertRawRecord(log)); } else { // 隙間埋め const latest = logs.find(l => isTimeBefore(new Date(l.date * 1000), current)); - const data = latest ? Chart.convertFlattenColumnsToObject(latest) as T : null; - chart.unshift(Chart.countUniqueFields(this.getNewLog(data)) as T); + const data = latest ? this.convertRawRecord(latest) : null; + chart.unshift(this.getNewLog(data)); } } - const res = {} as Record; + const res = {} as ChartResult; /** * [{ foo: 1, bar: 5 }, { foo: 2, bar: 6 }, { foo: 3, bar: 7 }] @@ -555,36 +672,26 @@ export default abstract class Chart> { * { foo: [1, 2, 3], bar: [5, 6, 7] } * ãĢする */ - const compact = (x: Obj, path?: string): void => { - for (const [k, v] of Object.entries(x)) { - const p = path ? `${path}.${k}` : k; - if (typeof v === 'object' && !Array.isArray(v)) { - compact(v, p); + for (const record of chart) { + for (const [k, v] of Object.entries(record) as ([keyof typeof record, number])[]) { + if (res[k]) { + res[k].push(v); } else { - const values = chart.map(s => nestedProperty.get(s, p)); - nestedProperty.set(res, p, values); + res[k] = [v]; } } - }; - - compact(chart[0]); - - return res as ArrayValue; - } -} - -export function convertLog(logSchema: Schema): Schema { - const v: Schema = JSON.parse(JSON.stringify(logSchema)); // copy - if (v.type === 'number') { - v.type = 'array'; - v.items = { - type: 'number' as const, - optional: false as const, nullable: false as const, - }; - } else if (v.type === 'object') { - for (const k of Object.keys(v.properties!)) { - v.properties![k] = convertLog(v.properties![k]); } + + return res; + } + + @autobind + public async getChart(span: 'hour' | 'day', amount: number, cursor: Date | null, group: string | null = null): Promise>> { + const result = await this.getChartRaw(span, amount, cursor, group); + const object = {}; + for (const [k, v] of Object.entries(result)) { + nestedProperty.set(object, k, v); + } + return object as Unflatten>; } - return v; } diff --git a/packages/backend/src/services/chart/entities.ts b/packages/backend/src/services/chart/entities.ts index dedbd47080..569b328557 100644 --- a/packages/backend/src/services/chart/entities.ts +++ b/packages/backend/src/services/chart/entities.ts @@ -1,7 +1,6 @@ import { entity as FederationChart } from './charts/entities/federation'; import { entity as NotesChart } from './charts/entities/notes'; import { entity as UsersChart } from './charts/entities/users'; -import { entity as NetworkChart } from './charts/entities/network'; import { entity as ActiveUsersChart } from './charts/entities/active-users'; import { entity as InstanceChart } from './charts/entities/instance'; import { entity as PerUserNotesChart } from './charts/entities/per-user-notes'; @@ -10,12 +9,12 @@ import { entity as PerUserReactionsChart } from './charts/entities/per-user-reac import { entity as HashtagChart } from './charts/entities/hashtag'; import { entity as PerUserFollowingChart } from './charts/entities/per-user-following'; import { entity as PerUserDriveChart } from './charts/entities/per-user-drive'; +import { entity as ApRequestChart } from './charts/entities/ap-request'; export const entities = [ FederationChart.hour, FederationChart.day, NotesChart.hour, NotesChart.day, UsersChart.hour, UsersChart.day, - NetworkChart.hour, NetworkChart.day, ActiveUsersChart.hour, ActiveUsersChart.day, InstanceChart.hour, InstanceChart.day, PerUserNotesChart.hour, PerUserNotesChart.day, @@ -24,4 +23,5 @@ export const entities = [ HashtagChart.hour, HashtagChart.day, PerUserFollowingChart.hour, PerUserFollowingChart.day, PerUserDriveChart.hour, PerUserDriveChart.day, + ApRequestChart.hour, ApRequestChart.day, ]; diff --git a/packages/backend/src/services/chart/index.ts b/packages/backend/src/services/chart/index.ts index 0b9887b36f..1c0f7aadc1 100644 --- a/packages/backend/src/services/chart/index.ts +++ b/packages/backend/src/services/chart/index.ts @@ -3,7 +3,6 @@ import { beforeShutdown } from '@/misc/before-shutdown'; import FederationChart from './charts/federation'; import NotesChart from './charts/notes'; import UsersChart from './charts/users'; -import NetworkChart from './charts/network'; import ActiveUsersChart from './charts/active-users'; import InstanceChart from './charts/instance'; import PerUserNotesChart from './charts/per-user-notes'; @@ -12,11 +11,11 @@ import PerUserReactionsChart from './charts/per-user-reactions'; import HashtagChart from './charts/hashtag'; import PerUserFollowingChart from './charts/per-user-following'; import PerUserDriveChart from './charts/per-user-drive'; +import ApRequestChart from './charts/ap-request'; export const federationChart = new FederationChart(); export const notesChart = new NotesChart(); export const usersChart = new UsersChart(); -export const networkChart = new NetworkChart(); export const activeUsersChart = new ActiveUsersChart(); export const instanceChart = new InstanceChart(); export const perUserNotesChart = new PerUserNotesChart(); @@ -25,12 +24,12 @@ export const perUserReactionsChart = new PerUserReactionsChart(); export const hashtagChart = new HashtagChart(); export const perUserFollowingChart = new PerUserFollowingChart(); export const perUserDriveChart = new PerUserDriveChart(); +export const apRequestChart = new ApRequestChart(); const charts = [ federationChart, notesChart, usersChart, - networkChart, activeUsersChart, instanceChart, perUserNotesChart, @@ -39,6 +38,7 @@ const charts = [ hashtagChart, perUserFollowingChart, perUserDriveChart, + apRequestChart, ]; // 20分おきãĢãƒĄãƒĸãƒĒæƒ…å ąã‚’DBãĢ書きčžŧãŋ diff --git a/packages/backend/src/services/drive/add-file.ts b/packages/backend/src/services/drive/add-file.ts index a89e068f45..9a8a543904 100644 --- a/packages/backend/src/services/drive/add-file.ts +++ b/packages/backend/src/services/drive/add-file.ts @@ -160,8 +160,8 @@ export async function generateAlts(path: string, type: string, generateWeb: bool webpublic: null, thumbnail, }; - } catch (e) { - logger.warn(`GenerateVideoThumbnail failed: ${e}`); + } catch (err) { + logger.warn(`GenerateVideoThumbnail failed: ${err}`); return { webpublic: null, thumbnail: null, @@ -191,8 +191,8 @@ export async function generateAlts(path: string, type: string, generateWeb: bool thumbnail: null, }; } - } catch (e) { - logger.warn(`sharp failed: ${e}`); + } catch (err) { + logger.warn(`sharp failed: ${err}`); return { webpublic: null, thumbnail: null, @@ -215,8 +215,8 @@ export async function generateAlts(path: string, type: string, generateWeb: bool } else { logger.debug(`web image not created (not an required image)`); } - } catch (e) { - logger.warn(`web image not created (an error occured)`, e); + } catch (err) { + logger.warn(`web image not created (an error occured)`, err as Error); } } else { logger.info(`web image not created (from remote)`); @@ -234,8 +234,8 @@ export async function generateAlts(path: string, type: string, generateWeb: bool } else { logger.debug(`thumbnail not created (not an required file)`); } - } catch (e) { - logger.warn(`thumbnail not created (an error occured)`, e); + } catch (err) { + logger.warn(`thumbnail not created (an error occured)`, err as Error); } // #endregion thumbnail @@ -451,9 +451,9 @@ export async function addFile({ file.storedInternal = false; file = await DriveFiles.insert(file).then(x => DriveFiles.findOneOrFail(x.identifiers[0])); - } catch (e) { + } catch (err) { // duplicate key error (when already registered) - if (isDuplicateKeyValueError(e)) { + if (isDuplicateKeyValueError(err)) { logger.info(`already registered ${file.uri}`); file = await DriveFiles.findOne({ @@ -461,8 +461,8 @@ export async function addFile({ userId: user ? user.id : null, }) as DriveFile; } else { - logger.error(e); - throw e; + logger.error(err as Error); + throw err; } } } else { diff --git a/packages/backend/src/services/logger.ts b/packages/backend/src/services/logger.ts index 626cc6b44c..1962088b63 100644 --- a/packages/backend/src/services/logger.ts +++ b/packages/backend/src/services/logger.ts @@ -1,6 +1,6 @@ import * as cluster from 'cluster'; import * as chalk from 'chalk'; -import * as dateformat from 'dateformat'; +import { format as dateFormat } from 'date-fns'; import { envOption } from '../env'; import config from '@/config/index'; @@ -57,7 +57,7 @@ export default class Logger { return; } - const time = dateformat(new Date(), 'HH:MM:ss'); + const time = dateFormat(new Date(), 'HH:mm:ss'); const worker = cluster.isPrimary ? '*' : cluster.worker.id; const l = level === 'error' ? important ? chalk.bgRed.white('ERR ') : chalk.red('ERR ') : @@ -116,7 +116,7 @@ export default class Logger { } public debug(message: string, data?: Record | null, important = false): void { // デバッグį”¨ãĢäŊŋう(開į™ē者ãĢåŋ…čĻã ãŒåˆŠį”¨č€…ãĢ不čĻãĒæƒ…å ą) - if (process.env.NODE_ENV != 'production' || envOption.verbose) { + if (process.env.NODE_ENV !== 'production' || envOption.verbose) { this.log('debug', message, data, important); } } diff --git a/packages/backend/src/services/messages/create.ts b/packages/backend/src/services/messages/create.ts index e1bef09a79..dc89bc785d 100644 --- a/packages/backend/src/services/messages/create.ts +++ b/packages/backend/src/services/messages/create.ts @@ -13,7 +13,7 @@ import renderCreate from '@/remote/activitypub/renderer/create'; import { renderActivity } from '@/remote/activitypub/renderer/index'; import { deliver } from '@/queue/index'; -export async function createMessage(user: { id: User['id']; host: User['host']; }, recipientUser: User | undefined, recipientGroup: UserGroup | undefined, text: string | undefined, file: DriveFile | null, uri?: string) { +export async function createMessage(user: { id: User['id']; host: User['host']; }, recipientUser: User | undefined, recipientGroup: UserGroup | undefined, text: string | null | undefined, file: DriveFile | null, uri?: string) { const message = { id: genId(), createdAt: new Date(), diff --git a/packages/backend/src/services/note/create.ts b/packages/backend/src/services/note/create.ts index fb22bd6593..7a4c2cef12 100644 --- a/packages/backend/src/services/note/create.ts +++ b/packages/backend/src/services/note/create.ts @@ -59,7 +59,7 @@ class NotificationManager { if (exist) { // ã€ŒãƒĄãƒŗã‚ˇãƒ§ãƒŗされãĻいるかつčŋ”äŋĄã•ã‚ŒãĻã„ã‚‹ã€å ´åˆã¯ã€ãƒĄãƒŗã‚ˇãƒ§ãƒŗとしãĻぎ通įŸĨではãĒくčŋ”äŋĄã¨ã—ãĻぎ通įŸĨãĢする - if (reason != 'mention') { + if (reason !== 'mention') { exist.reason = reason; } } else { @@ -111,7 +111,7 @@ type Option = { app?: App | null; }; -export default async (user: { id: User['id']; username: User['username']; host: User['host']; isSilenced: User['isSilenced']; }, data: Option, silent = false) => new Promise(async (res, rej) => { +export default async (user: { id: User['id']; username: User['username']; host: User['host']; isSilenced: User['isSilenced']; createdAt: User['createdAt']; }, data: Option, silent = false) => new Promise(async (res, rej) => { // チãƒŖãƒŗネãƒĢ外ãĢãƒĒãƒ—ãƒŠã‚¤ã—ãŸã‚‰å¯žčąĄãŽã‚šã‚ŗãƒŧプãĢ合わせる // (クナイã‚ĸãƒŗトã‚ĩイドでやãŖãĻã‚‚č‰¯ã„å‡Ļį†ã ã¨æ€ã†ã‘おとりあえずã‚ĩãƒŧバãƒŧã‚ĩイドで) if (data.reply && data.channel && data.reply.channelId !== data.channel.id) { @@ -201,7 +201,7 @@ export default async (user: { id: User['id']; username: User['username']; host: mentionedUsers.push(await Users.findOneOrFail(data.reply.userId)); } - if (data.visibility == 'specified') { + if (data.visibility === 'specified') { if (data.visibleUsers == null) throw new Error('invalid param'); for (const u of data.visibleUsers) { @@ -297,11 +297,10 @@ export default async (user: { id: User['id']; username: User['username']; host: } if (!silent) { - // ロãƒŧã‚ĢãƒĢãƒĻãƒŧã‚ļãƒŧぎチãƒŖãƒŧトはã‚ŋイムナイãƒŗ取垗時ãĢ更新しãĻいるぎでãƒĒãƒĸãƒŧトãƒĻãƒŧã‚ļãƒŧぎ場合だけでよい - if (Users.isRemoteUser(user)) activeUsersChart.update(user); + if (Users.isLocalUser(user)) activeUsersChart.write(user); // æœĒčĒ­é€šįŸĨをäŊœæˆ - if (data.visibility == 'specified') { + if (data.visibility === 'specified') { if (data.visibleUsers == null) throw new Error('invalid param'); for (const u of data.visibleUsers) { @@ -439,7 +438,7 @@ export default async (user: { id: User['id']; username: User['username']; host: async function renderNoteOrRenoteActivity(data: Option, note: Note) { if (data.localOnly) return null; - const content = data.renote && data.text == null && data.poll == null && (data.files == null || data.files.length == 0) + const content = data.renote && data.text == null && data.poll == null && (data.files == null || data.files.length === 0) ? renderAnnounce(data.renote.uri ? data.renote.uri : `${config.url}/notes/${data.renote.id}`, note) : renderCreate(await renderNote(note, false), note); @@ -478,7 +477,7 @@ async function insertNote(user: { id: User['id']; host: User['host']; }, data: O userId: user.id, localOnly: data.localOnly!, visibility: data.visibility as any, - visibleUserIds: data.visibility == 'specified' + visibleUserIds: data.visibility === 'specified' ? data.visibleUsers ? data.visibleUsers.map(u => u.id) : [] @@ -502,7 +501,7 @@ async function insertNote(user: { id: User['id']; host: User['host']; }, data: O insert.mentions = mentionedUsers.map(u => u.id); const profiles = await UserProfiles.find({ userId: In(insert.mentions) }); insert.mentionedRemoteUsers = JSON.stringify(mentionedUsers.filter(u => Users.isRemoteUser(u)).map(u => { - const profile = profiles.find(p => p.userId == u.id); + const profile = profiles.find(p => p.userId === u.id); const url = profile != null ? profile.url : null; return { uri: u.uri, diff --git a/packages/backend/src/services/note/delete.ts b/packages/backend/src/services/note/delete.ts index 64383ee928..77723fe8b7 100644 --- a/packages/backend/src/services/note/delete.ts +++ b/packages/backend/src/services/note/delete.ts @@ -39,7 +39,7 @@ export default async function(user: User, note: Note, quiet = false) { let renote: Note | undefined; // if deletd note is renote - if (note.renoteId && note.text == null && !note.hasPoll && (note.fileIds == null || note.fileIds.length == 0)) { + if (note.renoteId && note.text == null && !note.hasPoll && (note.fileIds == null || note.fileIds.length === 0)) { renote = await Notes.findOne({ id: note.renoteId, }); diff --git a/packages/backend/src/services/note/reaction/create.ts b/packages/backend/src/services/note/reaction/create.ts index 47f46419dd..c01d43c221 100644 --- a/packages/backend/src/services/note/reaction/create.ts +++ b/packages/backend/src/services/note/reaction/create.ts @@ -76,7 +76,7 @@ export default async (user: { id: User['id']; host: User['host']; }, note: Note, // ã‚Ģã‚šã‚ŋムįĩĩ文字ãƒĒã‚ĸã‚¯ã‚ˇãƒ§ãƒŗだãŖたらįĩĩæ–‡å­—æƒ…å ąã‚‚é€ã‚‹ const decodedReaction = decodeReaction(reaction); - let emoji = await Emojis.findOne({ + const emoji = await Emojis.findOne({ where: { name: decodedReaction.name, host: decodedReaction.host, diff --git a/packages/backend/src/services/note/read.ts b/packages/backend/src/services/note/read.ts index aaf1c5ed71..032f1e84e6 100644 --- a/packages/backend/src/services/note/read.ts +++ b/packages/backend/src/services/note/read.ts @@ -52,7 +52,7 @@ export default async function( if (note.user != null) { // たãļんnullãĢãĒることはį„Ąã„はずだけお一åŋœ for (const antenna of myAntennas) { - if (await checkHitAntenna(antenna, note, note.user as any, undefined, Array.from(following))) { + if (await checkHitAntenna(antenna, note, note.user, undefined, Array.from(following))) { readAntennaNotes.push(note); } } diff --git a/packages/backend/src/services/register-or-fetch-instance-doc.ts b/packages/backend/src/services/register-or-fetch-instance-doc.ts index 18b42ed15b..c42506a2ed 100644 --- a/packages/backend/src/services/register-or-fetch-instance-doc.ts +++ b/packages/backend/src/services/register-or-fetch-instance-doc.ts @@ -1,6 +1,5 @@ import { Instance } from '@/models/entities/instance'; import { Instances } from '@/models/index'; -import { federationChart } from '@/services/chart/index'; import { genId } from '@/misc/gen-id'; import { toPuny } from '@/misc/convert-host'; import { Cache } from '@/misc/cache'; @@ -23,8 +22,6 @@ export async function registerOrFetchInstanceDoc(host: string): Promise Instances.findOneOrFail(x.identifiers[0])); - federationChart.update(true); - cache.set(host, i); return i; } else { diff --git a/packages/backend/src/services/send-email.ts b/packages/backend/src/services/send-email.ts index f5f36148f6..1fd406cb68 100644 --- a/packages/backend/src/services/send-email.ts +++ b/packages/backend/src/services/send-email.ts @@ -114,9 +114,9 @@ export async function sendEmail(to: string, subject: string, html: string, text: `, }); - logger.info('Message sent: %s', info.messageId); - } catch (e) { - logger.error(e); - throw e; + logger.info(`Message sent: ${info.messageId}`); + } catch (err) { + logger.error(err as Error); + throw err; } } diff --git a/packages/backend/test/api.ts b/packages/backend/test/api.ts index 99fb196dcd..c4802e224e 100644 --- a/packages/backend/test/api.ts +++ b/packages/backend/test/api.ts @@ -1,970 +1,83 @@ -/* process.env.NODE_ENV = 'test'; import * as assert from 'assert'; import * as childProcess from 'child_process'; -import { async, signup, request, post, react, uploadFile } from './utils'; +import { async, signup, request, post, react, uploadFile, startServer, shutdownServer } from './utils'; describe('API', () => { let p: childProcess.ChildProcess; + let alice: any; + let bob: any; + let carol: any; - beforeEach(done => { - p = childProcess.spawn('node', [__dirname + '/../index.js'], { - stdio: ['inherit', 'inherit', 'ipc'], - env: { NODE_ENV: 'test' } - }); - p.on('message', message => { - if (message === 'ok') { - done(); - } - }); + before(async () => { + p = await startServer(); + alice = await signup({ username: 'alice' }); + bob = await signup({ username: 'bob' }); + carol = await signup({ username: 'carol' }); }); - afterEach(() => { - p.kill(); + after(async () => { + await shutdownServer(p); }); - describe('signup', () => { - it('不æ­ŖãĒãƒĻãƒŧã‚ļãƒŧ名でã‚ĸã‚Ģã‚ĻãƒŗトがäŊœæˆã§ããĒい', async(async () => { - const res = await request('/signup', { - username: 'test.', - password: 'test' + describe('General validation', () => { + it('wrong type', async(async () => { + const res = await request('/test', { + required: true, + string: 42, }); assert.strictEqual(res.status, 400); })); - it('įŠēぎパ゚ワãƒŧドでã‚ĸã‚Ģã‚ĻãƒŗトがäŊœæˆã§ããĒい', async(async () => { - const res = await request('/signup', { - username: 'test', - password: '' + it('missing require param', async(async () => { + const res = await request('/test', { + string: 'a', }); assert.strictEqual(res.status, 400); })); - it('æ­Ŗしくã‚ĸã‚Ģã‚ĻãƒŗトがäŊœæˆã§ãã‚‹', async(async () => { - const me = { - username: 'test', - password: 'test' - }; - - const res = await request('/signup', me); - - assert.strictEqual(res.status, 200); - assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true); - assert.strictEqual(res.body.username, me.username); - })); - - it('同じãƒĻãƒŧã‚ļãƒŧ名ぎã‚ĸã‚Ģã‚ĻãƒŗトはäŊœæˆã§ããĒい', async(async () => { - await signup({ - username: 'test' - }); - - const res = await request('/signup', { - username: 'test', - password: 'test' - }); - - assert.strictEqual(res.status, 400); - })); - }); - - describe('signin', () => { - it('間違ãŖたパ゚ワãƒŧドでã‚ĩイãƒŗイãƒŗできãĒい', async(async () => { - await signup({ - username: 'test', - password: 'foo' - }); - - const res = await request('/signin', { - username: 'test', - password: 'bar' - }); - - assert.strictEqual(res.status, 403); - })); - - it('クエãƒĒをイãƒŗã‚¸ã‚§ã‚¯ã‚ˇãƒ§ãƒŗできãĒい', async(async () => { - await signup({ - username: 'test' - }); - - const res = await request('/signin', { - username: 'test', - password: { - $gt: '' - } - }); - - assert.strictEqual(res.status, 400); - })); - - it('æ­Ŗã—ã„æƒ…å ąã§ã‚ĩイãƒŗイãƒŗできる', async(async () => { - await signup({ - username: 'test', - password: 'foo' - }); - - const res = await request('/signin', { - username: 'test', - password: 'foo' - }); - - assert.strictEqual(res.status, 200); - })); - }); - - describe('i/update', () => { - it('ã‚ĸã‚Ģã‚Ļãƒŗãƒˆč¨­åŽšã‚’æ›´æ–°ã§ãã‚‹', async(async () => { - const me = await signup(); - - const myName = '大厤æĢģ子'; - const myLocation = '七æŖŽä¸­'; - const myBirthday = '2000-09-07'; - - const res = await request('/i/update', { - name: myName, - location: myLocation, - birthday: myBirthday - }, me); - - assert.strictEqual(res.status, 200); - assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true); - assert.strictEqual(res.body.name, myName); - assert.strictEqual(res.body.location, myLocation); - assert.strictEqual(res.body.birthday, myBirthday); - })); - - it('名前をįŠēį™ŊãĢできãĒい', async(async () => { - const me = await signup(); - const res = await request('/i/update', { - name: ' ' - }, me); - assert.strictEqual(res.status, 400); - })); - - it('čĒ•į”Ÿæ—ĨãŽč¨­åŽšã‚’å‰Šé™¤ã§ãã‚‹', async(async () => { - const me = await signup(); - await request('/i/update', { - birthday: '2000-09-07' - }, me); - - const res = await request('/i/update', { - birthday: null - }, me); - - assert.strictEqual(res.status, 200); - assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true); - assert.strictEqual(res.body.birthday, null); - })); - - it('不æ­ŖãĒčĒ•į”Ÿæ—ĨぎåŊĸåŧã§æ€’られる', async(async () => { - const me = await signup(); - const res = await request('/i/update', { - birthday: '2000/09/07' - }, me); - assert.strictEqual(res.status, 400); - })); - }); - - describe('users/show', () => { - it('ãƒĻãƒŧã‚ļãƒŧが取垗できる', async(async () => { - const me = await signup(); - - const res = await request('/users/show', { - userId: me.id - }, me); - - assert.strictEqual(res.status, 200); - assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true); - assert.strictEqual(res.body.id, me.id); - })); - - it('ãƒĻãƒŧã‚ļãƒŧが存在しãĒかãŖたら怒る', async(async () => { - const res = await request('/users/show', { - userId: '000000000000000000000000' + it('invalid misskey:id (empty string)', async(async () => { + const res = await request('/test', { + required: true, + id: '', }); assert.strictEqual(res.status, 400); })); - it('間違ãŖたIDで怒られる', async(async () => { - const res = await request('/users/show', { - userId: 'kyoppie' + it('valid misskey:id', async(async () => { + const res = await request('/test', { + required: true, + id: '8wvhjghbxu', }); - assert.strictEqual(res.status, 400); + assert.strictEqual(res.status, 200); })); - }); - describe('notes/show', () => { - it('投į¨ŋが取垗できる', async(async () => { - const me = await signup(); - const myPost = await post(me, { - text: 'test' + it('default value', async(async () => { + const res = await request('/test', { + required: true, + string: 'a', }); - - const res = await request('/notes/show', { - noteId: myPost.id - }, me); - assert.strictEqual(res.status, 200); - assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true); - assert.strictEqual(res.body.id, myPost.id); - assert.strictEqual(res.body.text, myPost.text); + assert.strictEqual(res.body.default, 'hello'); })); - it('投į¨ŋが存在しãĒかãŖたら怒る', async(async () => { - const res = await request('/notes/show', { - noteId: '000000000000000000000000' + it('can set null even if it has default value', async(async () => { + const res = await request('/test', { + required: true, + nullableDefault: null, }); - assert.strictEqual(res.status, 400); + assert.strictEqual(res.status, 200); + assert.strictEqual(res.body.nullableDefault, null); })); - it('間違ãŖたIDで怒られる', async(async () => { - const res = await request('/notes/show', { - noteId: 'kyoppie' + it('cannot set undefined if it has default value', async(async () => { + const res = await request('/test', { + required: true, + nullableDefault: undefined, }); - assert.strictEqual(res.status, 400); - })); - }); - - describe('notes/reactions/create', () => { - it('ãƒĒã‚ĸã‚¯ã‚ˇãƒ§ãƒŗできる', async(async () => { - const bob = await signup({ username: 'bob' }); - const bobPost = await post(bob); - - const alice = await signup({ username: 'alice' }); - const res = await request('/notes/reactions/create', { - noteId: bobPost.id, - reaction: 'like' - }, alice); - - assert.strictEqual(res.status, 204); - })); - - it('č‡Ē分ぎ投į¨ŋãĢはãƒĒã‚ĸã‚¯ã‚ˇãƒ§ãƒŗできãĒい', async(async () => { - const me = await signup(); - const myPost = await post(me); - - const res = await request('/notes/reactions/create', { - noteId: myPost.id, - reaction: 'like' - }, me); - - assert.strictEqual(res.status, 400); - })); - - it('äēŒé‡ãĢãƒĒã‚ĸã‚¯ã‚ˇãƒ§ãƒŗできãĒい', async(async () => { - const bob = await signup({ username: 'bob' }); - const bobPost = await post(bob); - - const alice = await signup({ username: 'alice' }); - await react(alice, bobPost, 'like'); - - const res = await request('/notes/reactions/create', { - noteId: bobPost.id, - reaction: 'like' - }, alice); - - assert.strictEqual(res.status, 400); - })); - - it('存在しãĒい投į¨ŋãĢはãƒĒã‚ĸã‚¯ã‚ˇãƒ§ãƒŗできãĒい', async(async () => { - const me = await signup(); - - const res = await request('/notes/reactions/create', { - noteId: '000000000000000000000000', - reaction: 'like' - }, me); - - assert.strictEqual(res.status, 400); - })); - - it('įŠēãŽãƒ‘ãƒŠãƒĄãƒŧã‚ŋで怒られる', async(async () => { - const me = await signup(); - - const res = await request('/notes/reactions/create', {}, me); - - assert.strictEqual(res.status, 400); - })); - - it('間違ãŖたIDで怒られる', async(async () => { - const me = await signup(); - - const res = await request('/notes/reactions/create', { - noteId: 'kyoppie', - reaction: 'like' - }, me); - - assert.strictEqual(res.status, 400); - })); - }); - - describe('following/create', () => { - it('フりロãƒŧできる', async(async () => { - const alice = await signup({ username: 'alice' }); - const bob = await signup({ username: 'bob' }); - - const res = await request('/following/create', { - userId: alice.id - }, bob); - assert.strictEqual(res.status, 200); - })); - - it('æ—ĸãĢフりロãƒŧしãĻいる場合は怒る', async(async () => { - const alice = await signup({ username: 'alice' }); - const bob = await signup({ username: 'bob' }); - await request('/following/create', { - userId: alice.id - }, bob); - - const res = await request('/following/create', { - userId: alice.id - }, bob); - - assert.strictEqual(res.status, 400); - })); - - it('存在しãĒいãƒĻãƒŧã‚ļãƒŧはフりロãƒŧできãĒい', async(async () => { - const alice = await signup({ username: 'alice' }); - - const res = await request('/following/create', { - userId: '000000000000000000000000' - }, alice); - - assert.strictEqual(res.status, 400); - })); - - it('č‡Ē分č‡ĒčēĢはフりロãƒŧできãĒい', async(async () => { - const alice = await signup({ username: 'alice' }); - - const res = await request('/following/create', { - userId: alice.id - }, alice); - - assert.strictEqual(res.status, 400); - })); - - it('įŠēãŽãƒ‘ãƒŠãƒĄãƒŧã‚ŋで怒られる', async(async () => { - const alice = await signup({ username: 'alice' }); - - const res = await request('/following/create', {}, alice); - - assert.strictEqual(res.status, 400); - })); - - it('間違ãŖたIDで怒られる', async(async () => { - const alice = await signup({ username: 'alice' }); - - const res = await request('/following/create', { - userId: 'foo' - }, alice); - - assert.strictEqual(res.status, 400); - })); - }); - - describe('following/delete', () => { - it('フりロãƒŧč§Ŗ除できる', async(async () => { - const alice = await signup({ username: 'alice' }); - const bob = await signup({ username: 'bob' }); - await request('/following/create', { - userId: alice.id - }, bob); - - const res = await request('/following/delete', { - userId: alice.id - }, bob); - - assert.strictEqual(res.status, 200); - })); - - it('フりロãƒŧしãĻいãĒい場合は怒る', async(async () => { - const alice = await signup({ username: 'alice' }); - const bob = await signup({ username: 'bob' }); - - const res = await request('/following/delete', { - userId: alice.id - }, bob); - - assert.strictEqual(res.status, 400); - })); - - it('存在しãĒいãƒĻãƒŧã‚ļãƒŧはフりロãƒŧč§Ŗ除できãĒい', async(async () => { - const alice = await signup({ username: 'alice' }); - - const res = await request('/following/delete', { - userId: '000000000000000000000000' - }, alice); - - assert.strictEqual(res.status, 400); - })); - - it('č‡Ē分č‡ĒčēĢはフりロãƒŧč§Ŗ除できãĒい', async(async () => { - const alice = await signup({ username: 'alice' }); - - const res = await request('/following/delete', { - userId: alice.id - }, alice); - - assert.strictEqual(res.status, 400); - })); - - it('įŠēãŽãƒ‘ãƒŠãƒĄãƒŧã‚ŋで怒られる', async(async () => { - const alice = await signup({ username: 'alice' }); - - const res = await request('/following/delete', {}, alice); - - assert.strictEqual(res.status, 400); - })); - - it('間違ãŖたIDで怒られる', async(async () => { - const alice = await signup({ username: 'alice' }); - - const res = await request('/following/delete', { - userId: 'kyoppie' - }, alice); - - assert.strictEqual(res.status, 400); - })); - }); - - describe('drive', () => { - it('ãƒ‰ãƒŠã‚¤ãƒ–æƒ…å ąã‚’å–åž—ã§ãã‚‹', async(async () => { - const bob = await signup({ username: 'bob' }); - await uploadFile({ - userId: me.id, - size: 256 - }); - await uploadFile({ - userId: me.id, - size: 512 - }); - await uploadFile({ - userId: me.id, - size: 1024 - }); - const res = await request('/drive', {}, me); - assert.strictEqual(res.status, 200); - assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true); - expect(res.body).have.property('usage').eql(1792); - })); - }); - - describe('drive/files/create', () => { - it('ãƒ•ã‚Ąã‚¤ãƒĢをäŊœæˆã§ãã‚‹', async(async () => { - const alice = await signup({ username: 'alice' }); - - const res = await uploadFile(alice); - - assert.strictEqual(res.status, 200); - assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true); - assert.strictEqual(res.body.name, 'Lenna.png'); - })); - - it('ãƒ•ã‚Ąã‚¤ãƒĢãĢ名前をäģ˜ã‘られる', async(async () => { - const alice = await signup({ username: 'alice' }); - - const res = await assert.request(server) - .post('/drive/files/create') - .field('i', alice.token) - .field('name', 'Belmond.png') - .attach('file', fs.readFileSync(__dirname + '/resources/Lenna.png'), 'Lenna.png'); - - expect(res).have.status(200); - expect(res.body).be.a('object'); - expect(res.body).have.property('name').eql('Belmond.png'); - })); - - it('ãƒ•ã‚Ąã‚¤ãƒĢį„Ąã—で怒られる', async(async () => { - const alice = await signup({ username: 'alice' }); - - const res = await request('/drive/files/create', {}, alice); - - assert.strictEqual(res.status, 400); - })); - - it('SVGãƒ•ã‚Ąã‚¤ãƒĢをäŊœæˆã§ãã‚‹', async(async () => { - const izumi = await signup({ username: 'izumi' }); - - const res = await uploadFile(izumi, __dirname + '/resources/image.svg'); - - assert.strictEqual(res.status, 200); - assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true); - assert.strictEqual(res.body.name, 'image.svg'); - assert.strictEqual(res.body.type, 'image/svg+xml'); - })); - }); - - describe('drive/files/update', () => { - it('名前を更新できる', async(async () => { - const alice = await signup({ username: 'alice' }); - const file = await uploadFile(alice); - const newName = 'ã„ãĄã”ãƒ‘ã‚šã‚ŋ.png'; - - const res = await request('/drive/files/update', { - fileId: file.id, - name: newName - }, alice); - - assert.strictEqual(res.status, 200); - assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true); - assert.strictEqual(res.body.name, newName); - })); - - it('äģ–äēēãŽãƒ•ã‚Ąã‚¤ãƒĢは更新できãĒい', async(async () => { - const bob = await signup({ username: 'bob' }); - const alice = await signup({ username: 'alice' }); - const file = await uploadFile(bob); - - const res = await request('/drive/files/update', { - fileId: file.id, - name: 'ã„ãĄã”ãƒ‘ã‚šã‚ŋ.png' - }, alice); - - assert.strictEqual(res.status, 400); - })); - - it('čĻĒフりãƒĢダを更新できる', async(async () => { - const alice = await signup({ username: 'alice' }); - const file = await uploadFile(alice); - const folder = (await request('/drive/folders/create', { - name: 'test' - }, alice)).body; - - const res = await request('/drive/files/update', { - fileId: file.id, - folderId: folder.id - }, alice); - - assert.strictEqual(res.status, 200); - assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true); - assert.strictEqual(res.body.folderId, folder.id); - })); - - it('čĻĒフりãƒĢダをį„Ąã—ãĢできる', async(async () => { - const alice = await signup({ username: 'alice' }); - const file = await uploadFile(alice); - - const folder = (await request('/drive/folders/create', { - name: 'test' - }, alice)).body; - - await request('/drive/files/update', { - fileId: file.id, - folderId: folder.id - }, alice); - - const res = await request('/drive/files/update', { - fileId: file.id, - folderId: null - }, alice); - - assert.strictEqual(res.status, 200); - assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true); - assert.strictEqual(res.body.folderId, null); - })); - - it('äģ–äēēぎフりãƒĢダãĢはå…ĨれられãĒい', async(async () => { - const bob = await signup({ username: 'bob' }); - const alice = await signup({ username: 'alice' }); - const file = await uploadFile(alice); - const folder = (await request('/drive/folders/create', { - name: 'test' - }, bob)).body; - - const res = await request('/drive/files/update', { - fileId: file.id, - folderId: folder.id - }, alice); - - assert.strictEqual(res.status, 400); - })); - - it('存在しãĒいフりãƒĢダで怒られる', async(async () => { - const alice = await signup({ username: 'alice' }); - const file = await uploadFile(alice); - - const res = await request('/drive/files/update', { - fileId: file.id, - folderId: '000000000000000000000000' - }, alice); - - assert.strictEqual(res.status, 400); - })); - - it('不æ­ŖãĒフりãƒĢダIDで怒られる', async(async () => { - const alice = await signup({ username: 'alice' }); - const file = await uploadFile(alice); - - const res = await request('/drive/files/update', { - fileId: file.id, - folderId: 'foo' - }, alice); - - assert.strictEqual(res.status, 400); - })); - - it('ãƒ•ã‚Ąã‚¤ãƒĢが存在しãĒかãŖたら怒る', async(async () => { - const alice = await signup({ username: 'alice' }); - - const res = await request('/drive/files/update', { - fileId: '000000000000000000000000', - name: 'ã„ãĄã”ãƒ‘ã‚šã‚ŋ.png' - }, alice); - - assert.strictEqual(res.status, 400); - })); - - it('間違ãŖたIDで怒られる', async(async () => { - const alice = await signup({ username: 'alice' }); - - const res = await request('/drive/files/update', { - fileId: 'kyoppie', - name: 'ã„ãĄã”ãƒ‘ã‚šã‚ŋ.png' - }, alice); - - assert.strictEqual(res.status, 400); - })); - }); - - describe('drive/folders/create', () => { - it('フりãƒĢダをäŊœæˆã§ãã‚‹', async(async () => { - const alice = await signup({ username: 'alice' }); - - const res = await request('/drive/folders/create', { - name: 'test' - }, alice); - - assert.strictEqual(res.status, 200); - assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true); - assert.strictEqual(res.body.name, 'test'); - })); - }); - - describe('drive/folders/update', () => { - it('名前を更新できる', async(async () => { - const alice = await signup({ username: 'alice' }); - const folder = (await request('/drive/folders/create', { - name: 'test' - }, alice)).body; - - const res = await request('/drive/folders/update', { - folderId: folder.id, - name: 'new name' - }, alice); - - assert.strictEqual(res.status, 200); - assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true); - assert.strictEqual(res.body.name, 'new name'); - })); - - it('äģ–äēēぎフりãƒĢダを更新できãĒい', async(async () => { - const bob = await signup({ username: 'bob' }); - const alice = await signup({ username: 'alice' }); - const folder = (await request('/drive/folders/create', { - name: 'test' - }, bob)).body; - - const res = await request('/drive/folders/update', { - folderId: folder.id, - name: 'new name' - }, alice); - - assert.strictEqual(res.status, 400); - })); - - it('čĻĒフりãƒĢダを更新できる', async(async () => { - const alice = await signup({ username: 'alice' }); - const folder = (await request('/drive/folders/create', { - name: 'test' - }, alice)).body; - const parentFolder = (await request('/drive/folders/create', { - name: 'parent' - }, alice)).body; - - const res = await request('/drive/folders/update', { - folderId: folder.id, - parentId: parentFolder.id - }, alice); - - assert.strictEqual(res.status, 200); - assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true); - assert.strictEqual(res.body.parentId, parentFolder.id); - })); - - it('čĻĒフりãƒĢダをį„Ąã—ãĢ更新できる', async(async () => { - const alice = await signup({ username: 'alice' }); - const folder = (await request('/drive/folders/create', { - name: 'test' - }, alice)).body; - const parentFolder = (await request('/drive/folders/create', { - name: 'parent' - }, alice)).body; - await request('/drive/folders/update', { - folderId: folder.id, - parentId: parentFolder.id - }, alice); - - const res = await request('/drive/folders/update', { - folderId: folder.id, - parentId: null - }, alice); - - assert.strictEqual(res.status, 200); - assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true); - assert.strictEqual(res.body.parentId, null); - })); - - it('äģ–äēēぎフりãƒĢダをčĻĒフりãƒĢダãĢč¨­åŽšã§ããĒい', async(async () => { - const bob = await signup({ username: 'bob' }); - const alice = await signup({ username: 'alice' }); - const folder = (await request('/drive/folders/create', { - name: 'test' - }, alice)).body; - const parentFolder = (await request('/drive/folders/create', { - name: 'parent' - }, bob)).body; - - const res = await request('/drive/folders/update', { - folderId: folder.id, - parentId: parentFolder.id - }, alice); - - assert.strictEqual(res.status, 400); - })); - - it('フりãƒĢダがåžĒį’°ã™ã‚‹ã‚ˆã†ãĒ構造ãĢできãĒい', async(async () => { - const alice = await signup({ username: 'alice' }); - const folder = (await request('/drive/folders/create', { - name: 'test' - }, alice)).body; - const parentFolder = (await request('/drive/folders/create', { - name: 'parent' - }, alice)).body; - await request('/drive/folders/update', { - folderId: parentFolder.id, - parentId: folder.id - }, alice); - - const res = await request('/drive/folders/update', { - folderId: folder.id, - parentId: parentFolder.id - }, alice); - - assert.strictEqual(res.status, 400); - })); - - it('フりãƒĢダがåžĒį’°ã™ã‚‹ã‚ˆã†ãĒ構造ãĢできãĒい(再帰įš„)', async(async () => { - const alice = await signup({ username: 'alice' }); - const folderA = (await request('/drive/folders/create', { - name: 'test' - }, alice)).body; - const folderB = (await request('/drive/folders/create', { - name: 'test' - }, alice)).body; - const folderC = (await request('/drive/folders/create', { - name: 'test' - }, alice)).body; - await request('/drive/folders/update', { - folderId: folderB.id, - parentId: folderA.id - }, alice); - await request('/drive/folders/update', { - folderId: folderC.id, - parentId: folderB.id - }, alice); - - const res = await request('/drive/folders/update', { - folderId: folderA.id, - parentId: folderC.id - }, alice); - - assert.strictEqual(res.status, 400); - })); - - it('フりãƒĢダがåžĒį’°ã™ã‚‹ã‚ˆã†ãĒ構造ãĢできãĒい(č‡ĒčēĢ)', async(async () => { - const arisugawa = await signup({ username: 'arisugawa' }); - const folderA = (await request('/drive/folders/create', { - name: 'test' - }, arisugawa)).body; - - const res = await request('/drive/folders/update', { - folderId: folderA.id, - parentId: folderA.id - }, arisugawa); - - assert.strictEqual(res.status, 400); - })); - - it('存在しãĒいčĻĒフりãƒĢãƒ€ã‚’č¨­åŽšã§ããĒい', async(async () => { - const alice = await signup({ username: 'alice' }); - const folder = (await request('/drive/folders/create', { - name: 'test' - }, alice)).body; - - const res = await request('/drive/folders/update', { - folderId: folder.id, - parentId: '000000000000000000000000' - }, alice); - - assert.strictEqual(res.status, 400); - })); - - it('不æ­ŖãĒčĻĒフりãƒĢダIDで怒られる', async(async () => { - const alice = await signup({ username: 'alice' }); - const folder = (await request('/drive/folders/create', { - name: 'test' - }, alice)).body; - - const res = await request('/drive/folders/update', { - folderId: folder.id, - parentId: 'foo' - }, alice); - - assert.strictEqual(res.status, 400); - })); - - it('存在しãĒいフりãƒĢダを更新できãĒい', async(async () => { - const alice = await signup({ username: 'alice' }); - - const res = await request('/drive/folders/update', { - folderId: '000000000000000000000000' - }, alice); - - assert.strictEqual(res.status, 400); - })); - - it('不æ­ŖãĒフりãƒĢダIDで怒られる', async(async () => { - const alice = await signup({ username: 'alice' }); - - const res = await request('/drive/folders/update', { - folderId: 'foo' - }, alice); - - assert.strictEqual(res.status, 400); - })); - }); - - describe('messaging/messages/create', () => { - it('ãƒĄãƒƒã‚ģãƒŧジを送äŋĄã§ãã‚‹', async(async () => { - const alice = await signup({ username: 'alice' }); - const bob = await signup({ username: 'bob' }); - - const res = await request('/messaging/messages/create', { - userId: bob.id, - text: 'test' - }, alice); - - assert.strictEqual(res.status, 200); - assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true); - assert.strictEqual(res.body.text, 'test'); - })); - - it('č‡Ē分č‡ĒčēĢãĢã¯ãƒĄãƒƒã‚ģãƒŧジを送äŋĄã§ããĒい', async(async () => { - const alice = await signup({ username: 'alice' }); - - const res = await request('/messaging/messages/create', { - userId: alice.id, - text: 'Yo' - }, alice); - - assert.strictEqual(res.status, 400); - })); - - it('存在しãĒいãƒĻãƒŧã‚ļãƒŧãĢã¯ãƒĄãƒƒã‚ģãƒŧジを送äŋĄã§ããĒい', async(async () => { - const alice = await signup({ username: 'alice' }); - - const res = await request('/messaging/messages/create', { - userId: '000000000000000000000000', - text: 'test' - }, alice); - - assert.strictEqual(res.status, 400); - })); - - it('不æ­ŖãĒãƒĻãƒŧã‚ļãƒŧIDで怒られる', async(async () => { - const alice = await signup({ username: 'alice' }); - - const res = await request('/messaging/messages/create', { - userId: 'foo', - text: 'test' - }, alice); - - assert.strictEqual(res.status, 400); - })); - - it('テキ゚トがį„ĄããĻ怒られる', async(async () => { - const alice = await signup({ username: 'alice' }); - const bob = await signup({ username: 'bob' }); - - const res = await request('/messaging/messages/create', { - userId: bob.id - }, alice); - - assert.strictEqual(res.status, 400); - })); - - it('文字数ã‚Ēãƒŧバãƒŧで怒られる', async(async () => { - const alice = await signup({ username: 'alice' }); - const bob = await signup({ username: 'bob' }); - - const res = await request('/messaging/messages/create', { - userId: bob.id, - text: '!'.repeat(1001) - }, alice); - - assert.strictEqual(res.status, 400); - })); - }); - - describe('notes/replies', () => { - it('č‡Ē分ãĢ閲čĻ§æ¨Šé™ãŽãĒい投į¨ŋはåĢぞれãĒい', async(async () => { - const alice = await signup({ username: 'alice' }); - const bob = await signup({ username: 'bob' }); - const carol = await signup({ username: 'carol' }); - - const alicePost = await post(alice, { - text: 'foo' - }); - - await post(bob, { - replyId: alicePost.id, - text: 'bar', - visibility: 'specified', - visibleUserIds: [alice.id] - }); - - const res = await request('/notes/replies', { - noteId: alicePost.id - }, carol); - - assert.strictEqual(res.status, 200); - assert.strictEqual(Array.isArray(res.body), true); - assert.strictEqual(res.body.length, 0); - })); - }); - - describe('notes/timeline', () => { - it('フりロワãƒŧ限厚投į¨ŋがåĢぞれる', async(async () => { - const alice = await signup({ username: 'alice' }); - const bob = await signup({ username: 'bob' }); - - await request('/following/create', { - userId: alice.id - }, bob); - - const alicePost = await post(alice, { - text: 'foo', - visibility: 'followers' - }); - - const res = await request('/notes/timeline', {}, bob); - - assert.strictEqual(res.status, 200); - assert.strictEqual(Array.isArray(res.body), true); - assert.strictEqual(res.body.length, 1); - assert.strictEqual(res.body[0].id, alicePost.id); + assert.strictEqual(res.body.nullableDefault, 'hello'); })); }); }); -*/ diff --git a/packages/backend/test/chart.ts b/packages/backend/test/chart.ts index 66000bc928..bd8d4c8171 100644 --- a/packages/backend/test/chart.ts +++ b/packages/backend/test/chart.ts @@ -6,14 +6,17 @@ import { async, initTestDb } from './utils'; import TestChart from '../src/services/chart/charts/test'; import TestGroupedChart from '../src/services/chart/charts/test-grouped'; import TestUniqueChart from '../src/services/chart/charts/test-unique'; +import TestIntersectionChart from '../src/services/chart/charts/test-intersection'; import * as _TestChart from '../src/services/chart/charts/entities/test'; import * as _TestGroupedChart from '../src/services/chart/charts/entities/test-grouped'; import * as _TestUniqueChart from '../src/services/chart/charts/entities/test-unique'; +import * as _TestIntersectionChart from '../src/services/chart/charts/entities/test-intersection'; describe('Chart', () => { let testChart: TestChart; let testGroupedChart: TestGroupedChart; let testUniqueChart: TestUniqueChart; + let testIntersectionChart: TestIntersectionChart; let clock: lolex.Clock; beforeEach(async(async () => { @@ -21,11 +24,13 @@ describe('Chart', () => { _TestChart.entity.hour, _TestChart.entity.day, _TestGroupedChart.entity.hour, _TestGroupedChart.entity.day, _TestUniqueChart.entity.hour, _TestUniqueChart.entity.day, + _TestIntersectionChart.entity.hour, _TestIntersectionChart.entity.day, ]); testChart = new TestChart(); testGroupedChart = new TestGroupedChart(); testUniqueChart = new TestUniqueChart(); + testIntersectionChart = new TestIntersectionChart(); clock = lolex.install({ now: new Date(Date.UTC(2000, 0, 1, 0, 0, 0)) @@ -426,6 +431,53 @@ describe('Chart', () => { foo: [2, 0, 0], }); })); + + describe('Intersection', () => { + it('æĄäģļがæē€ãŸã•ã‚ŒãĻいãĒい場合はã‚Ģã‚ĻãƒŗトされãĒい', async(async () => { + await testIntersectionChart.addA('alice'); + await testIntersectionChart.addA('bob'); + await testIntersectionChart.addB('carol'); + await testIntersectionChart.save(); + + const chartHours = await testIntersectionChart.getChart('hour', 3, null); + const chartDays = await testIntersectionChart.getChart('day', 3, null); + + assert.deepStrictEqual(chartHours, { + a: [2, 0, 0], + b: [1, 0, 0], + aAndB: [0, 0, 0], + }); + + assert.deepStrictEqual(chartDays, { + a: [2, 0, 0], + b: [1, 0, 0], + aAndB: [0, 0, 0], + }); + })); + + it('æĄäģļがæē€ãŸã•ã‚ŒãĻいる場合ãĢã‚Ģã‚Ļãƒŗトされる', async(async () => { + await testIntersectionChart.addA('alice'); + await testIntersectionChart.addA('bob'); + await testIntersectionChart.addB('carol'); + await testIntersectionChart.addB('alice'); + await testIntersectionChart.save(); + + const chartHours = await testIntersectionChart.getChart('hour', 3, null); + const chartDays = await testIntersectionChart.getChart('day', 3, null); + + assert.deepStrictEqual(chartHours, { + a: [2, 0, 0], + b: [2, 0, 0], + aAndB: [1, 0, 0], + }); + + assert.deepStrictEqual(chartDays, { + a: [2, 0, 0], + b: [2, 0, 0], + aAndB: [1, 0, 0], + }); + })); + }); }); describe('Resync', () => { diff --git a/packages/backend/test/endpoints.ts b/packages/backend/test/endpoints.ts new file mode 100644 index 0000000000..4df080030a --- /dev/null +++ b/packages/backend/test/endpoints.ts @@ -0,0 +1,858 @@ +/* +process.env.NODE_ENV = 'test'; + +import * as assert from 'assert'; +import * as childProcess from 'child_process'; +import { async, signup, request, post, react, uploadFile, startServer, shutdownServer } from './utils'; + +describe('API: Endpoints', () => { + let p: childProcess.ChildProcess; + let alice: any; + let bob: any; + let carol: any; + + before(async () => { + p = await startServer(); + alice = await signup({ username: 'alice' }); + bob = await signup({ username: 'bob' }); + carol = await signup({ username: 'carol' }); + }); + + after(async () => { + await shutdownServer(p); + }); + + describe('signup', () => { + it('不æ­ŖãĒãƒĻãƒŧã‚ļãƒŧ名でã‚ĸã‚Ģã‚ĻãƒŗトがäŊœæˆã§ããĒい', async(async () => { + const res = await request('/signup', { + username: 'test.', + password: 'test' + }); + assert.strictEqual(res.status, 400); + })); + + it('įŠēぎパ゚ワãƒŧドでã‚ĸã‚Ģã‚ĻãƒŗトがäŊœæˆã§ããĒい', async(async () => { + const res = await request('/signup', { + username: 'test', + password: '' + }); + assert.strictEqual(res.status, 400); + })); + + it('æ­Ŗしくã‚ĸã‚Ģã‚ĻãƒŗトがäŊœæˆã§ãã‚‹', async(async () => { + const me = { + username: 'test1', + password: 'test1' + }; + + const res = await request('/signup', me); + + assert.strictEqual(res.status, 200); + assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true); + assert.strictEqual(res.body.username, me.username); + })); + + it('同じãƒĻãƒŧã‚ļãƒŧ名ぎã‚ĸã‚Ģã‚ĻãƒŗトはäŊœæˆã§ããĒい', async(async () => { + await signup({ + username: 'test2' + }); + + const res = await request('/signup', { + username: 'test2', + password: 'test2' + }); + + assert.strictEqual(res.status, 400); + })); + }); + + describe('signin', () => { + it('間違ãŖたパ゚ワãƒŧドでã‚ĩイãƒŗイãƒŗできãĒい', async(async () => { + await signup({ + username: 'test3', + password: 'foo' + }); + + const res = await request('/signin', { + username: 'test3', + password: 'bar' + }); + + assert.strictEqual(res.status, 403); + })); + + it('クエãƒĒをイãƒŗã‚¸ã‚§ã‚¯ã‚ˇãƒ§ãƒŗできãĒい', async(async () => { + await signup({ + username: 'test4' + }); + + const res = await request('/signin', { + username: 'test4', + password: { + $gt: '' + } + }); + + assert.strictEqual(res.status, 400); + })); + + it('æ­Ŗã—ã„æƒ…å ąã§ã‚ĩイãƒŗイãƒŗできる', async(async () => { + await signup({ + username: 'test5', + password: 'foo' + }); + + const res = await request('/signin', { + username: 'test5', + password: 'foo' + }); + + assert.strictEqual(res.status, 200); + })); + }); + + describe('i/update', () => { + it('ã‚ĸã‚Ģã‚Ļãƒŗãƒˆč¨­åŽšã‚’æ›´æ–°ã§ãã‚‹', async(async () => { + const myName = '大厤æĢģ子'; + const myLocation = '七æŖŽä¸­'; + const myBirthday = '2000-09-07'; + + const res = await request('/i/update', { + name: myName, + location: myLocation, + birthday: myBirthday + }, alice); + + assert.strictEqual(res.status, 200); + assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true); + assert.strictEqual(res.body.name, myName); + assert.strictEqual(res.body.location, myLocation); + assert.strictEqual(res.body.birthday, myBirthday); + })); + + it('名前をįŠēį™ŊãĢできãĒい', async(async () => { + const res = await request('/i/update', { + name: ' ' + }, alice); + assert.strictEqual(res.status, 400); + })); + + it('čĒ•į”Ÿæ—ĨãŽč¨­åŽšã‚’å‰Šé™¤ã§ãã‚‹', async(async () => { + await request('/i/update', { + birthday: '2000-09-07' + }, alice); + + const res = await request('/i/update', { + birthday: null + }, alice); + + assert.strictEqual(res.status, 200); + assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true); + assert.strictEqual(res.body.birthday, null); + })); + + it('不æ­ŖãĒčĒ•į”Ÿæ—ĨぎåŊĸåŧã§æ€’られる', async(async () => { + const res = await request('/i/update', { + birthday: '2000/09/07' + }, alice); + assert.strictEqual(res.status, 400); + })); + }); + + describe('users/show', () => { + it('ãƒĻãƒŧã‚ļãƒŧが取垗できる', async(async () => { + const res = await request('/users/show', { + userId: alice.id + }, alice); + + assert.strictEqual(res.status, 200); + assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true); + assert.strictEqual(res.body.id, alice.id); + })); + + it('ãƒĻãƒŧã‚ļãƒŧが存在しãĒかãŖたら怒る', async(async () => { + const res = await request('/users/show', { + userId: '000000000000000000000000' + }); + assert.strictEqual(res.status, 400); + })); + + it('間違ãŖたIDで怒られる', async(async () => { + const res = await request('/users/show', { + userId: 'kyoppie' + }); + assert.strictEqual(res.status, 400); + })); + }); + + describe('notes/show', () => { + it('投į¨ŋが取垗できる', async(async () => { + const myPost = await post(alice, { + text: 'test' + }); + + const res = await request('/notes/show', { + noteId: myPost.id + }, alice); + + assert.strictEqual(res.status, 200); + assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true); + assert.strictEqual(res.body.id, myPost.id); + assert.strictEqual(res.body.text, myPost.text); + })); + + it('投į¨ŋが存在しãĒかãŖたら怒る', async(async () => { + const res = await request('/notes/show', { + noteId: '000000000000000000000000' + }); + assert.strictEqual(res.status, 400); + })); + + it('間違ãŖたIDで怒られる', async(async () => { + const res = await request('/notes/show', { + noteId: 'kyoppie' + }); + assert.strictEqual(res.status, 400); + })); + }); + + describe('notes/reactions/create', () => { + it('ãƒĒã‚ĸã‚¯ã‚ˇãƒ§ãƒŗできる', async(async () => { + const bobPost = await post(bob); + + const alice = await signup({ username: 'alice' }); + const res = await request('/notes/reactions/create', { + noteId: bobPost.id, + reaction: '👍', + }, alice); + + assert.strictEqual(res.status, 204); + })); + + it('č‡Ē分ぎ投į¨ŋãĢもãƒĒã‚ĸã‚¯ã‚ˇãƒ§ãƒŗできる', async(async () => { + const myPost = await post(alice); + + const res = await request('/notes/reactions/create', { + noteId: myPost.id, + reaction: '👍', + }, alice); + + assert.strictEqual(res.status, 204); + })); + + it('äēŒé‡ãĢãƒĒã‚ĸã‚¯ã‚ˇãƒ§ãƒŗできãĒい', async(async () => { + const bobPost = await post(bob); + + await react(alice, bobPost, 'like'); + + const res = await request('/notes/reactions/create', { + noteId: bobPost.id, + reaction: '👍', + }, alice); + + assert.strictEqual(res.status, 400); + })); + + it('存在しãĒい投į¨ŋãĢはãƒĒã‚ĸã‚¯ã‚ˇãƒ§ãƒŗできãĒい', async(async () => { + const res = await request('/notes/reactions/create', { + noteId: '000000000000000000000000', + reaction: '👍', + }, alice); + + assert.strictEqual(res.status, 400); + })); + + it('įŠēãŽãƒ‘ãƒŠãƒĄãƒŧã‚ŋで怒られる', async(async () => { + const res = await request('/notes/reactions/create', {}, alice); + + assert.strictEqual(res.status, 400); + })); + + it('間違ãŖたIDで怒られる', async(async () => { + const res = await request('/notes/reactions/create', { + noteId: 'kyoppie', + reaction: '👍', + }, alice); + + assert.strictEqual(res.status, 400); + })); + }); + + describe('following/create', () => { + it('フりロãƒŧできる', async(async () => { + const res = await request('/following/create', { + userId: alice.id + }, bob); + + assert.strictEqual(res.status, 200); + })); + + it('æ—ĸãĢフりロãƒŧしãĻいる場合は怒る', async(async () => { + const res = await request('/following/create', { + userId: alice.id + }, bob); + + assert.strictEqual(res.status, 400); + })); + + it('存在しãĒいãƒĻãƒŧã‚ļãƒŧはフりロãƒŧできãĒい', async(async () => { + const res = await request('/following/create', { + userId: '000000000000000000000000' + }, alice); + + assert.strictEqual(res.status, 400); + })); + + it('č‡Ē分č‡ĒčēĢはフりロãƒŧできãĒい', async(async () => { + const res = await request('/following/create', { + userId: alice.id + }, alice); + + assert.strictEqual(res.status, 400); + })); + + it('įŠēãŽãƒ‘ãƒŠãƒĄãƒŧã‚ŋで怒られる', async(async () => { + const res = await request('/following/create', {}, alice); + + assert.strictEqual(res.status, 400); + })); + + it('間違ãŖたIDで怒られる', async(async () => { + const res = await request('/following/create', { + userId: 'foo' + }, alice); + + assert.strictEqual(res.status, 400); + })); + }); + + describe('following/delete', () => { + it('フりロãƒŧč§Ŗ除できる', async(async () => { + await request('/following/create', { + userId: alice.id + }, bob); + + const res = await request('/following/delete', { + userId: alice.id + }, bob); + + assert.strictEqual(res.status, 200); + })); + + it('フりロãƒŧしãĻいãĒい場合は怒る', async(async () => { + const res = await request('/following/delete', { + userId: alice.id + }, bob); + + assert.strictEqual(res.status, 400); + })); + + it('存在しãĒいãƒĻãƒŧã‚ļãƒŧはフりロãƒŧč§Ŗ除できãĒい', async(async () => { + const res = await request('/following/delete', { + userId: '000000000000000000000000' + }, alice); + + assert.strictEqual(res.status, 400); + })); + + it('č‡Ē分č‡ĒčēĢはフりロãƒŧč§Ŗ除できãĒい', async(async () => { + const res = await request('/following/delete', { + userId: alice.id + }, alice); + + assert.strictEqual(res.status, 400); + })); + + it('įŠēãŽãƒ‘ãƒŠãƒĄãƒŧã‚ŋで怒られる', async(async () => { + const res = await request('/following/delete', {}, alice); + + assert.strictEqual(res.status, 400); + })); + + it('間違ãŖたIDで怒られる', async(async () => { + const res = await request('/following/delete', { + userId: 'kyoppie' + }, alice); + + assert.strictEqual(res.status, 400); + })); + }); + + describe('drive', () => { + it('ãƒ‰ãƒŠã‚¤ãƒ–æƒ…å ąã‚’å–åž—ã§ãã‚‹', async(async () => { + await uploadFile({ + userId: alice.id, + size: 256 + }); + await uploadFile({ + userId: alice.id, + size: 512 + }); + await uploadFile({ + userId: alice.id, + size: 1024 + }); + const res = await request('/drive', {}, alice); + assert.strictEqual(res.status, 200); + assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true); + expect(res.body).have.property('usage').eql(1792); + })); + }); + + describe('drive/files/create', () => { + it('ãƒ•ã‚Ąã‚¤ãƒĢをäŊœæˆã§ãã‚‹', async(async () => { + const res = await uploadFile(alice); + + assert.strictEqual(res.status, 200); + assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true); + assert.strictEqual(res.body.name, 'Lenna.png'); + })); + + it('ãƒ•ã‚Ąã‚¤ãƒĢãĢ名前をäģ˜ã‘られる', async(async () => { + const res = await assert.request(server) + .post('/drive/files/create') + .field('i', alice.token) + .field('name', 'Belmond.png') + .attach('file', fs.readFileSync(__dirname + '/resources/Lenna.png'), 'Lenna.png'); + + expect(res).have.status(200); + expect(res.body).be.a('object'); + expect(res.body).have.property('name').eql('Belmond.png'); + })); + + it('ãƒ•ã‚Ąã‚¤ãƒĢį„Ąã—で怒られる', async(async () => { + const res = await request('/drive/files/create', {}, alice); + + assert.strictEqual(res.status, 400); + })); + + it('SVGãƒ•ã‚Ąã‚¤ãƒĢをäŊœæˆã§ãã‚‹', async(async () => { + const res = await uploadFile(alice, __dirname + '/resources/image.svg'); + + assert.strictEqual(res.status, 200); + assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true); + assert.strictEqual(res.body.name, 'image.svg'); + assert.strictEqual(res.body.type, 'image/svg+xml'); + })); + }); + + describe('drive/files/update', () => { + it('名前を更新できる', async(async () => { + const file = await uploadFile(alice); + const newName = 'ã„ãĄã”ãƒ‘ã‚šã‚ŋ.png'; + + const res = await request('/drive/files/update', { + fileId: file.id, + name: newName + }, alice); + + assert.strictEqual(res.status, 200); + assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true); + assert.strictEqual(res.body.name, newName); + })); + + it('äģ–äēēãŽãƒ•ã‚Ąã‚¤ãƒĢは更新できãĒい', async(async () => { + const file = await uploadFile(bob); + + const res = await request('/drive/files/update', { + fileId: file.id, + name: 'ã„ãĄã”ãƒ‘ã‚šã‚ŋ.png' + }, alice); + + assert.strictEqual(res.status, 400); + })); + + it('čĻĒフりãƒĢダを更新できる', async(async () => { + const file = await uploadFile(alice); + const folder = (await request('/drive/folders/create', { + name: 'test' + }, alice)).body; + + const res = await request('/drive/files/update', { + fileId: file.id, + folderId: folder.id + }, alice); + + assert.strictEqual(res.status, 200); + assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true); + assert.strictEqual(res.body.folderId, folder.id); + })); + + it('čĻĒフりãƒĢダをį„Ąã—ãĢできる', async(async () => { + const file = await uploadFile(alice); + + const folder = (await request('/drive/folders/create', { + name: 'test' + }, alice)).body; + + await request('/drive/files/update', { + fileId: file.id, + folderId: folder.id + }, alice); + + const res = await request('/drive/files/update', { + fileId: file.id, + folderId: null + }, alice); + + assert.strictEqual(res.status, 200); + assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true); + assert.strictEqual(res.body.folderId, null); + })); + + it('äģ–äēēぎフりãƒĢダãĢはå…ĨれられãĒい', async(async () => { + const file = await uploadFile(alice); + const folder = (await request('/drive/folders/create', { + name: 'test' + }, bob)).body; + + const res = await request('/drive/files/update', { + fileId: file.id, + folderId: folder.id + }, alice); + + assert.strictEqual(res.status, 400); + })); + + it('存在しãĒいフりãƒĢダで怒られる', async(async () => { + const file = await uploadFile(alice); + + const res = await request('/drive/files/update', { + fileId: file.id, + folderId: '000000000000000000000000' + }, alice); + + assert.strictEqual(res.status, 400); + })); + + it('不æ­ŖãĒフりãƒĢダIDで怒られる', async(async () => { + const file = await uploadFile(alice); + + const res = await request('/drive/files/update', { + fileId: file.id, + folderId: 'foo' + }, alice); + + assert.strictEqual(res.status, 400); + })); + + it('ãƒ•ã‚Ąã‚¤ãƒĢが存在しãĒかãŖたら怒る', async(async () => { + const res = await request('/drive/files/update', { + fileId: '000000000000000000000000', + name: 'ã„ãĄã”ãƒ‘ã‚šã‚ŋ.png' + }, alice); + + assert.strictEqual(res.status, 400); + })); + + it('間違ãŖたIDで怒られる', async(async () => { + const res = await request('/drive/files/update', { + fileId: 'kyoppie', + name: 'ã„ãĄã”ãƒ‘ã‚šã‚ŋ.png' + }, alice); + + assert.strictEqual(res.status, 400); + })); + }); + + describe('drive/folders/create', () => { + it('フりãƒĢダをäŊœæˆã§ãã‚‹', async(async () => { + const res = await request('/drive/folders/create', { + name: 'test' + }, alice); + + assert.strictEqual(res.status, 200); + assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true); + assert.strictEqual(res.body.name, 'test'); + })); + }); + + describe('drive/folders/update', () => { + it('名前を更新できる', async(async () => { + const folder = (await request('/drive/folders/create', { + name: 'test' + }, alice)).body; + + const res = await request('/drive/folders/update', { + folderId: folder.id, + name: 'new name' + }, alice); + + assert.strictEqual(res.status, 200); + assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true); + assert.strictEqual(res.body.name, 'new name'); + })); + + it('äģ–äēēぎフりãƒĢダを更新できãĒい', async(async () => { + const folder = (await request('/drive/folders/create', { + name: 'test' + }, bob)).body; + + const res = await request('/drive/folders/update', { + folderId: folder.id, + name: 'new name' + }, alice); + + assert.strictEqual(res.status, 400); + })); + + it('čĻĒフりãƒĢダを更新できる', async(async () => { + const folder = (await request('/drive/folders/create', { + name: 'test' + }, alice)).body; + const parentFolder = (await request('/drive/folders/create', { + name: 'parent' + }, alice)).body; + + const res = await request('/drive/folders/update', { + folderId: folder.id, + parentId: parentFolder.id + }, alice); + + assert.strictEqual(res.status, 200); + assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true); + assert.strictEqual(res.body.parentId, parentFolder.id); + })); + + it('čĻĒフりãƒĢダをį„Ąã—ãĢ更新できる', async(async () => { + const folder = (await request('/drive/folders/create', { + name: 'test' + }, alice)).body; + const parentFolder = (await request('/drive/folders/create', { + name: 'parent' + }, alice)).body; + await request('/drive/folders/update', { + folderId: folder.id, + parentId: parentFolder.id + }, alice); + + const res = await request('/drive/folders/update', { + folderId: folder.id, + parentId: null + }, alice); + + assert.strictEqual(res.status, 200); + assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true); + assert.strictEqual(res.body.parentId, null); + })); + + it('äģ–äēēぎフりãƒĢダをčĻĒフりãƒĢダãĢč¨­åŽšã§ããĒい', async(async () => { + const folder = (await request('/drive/folders/create', { + name: 'test' + }, alice)).body; + const parentFolder = (await request('/drive/folders/create', { + name: 'parent' + }, bob)).body; + + const res = await request('/drive/folders/update', { + folderId: folder.id, + parentId: parentFolder.id + }, alice); + + assert.strictEqual(res.status, 400); + })); + + it('フりãƒĢダがåžĒį’°ã™ã‚‹ã‚ˆã†ãĒ構造ãĢできãĒい', async(async () => { + const folder = (await request('/drive/folders/create', { + name: 'test' + }, alice)).body; + const parentFolder = (await request('/drive/folders/create', { + name: 'parent' + }, alice)).body; + await request('/drive/folders/update', { + folderId: parentFolder.id, + parentId: folder.id + }, alice); + + const res = await request('/drive/folders/update', { + folderId: folder.id, + parentId: parentFolder.id + }, alice); + + assert.strictEqual(res.status, 400); + })); + + it('フりãƒĢダがåžĒį’°ã™ã‚‹ã‚ˆã†ãĒ構造ãĢできãĒい(再帰įš„)', async(async () => { + const folderA = (await request('/drive/folders/create', { + name: 'test' + }, alice)).body; + const folderB = (await request('/drive/folders/create', { + name: 'test' + }, alice)).body; + const folderC = (await request('/drive/folders/create', { + name: 'test' + }, alice)).body; + await request('/drive/folders/update', { + folderId: folderB.id, + parentId: folderA.id + }, alice); + await request('/drive/folders/update', { + folderId: folderC.id, + parentId: folderB.id + }, alice); + + const res = await request('/drive/folders/update', { + folderId: folderA.id, + parentId: folderC.id + }, alice); + + assert.strictEqual(res.status, 400); + })); + + it('フりãƒĢダがåžĒį’°ã™ã‚‹ã‚ˆã†ãĒ構造ãĢできãĒい(č‡ĒčēĢ)', async(async () => { + const folderA = (await request('/drive/folders/create', { + name: 'test' + }, alice)).body; + + const res = await request('/drive/folders/update', { + folderId: folderA.id, + parentId: folderA.id + }, alice); + + assert.strictEqual(res.status, 400); + })); + + it('存在しãĒいčĻĒフりãƒĢãƒ€ã‚’č¨­åŽšã§ããĒい', async(async () => { + const folder = (await request('/drive/folders/create', { + name: 'test' + }, alice)).body; + + const res = await request('/drive/folders/update', { + folderId: folder.id, + parentId: '000000000000000000000000' + }, alice); + + assert.strictEqual(res.status, 400); + })); + + it('不æ­ŖãĒčĻĒフりãƒĢダIDで怒られる', async(async () => { + const folder = (await request('/drive/folders/create', { + name: 'test' + }, alice)).body; + + const res = await request('/drive/folders/update', { + folderId: folder.id, + parentId: 'foo' + }, alice); + + assert.strictEqual(res.status, 400); + })); + + it('存在しãĒいフりãƒĢダを更新できãĒい', async(async () => { + const res = await request('/drive/folders/update', { + folderId: '000000000000000000000000' + }, alice); + + assert.strictEqual(res.status, 400); + })); + + it('不æ­ŖãĒフりãƒĢダIDで怒られる', async(async () => { + const res = await request('/drive/folders/update', { + folderId: 'foo' + }, alice); + + assert.strictEqual(res.status, 400); + })); + }); + + describe('messaging/messages/create', () => { + it('ãƒĄãƒƒã‚ģãƒŧジを送äŋĄã§ãã‚‹', async(async () => { + const res = await request('/messaging/messages/create', { + userId: bob.id, + text: 'test' + }, alice); + + assert.strictEqual(res.status, 200); + assert.strictEqual(typeof res.body === 'object' && !Array.isArray(res.body), true); + assert.strictEqual(res.body.text, 'test'); + })); + + it('č‡Ē分č‡ĒčēĢãĢã¯ãƒĄãƒƒã‚ģãƒŧジを送äŋĄã§ããĒい', async(async () => { + const res = await request('/messaging/messages/create', { + userId: alice.id, + text: 'Yo' + }, alice); + + assert.strictEqual(res.status, 400); + })); + + it('存在しãĒいãƒĻãƒŧã‚ļãƒŧãĢã¯ãƒĄãƒƒã‚ģãƒŧジを送äŋĄã§ããĒい', async(async () => { + const res = await request('/messaging/messages/create', { + userId: '000000000000000000000000', + text: 'test' + }, alice); + + assert.strictEqual(res.status, 400); + })); + + it('不æ­ŖãĒãƒĻãƒŧã‚ļãƒŧIDで怒られる', async(async () => { + const res = await request('/messaging/messages/create', { + userId: 'foo', + text: 'test' + }, alice); + + assert.strictEqual(res.status, 400); + })); + + it('テキ゚トがį„ĄããĻ怒られる', async(async () => { + const res = await request('/messaging/messages/create', { + userId: bob.id + }, alice); + + assert.strictEqual(res.status, 400); + })); + + it('文字数ã‚Ēãƒŧバãƒŧで怒られる', async(async () => { + const res = await request('/messaging/messages/create', { + userId: bob.id, + text: '!'.repeat(1001) + }, alice); + + assert.strictEqual(res.status, 400); + })); + }); + + describe('notes/replies', () => { + it('č‡Ē分ãĢ閲čĻ§æ¨Šé™ãŽãĒい投į¨ŋはåĢぞれãĒい', async(async () => { + const alicePost = await post(alice, { + text: 'foo' + }); + + await post(bob, { + replyId: alicePost.id, + text: 'bar', + visibility: 'specified', + visibleUserIds: [alice.id] + }); + + const res = await request('/notes/replies', { + noteId: alicePost.id + }, carol); + + assert.strictEqual(res.status, 200); + assert.strictEqual(Array.isArray(res.body), true); + assert.strictEqual(res.body.length, 0); + })); + }); + + describe('notes/timeline', () => { + it('フりロワãƒŧ限厚投į¨ŋがåĢぞれる', async(async () => { + await request('/following/create', { + userId: alice.id + }, bob); + + const alicePost = await post(alice, { + text: 'foo', + visibility: 'followers' + }); + + const res = await request('/notes/timeline', {}, bob); + + assert.strictEqual(res.status, 200); + assert.strictEqual(Array.isArray(res.body), true); + assert.strictEqual(res.body.length, 1); + assert.strictEqual(res.body[0].id, alicePost.id); + })); + }); +}); +*/ diff --git a/packages/backend/tsconfig.json b/packages/backend/tsconfig.json index 3311e117de..f3252b2860 100644 --- a/packages/backend/tsconfig.json +++ b/packages/backend/tsconfig.json @@ -30,7 +30,7 @@ "outDir": "./built", "typeRoots": [ "./node_modules/@types", - "./@types" + "./src/@types" ], "lib": [ "esnext" diff --git a/packages/backend/yarn.lock b/packages/backend/yarn.lock index 99e2e2306e..c1932a9053 100644 --- a/packages/backend/yarn.lock +++ b/packages/backend/yarn.lock @@ -82,14 +82,14 @@ pump "^3.0.0" secure-json-parse "^2.1.0" -"@eslint/eslintrc@^1.0.5": - version "1.0.5" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.0.5.tgz#33f1b838dbf1f923bfa517e008362b78ddbbf318" - integrity sha512-BLxsnmK3KyPunz5wmCCpqy0YelEoxxGmH73Is+Z74oOTMtExcjkr3dDR6quwrjh1YspA8DH9gnX1o069KiS9AQ== +"@eslint/eslintrc@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.1.0.tgz#583d12dbec5d4f22f333f9669f7d0b7c7815b4d3" + integrity sha512-C1DfL7XX4nPqGd6jcP01W9pVM1HYCuUkFk1432D7F0v3JSlUIeOYn9oCoi3eoLZ+iwBSb29BMFxxny0YrrEZqg== dependencies: ajv "^6.12.4" debug "^4.3.2" - espree "^9.2.0" + espree "^9.3.1" globals "^13.9.0" ignore "^4.0.6" import-fresh "^3.2.1" @@ -216,10 +216,10 @@ require-from-string "^2.0.2" uri-js "^4.2.2" -"@redocly/openapi-core@1.0.0-beta.79": - version "1.0.0-beta.79" - resolved "https://registry.yarnpkg.com/@redocly/openapi-core/-/openapi-core-1.0.0-beta.79.tgz#7512b3507ab99dc78226f9069669c5302abb0969" - integrity sha512-do79vGt3iiHsaVG9LKY8dH+d1E7TLHr+3T+CQ1lqagtWVjYOxqGaoxAT8tRD7R1W0z8BmS4e2poNON6c1sxP5g== +"@redocly/openapi-core@1.0.0-beta.82": + version "1.0.0-beta.82" + resolved "https://registry.yarnpkg.com/@redocly/openapi-core/-/openapi-core-1.0.0-beta.82.tgz#5f232e9c8f82499c2d96d9268b22423c8f859fe2" + integrity sha512-bJ0WclpgkFv4aa5QWU83ARCv3VQJy9U94reb1chOTg9s2bsTHbYuUwRv6G370a7lFXX86AyB3dT5IzS6GytLlA== dependencies: "@redocly/ajv" "^8.6.4" "@types/node" "^14.11.8" @@ -238,9 +238,9 @@ integrity sha512-JiX9vxoKMmu8Y3Zr2RVathBL1Cdu4Nt4MuNWemt1Nc06A0RAin9c5FArkhGsyMBWfCu4zj+9b+GxtjAnE4qqLQ== "@sindresorhus/is@^4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-4.0.0.tgz#2ff674e9611b45b528896d820d3d7a812de2f0e4" - integrity sha512-FyD2meJpDPjyNQejSjvnhpgI/azsQkA4lGbuu5BQZfjvJ9cbRZXzeWL2HceCekW4lixO9JPesIIQkSoLjeJHNQ== + version "4.4.0" + resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-4.4.0.tgz#e277e5bdbdf7cb1e20d320f02f5e2ed113cd3185" + integrity sha512-QppPM/8l3Mawvh4rn9CNEYIU9bxpXUCRMaX9yUpvBk1nMKusLKpfXGDEKExKaPhLzcn3lzil7pR6rnJ11HgeRQ== "@sinonjs/commons@^1.7.0": version "1.7.2" @@ -249,10 +249,10 @@ dependencies: type-detect "4.0.8" -"@sinonjs/fake-timers@7.1.2": - version "7.1.2" - resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-7.1.2.tgz#2524eae70c4910edccf99b2f4e6efc5894aff7b5" - integrity sha512-iQADsW4LBMISqZ6Ci1dupJL9pprqwcVFTcOsEmQOEhW+KLCVn/Y4Jrvg2k19fIHCp+iFprriYPTdRcQR8NbUPg== +"@sinonjs/fake-timers@9.1.0": + version "9.1.0" + resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-9.1.0.tgz#8c92c56f195e0bed4c893ba59c8e3d55831ca0df" + integrity sha512-M8vapsv9qQupMdzrVzkn5rb9jG7aUTEPAZdMtME2PuBaefksFZVE2C1g4LBRTkF/k3nRDNbDc5tp5NFC1PEYxA== dependencies: "@sinonjs/commons" "^1.7.0" @@ -289,6 +289,11 @@ resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== +"@tootallnate/once@2": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf" + integrity sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A== + "@tsconfig/node10@^1.0.7": version "1.0.7" resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.7.tgz#1eb1de36c73478a2479cc661ef5af1c16d86d606" @@ -316,11 +321,6 @@ dependencies: "@types/node" "*" -"@types/anymatch@*": - version "1.3.1" - resolved "https://registry.yarnpkg.com/@types/anymatch/-/anymatch-1.3.1.tgz#336badc1beecb9dacc38bea2cf32adf627a8421a" - integrity sha512-/+CRPXpBDpo2RK9C68N3b2cOvO0Cf5B9aPijHsoDQTHivnGSObdOF2BRQOYjojWTDy6nQvMjmqRXIxH55VjxxA== - "@types/bcryptjs@2.4.2": version "2.4.2" resolved "https://registry.yarnpkg.com/@types/bcryptjs/-/bcryptjs-2.4.2.tgz#e3530eac9dd136bfdfb0e43df2c4c5ce1f77dfae" @@ -334,10 +334,10 @@ "@types/connect" "*" "@types/node" "*" -"@types/bull@3.15.7": - version "3.15.7" - resolved "https://registry.yarnpkg.com/@types/bull/-/bull-3.15.7.tgz#a9d7fb332cc02dc021d0eb234b9604b356e9e6de" - integrity sha512-7NC7XN5NoS0A+leJ/dR69ZfKaegOlCZaii/xGgKnCyh1UYisRncibImb7VMwrc3OdJcbDJt6+4om70TeNl3J7g== +"@types/bull@3.15.8": + version "3.15.8" + resolved "https://registry.yarnpkg.com/@types/bull/-/bull-3.15.8.tgz#ae2139f94490d740b37c8da5d828ce75dd82ce7c" + integrity sha512-8DbSPMSsZH5PWPnGEkAZLYgJEH4ghHJNKF7LB6Wr5R0/v6g+Vs+JoaA7kcvLtHE936xg2WpFPkaoaJgExOmKDw== dependencies: "@types/ioredis" "*" "@types/redis" "^2.8.0" @@ -386,11 +386,6 @@ "@types/keygrip" "*" "@types/node" "*" -"@types/dateformat@3.0.1": - version "3.0.1" - resolved "https://registry.yarnpkg.com/@types/dateformat/-/dateformat-3.0.1.tgz#98d747a2e5e9a56070c6bf14e27bff56204e34cc" - integrity sha512-KlPPdikagvL6ELjWsljbyDIPzNCeliYkqRpI+zea99vBBbCIA5JNshZAwQKTON139c87y9qvTFVgkFd14rtS4g== - "@types/disposable-email-domains@^1.0.1": version "1.0.2" resolved "https://registry.yarnpkg.com/@types/disposable-email-domains/-/disposable-email-domains-1.0.2.tgz#0280f6b38fa7f14e54b056a434135ecd254483b1" @@ -401,27 +396,6 @@ resolved "https://registry.yarnpkg.com/@types/escape-regexp/-/escape-regexp-0.0.1.tgz#f1a977ccdf2ef059e9862bd3af5e92cbbe723e0e" integrity sha512-ogj/ZTIdeFkiuxDwawYuZSIgC6suFGgBeZPr6Xs5lHEcvIXTjXGtH+/n8f1XhZhespaUwJ5LIGRICPji972FLw== -"@types/eslint-scope@^3.7.0": - version "3.7.0" - resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.0.tgz#4792816e31119ebd506902a482caec4951fabd86" - integrity sha512-O/ql2+rrCUe2W2rs7wMR+GqPRcgB6UiqN5RhrR5xruFlY7l9YLMn0ZkDzjoHLeiFkR8MCQZVudUuuvQ2BLC9Qw== - dependencies: - "@types/eslint" "*" - "@types/estree" "*" - -"@types/eslint@*": - version "7.2.0" - resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-7.2.0.tgz#eb5c5b575237334df24c53195e37b53d66478d7b" - integrity sha512-LpUXkr7fnmPXWGxB0ZuLEzNeTURuHPavkC5zuU4sg62/TgL5ZEjamr5Y8b6AftwHtx2bPJasI+CL0TT2JwQ7aA== - dependencies: - "@types/estree" "*" - "@types/json-schema" "*" - -"@types/estree@*", "@types/estree@^0.0.46": - version "0.0.46" - resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.46.tgz#0fb6bfbbeabd7a30880504993369c4bf1deab1fe" - integrity sha512-laIjwTQaD+5DukBZaygQ79K1Z0jb1bPEMRrkXSLjtCcZm+abyp5YbrqpSLzD42FwWW6gK/aS4NYpJ804nG2brg== - "@types/express-serve-static-core@*": version "4.17.5" resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.5.tgz#a00ac7dadd746ae82477443e4d480a6a93ea083c" @@ -496,11 +470,6 @@ "@types/parse5" "*" "@types/tough-cookie" "*" -"@types/json-schema@*": - version "7.0.5" - resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.5.tgz#dcce4430e64b443ba8945f0290fb564ad5bac6dd" - integrity sha512-7+2BITlgjgDhH0vvwZU/HZJVyk+2XUlvxXe8dFMedNX/aMkaOq++rMAFXc0tM7ij15QaWlbdQASBR9dihi+bDQ== - "@types/json-schema@^7.0.6": version "7.0.6" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.6.tgz#f4c7ec43e81b319a9815115031709f26987891f0" @@ -648,10 +617,10 @@ resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA== -"@types/mocha@8.2.3": - version "8.2.3" - resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-8.2.3.tgz#bbeb55fbc73f28ea6de601fbfa4613f58d785323" - integrity sha512-ekGvFhFgrc2zYQoX4JeZPmVzZxw6Dtllga7iGHzfbYIYkAMUx/sAFP2GdFpLff+vdHXu5fl7WX9AT+TtqYcsyw== +"@types/mocha@9.1.0": + version "9.1.0" + resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-9.1.0.tgz#baf17ab2cca3fcce2d322ebc30454bff487efad5" + integrity sha512-QCWHkbMv4Y5U9oW10Uxbr45qMMSzl4OzijsozynUAgx3kEHUdXB00udx2dWDQ7f2TU2a2uuiFaRZjCe3unPpeg== "@types/node-fetch@3.0.3": version "3.0.3" @@ -665,10 +634,10 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-16.6.2.tgz#331b7b9f8621c638284787c5559423822fdffc50" integrity sha512-LSw8TZt12ZudbpHc6EkIyDM3nHVWKYrAvGy6EAJfNfjusbwnThqjqxUKKRwuV3iWYeW/LYMzNgaq3MaLffQ2xA== -"@types/node@17.0.10": - version "17.0.10" - resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.10.tgz#616f16e9d3a2a3d618136b1be244315d95bd7cab" - integrity sha512-S/3xB4KzyFxYGCppyDt68yzBU9ysL88lSdIah4D6cptdcltc4NCPCAMc0+PCpg/lLIyC7IPvj2Z52OJWeIUkog== +"@types/node@17.0.18": + version "17.0.18" + resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.18.tgz#3b4fed5cfb58010e3a2be4b6e74615e4847f1074" + integrity sha512-eKj4f/BsN/qcculZiRSujogjvp5O/k4lOW5m35NopjZM/QwLOR075a8pJW5hD+Rtdm2DaCVPENS6KtSQnUD6BA== "@types/node@^14.11.8": version "14.17.9" @@ -770,13 +739,6 @@ resolved "https://registry.yarnpkg.com/@types/rename/-/rename-1.0.4.tgz#30c6f0306042591a560361ea02639e89647dd173" integrity sha512-eV81+6bVv2mdCBahkMefjEUwAjKDAP3AuyhqWCWRxcRaeVdUeHUBaoq2zSz+5HNHF2jzTajMcfLvJsy4K3cbwA== -"@types/request-stats@3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@types/request-stats/-/request-stats-3.0.0.tgz#d3909a9f778b8ae0b42fb8c1ed20cb936ed95f99" - integrity sha512-POsDF7nETH8up49iBNvbZuO0pEk9F+TG0rXCkvjxCClcOS99xfF+mKmJteYlwKYpuRKkixzysKlL8rwN1hU2lw== - dependencies: - "@types/node" "*" - "@types/responselike@*", "@types/responselike@^1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@types/responselike/-/responselike-1.0.0.tgz#251f4fe7d154d2bad125abe1b429b23afd262e29" @@ -791,10 +753,10 @@ dependencies: htmlparser2 "^6.0.0" -"@types/seedrandom@2.4.28": - version "2.4.28" - resolved "https://registry.yarnpkg.com/@types/seedrandom/-/seedrandom-2.4.28.tgz#9ce8fa048c1e8c85cb71d7fe4d704e000226036f" - integrity sha512-SMA+fUwULwK7sd/ZJicUztiPs8F1yCPwF3O23Z9uQ32ME5Ha0NmDK9+QTsYE4O2tHXChzXomSWWeIhCnoN1LqA== +"@types/seedrandom@3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@types/seedrandom/-/seedrandom-3.0.1.tgz#1254750a4fec4aff2ebec088ccd0bb02e91fedb4" + integrity sha512-giB9gzDeiCeloIXDgzFBCgjj1k4WxcDrZtGl6h1IqmUPlxF+Nx8Ve+96QCyDZ/HseB/uvDsKbpib9hU5cU53pw== "@types/serve-static@*": version "1.13.3" @@ -811,15 +773,10 @@ dependencies: "@types/node" "*" -"@types/sinonjs__fake-timers@6.0.4": - version "6.0.4" - resolved "https://registry.yarnpkg.com/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-6.0.4.tgz#0ecc1b9259b76598ef01942f547904ce61a6a77d" - integrity sha512-IFQTJARgMUBF+xVd2b+hIgXWrZEjND3vJtRCvIelcFB5SIXfjV4bOHbHJ0eXKh+0COrBRc8MqteKAz/j88rE0A== - -"@types/source-list-map@*": - version "0.1.2" - resolved "https://registry.yarnpkg.com/@types/source-list-map/-/source-list-map-0.1.2.tgz#0078836063ffaf17412349bba364087e0ac02ec9" - integrity sha512-K5K+yml8LTo9bWJI/rECfIPrGgxdpeNbj+d53lwN4QjW1MCwlkhUms+gtdzigTeUyBr09+u8BwOIY3MXvHdcsA== +"@types/sinonjs__fake-timers@8.1.1": + version "8.1.1" + resolved "https://registry.yarnpkg.com/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.1.tgz#b49c2c70150141a15e0fa7e79cf1f92a72934ce3" + integrity sha512-0kSuKjAS0TrGLJ0M/+8MaFkGsQhZpB6pxOmvS3K8FYI72K//YmdfoW9X2qPsAKh1mkwxGD5zib9s1FIFed6E8g== "@types/speakeasy@2.0.7": version "2.0.7" @@ -828,11 +785,6 @@ dependencies: "@types/node" "*" -"@types/tapable@^1": - version "1.0.7" - resolved "https://registry.yarnpkg.com/@types/tapable/-/tapable-1.0.7.tgz#545158342f949e8fd3bfd813224971ecddc3fac4" - integrity sha512-0VBprVqfgFD7Ehb2vd8Lh9TG3jP98gvr8rgehQqzztZNI7o8zS8Ad4jyZneKELphpuE212D8J70LnSNQSyO6bQ== - "@types/throttle-debounce@2.1.0": version "2.1.0" resolved "https://registry.yarnpkg.com/@types/throttle-debounce/-/throttle-debounce-2.1.0.tgz#1c3df624bfc4b62f992d3012b84c56d41eab3776" @@ -853,13 +805,6 @@ resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.0.tgz#fef1904e4668b6e5ecee60c52cc6a078ffa6697d" integrity sha512-I99sngh224D0M7XgW1s120zxCt3VYQ3IQsuw3P3jbq5GG4yc79+ZjyKznyOGIQrflfylLgcfekeZW/vk0yng6A== -"@types/uglify-js@*": - version "3.9.0" - resolved "https://registry.yarnpkg.com/@types/uglify-js/-/uglify-js-3.9.0.tgz#4490a140ca82aa855ad68093829e7fd6ae94ea87" - integrity sha512-3ZcoyPYHVOCcLpnfZwD47KFLr8W/mpUcgjpf1M4Q78TMJIw7KMAHSjiCLJp1z3ZrBR9pTLbe191O0TldFK5zcw== - dependencies: - source-map "^0.6.1" - "@types/uuid@8.3.4": version "8.3.4" resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.4.tgz#bd86a43617df0594787d38b735f55c805becf1bc" @@ -872,48 +817,10 @@ dependencies: "@types/node" "*" -"@types/webpack-sources@*": - version "0.1.7" - resolved "https://registry.yarnpkg.com/@types/webpack-sources/-/webpack-sources-0.1.7.tgz#0a330a9456113410c74a5d64180af0cbca007141" - integrity sha512-XyaHrJILjK1VHVC4aVlKsdNN5KBTwufMb43cQs+flGxtPAf/1Qwl8+Q0tp5BwEGaI8D6XT1L+9bSWXckgkjTLw== - dependencies: - "@types/node" "*" - "@types/source-list-map" "*" - source-map "^0.6.1" - -"@types/webpack-stream@3.2.12": - version "3.2.12" - resolved "https://registry.yarnpkg.com/@types/webpack-stream/-/webpack-stream-3.2.12.tgz#cf13e64067a662a7acd8cd0524b3f64c86b0ecb6" - integrity sha512-znMUl4kKT0V0SwkUgRgwUNSAO7J5I/jdTCBNy3utkCsgMJ3IHp4FBTDwsQC+tfQ73TWeKIH05QNmbUYmeGThGw== - dependencies: - "@types/node" "*" - "@types/webpack" "^4" - -"@types/webpack@5.28.0": - version "5.28.0" - resolved "https://registry.yarnpkg.com/@types/webpack/-/webpack-5.28.0.tgz#78dde06212f038d77e54116cfe69e88ae9ed2c03" - integrity sha512-8cP0CzcxUiFuA9xGJkfeVpqmWTk9nx6CWwamRGCj95ph1SmlRRk9KlCZ6avhCbZd4L68LvYT6l1kpdEnQXrF8w== - dependencies: - "@types/node" "*" - tapable "^2.2.0" - webpack "^5" - -"@types/webpack@^4": - version "4.41.27" - resolved "https://registry.yarnpkg.com/@types/webpack/-/webpack-4.41.27.tgz#f47da488c8037e7f1b2dbf2714fbbacb61ec0ffc" - integrity sha512-wK/oi5gcHi72VMTbOaQ70VcDxSQ1uX8S2tukBK9ARuGXrYM/+u4ou73roc7trXDNmCxCoerE8zruQqX/wuHszA== - dependencies: - "@types/anymatch" "*" - "@types/node" "*" - "@types/tapable" "^1" - "@types/uglify-js" "*" - "@types/webpack-sources" "*" - source-map "^0.6.0" - -"@types/websocket@1.0.4": - version "1.0.4" - resolved "https://registry.yarnpkg.com/@types/websocket/-/websocket-1.0.4.tgz#1dc497280d8049a5450854dd698ee7e6ea9e60b8" - integrity sha512-qn1LkcFEKK8RPp459jkjzsfpbsx36BBt3oC3pITYtkoBw/aVX+EZFa5j3ThCRTNpLFvIMr5dSTD4RaMdilIOpA== +"@types/websocket@1.0.5": + version "1.0.5" + resolved "https://registry.yarnpkg.com/@types/websocket/-/websocket-1.0.5.tgz#3fb80ed8e07f88e51961211cd3682a3a4a81569c" + integrity sha512-NbsqiNX9CnEfC1Z0Vf4mE1SgAJ07JnRYcNex7AJ9zAVzmiGHmjKFEk7O4TJIsgv2B1sLEb6owKFZrACwdYngsQ== dependencies: "@types/node" "*" @@ -929,14 +836,14 @@ resolved "https://registry.yarnpkg.com/@types/zen-observable/-/zen-observable-0.8.2.tgz#808c9fa7e4517274ed555fa158f2de4b4f468e71" integrity sha512-HrCIVMLjE1MOozVoD86622S7aunluLb2PJdPfb3nYiEtohm8mIB/vyv0Fd37AdeMFrTUQXEunw78YloMA3Qilg== -"@typescript-eslint/eslint-plugin@5.10.0": - version "5.10.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.10.0.tgz#e90afea96dff8620892ad216b0e4ccdf8ee32d3a" - integrity sha512-XXVKnMsq2fuu9K2KsIxPUGqb6xAImz8MEChClbXmE3VbveFtBUU5bzM6IPVWqzyADIgdkS2Ws/6Xo7W2TeZWjQ== +"@typescript-eslint/eslint-plugin@5.12.0": + version "5.12.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.12.0.tgz#bb46dd7ce7015c0928b98af1e602118e97df6c70" + integrity sha512-fwCMkDimwHVeIOKeBHiZhRUfJXU8n6xW1FL9diDxAyGAFvKcH4csy0v7twivOQdQdA0KC8TDr7GGRd3L4Lv0rQ== dependencies: - "@typescript-eslint/scope-manager" "5.10.0" - "@typescript-eslint/type-utils" "5.10.0" - "@typescript-eslint/utils" "5.10.0" + "@typescript-eslint/scope-manager" "5.12.0" + "@typescript-eslint/type-utils" "5.12.0" + "@typescript-eslint/utils" "5.12.0" debug "^4.3.2" functional-red-black-tree "^1.0.1" ignore "^5.1.8" @@ -944,69 +851,69 @@ semver "^7.3.5" tsutils "^3.21.0" -"@typescript-eslint/parser@5.10.0": - version "5.10.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.10.0.tgz#8f59e036f5f1cffc178cacbd5ccdd02aeb96c91c" - integrity sha512-pJB2CCeHWtwOAeIxv8CHVGJhI5FNyJAIpx5Pt72YkK3QfEzt6qAlXZuyaBmyfOdM62qU0rbxJzNToPTVeJGrQw== +"@typescript-eslint/parser@5.12.0": + version "5.12.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.12.0.tgz#0ca669861813df99ce54916f66f524c625ed2434" + integrity sha512-MfSwg9JMBojMUoGjUmX+D2stoQj1CBYTCP0qnnVtu9A+YQXVKNtLjasYh+jozOcrb/wau8TCfWOkQTiOAruBog== dependencies: - "@typescript-eslint/scope-manager" "5.10.0" - "@typescript-eslint/types" "5.10.0" - "@typescript-eslint/typescript-estree" "5.10.0" + "@typescript-eslint/scope-manager" "5.12.0" + "@typescript-eslint/types" "5.12.0" + "@typescript-eslint/typescript-estree" "5.12.0" debug "^4.3.2" -"@typescript-eslint/scope-manager@5.10.0": - version "5.10.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.10.0.tgz#bb5d872e8b9e36203908595507fbc4d3105329cb" - integrity sha512-tgNgUgb4MhqK6DoKn3RBhyZ9aJga7EQrw+2/OiDk5hKf3pTVZWyqBi7ukP+Z0iEEDMF5FDa64LqODzlfE4O/Dg== +"@typescript-eslint/scope-manager@5.12.0": + version "5.12.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.12.0.tgz#59619e6e5e2b1ce6cb3948b56014d3a24da83f5e" + integrity sha512-GAMobtIJI8FGf1sLlUWNUm2IOkIjvn7laFWyRx7CLrv6nLBI7su+B7lbStqVlK5NdLvHRFiJo2HhiDF7Ki01WQ== dependencies: - "@typescript-eslint/types" "5.10.0" - "@typescript-eslint/visitor-keys" "5.10.0" + "@typescript-eslint/types" "5.12.0" + "@typescript-eslint/visitor-keys" "5.12.0" -"@typescript-eslint/type-utils@5.10.0": - version "5.10.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.10.0.tgz#8524b9479c19c478347a7df216827e749e4a51e5" - integrity sha512-TzlyTmufJO5V886N+hTJBGIfnjQDQ32rJYxPaeiyWKdjsv2Ld5l8cbS7pxim4DeNs62fKzRSt8Q14Evs4JnZyQ== +"@typescript-eslint/type-utils@5.12.0": + version "5.12.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.12.0.tgz#aaf45765de71c6d9707c66ccff76ec2b9aa31bb6" + integrity sha512-9j9rli3zEBV+ae7rlbBOotJcI6zfc6SHFMdKI9M3Nc0sy458LJ79Os+TPWeBBL96J9/e36rdJOfCuyRSgFAA0Q== dependencies: - "@typescript-eslint/utils" "5.10.0" + "@typescript-eslint/utils" "5.12.0" debug "^4.3.2" tsutils "^3.21.0" -"@typescript-eslint/types@5.10.0": - version "5.10.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.10.0.tgz#beb3cb345076f5b088afe996d57bcd1dfddaa75c" - integrity sha512-wUljCgkqHsMZbw60IbOqT/puLfyqqD5PquGiBo1u1IS3PLxdi3RDGlyf032IJyh+eQoGhz9kzhtZa+VC4eWTlQ== +"@typescript-eslint/types@5.12.0": + version "5.12.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.12.0.tgz#5b4030a28222ee01e851836562c07769eecda0b8" + integrity sha512-JowqbwPf93nvf8fZn5XrPGFBdIK8+yx5UEGs2QFAYFI8IWYfrzz+6zqlurGr2ctShMaJxqwsqmra3WXWjH1nRQ== -"@typescript-eslint/typescript-estree@5.10.0": - version "5.10.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.10.0.tgz#4be24a3dea0f930bb1397c46187d0efdd955a224" - integrity sha512-x+7e5IqfwLwsxTdliHRtlIYkgdtYXzE0CkFeV6ytAqq431ZyxCFzNMNR5sr3WOlIG/ihVZr9K/y71VHTF/DUQA== +"@typescript-eslint/typescript-estree@5.12.0": + version "5.12.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.12.0.tgz#cabf545fd592722f0e2b4104711e63bf89525cd2" + integrity sha512-Dd9gVeOqt38QHR0BEA8oRaT65WYqPYbIc5tRFQPkfLquVEFPD1HAtbZT98TLBkEcCkvwDYOAvuSvAD9DnQhMfQ== dependencies: - "@typescript-eslint/types" "5.10.0" - "@typescript-eslint/visitor-keys" "5.10.0" + "@typescript-eslint/types" "5.12.0" + "@typescript-eslint/visitor-keys" "5.12.0" debug "^4.3.2" globby "^11.0.4" is-glob "^4.0.3" semver "^7.3.5" tsutils "^3.21.0" -"@typescript-eslint/utils@5.10.0": - version "5.10.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.10.0.tgz#c3d152a85da77c400e37281355561c72fb1b5a65" - integrity sha512-IGYwlt1CVcFoE2ueW4/ioEwybR60RAdGeiJX/iDAw0t5w0wK3S7QncDwpmsM70nKgGTuVchEWB8lwZwHqPAWRg== +"@typescript-eslint/utils@5.12.0": + version "5.12.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.12.0.tgz#92fd3193191621ab863add2f553a7b38b65646af" + integrity sha512-k4J2WovnMPGI4PzKgDtQdNrCnmBHpMUFy21qjX2CoPdoBcSBIMvVBr9P2YDP8jOqZOeK3ThOL6VO/sy6jtnvzw== dependencies: "@types/json-schema" "^7.0.9" - "@typescript-eslint/scope-manager" "5.10.0" - "@typescript-eslint/types" "5.10.0" - "@typescript-eslint/typescript-estree" "5.10.0" + "@typescript-eslint/scope-manager" "5.12.0" + "@typescript-eslint/types" "5.12.0" + "@typescript-eslint/typescript-estree" "5.12.0" eslint-scope "^5.1.1" eslint-utils "^3.0.0" -"@typescript-eslint/visitor-keys@5.10.0": - version "5.10.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.10.0.tgz#770215497ad67cd15a572b52089991d5dfe06281" - integrity sha512-GMxj0K1uyrFLPKASLmZzCuSddmjZVbVj3Ouy5QVuIGKZopxvOr24JsS7gruz6C3GExE01mublZ3mIBOaon9zuQ== +"@typescript-eslint/visitor-keys@5.12.0": + version "5.12.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.12.0.tgz#1ac9352ed140b07ba144ebf371b743fdf537ec16" + integrity sha512-cFwTlgnMV6TgezQynx2c/4/tx9Tufbuo9LPzmWqyRC3QC4qTGkAG1C6pBr0/4I10PAI/FlYunI3vJjIcu+ZHMg== dependencies: - "@typescript-eslint/types" "5.10.0" + "@typescript-eslint/types" "5.12.0" eslint-visitor-keys "^3.0.0" "@ungap/promise-all-settled@1.1.2": @@ -1014,137 +921,6 @@ resolved "https://registry.yarnpkg.com/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz#aa58042711d6e3275dd37dc597e5d31e8c290a44" integrity sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q== -"@webassemblyjs/ast@1.11.0": - version "1.11.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.0.tgz#a5aa679efdc9e51707a4207139da57920555961f" - integrity sha512-kX2W49LWsbthrmIRMbQZuQDhGtjyqXfEmmHyEi4XWnSZtPmxY0+3anPIzsnRb45VH/J55zlOfWvZuY47aJZTJg== - dependencies: - "@webassemblyjs/helper-numbers" "1.11.0" - "@webassemblyjs/helper-wasm-bytecode" "1.11.0" - -"@webassemblyjs/floating-point-hex-parser@1.11.0": - version "1.11.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.0.tgz#34d62052f453cd43101d72eab4966a022587947c" - integrity sha512-Q/aVYs/VnPDVYvsCBL/gSgwmfjeCb4LW8+TMrO3cSzJImgv8lxxEPM2JA5jMrivE7LSz3V+PFqtMbls3m1exDA== - -"@webassemblyjs/helper-api-error@1.11.0": - version "1.11.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.0.tgz#aaea8fb3b923f4aaa9b512ff541b013ffb68d2d4" - integrity sha512-baT/va95eXiXb2QflSx95QGT5ClzWpGaa8L7JnJbgzoYeaA27FCvuBXU758l+KXWRndEmUXjP0Q5fibhavIn8w== - -"@webassemblyjs/helper-buffer@1.11.0": - version "1.11.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.0.tgz#d026c25d175e388a7dbda9694e91e743cbe9b642" - integrity sha512-u9HPBEl4DS+vA8qLQdEQ6N/eJQ7gT7aNvMIo8AAWvAl/xMrcOSiI2M0MAnMCy3jIFke7bEee/JwdX1nUpCtdyA== - -"@webassemblyjs/helper-numbers@1.11.0": - version "1.11.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.0.tgz#7ab04172d54e312cc6ea4286d7d9fa27c88cd4f9" - integrity sha512-DhRQKelIj01s5IgdsOJMKLppI+4zpmcMQ3XboFPLwCpSNH6Hqo1ritgHgD0nqHeSYqofA6aBN/NmXuGjM1jEfQ== - dependencies: - "@webassemblyjs/floating-point-hex-parser" "1.11.0" - "@webassemblyjs/helper-api-error" "1.11.0" - "@xtuc/long" "4.2.2" - -"@webassemblyjs/helper-wasm-bytecode@1.11.0": - version "1.11.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.0.tgz#85fdcda4129902fe86f81abf7e7236953ec5a4e1" - integrity sha512-MbmhvxXExm542tWREgSFnOVo07fDpsBJg3sIl6fSp9xuu75eGz5lz31q7wTLffwL3Za7XNRCMZy210+tnsUSEA== - -"@webassemblyjs/helper-wasm-section@1.11.0": - version "1.11.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.0.tgz#9ce2cc89300262509c801b4af113d1ca25c1a75b" - integrity sha512-3Eb88hcbfY/FCukrg6i3EH8H2UsD7x8Vy47iVJrP967A9JGqgBVL9aH71SETPx1JrGsOUVLo0c7vMCN22ytJew== - dependencies: - "@webassemblyjs/ast" "1.11.0" - "@webassemblyjs/helper-buffer" "1.11.0" - "@webassemblyjs/helper-wasm-bytecode" "1.11.0" - "@webassemblyjs/wasm-gen" "1.11.0" - -"@webassemblyjs/ieee754@1.11.0": - version "1.11.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.11.0.tgz#46975d583f9828f5d094ac210e219441c4e6f5cf" - integrity sha512-KXzOqpcYQwAfeQ6WbF6HXo+0udBNmw0iXDmEK5sFlmQdmND+tr773Ti8/5T/M6Tl/413ArSJErATd8In3B+WBA== - dependencies: - "@xtuc/ieee754" "^1.2.0" - -"@webassemblyjs/leb128@1.11.0": - version "1.11.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.11.0.tgz#f7353de1df38aa201cba9fb88b43f41f75ff403b" - integrity sha512-aqbsHa1mSQAbeeNcl38un6qVY++hh8OpCOzxhixSYgbRfNWcxJNJQwe2rezK9XEcssJbbWIkblaJRwGMS9zp+g== - dependencies: - "@xtuc/long" "4.2.2" - -"@webassemblyjs/utf8@1.11.0": - version "1.11.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.11.0.tgz#86e48f959cf49e0e5091f069a709b862f5a2cadf" - integrity sha512-A/lclGxH6SpSLSyFowMzO/+aDEPU4hvEiooCMXQPcQFPPJaYcPQNKGOCLUySJsYJ4trbpr+Fs08n4jelkVTGVw== - -"@webassemblyjs/wasm-edit@1.11.0": - version "1.11.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.0.tgz#ee4a5c9f677046a210542ae63897094c2027cb78" - integrity sha512-JHQ0damXy0G6J9ucyKVXO2j08JVJ2ntkdJlq1UTiUrIgfGMmA7Ik5VdC/L8hBK46kVJgujkBIoMtT8yVr+yVOQ== - dependencies: - "@webassemblyjs/ast" "1.11.0" - "@webassemblyjs/helper-buffer" "1.11.0" - "@webassemblyjs/helper-wasm-bytecode" "1.11.0" - "@webassemblyjs/helper-wasm-section" "1.11.0" - "@webassemblyjs/wasm-gen" "1.11.0" - "@webassemblyjs/wasm-opt" "1.11.0" - "@webassemblyjs/wasm-parser" "1.11.0" - "@webassemblyjs/wast-printer" "1.11.0" - -"@webassemblyjs/wasm-gen@1.11.0": - version "1.11.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.0.tgz#3cdb35e70082d42a35166988dda64f24ceb97abe" - integrity sha512-BEUv1aj0WptCZ9kIS30th5ILASUnAPEvE3tVMTrItnZRT9tXCLW2LEXT8ezLw59rqPP9klh9LPmpU+WmRQmCPQ== - dependencies: - "@webassemblyjs/ast" "1.11.0" - "@webassemblyjs/helper-wasm-bytecode" "1.11.0" - "@webassemblyjs/ieee754" "1.11.0" - "@webassemblyjs/leb128" "1.11.0" - "@webassemblyjs/utf8" "1.11.0" - -"@webassemblyjs/wasm-opt@1.11.0": - version "1.11.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.0.tgz#1638ae188137f4bb031f568a413cd24d32f92978" - integrity sha512-tHUSP5F4ywyh3hZ0+fDQuWxKx3mJiPeFufg+9gwTpYp324mPCQgnuVKwzLTZVqj0duRDovnPaZqDwoyhIO8kYg== - dependencies: - "@webassemblyjs/ast" "1.11.0" - "@webassemblyjs/helper-buffer" "1.11.0" - "@webassemblyjs/wasm-gen" "1.11.0" - "@webassemblyjs/wasm-parser" "1.11.0" - -"@webassemblyjs/wasm-parser@1.11.0": - version "1.11.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.0.tgz#3e680b8830d5b13d1ec86cc42f38f3d4a7700754" - integrity sha512-6L285Sgu9gphrcpDXINvm0M9BskznnzJTE7gYkjDbxET28shDqp27wpruyx3C2S/dvEwiigBwLA1cz7lNUi0kw== - dependencies: - "@webassemblyjs/ast" "1.11.0" - "@webassemblyjs/helper-api-error" "1.11.0" - "@webassemblyjs/helper-wasm-bytecode" "1.11.0" - "@webassemblyjs/ieee754" "1.11.0" - "@webassemblyjs/leb128" "1.11.0" - "@webassemblyjs/utf8" "1.11.0" - -"@webassemblyjs/wast-printer@1.11.0": - version "1.11.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.11.0.tgz#680d1f6a5365d6d401974a8e949e05474e1fab7e" - integrity sha512-Fg5OX46pRdTgB7rKIUojkh9vXaVN6sGYCnEiJN1GYkb0RPwShZXp6KTDqmoMdQPKhcroOXh3fEzmkWmCYaKYhQ== - dependencies: - "@webassemblyjs/ast" "1.11.0" - "@xtuc/long" "4.2.2" - -"@xtuc/ieee754@^1.2.0": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790" - integrity sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA== - -"@xtuc/long@4.2.2": - version "4.2.2" - resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d" - integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== - abab@^2.0.3, abab@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.5.tgz#c0b678fb32d60fc1219c784d6a826fe385aeb79a" @@ -1198,22 +974,12 @@ acorn@^7.1.1: resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== -acorn@^8.0.4: - version "8.1.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.1.0.tgz#52311fd7037ae119cbb134309e901aa46295b3fe" - integrity sha512-LWCF/Wn0nfHOmJ9rzQApGnxnvgfROzGilS8936rqN/lfcYkY9MYZzdMqN+2NJ4SlTc+m5HiSa+kNfDtI64dwUA== - -acorn@^8.2.4: - version "8.2.4" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.2.4.tgz#caba24b08185c3b56e3168e97d15ed17f4d31fd0" - integrity sha512-Ibt84YwBDDA890eDiDCEqcbwvHlBvzzDkU2cGBBDDI1QWT12jTiXIOn2CIw5KK4i6N5Z2HUxwYjzriDyqaqqZg== - acorn@^8.4.1: version "8.4.1" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.4.1.tgz#56c36251fc7cabc7096adc18f05afe814321a28c" integrity sha512-asabaBSkEKosYKMITunzX177CXxQ4Q8BSSzMTKD+FefUhipQC70gfW5SiUDhYQ3vk8G+81HqQk7Fv9OXwwn9KA== -acorn@^8.7.0: +acorn@^8.5.0, acorn@^8.7.0: version "8.7.0" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.0.tgz#90951fde0f8f09df93549481e5fc141445b791cf" integrity sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ== @@ -1247,6 +1013,16 @@ ajv-keywords@^3.5.2: resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== +ajv@8.10.0: + version "8.10.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.10.0.tgz#e573f719bd3af069017e3b66538ab968d040e54d" + integrity sha512-bzqAEZOjkrUMl2afH8dknrq5KEk2SrwdBROR+vH1EKVQTqaUbJVPdc/gEdggTMM0Se+s+Ja4ju4TlNcStKl2Hw== + dependencies: + fast-deep-equal "^3.1.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + uri-js "^4.2.2" + ajv@^6.10.0, ajv@^6.12.4, ajv@^6.12.5: version "6.12.5" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.5.tgz#19b0e8bae8f476e5ba666300387775fb1a00a4da" @@ -1476,10 +1252,10 @@ autwh@0.1.0: dependencies: oauth "0.9.15" -aws-sdk@2.1061.0: - version "2.1061.0" - resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.1061.0.tgz#79c75e6856e5a59e0857d0d066a8ff5ff5e0d752" - integrity sha512-T29yV+EPo4Fis9hAArxAXS/u6utKnlBq3DEu85LTSIA8i6e6Xg7e9u7Rveo8DmrlVrf7EGCNThaeF9WERHnwLg== +aws-sdk@2.1067.0: + version "2.1067.0" + resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.1067.0.tgz#2e7f5a2d765fcf77a45f25fdd1f12a64942628a7" + integrity sha512-3Ys1k4cNQy4z37IpPjQ9c5ldkXMeZGbWoarKHynPPY3WCEj+Nw2u6zk484fA9/lTHNN3YesLuZ0OmEzGgjFEOw== dependencies: buffer "4.9.2" events "1.1.1" @@ -1584,10 +1360,10 @@ bluebird@~3.4.1: resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.4.7.tgz#f72d760be09b7f76d08ed8fae98b289a8d05fab3" integrity sha1-9y12C+Cbf3bQjtj66Ysomo0F+rM= -blurhash@1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/blurhash/-/blurhash-1.1.4.tgz#a7010ceb3019cd2c9809b17c910ebf6175d29244" - integrity sha512-MXIPz6zwYUKayju+Uidf83KhH0vodZfeRl6Ich8Gu+KGl0JgKiFq9LsfqV7cVU5fKD/AotmduZqvOfrGKOfTaA== +blurhash@1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/blurhash/-/blurhash-1.1.5.tgz#3034104cd5dce5a3e5caa871ae2f0f1f2d0ab566" + integrity sha512-a+LO3A2DfxTaTztsmkbLYmUzUeApi0LZuKalwbNmqAHR6HhJGMt1qSV/R3wc+w4DL28holjqO3Bg74aUGavGjg== bn.js@^4.0.0: version "4.11.8" @@ -1614,10 +1390,10 @@ braces@^3.0.1, braces@~3.0.2: dependencies: fill-range "^7.0.1" -broadcast-channel@4.9.0: - version "4.9.0" - resolved "https://registry.yarnpkg.com/broadcast-channel/-/broadcast-channel-4.9.0.tgz#8af337d4ea19aeb6b819ec2eb3dda942b28c724c" - integrity sha512-xWzFb3wrOZGJF2kOSs2D3KvHXdLDMVb+WypEIoNvwblcHgUBydVy65pDJ9RS4WN9Kyvs0UVQuCCzfKme0G6Qjw== +broadcast-channel@4.10.0: + version "4.10.0" + resolved "https://registry.yarnpkg.com/broadcast-channel/-/broadcast-channel-4.10.0.tgz#d19fb902df227df40b1b580351713d30c302d198" + integrity sha512-hOUh312XyHk6JTVyX9cyXaH1UYs+2gHVtnW16oQAu9FL7ALcXGXc/YoJWqlkV8vUn14URQPMmRi4A9q4UrwVEQ== dependencies: "@babel/runtime" "^7.16.0" detect-node "^2.1.0" @@ -1638,17 +1414,6 @@ browser-stdout@1.3.1: resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== -browserslist@^4.14.5: - version "4.16.3" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.16.3.tgz#340aa46940d7db878748567c5dea24a48ddf3717" - integrity sha512-vIyhWmIkULaq04Gt93txdh+j02yX/JzlyhLYbV3YQCn/zvES3JnY7TifHHvvr1w5hTDluNKMkV05cs4vy8Q7sw== - dependencies: - caniuse-lite "^1.0.30001181" - colorette "^1.2.1" - electron-to-chromium "^1.3.649" - escalade "^3.1.1" - node-releases "^1.1.70" - buffer-crc32@^0.2.1, buffer-crc32@^0.2.13: version "0.2.13" resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" @@ -1711,16 +1476,17 @@ bufferutil@^4.0.1: dependencies: node-gyp-build "~3.7.0" -bull@4.2.1: - version "4.2.1" - resolved "https://registry.yarnpkg.com/bull/-/bull-4.2.1.tgz#c5a7e1496c7903274ce90192e4e5cb18f6c866c0" - integrity sha512-YkCQZMOub++siHw3SbYYXZ5xGEn6Tt3BPoCVq/irPNCxUqUYzta8yDlXyyAsfMKMVj0M7PcnynUabfMf9PFpOA== +bull@4.5.5: + version "4.5.5" + resolved "https://registry.yarnpkg.com/bull/-/bull-4.5.5.tgz#6521e514aaff77d3d40f780dd0214e1738c16b32" + integrity sha512-4GX9zoDwkSgdITGWUWGb1kwddgffOyeTLxeBzANbXhBGFb5/HypwsMrInyWQ4GmpZG/oM3oQpUqr+KGX8s7K4Q== dependencies: - cron-parser "^2.13.0" + cron-parser "^4.2.1" debuglog "^1.0.0" get-port "^5.1.1" ioredis "^4.27.0" lodash "^4.17.21" + msgpackr "^1.5.2" p-timeout "^3.2.0" semver "^7.3.2" uuid "^8.3.0" @@ -1821,11 +1587,6 @@ camelcase@^6.0.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.2.0.tgz#924af881c9d525ac9d87f40d964e5cea982a1809" integrity sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg== -caniuse-lite@^1.0.30001181: - version "1.0.30001191" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001191.tgz#bacb432b6701f690c8c5f7c680166b9a9f0843d9" - integrity sha512-xJJqzyd+7GCJXkcoBiQ1GuxEiOBCLQ0aVW9HMekifZsAVGdj5eJ4mFB9fEhSHipq9IOk/QXFJUiIr9lZT+EsGw== - canonicalize@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/canonicalize/-/canonicalize-1.0.1.tgz#657b4f3fa38a6ecb97a9e5b7b26d7a19cc6e0da9" @@ -1920,7 +1681,7 @@ cheerio@0.22.0: lodash.reject "^4.4.0" lodash.some "^4.4.0" -chokidar@3.5.1, chokidar@^3.3.1, chokidar@^3.5.2: +chokidar@3.5.3, chokidar@^3.3.1, chokidar@^3.5.2: version "3.3.1" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.3.1.tgz#c84e5b3d18d9a4d77558fef466b1bf16bbeb3450" integrity sha512-4QYCEWOcK3OJrxwvyyAOxFuhpvOVCYkr33LPfFNBjAD/w3sEzWsp2BUOkI4l9bHvWioAd0rc6NlHUOEaWkTeqg== @@ -1945,13 +1706,6 @@ chownr@^2.0.0: resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== -chrome-trace-event@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz#234090ee97c7d4ad1a2c4beae27505deffc608a4" - integrity sha512-9e/zx1jw7B4CO+c/RXoCsfg/x1AfUBioy4owYH0bJprEYAx5hRFLRhWBqHAG57D0ZM4H7vxbP7bPe0VwhQRYDQ== - dependencies: - tslib "^1.9.0" - clean-stack@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" @@ -2053,10 +1807,10 @@ color-name@^1.0.0, color-name@~1.1.4: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== -color-string@^1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.6.0.tgz#c3915f61fe267672cb7e1e064c9d692219f6c312" - integrity sha512-c/hGS+kRWJutUBEngKKmk4iH3sD59MBkoxVapS/0wgpCz2u7XsNloxknyvBhzwEs1IbV36D9PwqLPJ2DTu3vMA== +color-string@^1.9.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.9.0.tgz#63b6ebd1bec11999d1df3a79a7569451ac2be8aa" + integrity sha512-9Mrz2AQLefkH1UvASKj6v6hj/7eWgjnT/cVsR8CumieLoT+g900exWeNogqtweI8dxloXN9BDQTYro1oWu/5CQ== dependencies: color-name "^1.0.0" simple-swizzle "^0.2.2" @@ -2066,15 +1820,15 @@ color-support@^1.1.2: resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== -color@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/color/-/color-4.0.1.tgz#21df44cd10245a91b1ccf5ba031609b0e10e7d67" - integrity sha512-rpZjOKN5O7naJxkH2Rx1sZzzBgaiWECc6BYXjeCE6kF0kcASJYbUq02u7JqIHwCb/j3NhV+QhRL2683aICeGZA== +color@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/color/-/color-4.2.0.tgz#0c782459a3e98838ea01e4bc0fb43310ca35af78" + integrity sha512-hHTcrbvEnGjC7WBMk6ibQWFVDgEFTVmjrz2Q5HlU6ltwxv0JJN2Z8I7uRbWeQLF04dikxs8zgyZkazRJvSMtyQ== dependencies: color-convert "^2.0.1" - color-string "^1.6.0" + color-string "^1.9.0" -colorette@^1.2.0, colorette@^1.2.1: +colorette@^1.2.0: version "1.2.2" resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.2.tgz#cbcc79d5e99caea2dbf10eb3a26fd8b3e6acfa94" integrity sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w== @@ -2086,7 +1840,7 @@ combined-stream@^1.0.8: dependencies: delayed-stream "~1.0.0" -commander@^2.19.0, commander@^2.20.0: +commander@^2.19.0: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== @@ -2195,7 +1949,7 @@ core-util-is@1.0.2, core-util-is@~1.0.0: resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= -crc-32@1.2.0, crc-32@^1.2.0: +crc-32@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/crc-32/-/crc-32-1.2.0.tgz#cb2db6e29b88508e32d9dd0ec1693e7b41a18208" integrity sha512-1uBwHxF+Y/4yF5G48fwnKq6QsIXheor3ZLPT80yGBV1oEUwpPojlEhQbWKVw1VwcTQyMGHK1/XMmTjmlsmTTGA== @@ -2216,13 +1970,12 @@ create-require@^1.1.0: resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== -cron-parser@^2.13.0: - version "2.13.0" - resolved "https://registry.yarnpkg.com/cron-parser/-/cron-parser-2.13.0.tgz#6f930bb6f2931790d2a9eec83b3ec276e27a6725" - integrity sha512-UWeIpnRb0eyoWPVk+pD3TDpNx3KCFQeezO224oJIkktBrcW6RoAPOx5zIKprZGfk6vcYSmA8yQXItejSaDBhbQ== +cron-parser@^4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/cron-parser/-/cron-parser-4.2.1.tgz#b43205d05ccd5c93b097dae64f3bd811f5993af3" + integrity sha512-5sJBwDYyCp+0vU5b7POl8zLWfgV5fOHxlc45FWoWdHecGC7MQHCjx0CHivCMRnGFovghKhhyYM+Zm9DcY5qcHg== dependencies: - is-nan "^1.2.1" - moment-timezone "^0.5.25" + luxon "^1.28.0" cross-env@7.0.3: version "7.0.3" @@ -2255,10 +2008,10 @@ css-what@2.1: resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.3.tgz#a6d7604573365fe74686c3f311c56513d88285f2" integrity sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg== -cssom@^0.4.4: - version "0.4.4" - resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.4.4.tgz#5a66cf93d2d0b661d80bf6a44fb65f5c2e4e0a10" - integrity sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw== +cssom@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.5.0.tgz#d254fa92cd8b6fbd83811b9fbaed34663cc17c36" + integrity sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw== cssom@~0.3.6: version "0.3.8" @@ -2297,19 +2050,19 @@ data-uri-to-buffer@^4.0.0: resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-4.0.0.tgz#b5db46aea50f6176428ac05b73be39a57701a64b" integrity sha512-Vr3mLBA8qWmcuschSLAOogKgQ/Jwxulv3RNE4FXnYWRGujzrRWQI4m12fQqRkwX06C0KanhLr4hK+GydchZsaA== -data-urls@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-2.0.0.tgz#156485a72963a970f5d5821aaf642bef2bf2db9b" - integrity sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ== +data-urls@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-3.0.1.tgz#597fc2ae30f8bc4dbcf731fcd1b1954353afc6f8" + integrity sha512-Ds554NeT5Gennfoo9KN50Vh6tpgtvYEwraYjejXnyTpu1C7oXKxdFk75REooENHE8ndTVOJuv+BEs4/J/xcozw== dependencies: abab "^2.0.3" - whatwg-mimetype "^2.3.0" - whatwg-url "^8.0.0" + whatwg-mimetype "^3.0.0" + whatwg-url "^10.0.0" -dateformat@4.5.1: - version "4.5.1" - resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-4.5.1.tgz#c20e7a9ca77d147906b6dc2261a8be0a5bd2173c" - integrity sha512-OD0TZ+B7yP7ZgpJf5K2DIbj3FZvFvxgFUuaqA/V5zTjAtAAXZ1E8bktHxmAGs4x5b7PflqA9LeQ84Og7wYtF7Q== +date-fns@2.28.0: + version "2.28.0" + resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.28.0.tgz#9570d656f5fc13143e50c975a3b6bbeb46cd08b2" + integrity sha512-8d35hViGYx/QH0icHYCeLmsLmMUheMmTyV9Fcm6gvNwdw31yXXH+O85sOBJ+OLnLQMKZowvpKb6FgMIQjcpvQw== debug@2, debug@^2.2.0, debug@^2.5.2, debug@^2.6.9: version "2.6.9" @@ -2318,7 +2071,7 @@ debug@2, debug@^2.2.0, debug@^2.5.2, debug@^2.6.9: dependencies: ms "2.0.0" -debug@4, debug@4.3.1, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1: +debug@4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1: version "4.3.1" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee" integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ== @@ -2368,10 +2121,10 @@ decamelize@^4.0.0: resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837" integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ== -decimal.js@^10.2.1: - version "10.2.1" - resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.2.1.tgz#238ae7b0f0c793d3e3cea410108b35a2c01426a3" - integrity sha512-KaL7+6Fw6i5A2XSnsbhm/6B+NuEA7TZ4vqxnd5tXz9sbKtrN9Srj8ab4vKVdK8YAqZO9P1kg45Y6YLoduPf+kw== +decimal.js@^10.3.1: + version "10.3.1" + resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.3.1.tgz#d8c3a444a9c6774ba60ca6ad7261c3a94fd5e783" + integrity sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ== decompress-response@^6.0.0: version "6.0.0" @@ -2462,10 +2215,10 @@ detect-file@^1.0.0: resolved "https://registry.yarnpkg.com/detect-file/-/detect-file-1.0.0.tgz#f0d66d03672a825cb1b73bdb3fe62310c8e552b7" integrity sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc= -detect-libc@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" - integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups= +detect-libc@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.0.tgz#c528bc09bc6d1aa30149228240917c225448f204" + integrity sha512-S55LzUl8HUav8l9E2PBTlC5PAJrHK7tkM+XXFGD+fbsbkTzhCpG6K05LxJcUOEWzMa4v6ptcMZ9s3fOdJDu0Zw== detect-node@2.1.0, detect-node@^2.1.0: version "2.1.0" @@ -2566,12 +2319,12 @@ domelementtype@^2.2.0: resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.2.0.tgz#9a0b6c2782ed6a1c7323d42267183df9bd8b1d57" integrity sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A== -domexception@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/domexception/-/domexception-2.0.1.tgz#fb44aefba793e1574b0af6aed2801d057529f304" - integrity sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg== +domexception@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/domexception/-/domexception-4.0.0.tgz#4ad1be56ccadc86fc76d033353999a8037d03673" + integrity sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw== dependencies: - webidl-conversions "^5.0.0" + webidl-conversions "^7.0.0" domhandler@^2.3.0: version "2.4.2" @@ -2661,11 +2414,6 @@ ee-first@1.1.1: resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= -electron-to-chromium@^1.3.649: - version "1.3.672" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.672.tgz#3a6e335016dab4bc584d5292adc4f98f54541f6a" - integrity sha512-gFQe7HBb0lbOMqK2GAS5/1F+B0IMdYiAgB9OT/w1F4M7lgJK2aNOMNOM622aEax+nS1cTMytkiT0uMOkbtFmHw== - emoji-regex@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" @@ -2708,14 +2456,6 @@ enhanced-resolve@^5.0.0: graceful-fs "^4.2.4" tapable "^2.2.0" -enhanced-resolve@^5.7.0: - version "5.7.0" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.7.0.tgz#525c5d856680fbd5052de453ac83e32049958b5c" - integrity sha512-6njwt/NsZFUKhM6j9U8hzVyD4E4r0x7NQzhTCbcWOJ0IQjNSAoalWmb0AE51Wn+fwan5qVESWi7t2ToBxs9vrw== - dependencies: - graceful-fs "^4.2.4" - tapable "^2.2.0" - entities@^1.1.1, entities@~1.1.1: version "1.1.2" resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.2.tgz#bdfa735299664dfafd34529ed4f8522a275fea56" @@ -2762,11 +2502,6 @@ es-abstract@^1.19.0, es-abstract@^1.19.1: string.prototype.trimstart "^1.0.4" unbox-primitive "^1.0.1" -es-module-lexer@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-0.4.0.tgz#21f4181cc8b7eee06855f1c59e6087c7bc4f77b0" - integrity sha512-iuEGihqqhKWFgh72Q/Jtch7V2t/ft8w8IPP2aEN8ArYKO+IWyo6hsi96hCdgyeEDQIV3InhYQ9BlwUFPGXrbEQ== - es-to-primitive@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" @@ -2882,10 +2617,10 @@ eslint-scope@^5.1.1: esrecurse "^4.3.0" estraverse "^4.1.1" -eslint-scope@^7.1.0: - version "7.1.0" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.1.0.tgz#c1f6ea30ac583031f203d65c73e723b01298f153" - integrity sha512-aWwkhnS0qAXqNOgKOK0dJ2nvzEbhEvpy8OlJ9kZ0FeZnA6zpjv1/Vei+puGFFX7zkPCkHHXb7IDX3A+7yPrRWg== +eslint-scope@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.1.1.tgz#fff34894c2f65e5226d3041ac480b4513a163642" + integrity sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw== dependencies: esrecurse "^4.3.0" estraverse "^5.2.0" @@ -2907,17 +2642,17 @@ eslint-visitor-keys@^3.0.0: resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.0.0.tgz#e32e99c6cdc2eb063f204eda5db67bfe58bb4186" integrity sha512-mJOZa35trBTb3IyRmo8xmKBZlxf+N7OnUl4+ZhJHs/r+0770Wh/LEACE2pqMGMe27G/4y8P2bYGk4J70IC5k1Q== -eslint-visitor-keys@^3.1.0, eslint-visitor-keys@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.2.0.tgz#6fbb166a6798ee5991358bc2daa1ba76cc1254a1" - integrity sha512-IOzT0X126zn7ALX0dwFiUQEdsfzrm4+ISsQS8nukaJXwEyYKRSnEIIDULYg1mCtGp7UUXgfGl7BIolXREQK+XQ== +eslint-visitor-keys@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz#f6480fa6b1f30efe2d1968aa8ac745b862469826" + integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA== -eslint@8.7.0: - version "8.7.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.7.0.tgz#22e036842ee5b7cf87b03fe237731675b4d3633c" - integrity sha512-ifHYzkBGrzS2iDU7KjhCAVMGCvF6M3Xfs8X8b37cgrUlDt6bWRTpRh6T/gtSXv1HJ/BUGgmjvNvOEGu85Iif7w== +eslint@8.9.0: + version "8.9.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.9.0.tgz#a2a8227a99599adc4342fd9b854cb8d8d6412fdb" + integrity sha512-PB09IGwv4F4b0/atrbcMFboF/giawbBLVC7fyDamk5Wtey4Jh2K+rYaBhCAbUyEI4QzB1ly09Uglc9iCtFaG2Q== dependencies: - "@eslint/eslintrc" "^1.0.5" + "@eslint/eslintrc" "^1.1.0" "@humanwhocodes/config-array" "^0.9.2" ajv "^6.10.0" chalk "^4.0.0" @@ -2925,10 +2660,10 @@ eslint@8.7.0: debug "^4.3.2" doctrine "^3.0.0" escape-string-regexp "^4.0.0" - eslint-scope "^7.1.0" + eslint-scope "^7.1.1" eslint-utils "^3.0.0" - eslint-visitor-keys "^3.2.0" - espree "^9.3.0" + eslint-visitor-keys "^3.3.0" + espree "^9.3.1" esquery "^1.4.0" esutils "^2.0.2" fast-deep-equal "^3.1.3" @@ -2958,14 +2693,14 @@ esm@^3.2.22: resolved "https://registry.yarnpkg.com/esm/-/esm-3.2.25.tgz#342c18c29d56157688ba5ce31f8431fbb795cc10" integrity sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA== -espree@^9.2.0, espree@^9.3.0: - version "9.3.0" - resolved "https://registry.yarnpkg.com/espree/-/espree-9.3.0.tgz#c1240d79183b72aaee6ccfa5a90bc9111df085a8" - integrity sha512-d/5nCsb0JcqsSEeQzFZ8DH1RmxPcglRWh24EFTlUEmCKoehXGdpsx0RkHDubqUI8LSAIKMQp4r9SzQ3n+sm4HQ== +espree@^9.3.1: + version "9.3.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-9.3.1.tgz#8793b4bc27ea4c778c19908e0719e7b8f4115bcd" + integrity sha512-bvdyLmJMfwkV3NCRl5ZhJf22zBFo1y8bYh3VYb+bfzqNB4Je68P2sSuXyuFquzWLebHpNd2/d5uv7yoP9ISnGQ== dependencies: acorn "^8.7.0" acorn-jsx "^5.3.1" - eslint-visitor-keys "^3.1.0" + eslint-visitor-keys "^3.3.0" esprima@^4.0.1: version "4.0.1" @@ -3021,24 +2756,19 @@ events@1.1.1: resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924" integrity sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ= -events@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/events/-/events-3.2.0.tgz#93b87c18f8efcd4202a461aec4dfc0556b639379" - integrity sha512-/46HWwbfCX2xTawVfkKLGxMifJYQBWMwY1mjywRtb4c9x8l5NP3KoJtnIOiL1hfdRkIuYhETxQlo62IF8tcnlg== - -execa@6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/execa/-/execa-6.0.0.tgz#598b46f09ae44f5d8097a30cfb1681d0f0371503" - integrity sha512-m4wU9j4Z9nXXoqT8RSfl28JSwmMNLFF69OON8H/lL3NeU0tNpGz313bcOfYoBBHokB0dC2tMl3VUcKgHELhL2Q== +execa@6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-6.1.0.tgz#cea16dee211ff011246556388effa0818394fb20" + integrity sha512-QVWlX2e50heYJcCPG0iWtf8r0xjEYfz/OYLGDYH+IyjWezzPNxz63qNFOu0l4YftGWuizFVZHHs8PrLU5p2IDA== dependencies: cross-spawn "^7.0.3" get-stream "^6.0.1" human-signals "^3.0.1" is-stream "^3.0.0" merge-stream "^2.0.0" - npm-run-path "^5.0.1" + npm-run-path "^5.1.0" onetime "^6.0.0" - signal-exit "^3.0.5" + signal-exit "^3.0.7" strip-final-newline "^3.0.0" exit-on-epipe@~1.0.1: @@ -3241,10 +2971,10 @@ follow-redirects@^1.14.4: resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.7.tgz#2004c02eb9436eee9a21446a6477debf17e81685" integrity sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ== -form-data@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.0.tgz#31b7e39c85f1355b7139ee0c647cf0de7f83c682" - integrity sha512-CKMFDglpbMi6PyN+brwB9Q/GOw0eAnsrEZDgcsH5Krhz5Od/haKHAX0NmQfha2zPPz0JpWzA7GJHGSnvCRLWsg== +form-data@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" + integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== dependencies: asynckit "^0.4.0" combined-stream "^1.0.8" @@ -3406,9 +3136,9 @@ github-from-package@0.0.0: integrity sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4= glob-parent@^5.1.0, glob-parent@~5.1.0: - version "5.1.1" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229" - integrity sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ== + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== dependencies: is-glob "^4.0.1" @@ -3419,15 +3149,10 @@ glob-parent@^6.0.1: dependencies: is-glob "^4.0.3" -glob-to-regexp@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" - integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== - -glob@7.1.6, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: - version "7.1.6" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" - integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== +glob@7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" + integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== dependencies: fs.realpath "^1.0.0" inflight "^1.0.4" @@ -3436,10 +3161,10 @@ glob@7.1.6, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: once "^1.3.0" path-is-absolute "^1.0.0" -glob@7.2.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" - integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== +glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: + version "7.1.6" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" + integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== dependencies: fs.realpath "^1.0.0" inflight "^1.0.4" @@ -3619,12 +3344,12 @@ hpagent@^0.1.1: resolved "https://registry.yarnpkg.com/hpagent/-/hpagent-0.1.1.tgz#66f67f16e5c7a8b59a068e40c2658c2c749ad5e2" integrity sha512-IxJWQiY0vmEjetHdoE9HZjD4Cx+mYTr25tR7JCxXaiI3QxW0YqYyM11KyZbHufoa/piWhMb2+D3FGpMgmA2cFQ== -html-encoding-sniffer@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz#42a6dc4fd33f00281176e8b23759ca4e4fa185f3" - integrity sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ== +html-encoding-sniffer@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz#2cb1a8cf0db52414776e5b2a7a04d5dd98158de9" + integrity sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA== dependencies: - whatwg-encoding "^1.0.5" + whatwg-encoding "^2.0.0" html-entities@2.3.2: version "2.3.2" @@ -3687,13 +3412,6 @@ http-errors@~1.6.2: setprototypeof "1.1.0" statuses ">= 1.4.0 < 2" -http-headers@^3.0.1: - version "3.0.2" - resolved "https://registry.yarnpkg.com/http-headers/-/http-headers-3.0.2.tgz#5147771292f0b39d6778d930a3a59a76fc7ef44d" - integrity sha512-87E1I+2Wg4dxxz4rcxElo3dxO/w1ZtgL1yA0Sb6vH3qU16vRKq1NjWQv9SCY3ly2OQROcoxHZOUpmelS+k6wOw== - dependencies: - next-line "^1.1.0" - http-proxy-agent@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz#8a8c8ef7f5932ccf953c296ca8291b95aa74aa3a" @@ -3703,6 +3421,15 @@ http-proxy-agent@^4.0.1: agent-base "6" debug "4" +http-proxy-agent@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz#5129800203520d434f142bc78ff3c170800f2b43" + integrity sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w== + dependencies: + "@tootallnate/once" "2" + agent-base "6" + debug "4" + http-signature@1.3.6: version "1.3.6" resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.3.6.tgz#cb6fbfdf86d1c974f343be94e87f7fc128662cf9" @@ -3712,7 +3439,7 @@ http-signature@1.3.6: jsprim "^2.0.2" sshpk "^1.14.1" -http2-wrapper@^1.0.0-beta.5.0: +http2-wrapper@^1.0.0-beta.5.0, http2-wrapper@^1.0.0-beta.5.2: version "1.0.3" resolved "https://registry.yarnpkg.com/http2-wrapper/-/http2-wrapper-1.0.3.tgz#b8f55e0c1f25d4ebd08b3b0c2c079f9590800b3d" integrity sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg== @@ -3720,14 +3447,6 @@ http2-wrapper@^1.0.0-beta.5.0: quick-lru "^5.1.1" resolve-alpn "^1.0.0" -http2-wrapper@^1.0.0-beta.5.2: - version "1.0.0-beta.5.2" - resolved "https://registry.yarnpkg.com/http2-wrapper/-/http2-wrapper-1.0.0-beta.5.2.tgz#8b923deb90144aea65cf834b016a340fc98556f3" - integrity sha512-xYz9goEyBnC8XwXDTuC/MZ6t+MrKVQZOk4s7+PaDkwIsQd8IwqvM+0M6bA/2lvG8GHXcPdf+MejTUeO2LCPCeQ== - dependencies: - quick-lru "^5.1.1" - resolve-alpn "^1.0.0" - http_ece@1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/http_ece/-/http_ece-1.1.0.tgz#74780c6eb32d8ddfe9e36a83abcd81fe0cd4fb75" @@ -3767,6 +3486,13 @@ iconv-lite@0.4.24, iconv-lite@^0.4.4: dependencies: safer-buffer ">= 2.1.2 < 3" +iconv-lite@0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" + integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== + dependencies: + safer-buffer ">= 2.1.2 < 3.0.0" + iconv-lite@^0.6.2: version "0.6.2" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.2.tgz#ce13d1875b0c3a674bd6a04b7f76b01b1b6ded01" @@ -4017,14 +3743,7 @@ is-generator-function@^1.0.7: resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.7.tgz#d2132e529bb0000a7f80794d4bdf5cd5e5813522" integrity sha512-YZc5EwyO4f2kWCax7oegfuSr9mFz1ZvieNYBEjmukLxgXfBUbxAWGVF7GZf0zidYtoBl3WvC07YK0wT76a+Rtw== -is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" - integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== - dependencies: - is-extglob "^2.1.1" - -is-glob@^4.0.3: +is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: version "4.0.3" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== @@ -4043,13 +3762,6 @@ is-lambda@^1.0.1: resolved "https://registry.yarnpkg.com/is-lambda/-/is-lambda-1.0.1.tgz#3d9877899e6a53efc0160504cde15f82e6f061d5" integrity sha1-PZh3iZ5qU+/AFgUEzeFfgubwYdU= -is-nan@^1.2.1: - version "1.3.0" - resolved "https://registry.yarnpkg.com/is-nan/-/is-nan-1.3.0.tgz#85d1f5482f7051c2019f5673ccebdb06f3b0db03" - integrity sha512-z7bbREymOqt2CCaZVly8aC4ML3Xhfi0ekuOnjO2L8vKdl+CttdVoGZQhd4adMFAsxQ5VeRVwORs4tU8RH+HFtQ== - dependencies: - define-properties "^1.1.3" - is-negative-zero@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.1.tgz#3de746c18dda2319241a53675908d8f766f11c24" @@ -4152,6 +3864,11 @@ is-typedarray@^1.0.0: resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= +is-unicode-supported@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" + integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== + is-weakref@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.1.tgz#842dba4ec17fa9ac9850df2d6efbc1737274f2a2" @@ -4184,15 +3901,6 @@ isexe@^2.0.0: resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= -jest-worker@^26.6.2: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.6.2.tgz#7f72cbc4d643c365e27b9fd775f9d0eaa9c7a8ed" - integrity sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ== - dependencies: - "@types/node" "*" - merge-stream "^2.0.0" - supports-color "^7.0.0" - jmespath@0.16.0: version "0.16.0" resolved "https://registry.yarnpkg.com/jmespath/-/jmespath-0.16.0.tgz#b15b0a85dfd4d930d43e69ed605943c802785076" @@ -4224,13 +3932,6 @@ js-stringify@^1.0.2: resolved "https://registry.yarnpkg.com/js-stringify/-/js-stringify-1.0.2.tgz#1736fddfd9724f28a3682adc6230ae7e4e9679db" integrity sha1-Fzb939lyTyijaCrcYjCufk6Weds= -js-yaml@4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.0.0.tgz#f426bc0ff4b4051926cd588c71113183409a121f" - integrity sha512-pqon0s+4ScYUvX30wxQi3PogGFAlUyH0awepWvwkj4jD4v+ova3RiYw8bmA6x2rDrEaj8i/oWKoRxpVNW+Re8Q== - dependencies: - argparse "^2.0.1" - js-yaml@4.1.0, js-yaml@^4.0.0, js-yaml@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" @@ -4253,23 +3954,23 @@ jschardet@3.0.0: resolved "https://registry.yarnpkg.com/jschardet/-/jschardet-3.0.0.tgz#898d2332e45ebabbdb6bf2feece9feea9a99e882" integrity sha512-lJH6tJ77V8Nzd5QWRkFYCLc13a3vADkh3r/Fi8HupZGWk2OVVDfnZP8V/VgQgZ+lzW0kG2UGb5hFgt3V3ndotQ== -jsdom@16.7.0: - version "16.7.0" - resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-16.7.0.tgz#918ae71965424b197c819f8183a754e18977b710" - integrity sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw== +jsdom@19.0.0: + version "19.0.0" + resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-19.0.0.tgz#93e67c149fe26816d38a849ea30ac93677e16b6a" + integrity sha512-RYAyjCbxy/vri/CfnjUWJQQtZ3LKlLnDqj+9XLNnJPgEGeirZs3hllKR20re8LUZ6o1b1X4Jat+Qd26zmP41+A== dependencies: abab "^2.0.5" - acorn "^8.2.4" + acorn "^8.5.0" acorn-globals "^6.0.0" - cssom "^0.4.4" + cssom "^0.5.0" cssstyle "^2.3.0" - data-urls "^2.0.0" - decimal.js "^10.2.1" - domexception "^2.0.1" + data-urls "^3.0.1" + decimal.js "^10.3.1" + domexception "^4.0.0" escodegen "^2.0.0" - form-data "^3.0.0" - html-encoding-sniffer "^2.0.1" - http-proxy-agent "^4.0.1" + form-data "^4.0.0" + html-encoding-sniffer "^3.0.0" + http-proxy-agent "^5.0.0" https-proxy-agent "^5.0.0" is-potential-custom-element-name "^1.0.1" nwsapi "^2.2.0" @@ -4278,24 +3979,19 @@ jsdom@16.7.0: symbol-tree "^3.2.4" tough-cookie "^4.0.0" w3c-hr-time "^1.0.2" - w3c-xmlserializer "^2.0.0" - webidl-conversions "^6.1.0" - whatwg-encoding "^1.0.5" - whatwg-mimetype "^2.3.0" - whatwg-url "^8.5.0" - ws "^7.4.6" - xml-name-validator "^3.0.0" + w3c-xmlserializer "^3.0.0" + webidl-conversions "^7.0.0" + whatwg-encoding "^2.0.0" + whatwg-mimetype "^3.0.0" + whatwg-url "^10.0.0" + ws "^8.2.3" + xml-name-validator "^4.0.0" json-buffer@3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== -json-parse-better-errors@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" - integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== - json-schema-traverse@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" @@ -4610,11 +4306,6 @@ listenercount@~1.0.1: resolved "https://registry.yarnpkg.com/listenercount/-/listenercount-1.0.1.tgz#84c8a72ab59c4725321480c975e6508342e70937" integrity sha1-hMinKrWcRyUyFIDJdeZQg0LnCTc= -loader-runner@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.2.0.tgz#d7022380d66d14c5fb1d496b89864ebcfd478384" - integrity sha512-92+huvxMvYlMzMt0iIOukcwYBFpkYJdpl2xsZ7LrlayO7E8SOv+JJUEK17B/dJIHAOLMfh2dZZ/Y18WgmGtYNw== - loader-utils@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.0.tgz#e4cace5b816d425a166b5f097e10cd12b36064b0" @@ -4731,27 +4422,23 @@ lodash.some@^4.4.0: resolved "https://registry.yarnpkg.com/lodash.some/-/lodash.some-4.6.0.tgz#1bb9f314ef6b8baded13b549169b2a945eb68e4d" integrity sha1-G7nzFO9ri63tE7VJFpsqlF62jk0= -lodash.sortby@^4.7.0: - version "4.7.0" - resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" - integrity sha1-7dFMgk4sycHgsKG0K7UhBRakJDg= - lodash.union@^4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/lodash.union/-/lodash.union-4.6.0.tgz#48bb5088409f16f1821666641c44dd1aaae3cd88" integrity sha1-SLtQiECfFvGCFmZkHETdGqrjzYg= -lodash@^4.17.14, lodash@^4.17.19, lodash@^4.17.21, lodash@^4.7.0: +lodash@^4.17.14, lodash@^4.17.19, lodash@^4.17.21: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== -log-symbols@4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.0.0.tgz#69b3cc46d20f448eccdb75ea1fa733d9e821c920" - integrity sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA== +log-symbols@4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" + integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== dependencies: - chalk "^4.0.0" + chalk "^4.1.0" + is-unicode-supported "^0.1.0" lowercase-keys@^2.0.0: version "2.0.0" @@ -4773,6 +4460,11 @@ lru-cache@^6.0.0: dependencies: yallist "^4.0.0" +luxon@^1.28.0: + version "1.28.0" + resolved "https://registry.yarnpkg.com/luxon/-/luxon-1.28.0.tgz#e7f96daad3938c06a62de0fb027115d251251fbf" + integrity sha512-TfTiyvZhwBYM/7QdAVDh+7dBTBA29v4ik0Ce9zda3Mnf8on1S5KJI8P2jKFZ8+5C0jhmr0KwJEO/Wdpm0VeWJQ== + mailcheck@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/mailcheck/-/mailcheck-1.1.1.tgz#d87cf6ba0b64ba512199dbf93f1489f479591e34" @@ -4867,7 +4559,7 @@ mime-types@2.1.34: dependencies: mime-db "1.51.0" -mime-types@^2.1.12, mime-types@^2.1.18, mime-types@^2.1.27, mime-types@~2.1.24: +mime-types@^2.1.12, mime-types@^2.1.18, mime-types@~2.1.24: version "2.1.27" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.27.tgz#47949f98e279ea53119f5722e0f34e529bec009f" integrity sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w== @@ -4945,17 +4637,10 @@ minipass-sized@^1.0.3: dependencies: minipass "^3.0.0" -minipass@^3.0.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.1.1.tgz#7607ce778472a185ad6d89082aa2070f79cedcd5" - integrity sha512-UFqVihv6PQgwj8/yTGvl9kPz7xIAY+R5z6XYjRInD3Gk3qx6QGSD6zEcpeG4Dy/lQnv1J6zv8ejV90hyYIKf3w== - dependencies: - yallist "^4.0.0" - -minipass@^3.1.0, minipass@^3.1.1, minipass@^3.1.3: - version "3.1.3" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.1.3.tgz#7d42ff1f39635482e15f9cdb53184deebd5815fd" - integrity sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg== +minipass@^3.0.0, minipass@^3.1.0, minipass@^3.1.1, minipass@^3.1.3: + version "3.1.6" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.1.6.tgz#3b8150aa688a711a1521af5e8779c1d3bb4f45ee" + integrity sha512-rty5kpw9/z8SX9dmxblFA6edItUmwJgMeYDZRrwlIVN27i8gysGbznJwUggw2V/FVqFSDdWy040ZPS811DYAqQ== dependencies: yallist "^4.0.0" @@ -4967,10 +4652,10 @@ minizlib@^2.0.0, minizlib@^2.1.1: minipass "^3.0.0" yallist "^4.0.0" -misskey-js@0.0.13: - version "0.0.13" - resolved "https://registry.yarnpkg.com/misskey-js/-/misskey-js-0.0.13.tgz#03a4e469186e28752d599dc4093519eb64647970" - integrity sha512-kBdJdfe281gtykzzsrN3IAxWUQIimzPiJGyKWf863ggWJlWYVPmP9hTFlX2z8oPOaypgVBPEPHyw/jNUdc2DbQ== +misskey-js@0.0.14: + version "0.0.14" + resolved "https://registry.yarnpkg.com/misskey-js/-/misskey-js-0.0.14.tgz#1a616bdfbe81c6ee6900219eaf425bb5c714dd4d" + integrity sha512-bvLx6U3OwQwqHfp/WKwIVwdvNYAAPk0+YblXyxmSG3dwlzCgBRRLcB8o6bNruUDyJgh3t73pLDcOz3myxcUmww== dependencies: autobind-decorator "^2.4.0" eventemitter3 "^4.0.7" @@ -4998,45 +4683,37 @@ mkdirp@^1.0.3, mkdirp@^1.0.4, mkdirp@~1.0.3: resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== -mocha@8.4.0: - version "8.4.0" - resolved "https://registry.yarnpkg.com/mocha/-/mocha-8.4.0.tgz#677be88bf15980a3cae03a73e10a0fc3997f0cff" - integrity sha512-hJaO0mwDXmZS4ghXsvPVriOhsxQ7ofcpQdm8dE+jISUOKopitvnXFQmpRR7jd2K6VBG6E26gU3IAbXXGIbu4sQ== +mocha@9.2.0: + version "9.2.0" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-9.2.0.tgz#2bfba73d46e392901f877ab9a47b7c9c5d0275cc" + integrity sha512-kNn7E8g2SzVcq0a77dkphPsDSN7P+iYkqE0ZsGCYWRsoiKjOt+NvXfaagik8vuDa6W5Zw3qxe8Jfpt5qKf+6/Q== dependencies: "@ungap/promise-all-settled" "1.1.2" ansi-colors "4.1.1" browser-stdout "1.3.1" - chokidar "3.5.1" - debug "4.3.1" + chokidar "3.5.3" + debug "4.3.3" diff "5.0.0" escape-string-regexp "4.0.0" find-up "5.0.0" - glob "7.1.6" + glob "7.2.0" growl "1.10.5" he "1.2.0" - js-yaml "4.0.0" - log-symbols "4.0.0" + js-yaml "4.1.0" + log-symbols "4.1.0" minimatch "3.0.4" ms "2.1.3" - nanoid "3.1.20" - serialize-javascript "5.0.1" + nanoid "3.2.0" + serialize-javascript "6.0.0" strip-json-comments "3.1.1" supports-color "8.1.1" which "2.0.2" - wide-align "1.1.3" - workerpool "6.1.0" + workerpool "6.2.0" yargs "16.2.0" yargs-parser "20.2.4" yargs-unparser "2.0.0" -moment-timezone@^0.5.25: - version "0.5.28" - resolved "https://registry.yarnpkg.com/moment-timezone/-/moment-timezone-0.5.28.tgz#f093d789d091ed7b055d82aa81a82467f72e4338" - integrity sha512-TDJkZvAyKIVWg5EtVqRzU97w0Rb0YVbfpqyjgu6GwXCAohVRqwZjf4fOzDE6p1Ch98Sro/8hQQi65WDXW5STPw== - dependencies: - moment ">= 2.9.0" - -"moment@>= 2.9.0", moment@^2.22.2: +moment@^2.22.2: version "2.24.0" resolved "https://registry.yarnpkg.com/moment/-/moment-2.24.0.tgz#0d055d53f5052aa653c9f6eb68bb5d12bf5c2b5b" integrity sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg== @@ -5061,6 +4738,21 @@ ms@3.0.0-canary.1: resolved "https://registry.yarnpkg.com/ms/-/ms-3.0.0-canary.1.tgz#c7b34fbce381492fd0b345d1cf56e14d67b77b80" integrity sha512-kh8ARjh8rMN7Du2igDRO9QJnqCb2xYTJxyQYK7vJJS4TvLLmsbyhiKpSW+t+y26gyOyMd0riphX0GeWKU3ky5g== +msgpackr-extract@^1.0.14: + version "1.0.16" + resolved "https://registry.yarnpkg.com/msgpackr-extract/-/msgpackr-extract-1.0.16.tgz#701c4f6e6f25c100ae84557092274e8fffeefe45" + integrity sha512-fxdRfQUxPrL/TizyfYfMn09dK58e+d65bRD/fcaVH4052vj30QOzzqxcQIS7B0NsqlypEQ/6Du3QmP2DhWFfCA== + dependencies: + nan "^2.14.2" + node-gyp-build "^4.2.3" + +msgpackr@^1.5.2: + version "1.5.4" + resolved "https://registry.yarnpkg.com/msgpackr/-/msgpackr-1.5.4.tgz#2b6ea6cb7d79c0ad98fc76c68163c48eda50cf0d" + integrity sha512-Z7w5Jg+2Q9z9gJxeM68d7tSuWZZGnFIRhZnyqcZCa/1dKkhOCNvR1TUV3zzJ3+vj78vlwKRzUgVDlW4jiSOeDA== + optionalDependencies: + msgpackr-extract "^1.0.14" + multer@1.4.4: version "1.4.4" resolved "https://registry.yarnpkg.com/multer/-/multer-1.4.4.tgz#e2bc6cac0df57a8832b858d7418ccaa8ebaf7d8c" @@ -5089,7 +4781,7 @@ mz@^2.4.0, mz@^2.7.0: object-assign "^4.0.1" thenify-all "^1.0.0" -nan@^2.15.0: +nan@^2.14.2, nan@^2.15.0: version "2.15.0" resolved "https://registry.yarnpkg.com/nan/-/nan-2.15.0.tgz#3f34a473ff18e15c1b5626b62903b5ad6e665fee" integrity sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ== @@ -5101,10 +4793,10 @@ nano-time@1.0.0: dependencies: big-integer "^1.6.16" -nanoid@3.1.20: - version "3.1.20" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.20.tgz#badc263c6b1dcf14b71efaa85f6ab4c1d6cfc788" - integrity sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw== +nanoid@3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.2.0.tgz#62667522da6673971cca916a6d3eff3f415ff80c" + integrity sha512-fmsZYa9lpn69Ad5eDn7FMcnnSR+8R34W9qJEijxYhTbfOWzr22n1QxCMzXLK+ODyW2973V3Fux959iQoUxzUIA== nanoid@^3.1.30: version "3.1.30" @@ -5135,11 +4827,6 @@ negotiator@0.6.2, negotiator@^0.6.2: resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== -neo-async@^2.6.2: - version "2.6.2" - resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" - integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== - nested-property@4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/nested-property/-/nested-property-4.0.0.tgz#a67b5a31991e701e03cdbaa6453bc5b1011bb88d" @@ -5150,11 +4837,6 @@ netmask@^2.0.2: resolved "https://registry.yarnpkg.com/netmask/-/netmask-2.0.2.tgz#8b01a07644065d536383835823bc52004ebac5e7" integrity sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg== -next-line@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/next-line/-/next-line-1.1.0.tgz#fcae57853052b6a9bae8208e40dd7d3c2d304603" - integrity sha1-/K5XhTBStqm66CCOQN19PC0wRgM= - next-tick@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c" @@ -5167,10 +4849,10 @@ node-abi@^3.3.0: dependencies: semver "^7.3.5" -node-addon-api@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-4.2.0.tgz#117cbb5a959dff0992e1c586ae0393573e4d2a87" - integrity sha512-eazsqzwG2lskuzBqCGPi7Ac2UgOoMz8JVOXVhTvvPDYhthvNpefx8jWD8Np7Gv+2Sz0FlPWZk0nJV0z598Wn8Q== +node-addon-api@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-4.3.0.tgz#52a1a0b475193e0928e98e0426a0d1254782b77f" + integrity sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ== node-domexception@^1.0.0: version "1.0.0" @@ -5186,10 +4868,12 @@ node-fetch@*: fetch-blob "^3.1.4" formdata-polyfill "^4.0.10" -node-fetch@2.6.1, node-fetch@^2.6.1: - version "2.6.1" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" - integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== +node-fetch@2.6.7, node-fetch@^2.6.1: + version "2.6.7" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" + integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== + dependencies: + whatwg-url "^5.0.0" node-fetch@3.0.0-beta.9: version "3.0.0-beta.9" @@ -5199,6 +4883,11 @@ node-fetch@3.0.0-beta.9: data-uri-to-buffer "^3.0.1" fetch-blob "^2.1.1" +node-gyp-build@^4.2.3: + version "4.3.0" + resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.3.0.tgz#9f256b03e5826150be39c764bf51e993946d71a3" + integrity sha512-iWjXZvmboq0ja1pUGULQBexmxq8CV4xBhX7VDOTbL7ZR4FOowwY/VOtRxBN/yKxmdGoIp4j5ysNT4u3S2pDQ3Q== + node-gyp-build@~3.7.0: version "3.7.0" resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-3.7.0.tgz#daa77a4f547b9aed3e2aac779eaf151afd60ec8d" @@ -5220,11 +4909,6 @@ node-gyp@^8.4.1: tar "^6.1.2" which "^2.0.2" -node-releases@^1.1.70: - version "1.1.71" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.71.tgz#cb1334b179896b1c89ecfdd4b725fb7bbdfc7dbb" - integrity sha512-zR6HoT6LrLCRBwukmrVbHv0EpEQjksO6GmFcZQQuCAy139BEsoVKPYnf3jongYW83fAa1torLGYwxxky/p28sg== - nodemailer@6.7.2: version "6.7.2" resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-6.7.2.tgz#44b2ad5f7ed71b7067f7a21c4fedabaec62b85e0" @@ -5263,14 +4947,14 @@ normalize-path@^3.0.0, normalize-path@~3.0.0: integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== normalize-url@^4.1.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.0.tgz#453354087e6ca96957bd8f5baf753f5982142129" - integrity sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ== + version "4.5.1" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.1.tgz#0dd90cf1288ee1d1313b87081c9a5932ee48518a" + integrity sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA== -npm-run-path@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-5.0.1.tgz#748dd68ed7de377bb1f7132c7dafe657be5ab400" - integrity sha512-ybBJQUSyFwEEhqO2lXmyKOl9ucHtyZBWVM0h0FiMfT/+WKxCUZFa95qAR2X3w/w6oigN3B0b2UNHZbD+kdfD5w== +npm-run-path@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-5.1.0.tgz#bc62f7f3f6952d9894bd08944ba011a6ee7b7e00" + integrity sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q== dependencies: path-key "^4.0.0" @@ -5464,13 +5148,6 @@ p-limit@^3.0.2: dependencies: p-try "^2.0.0" -p-limit@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" - integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== - dependencies: - yocto-queue "^0.1.0" - p-locate@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" @@ -5604,9 +5281,9 @@ path-key@^4.0.0: integrity sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ== path-parse@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" - integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== path-to-regexp@^6.1.0: version "6.1.0" @@ -5633,10 +5310,10 @@ pg-int8@1.0.1: resolved "https://registry.yarnpkg.com/pg-int8/-/pg-int8-1.0.1.tgz#943bd463bf5b71b4170115f80f8efc9a0c0eb78c" integrity sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw== -pg-pool@^3.4.1: - version "3.4.1" - resolved "https://registry.yarnpkg.com/pg-pool/-/pg-pool-3.4.1.tgz#0e71ce2c67b442a5e862a9c182172c37eda71e9c" - integrity sha512-TVHxR/gf3MeJRvchgNHxsYsTCHQ+4wm3VIHSS19z8NC0+gioEhq1okDY1sm/TYbfoP6JLFx01s0ShvZ3puP/iQ== +pg-pool@^3.5.1: + version "3.5.1" + resolved "https://registry.yarnpkg.com/pg-pool/-/pg-pool-3.5.1.tgz#f499ce76f9bf5097488b3b83b19861f28e4ed905" + integrity sha512-6iCR0wVrro6OOHFsyavV+i6KYL4lVNyYAB9RD18w66xSzN+d8b66HiwuP30Gp1SH5O9T82fckkzsRjlrhD0ioQ== pg-protocol@^1.5.0: version "1.5.0" @@ -5654,15 +5331,15 @@ pg-types@^2.1.0: postgres-date "~1.0.4" postgres-interval "^1.1.0" -pg@8.7.1: - version "8.7.1" - resolved "https://registry.yarnpkg.com/pg/-/pg-8.7.1.tgz#9ea9d1ec225980c36f94e181d009ab9f4ce4c471" - integrity sha512-7bdYcv7V6U3KAtWjpQJJBww0UEsWuh4yQ/EjNf2HeO/NnvKjpvhEIe/A/TleP6wtmSKnUnghs5A9jUoK6iDdkA== +pg@8.7.3: + version "8.7.3" + resolved "https://registry.yarnpkg.com/pg/-/pg-8.7.3.tgz#8a5bdd664ca4fda4db7997ec634c6e5455b27c44" + integrity sha512-HPmH4GH4H3AOprDJOazoIcpI49XFsHCe8xlrjHkWiapdbHK+HLtbm/GQzXYAZwmPju/kzKhjaSfMACG+8cgJcw== dependencies: buffer-writer "2.0.0" packet-reader "1.0.0" pg-connection-string "^2.5.0" - pg-pool "^3.4.1" + pg-pool "^3.5.1" pg-protocol "^1.5.0" pg-types "^2.1.0" pgpass "1.x" @@ -5743,12 +5420,12 @@ postgres-interval@^1.1.0: dependencies: xtend "^4.0.0" -prebuild-install@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-7.0.0.tgz#3c5ce3902f1cb9d6de5ae94ca53575e4af0c1574" - integrity sha512-IvSenf33K7JcgddNz2D5w521EgO+4aMMjFt73Uk9FRzQ7P+QZPKrp7qPsDydsSwjGt3T5xRNnM1bj1zMTD5fTA== +prebuild-install@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-7.0.1.tgz#c10075727c318efe72412f333e0ef625beaf3870" + integrity sha512-QBSab31WqkyxpnMWQxubYAHR5S9B2+r81ucocew34Fkl98FhvKIF50jIJnNOBmAZfyNV7vE5T6gd3hTVWgY6tg== dependencies: - detect-libc "^1.0.3" + detect-libc "^2.0.0" expand-template "^2.0.3" github-from-package "0.0.0" minimist "^1.2.3" @@ -5796,10 +5473,10 @@ private-ip@2.3.3: is-ip "^3.1.0" netmask "^2.0.2" -probe-image-size@7.2.2: - version "7.2.2" - resolved "https://registry.yarnpkg.com/probe-image-size/-/probe-image-size-7.2.2.tgz#e5851b9be7864f21e3bac5e6e4fac9da9055b412" - integrity sha512-QUm+w1S9WTsT5GZB830u0BHExrUmF0J4fyRm5kbLUMEP3fl9UVYXc3xOBVqZNnH9tnvVEJO8vDk3PMtsLqjxug== +probe-image-size@7.2.3: + version "7.2.3" + resolved "https://registry.yarnpkg.com/probe-image-size/-/probe-image-size-7.2.3.tgz#d49c64be540ec8edea538f6f585f65a9b3ab4309" + integrity sha512-HubhG4Rb2UH8YtV4ba0Vp5bQ7L78RTONYu/ujmCu5nBI8wGv24s4E9xSKBi0N1MowRpxk76pFCpJtW0KPzOK0w== dependencies: lodash.merge "^4.6.2" needle "^2.5.2" @@ -6194,14 +5871,6 @@ rename@1.0.4: dependencies: debug "^2.5.2" -request-stats@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/request-stats/-/request-stats-3.0.0.tgz#769155dc8974d78d4a1cb87bbf14eaab985afe25" - integrity sha1-dpFV3Il0141KHLh7vxTqq5ha/iU= - dependencies: - http-headers "^3.0.1" - once "^1.4.0" - require-all@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/require-all/-/require-all-3.0.0.tgz#473d49704be310115ce124f77383b1ebd8671312" @@ -6325,10 +5994,10 @@ safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@~5.2.0: resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== -sanitize-html@2.6.1: - version "2.6.1" - resolved "https://registry.yarnpkg.com/sanitize-html/-/sanitize-html-2.6.1.tgz#5d37c08e189c61c0631560a889b10d9d155d000e" - integrity sha512-DzjSz3H5qDntD7s1TcWCSoRPmNR8UmA+y+xZQOvWgjATe2Br9ZW73+vD3Pj6Snrg0RuEuJdXgrKvnYuiuixRkA== +sanitize-html@2.7.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/sanitize-html/-/sanitize-html-2.7.0.tgz#e106205b468aca932e2f9baf241f24660d34e279" + integrity sha512-jfQelabOn5voO7FAfnQF7v+jsA6z9zC/O4ec0z3E35XPEtHYJT/OdUziVWlKW4irCr2kXaQAyXTXDHWAibg1tA== dependencies: deepmerge "^4.2.2" escape-string-regexp "^4.0.0" @@ -6397,10 +6066,10 @@ semver@^7.3.5: dependencies: lru-cache "^6.0.0" -serialize-javascript@5.0.1, serialize-javascript@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-5.0.1.tgz#7886ec848049a462467a97d3d918ebb2aaf934f4" - integrity sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA== +serialize-javascript@6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.0.tgz#efae5d88f45d7924141da8b5c3a7a7e663fefeb8" + integrity sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag== dependencies: randombytes "^2.1.0" @@ -6432,17 +6101,17 @@ sha.js@^2.4.11: inherits "^2.0.1" safe-buffer "^5.0.1" -sharp@0.29.3: - version "0.29.3" - resolved "https://registry.yarnpkg.com/sharp/-/sharp-0.29.3.tgz#0da183d626094c974516a48fab9b3e4ba92eb5c2" - integrity sha512-fKWUuOw77E4nhpyzCCJR1ayrttHoFHBT2U/kR/qEMRhvPEcluG4BKj324+SCO1e84+knXHwhJ1HHJGnUt4ElGA== +sharp@0.30.1: + version "0.30.1" + resolved "https://registry.yarnpkg.com/sharp/-/sharp-0.30.1.tgz#203efaf9acfc5c15c8a343800254621e56011c12" + integrity sha512-ycpz81q8AeVjz1pGvvirQBeJcYE2sXAjcLXR/69LWOe/oxavBLOrenZcTzvTXn83jqAGqY+OuwF+2kFXzbKtDA== dependencies: - color "^4.0.1" - detect-libc "^1.0.3" - node-addon-api "^4.2.0" - prebuild-install "^7.0.0" + color "^4.2.0" + detect-libc "^2.0.0" + node-addon-api "^4.3.0" + prebuild-install "^7.0.1" semver "^7.3.5" - simple-get "^4.0.0" + simple-get "^4.0.1" tar-fs "^2.1.1" tunnel-agent "^0.6.0" @@ -6477,20 +6146,20 @@ signal-exit@^3.0.0: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== -signal-exit@^3.0.5: - version "3.0.6" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.6.tgz#24e630c4b0f03fea446a2bd299e62b4a6ca8d0af" - integrity sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ== +signal-exit@^3.0.7: + version "3.0.7" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" + integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== simple-concat@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.0.tgz#7344cbb8b6e26fb27d66b2fc86f9f6d5997521c6" - integrity sha1-c0TLuLbib7J9ZrL8hvn21Zl1IcY= + version "1.0.1" + resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.1.tgz#f46976082ba35c2263f1c8ab5edfe26c41c9552f" + integrity sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q== -simple-get@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-4.0.0.tgz#73fa628278d21de83dadd5512d2cc1f4872bd675" - integrity sha512-ZalZGexYr3TA0SwySsr5HlgOOinS4Jsa8YB2GJ6lUNAazyAu4KG/VmzMTwAt2YVXzzVj8QmefmAonZIK2BSGcQ== +simple-get@^4.0.0, simple-get@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-4.0.1.tgz#4a39db549287c979d352112fa03fd99fd6bc3543" + integrity sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA== dependencies: decompress-response "^6.0.0" once "^1.3.1" @@ -6530,34 +6199,16 @@ socks@^2.6.1: ip "^1.1.5" smart-buffer "^4.1.0" -source-list-map@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" - integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw== - source-map-js@^0.6.2: version "0.6.2" resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-0.6.2.tgz#0bb5de631b41cfbda6cfba8bd05a80efdfd2385e" integrity sha512-/3GptzWzu0+0MBQFrDKzw/DvvMTUORvgY6k6jd/VS6iCR4RDTKWH6v6WPwQoUO8667uQEf9Oe38DxAYWY5F/Ug== -source-map-support@~0.5.19: - version "0.5.19" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" - integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== - dependencies: - buffer-from "^1.0.0" - source-map "^0.6.0" - -source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: +source-map@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== -source-map@~0.7.2: - version "0.7.3" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383" - integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ== - speakeasy@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/speakeasy/-/speakeasy-2.0.0.tgz#85c91a071b09a5cb8642590d983566165f57613a" @@ -6794,7 +6445,7 @@ supports-color@^5.3.0: dependencies: has-flag "^3.0.0" -supports-color@^7.0.0, supports-color@^7.1.0: +supports-color@^7.1.0: version "7.2.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== @@ -6813,12 +6464,12 @@ syslog-pro@1.0.0: dependencies: moment "^2.22.2" -systeminformation@5.9.9: - version "5.9.9" - resolved "https://registry.yarnpkg.com/systeminformation/-/systeminformation-5.9.9.tgz#aa8234a138363bd988f438fed3273370f79d7e30" - integrity sha512-xciy6NKCLfs4dqMD1Tdlo7v1/g0NfdA1EKsIptUQjlcVvpwHyjifAbNOF7ppFezGSMXxYE8me+l2+RlFF4lyTg== +systeminformation@5.11.3: + version "5.11.3" + resolved "https://registry.yarnpkg.com/systeminformation/-/systeminformation-5.11.3.tgz#8d14a444d398eadce5cb973a3d3d0ebb454ffcd9" + integrity sha512-sjvlk4SUefhwrONUeLijXt+NQyptAiqShd5v6bFJFNr9EVJUr3YSnNxDqCz0gp5EJBUj88pL1ssc8ZHPtngBOw== -tapable@^2.1.1, tapable@^2.2.0: +tapable@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.0.tgz#5c373d281d9c672848213d0e037d1c4165ab426b" integrity sha512-FBk4IesMV1rBxX2tfiK8RAmogtWn53puLOQlvO8XuwlgxcYbP4mVPS9Ph4aeamSyyVjOl24aYWAuc8U5kCVwMw== @@ -6865,19 +6516,7 @@ tar-stream@^2.1.4, tar-stream@^2.2.0: inherits "^2.0.3" readable-stream "^3.1.1" -tar@^6.0.2: - version "6.0.5" - resolved "https://registry.yarnpkg.com/tar/-/tar-6.0.5.tgz#bde815086e10b39f1dcd298e89d596e1535e200f" - integrity sha512-0b4HOimQHj9nXNEAA7zWwMM91Zhhba3pspja6sQbgTpynOJf+bkjBnfybNYzbpLbnwXnbyB4LOREvlyXLkCHSg== - dependencies: - chownr "^2.0.0" - fs-minipass "^2.0.0" - minipass "^3.0.0" - minizlib "^2.1.1" - mkdirp "^1.0.3" - yallist "^4.0.0" - -tar@^6.1.2: +tar@^6.0.2, tar@^6.1.2: version "6.1.11" resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.11.tgz#6760a38f003afa1b2ffd0ffe9e9abbd0eab3d621" integrity sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA== @@ -6889,27 +6528,6 @@ tar@^6.1.2: mkdirp "^1.0.3" yallist "^4.0.0" -terser-webpack-plugin@^5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.1.1.tgz#7effadee06f7ecfa093dbbd3e9ab23f5f3ed8673" - integrity sha512-5XNNXZiR8YO6X6KhSGXfY0QrGrCRlSwAEjIIrlRQR4W8nP69TaJUlh3bkuac6zzgspiGPfKEHcY295MMVExl5Q== - dependencies: - jest-worker "^26.6.2" - p-limit "^3.1.0" - schema-utils "^3.0.0" - serialize-javascript "^5.0.1" - source-map "^0.6.1" - terser "^5.5.1" - -terser@^5.5.1: - version "5.5.1" - resolved "https://registry.yarnpkg.com/terser/-/terser-5.5.1.tgz#540caa25139d6f496fdea056e414284886fb2289" - integrity sha512-6VGWZNVP2KTUcltUQJ25TtNjx/XgdDsBDKGt8nN0MpydU36LmbPPcMBd2kmtZNNGVVDLg44k7GKeHHj+4zPIBQ== - dependencies: - commander "^2.20.0" - source-map "~0.7.2" - source-map-support "~0.5.19" - text-table@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" @@ -6990,13 +6608,18 @@ tough-cookie@^4.0.0: punycode "^2.1.1" universalify "^0.1.2" -tr46@^2.0.0, tr46@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/tr46/-/tr46-2.0.2.tgz#03273586def1595ae08fedb38d7733cee91d2479" - integrity sha512-3n1qG+/5kg+jrbTzwAykB5yRYtQCTqOGKq5U5PE3b0a1/mzo6snDhjGS0zJVJunO0NrT3Dg1MLy5TjWP/UJppg== +tr46@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-3.0.0.tgz#555c4e297a950617e8eeddef633c87d4d9d6cbf9" + integrity sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA== dependencies: punycode "^2.1.1" +tr46@~0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" + integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= + trace-redirect@1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/trace-redirect/-/trace-redirect-1.0.6.tgz#ac629b5bf8247d30dde5a35fe9811b811075b504" @@ -7017,10 +6640,10 @@ ts-loader@9.2.6: micromatch "^4.0.0" semver "^7.3.4" -ts-node@10.4.0: - version "10.4.0" - resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.4.0.tgz#680f88945885f4e6cf450e7f0d6223dd404895f7" - integrity sha512-g0FlPvvCXSIO1JDF6S232P5jPYqBkRL9qly81ZgAOSU7rwI0stphCgd2kLiCrU9DjQCrJMWEqcNSjQL02s6d8A== +ts-node@10.5.0: + version "10.5.0" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.5.0.tgz#618bef5854c1fbbedf5e31465cbb224a1d524ef9" + integrity sha512-6kEJKwVxAJ35W4akuiysfKwKmjkbYxwQMTBaAxo9KKAx/Yd26mPUyhGz3ji+EsJoAgrLqVsYHNuuYwQe22lbtw== dependencies: "@cspotcode/source-map-support" "0.7.0" "@tsconfig/node10" "^1.0.7" @@ -7033,6 +6656,7 @@ ts-node@10.4.0: create-require "^1.1.0" diff "^4.0.1" make-error "^1.1.1" + v8-compile-cache-lib "^3.0.0" yn "3.1.1" tsc-alias@1.4.1: @@ -7057,7 +6681,7 @@ tsconfig-paths@3.12.0, tsconfig-paths@^3.12.0: minimist "^1.2.0" strip-bom "^3.0.0" -tslib@^1.8.1, tslib@^1.9.0: +tslib@^1.8.1: version "1.11.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.11.1.tgz#eb15d128827fbee2841549e171f45ed338ac7e35" integrity sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA== @@ -7150,10 +6774,10 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= -typeorm@0.2.41: - version "0.2.41" - resolved "https://registry.yarnpkg.com/typeorm/-/typeorm-0.2.41.tgz#88758101ac158dc0a0a903d70eaacea2974281cc" - integrity sha512-/d8CLJJxKPgsnrZWiMyPI0rz2MFZnBQrnQ5XP3Vu3mswv2WPexb58QM6BEtmRmlTMYN5KFWUz8SKluze+wS9xw== +typeorm@0.2.43: + version "0.2.43" + resolved "https://registry.yarnpkg.com/typeorm/-/typeorm-0.2.43.tgz#110d629ed5d0b014c735f0213f06b34abb32d298" + integrity sha512-j4SU8I0PsMWtF64s/9tOJmlexzfDsvXhTjSE2GXdSseUN8TYyfbCm/fJnC6lx3uHgBJL4z9bE8U/7qyC/FVmNw== dependencies: "@sqltools/formatter" "^1.2.2" app-root-path "^3.0.0" @@ -7168,6 +6792,7 @@ typeorm@0.2.41: reflect-metadata "^0.1.13" sha.js "^2.4.11" tslib "^2.1.0" + uuid "^8.3.2" xml2js "^0.4.23" yargs "^17.0.1" zen-observable-ts "^1.0.0" @@ -7282,11 +6907,16 @@ uuid@7.0.3: resolved "https://registry.yarnpkg.com/uuid/-/uuid-7.0.3.tgz#c5c9f2c8cf25dc0a372c4df1441c41f5bd0c680b" integrity sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg== -uuid@8.3.2, uuid@^8.3.0: +uuid@8.3.2, uuid@^8.3.0, uuid@^8.3.2: version "8.3.2" resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== +v8-compile-cache-lib@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.0.tgz#0582bcb1c74f3a2ee46487ceecf372e46bce53e8" + integrity sha512-mpSYqfsFvASnSn5qMiwrr4VKfumbPyONLCOPmsR3A6pTY/r0+tSaVbgPWSAIuzbk3lCTa+FForeTiO+wBQGkjA== + v8-compile-cache@^2.0.3: version "2.2.0" resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.2.0.tgz#9471efa3ef9128d2f7c6a7ca39c4dd6b5055b132" @@ -7318,20 +6948,12 @@ w3c-hr-time@^1.0.2: dependencies: browser-process-hrtime "^1.0.0" -w3c-xmlserializer@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz#3e7104a05b75146cc60f564380b7f683acf1020a" - integrity sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA== +w3c-xmlserializer@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-3.0.0.tgz#06cdc3eefb7e4d0b20a560a5a3aeb0d2d9a65923" + integrity sha512-3WFqGEgSXIyGhOmAFtlicJNMjEps8b1MG31NCA0/vOF9+nKMUW1ckhi9cnNHmf88Rzw5V+dwIwsm2C7X8k9aQg== dependencies: - xml-name-validator "^3.0.0" - -watchpack@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.0.0.tgz#b12248f32f0fd4799b7be0802ad1f6573a45955c" - integrity sha512-xSdCxxYZWNk3VK13bZRYhsQpfa8Vg63zXG+3pyU8ouqSLRCv4IGXIp9Kr226q6GBkGRlZrST2wwKtjfKz2m7Cg== - dependencies: - glob-to-regexp "^0.4.1" - graceful-fs "^4.1.2" + xml-name-validator "^4.0.0" web-push@3.4.5: version "3.4.5" @@ -7350,52 +6972,15 @@ web-streams-polyfill@^3.0.3: resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-3.2.0.tgz#a6b74026b38e4885869fb5c589e90b95ccfc7965" integrity sha512-EqPmREeOzttaLRm5HS7io98goBgZ7IVz79aDvqjD0kYXLtFZTc0T/U6wHTPKyIjb+MdN7DFIIX6hgdBEpWmfPA== -webidl-conversions@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-5.0.0.tgz#ae59c8a00b121543a2acc65c0434f57b0fc11aff" - integrity sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA== +webidl-conversions@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" + integrity sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE= -webidl-conversions@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-6.1.0.tgz#9111b4d7ea80acd40f5270d666621afa78b69514" - integrity sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w== - -webpack-sources@^2.1.1: - version "2.2.0" - resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-2.2.0.tgz#058926f39e3d443193b6c31547229806ffd02bac" - integrity sha512-bQsA24JLwcnWGArOKUxYKhX3Mz/nK1Xf6hxullKERyktjNMC4x8koOeaDNTA2fEJ09BdWLbM/iTW0ithREUP0w== - dependencies: - source-list-map "^2.0.1" - source-map "^0.6.1" - -webpack@^5: - version "5.33.2" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.33.2.tgz#c049717c9b038febf5a72fd2f53319ad59a8c1fc" - integrity sha512-X4b7F1sYBmJx8mlh2B7mV5szEkE0jYNJ2y3akgAP0ERi0vLCG1VvdsIxt8lFd4st6SUy0lf7W0CCQS566MBpJg== - dependencies: - "@types/eslint-scope" "^3.7.0" - "@types/estree" "^0.0.46" - "@webassemblyjs/ast" "1.11.0" - "@webassemblyjs/wasm-edit" "1.11.0" - "@webassemblyjs/wasm-parser" "1.11.0" - acorn "^8.0.4" - browserslist "^4.14.5" - chrome-trace-event "^1.0.2" - enhanced-resolve "^5.7.0" - es-module-lexer "^0.4.0" - eslint-scope "^5.1.1" - events "^3.2.0" - glob-to-regexp "^0.4.1" - graceful-fs "^4.2.4" - json-parse-better-errors "^1.0.2" - loader-runner "^4.2.0" - mime-types "^2.1.27" - neo-async "^2.6.2" - schema-utils "^3.0.0" - tapable "^2.1.1" - terser-webpack-plugin "^5.1.1" - watchpack "^2.0.0" - webpack-sources "^2.1.1" +webidl-conversions@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-7.0.0.tgz#256b4e1882be7debbf01d05f0aa2039778ea080a" + integrity sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g== websocket@1.0.34: version "1.0.34" @@ -7409,35 +6994,33 @@ websocket@1.0.34: utf-8-validate "^5.0.2" yaeti "^0.0.6" -whatwg-encoding@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz#5abacf777c32166a51d085d6b4f3e7d27113ddb0" - integrity sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw== +whatwg-encoding@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz#e7635f597fd87020858626805a2729fa7698ac53" + integrity sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg== dependencies: - iconv-lite "0.4.24" + iconv-lite "0.6.3" -whatwg-mimetype@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf" - integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g== +whatwg-mimetype@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz#5fa1a7623867ff1af6ca3dc72ad6b8a4208beba7" + integrity sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q== -whatwg-url@^8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-8.0.0.tgz#37f256cb746398e19b107bd6ef820b4ae2d15871" - integrity sha512-41ou2Dugpij8/LPO5Pq64K5q++MnRCBpEHvQr26/mArEKTkCV5aoXIqyhuYtE0pkqScXwhf2JP57rkRTYM29lQ== +whatwg-url@^10.0.0: + version "10.0.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-10.0.0.tgz#37264f720b575b4a311bd4094ed8c760caaa05da" + integrity sha512-CLxxCmdUby142H5FZzn4D8ikO1cmypvXVQktsgosNy4a4BHrDHeciBBGZhb0bNoR5/MltoCatso+vFjjGx8t0w== dependencies: - lodash.sortby "^4.7.0" - tr46 "^2.0.0" - webidl-conversions "^5.0.0" + tr46 "^3.0.0" + webidl-conversions "^7.0.0" -whatwg-url@^8.5.0: - version "8.5.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-8.5.0.tgz#7752b8464fc0903fec89aa9846fc9efe07351fd3" - integrity sha512-fy+R77xWv0AiqfLl4nuGUlQ3/6b5uNfQ4WAbGQVMYshCTCCPK9psC1nWh3XHuxGVCtlcDDQPQW1csmmIQo+fwg== +whatwg-url@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" + integrity sha1-lmRU6HZUYuN2RNNib2dCzotwll0= dependencies: - lodash "^4.7.0" - tr46 "^2.0.2" - webidl-conversions "^6.1.0" + tr46 "~0.0.3" + webidl-conversions "^3.0.0" which-boxed-primitive@^1.0.2: version "1.0.2" @@ -7469,7 +7052,7 @@ which@^1.1.1, which@^1.2.14: dependencies: isexe "^2.0.0" -wide-align@1.1.3, wide-align@^1.1.0: +wide-align@^1.1.0: version "1.1.3" resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA== @@ -7498,10 +7081,10 @@ word-wrap@^1.2.3, word-wrap@~1.2.3: resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== -workerpool@6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.1.0.tgz#a8e038b4c94569596852de7a8ea4228eefdeb37b" - integrity sha512-toV7q9rWNYha963Pl/qyeZ6wG+3nnsyvolaNUS8+R5Wtw6qJPTxIlOP1ZSvcGhEJw+l3HMMmtiNo9Gl61G4GVg== +workerpool@6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.0.tgz#827d93c9ba23ee2019c3ffaff5c27fccea289e8b" + integrity sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A== wrap-ansi@^6.2.0: version "6.2.0" @@ -7526,16 +7109,16 @@ wrappy@1: resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= -ws@8.4.2: +ws@8.5.0: + version "8.5.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.5.0.tgz#bfb4be96600757fe5382de12c670dab984a1ed4f" + integrity sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg== + +ws@^8.2.3: version "8.4.2" resolved "https://registry.yarnpkg.com/ws/-/ws-8.4.2.tgz#18e749868d8439f2268368829042894b6907aa0b" integrity sha512-Kbk4Nxyq7/ZWqr/tarI9yIt/+iNNFOjBXEWgTb4ydaNHBNGgvf2QHbS9fdfsndfjFlFwEd4Al+mw83YkaD10ZA== -ws@^7.4.6: - version "7.5.3" - resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.3.tgz#160835b63c7d97bfab418fc1b8a9fced2ac01a74" - integrity sha512-kQ/dHIzuLrS6Je9+uv81ueZomEwH0qVYstcAQ4/Z93K8zeko9gtAbttJWzoC5ukqXY1PpoouV3+VSOqEAFt5wg== - xev@2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/xev/-/xev-2.0.1.tgz#24484173a22115bc8a990ef5d4d5129695b827a7" @@ -7548,10 +7131,10 @@ xml-js@^1.6.11: dependencies: sax "^1.2.4" -xml-name-validator@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" - integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw== +xml-name-validator@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-4.0.0.tgz#79a006e2e63149a8600f15430f0a4725d1524835" + integrity sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw== xml2js@0.4.19: version "0.4.19" @@ -7695,11 +7278,6 @@ yn@3.1.1: resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== -yocto-queue@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" - integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== - zen-observable-ts@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/zen-observable-ts/-/zen-observable-ts-1.0.0.tgz#30d1202b81d8ba4c489e3781e8ca09abf0075e70" diff --git a/packages/client/.eslintrc.js b/packages/client/.eslintrc.js index d414f86ed3..acbb7c0c6b 100644 --- a/packages/client/.eslintrc.js +++ b/packages/client/.eslintrc.js @@ -18,6 +18,7 @@ module.exports = { // data ぎįĻæ­ĸį†į”ą: æŠŊ蹥įš„すぎるため // e ぎįĻæ­ĸį†į”ą: error や event ãĒãŠã€č¤‡æ•°ãŽã‚­ãƒŧワãƒŧドぎ頭文字であり分かりãĢくいため "id-denylist": ["error", "window", "data", "e"], + 'eqeqeq': ['error', 'always', { 'null': 'ignore' }], "vue/attributes-order": ["error", { "alphabetical": false }], diff --git a/packages/client/package.json b/packages/client/package.json index 71dd89bea4..2957251bfc 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -11,52 +11,50 @@ }, "dependencies": { "@discordapp/twemoji": "13.1.0", + "@fortawesome/fontawesome-free": "6.0.0", "@syuilo/aiscript": "0.11.1", - "@types/dateformat": "3.0.1", "@types/escape-regexp": "0.0.1", "@types/glob": "7.2.0", "@types/gulp": "4.0.9", "@types/gulp-rename": "2.0.1", "@types/is-url": "1.2.30", "@types/katex": "0.11.1", - "@types/matter-js": "0.17.6", - "@types/mocha": "8.2.3", + "@types/matter-js": "0.17.7", + "@types/mocha": "9.1.0", "@types/oauth": "0.9.1", "@types/parse5": "6.0.3", "@types/punycode": "2.1.0", "@types/qrcode": "1.4.2", "@types/random-seed": "0.3.3", - "@types/request-stats": "3.0.0", "@types/seedrandom": "2.4.28", "@types/throttle-debounce": "2.1.0", "@types/tinycolor2": "1.4.3", - "@types/tmp": "0.2.3", "@types/uuid": "8.3.4", "@types/web-push": "3.3.2", "@types/webpack": "5.28.0", "@types/webpack-stream": "3.2.12", - "@types/websocket": "1.0.4", + "@types/websocket": "1.0.5", "@types/ws": "8.2.2", - "@typescript-eslint/parser": "5.10.0", - "@vue/compiler-sfc": "3.2.29", + "@typescript-eslint/parser": "5.12.0", + "@vue/compiler-sfc": "3.2.31", "abort-controller": "3.0.0", "autobind-decorator": "2.4.0", "autosize": "5.0.1", "autwh": "0.1.0", - "blurhash": "1.1.4", - "broadcast-channel": "4.9.0", - "chart.js": "3.7.0", + "blurhash": "1.1.5", + "broadcast-channel": "4.10.0", + "chart.js": "3.7.1", "chartjs-adapter-date-fns": "2.0.0", + "chartjs-plugin-gradient": "0.2.1", "chartjs-plugin-zoom": "1.2.0", "compare-versions": "4.1.3", "content-disposition": "0.5.4", - "crc-32": "1.2.0", - "css-loader": "6.5.1", - "cssnano": "5.0.15", + "css-loader": "6.6.0", + "cssnano": "5.0.17", "date-fns": "2.28.0", "escape-regexp": "0.0.1", - "eslint": "8.7.0", - "eslint-plugin-vue": "8.3.0", + "eslint": "8.9.0", + "eslint-plugin-vue": "8.4.1", "eventemitter3": "4.0.7", "feed": "4.2.2", "glob": "7.2.0", @@ -69,16 +67,16 @@ "langmap": "0.0.16", "matter-js": "0.18.0", "mfm-js": "0.21.0", - "misskey-js": "0.0.13", - "mocha": "8.4.0", + "misskey-js": "0.0.14", + "mocha": "9.2.0", "ms": "2.1.3", "nested-property": "4.0.0", "parse5": "6.0.1", "photoswipe": "git+https://github.com/dimsemenov/photoswipe#v5-beta", "portscanner": "2.2.0", - "postcss": "8.4.5", + "postcss": "8.4.6", "postcss-loader": "6.2.1", - "prismjs": "1.26.0", + "prismjs": "1.27.0", "private-ip": "2.3.3", "promise-limit": "2.7.0", "pug": "3.0.2", @@ -87,11 +85,10 @@ "querystring": "0.2.1", "random-seed": "0.3.0", "reflect-metadata": "0.1.13", - "request-stats": "3.0.0", "rndstr": "1.0.0", "s-age": "1.1.2", - "sass": "1.49.0", - "sass-loader": "12.4.0", + "sass": "1.49.8", + "sass-loader": "12.6.0", "seedrandom": "3.0.5", "strict-event-emitter-types": "2.0.0", "stringz": "2.1.0", @@ -101,9 +98,8 @@ "three": "0.136.0", "throttle-debounce": "3.0.1", "tinycolor2": "1.4.2", - "tmp": "0.2.1", "ts-loader": "9.2.6", - "ts-node": "10.4.0", + "ts-node": "10.5.0", "tsc-alias": "1.5.0", "tsconfig-paths": "3.12.0", "twemoji-parser": "13.1.0", @@ -111,25 +107,23 @@ "uuid": "8.3.2", "v-debounce": "0.1.2", "vanilla-tilt": "1.7.2", - "vue": "3.2.29", + "vue": "3.2.31", "vue-loader": "17.0.0", "vue-prism-editor": "2.0.0-alpha.2", - "vue-router": "4.0.5", + "vue-router": "4.0.12", "vue-style-loader": "4.1.3", "vue-svg-loader": "0.17.0-beta.2", "vuedraggable": "4.0.1", "web-push": "3.4.5", - "webpack": "5.66.0", - "webpack-cli": "4.9.1", + "webpack": "5.69.1", + "webpack-cli": "4.9.2", "websocket": "1.0.34", - "ws": "8.4.2" + "ws": "8.5.0" }, "devDependencies": { - "@redocly/openapi-core": "1.0.0-beta.79", - "@types/fluent-ffmpeg": "2.1.20", - "@typescript-eslint/eslint-plugin": "5.10.0", + "@typescript-eslint/eslint-plugin": "5.12.0", "cross-env": "7.0.3", - "cypress": "9.3.1", + "cypress": "9.5.0", "eslint-plugin-import": "2.25.4", "start-server-and-test": "1.14.0" } diff --git a/packages/client/src/components/chart-tooltip.vue b/packages/client/src/components/chart-tooltip.vue new file mode 100644 index 0000000000..20e094a5a7 --- /dev/null +++ b/packages/client/src/components/chart-tooltip.vue @@ -0,0 +1,51 @@ + + + + + diff --git a/packages/client/src/components/chart.vue b/packages/client/src/components/chart.vue index d17c0c9f3e..b90c790c3f 100644 --- a/packages/client/src/components/chart.vue +++ b/packages/client/src/components/chart.vue @@ -8,7 +8,7 @@ diff --git a/packages/client/src/directives/get-size.ts b/packages/client/src/directives/get-size.ts index e3b5dea0f3..1fcd0718dc 100644 --- a/packages/client/src/directives/get-size.ts +++ b/packages/client/src/directives/get-size.ts @@ -1,34 +1,55 @@ import { Directive } from 'vue'; +const mountings = new Map void; +}>(); + +function calc(src: Element) { + const info = mountings.get(src); + const height = src.clientHeight; + const width = src.clientWidth; + + if (!info) return; + + // ã‚ĸクテã‚Ŗベãƒŧト前ãĒおでsrcが描į”ģされãĻいãĒい場合 + if (!height) { + // IntersectionObserverã§čĄ¨į¤ē検å‡ēする + if (!info.intersection) { + info.intersection = new IntersectionObserver(entries => { + if (entries.some(entry => entry.isIntersecting)) calc(src); + }); + } + info.intersection.observe(src); + return; + } + if (info.intersection) { + info.intersection.disconnect() + delete info.intersection; + }; + + info.fn(width, height); +}; + export default { mounted(src, binding, vn) { - const calc = () => { - const height = src.clientHeight; - const width = src.clientWidth; - // čĻį´ ãŒ(一時įš„ãĢ)DOMãĢ存在しãĒã„ã¨ãã¯č¨ˆįŽ—゚キップ - if (height === 0) return; - - binding.value(width, height); - }; - - calc(); - - // Vue3ではäŊŋえãĒくãĒãŖた - // į„ĄããĻも大丈å¤Ģか...īŧŸ - // TODO: ↑大丈å¤ĢじゃãĒかãŖたぎでč§Ŗæąēį­–ã‚’æŽĸす - //vn.context.$on('hook:activated', calc); - - const ro = new ResizeObserver((entries, observer) => { - calc(); + const resize = new ResizeObserver((entries, observer) => { + calc(src); }); - ro.observe(src); + resize.observe(src); - src._get_size_ro_ = ro; + mountings.set(src, { resize, fn: binding.value, }); + calc(src); }, unmounted(src, binding, vn) { binding.value(0, 0); - src._get_size_ro_.unobserve(src); + const info = mountings.get(src); + if (!info) return; + info.resize.disconnect(); + if (info.intersection) info.intersection.disconnect(); + mountings.delete(src); } -} as Directive; +} as Directive void>; diff --git a/packages/client/src/directives/size.ts b/packages/client/src/directives/size.ts index a72a97abcc..36f649f180 100644 --- a/packages/client/src/directives/size.ts +++ b/packages/client/src/directives/size.ts @@ -1,68 +1,107 @@ import { Directive } from 'vue'; +type Value = { max?: number[]; min?: number[]; }; + //const observers = new Map(); +const mountings = new Map(); + +type ClassOrder = { + add: string[]; + remove: string[]; +}; + +const cache = new Map(); + +function getClassOrder(width: number, queue: Value): ClassOrder { + const getMaxClass = (v: number) => `max-width_${v}px`; + const getMinClass = (v: number) => `min-width_${v}px`; + + return { + add: [ + ...(queue.max ? queue.max.filter(v => width <= v).map(getMaxClass) : []), + ...(queue.min ? queue.min.filter(v => width >= v).map(getMinClass) : []), + ], + remove: [ + ...(queue.max ? queue.max.filter(v => width > v).map(getMaxClass) : []), + ...(queue.min ? queue.min.filter(v => width < v).map(getMinClass) : []), + ] + }; +} + +function applyClassOrder(el: Element, order: ClassOrder) { + el.classList.add(...order.add); + el.classList.remove(...order.remove); +} + +function getOrderName(width: number, queue: Value): string { + return `${width}|${queue.max ? queue.max.join(',') : ''}|${queue.min ? queue.min.join(',') : ''}`; +} + +function calc(el: Element) { + const info = mountings.get(el); + const width = el.clientWidth; + + if (!info || info.previousWidth === width) return; + + // ã‚ĸクテã‚Ŗベãƒŧト前ãĒおでsrcが描į”ģされãĻいãĒい場合 + if (!width) { + // IntersectionObserverã§čĄ¨į¤ē検å‡ēする + if (!info.intersection) { + info.intersection = new IntersectionObserver(entries => { + if (entries.some(entry => entry.isIntersecting)) calc(el); + }); + } + info.intersection.observe(el); + return; + } + if (info.intersection) { + info.intersection.disconnect() + delete info.intersection; + }; + + mountings.set(el, Object.assign(info, { previousWidth: width })); + + const cached = cache.get(getOrderName(width, info.value)); + if (cached) { + applyClassOrder(el, cached); + } else { + const order = getClassOrder(width, info.value); + cache.set(getOrderName(width, info.value), order); + applyClassOrder(el, order); + } +} export default { mounted(src, binding, vn) { - const query = binding.value; + const resize = new ResizeObserver((entries, observer) => { + calc(src); + }); - const addClass = (el: Element, cls: string) => { - el.classList.add(cls); - }; + mountings.set(src, { + value: binding.value, + resize, + previousWidth: 0, + }); - const removeClass = (el: Element, cls: string) => { - el.classList.remove(cls); - }; + calc(src); + resize.observe(src); + }, - const calc = () => { - const width = src.clientWidth; - - // čĻį´ ãŒ(一時įš„ãĢ)DOMãĢ存在しãĒã„ã¨ãã¯č¨ˆįŽ—゚キップ - if (width === 0) return; - - if (query.max) { - for (const v of query.max) { - if (width <= v) { - addClass(src, 'max-width_' + v + 'px'); - } else { - removeClass(src, 'max-width_' + v + 'px'); - } - } - } - if (query.min) { - for (const v of query.min) { - if (width >= v) { - addClass(src, 'min-width_' + v + 'px'); - } else { - removeClass(src, 'min-width_' + v + 'px'); - } - } - } - }; - - calc(); - - window.addEventListener('resize', calc); - - // Vue3ではäŊŋえãĒくãĒãŖた - // į„ĄããĻも大丈å¤Ģか...īŧŸ - // TODO: ↑大丈å¤ĢじゃãĒかãŖたぎでč§Ŗæąēį­–ã‚’æŽĸす - //vn.context.$on('hook:activated', calc); - - //const ro = new ResizeObserver((entries, observer) => { - // calc(); - //}); - - //ro.observe(el); - - // TODO: 新たãĢプロパテã‚ŖをäŊœã‚‹ãŽã‚’やめMapをäŊŋう - // ãŸã ãƒĄãƒĸãƒĒįš„ãĢは↓ぎ斚がįœãƒĄãƒĸãƒĒかもしれãĒã„ãŽã§æ¤œč¨Žä¸­ - //el._ro_ = ro; - src._calc_ = calc; + updated(src, binding, vn) { + mountings.set(src, Object.assign({}, mountings.get(src), { value: binding.value })); + calc(src); }, unmounted(src, binding, vn) { - //el._ro_.unobserve(el); - window.removeEventListener('resize', src._calc_); + const info = mountings.get(src); + if (!info) return; + info.resize.disconnect(); + if (info.intersection) info.intersection.disconnect(); + mountings.delete(src); } -} as Directive; +} as Directive; diff --git a/packages/client/src/directives/tooltip.ts b/packages/client/src/directives/tooltip.ts index fffde14874..dd715227a4 100644 --- a/packages/client/src/directives/tooltip.ts +++ b/packages/client/src/directives/tooltip.ts @@ -48,7 +48,7 @@ export default { popup(import('@/components/ui/tooltip.vue'), { showing, text: self.text, - source: el + targetElement: el, }, {}, 'closed'); self._close = () => { @@ -56,8 +56,8 @@ export default { }; }; - el.addEventListener('selectstart', e => { - e.preventDefault(); + el.addEventListener('selectstart', ev => { + ev.preventDefault(); }); el.addEventListener(start, () => { diff --git a/packages/client/src/init.ts b/packages/client/src/init.ts index 81e41febd1..113324d494 100644 --- a/packages/client/src/init.ts +++ b/packages/client/src/init.ts @@ -14,7 +14,7 @@ if (localStorage.getItem('accounts') != null) { //#endregion import { computed, createApp, watch, markRaw, version as vueVersion } from 'vue'; -import * as compareVersions from 'compare-versions'; +import compareVersions from 'compare-versions'; import widgets from '@/widgets'; import directives from '@/directives'; @@ -32,7 +32,7 @@ import { defaultStore, ColdDeviceStorage } from '@/store'; import { fetchInstance, instance } from '@/instance'; import { makeHotkey } from '@/scripts/hotkey'; import { search } from '@/scripts/search'; -import { isMobile } from '@/scripts/is-mobile'; +import { deviceKind } from '@/scripts/device-kind'; import { initializeSw } from '@/scripts/initialize-sw'; import { reloadChannel } from '@/scripts/unison-reload'; import { reactionPicker } from '@/scripts/reaction-picker'; @@ -92,11 +92,10 @@ window.addEventListener('resize', () => { //#endregion // If mobile, insert the viewport meta tag -if (isMobile || window.innerWidth <= 1024) { +if (['smartphone', 'tablet'].includes(deviceKind)) { const viewport = document.getElementsByName('viewport').item(0); viewport.setAttribute('content', - `${viewport.getAttribute('content')},minimum-scale=1,maximum-scale=1,user-scalable=no`); - document.head.appendChild(viewport); + `${viewport.getAttribute('content')}, minimum-scale=1, maximum-scale=1, user-scalable=no, viewport-fit=cover`); } //#region Set lang attr diff --git a/packages/client/src/os.ts b/packages/client/src/os.ts index f3be5c68fb..95b4e87a1f 100644 --- a/packages/client/src/os.ts +++ b/packages/client/src/os.ts @@ -7,8 +7,10 @@ import * as Misskey from 'misskey-js'; import { apiUrl, url } from '@/config'; import MkPostFormDialog from '@/components/post-form-dialog.vue'; import MkWaitingDialog from '@/components/waiting-dialog.vue'; +import { MenuItem } from '@/types/menu'; import { resolve } from '@/router'; import { $i } from '@/account'; +import { defaultStore } from '@/store'; export const pendingApiRequestsCount = ref(0); @@ -470,7 +472,7 @@ export async function openEmojiPicker(src?: HTMLElement, opts, initialTextarea: }); } -export function popupMenu(items: any[] | Ref, src?: HTMLElement, options?: { +export function popupMenu(items: MenuItem[] | Ref, src?: HTMLElement, options?: { align?: string; width?: number; viaKeyboard?: boolean; @@ -494,7 +496,7 @@ export function popupMenu(items: any[] | Ref, src?: HTMLElement, options? }); } -export function contextMenu(items: any[], ev: MouseEvent) { +export function contextMenu(items: MenuItem[] | Ref, ev: MouseEvent) { ev.preventDefault(); return new Promise((resolve, reject) => { let dispose; @@ -541,7 +543,7 @@ export const uploads = ref<{ img: string; }[]>([]); -export function upload(file: File, folder?: any, name?: string): Promise { +export function upload(file: File, folder?: any, name?: string, keepOriginal: boolean = defaultStore.state.keepOriginalUploading): Promise { if (folder && typeof folder == 'object') folder = folder.id; return new Promise((resolve, reject) => { @@ -559,6 +561,8 @@ export function upload(file: File, folder?: any, name?: string): Promise - +

@@ -65,35 +65,50 @@
+ + + diff --git a/packages/client/src/pages/admin/files.vue b/packages/client/src/pages/admin/files.vue index 87dd12f489..c62f053092 100644 --- a/packages/client/src/pages/admin/files.vue +++ b/packages/client/src/pages/admin/files.vue @@ -28,7 +28,7 @@
- +
- + - {{ pageInfo.title }} + @@ -13,99 +13,89 @@ - diff --git a/packages/client/src/ui/deck.vue b/packages/client/src/ui/deck.vue index 9accc34a88..e4571d4091 100644 --- a/packages/client/src/ui/deck.vue +++ b/packages/client/src/ui/deck.vue @@ -276,7 +276,7 @@ export default defineComponent({ } > * { - font-size: 22px; + font-size: 20px; } &:disabled { diff --git a/packages/client/src/ui/universal.vue b/packages/client/src/ui/universal.vue index 8fe9dcffaf..000fec9b40 100644 --- a/packages/client/src/ui/universal.vue +++ b/packages/client/src/ui/universal.vue @@ -20,7 +20,7 @@ - +
@@ -31,9 +31,9 @@
- + - +
@@ -64,155 +64,133 @@
-