diff --git a/.github/workflows/ok-to-test.yml b/.github/workflows/ok-to-test.yml new file mode 100644 index 0000000000..87af3a6ba6 --- /dev/null +++ b/.github/workflows/ok-to-test.yml @@ -0,0 +1,36 @@ +# If someone with write access comments "/ok-to-test" on a pull request, emit a repository_dispatch event +name: Ok To Test + +on: + issue_comment: + types: [created] + +jobs: + ok-to-test: + runs-on: ubuntu-latest + # Only run for PRs, not issue comments + if: ${{ github.event.issue.pull_request }} + steps: + # Generate a GitHub App installation access token from an App ID and private key + # To create a new GitHub App: + # https://developer.github.com/apps/building-github-apps/creating-a-github-app/ + # See app.yml for an example app manifest + - name: Generate token + id: generate_token + uses: tibdex/github-app-token@v1 + with: + app_id: ${{ secrets.DEPLOYBOT_APP_ID }} + private_key: ${{ secrets.DEPLOYBOT_PRIVATE_KEY }} + + - name: Slash Command Dispatch + uses: peter-evans/slash-command-dispatch@v1 + env: + TOKEN: ${{ steps.generate_token.outputs.token }} + with: + token: ${{ env.TOKEN }} # GitHub App installation access token + # token: ${{ secrets.PERSONAL_ACCESS_TOKEN }} # PAT or OAuth token will also work + reaction-token: ${{ secrets.GITHUB_TOKEN }} + issue-type: pull-request + commands: deploy + named-args: true + permission: write diff --git a/.github/workflows/pr-preview-deploy.yml b/.github/workflows/pr-preview-deploy.yml new file mode 100644 index 0000000000..fd43bce9e6 --- /dev/null +++ b/.github/workflows/pr-preview-deploy.yml @@ -0,0 +1,95 @@ +# Run secret-dependent integration tests only after /deploy approval +on: + pull_request: + types: [opened, reopened, synchronize] + repository_dispatch: + types: [deploy-command] + +name: Deploy preview environment + +jobs: + # Repo owner has commented /deploy on a (fork-based) pull request + deploy-preview-environment: + runs-on: ubuntu-latest + if: + github.event_name == 'repository_dispatch' && + github.event.client_payload.slash_command.sha != '' && + contains(github.event.client_payload.pull_request.head.sha, github.event.client_payload.slash_command.sha) + steps: + - uses: actions/github-script@v5 + id: check-id + env: + number: ${{ github.event.client_payload.pull_request.number }} + job: ${{ github.job }} + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + result-encoding: string + script: | + const { data: pull } = await github.rest.pulls.get({ + ...context.repo, + pull_number: process.env.number + }); + const ref = pull.head.sha; + + const { data: checks } = await github.rest.checks.listForRef({ + ...context.repo, + ref + }); + + const check = checks.check_runs.filter(c => c.name === process.env.job); + + return check[0].id; + + - uses: actions/github-script@v5 + env: + check_id: ${{ steps.check-id.outputs.result }} + details_url: ${{ github.server_url }}/${{ github.repository }}/runs/${{ github.run_id }} + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + await github.rest.checks.update({ + ...context.repo, + check_run_id: process.env.check_id, + status: 'in_progress', + details_url: process.env.details_url + }); + + # Check out merge commit + - name: Fork based /deploy checkout + uses: actions/checkout@v2 + with: + ref: 'refs/pull/${{ github.event.client_payload.pull_request.number }}/merge' + + # + - name: Context + uses: okteto/context@latest + with: + token: ${{ secrets.OKTETO_TOKEN }} + + - name: Deploy preview environment + uses: ikuradon/deploy-preview@latest + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + name: pr-${{ github.event.client_payload.pull_request.number }}-syuilo + timeout: 15m + + # Update check run called "integration-fork" + - uses: actions/github-script@v5 + id: update-check-run + if: ${{ always() }} + env: + # Conveniently, job.status maps to https://developer.github.com/v3/checks/runs/#update-a-check-run + conclusion: ${{ job.status }} + check_id: ${{ steps.check-id.outputs.result }} + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const { data: result } = await github.rest.checks.update({ + ...context.repo, + check_run_id: process.env.check_id, + status: 'completed', + conclusion: process.env.conclusion + }); + + return result; diff --git a/.github/workflows/pr-preview-destroy.yml b/.github/workflows/pr-preview-destroy.yml new file mode 100644 index 0000000000..c14c3db5c5 --- /dev/null +++ b/.github/workflows/pr-preview-destroy.yml @@ -0,0 +1,21 @@ +# file: .github/workflows/preview-closed.yaml +on: + pull_request: + types: + - closed + +name: Destroy preview environment + +jobs: + destroy-preview-environment: + runs-on: ubuntu-latest + steps: + - name: Context + uses: okteto/context@latest + with: + token: ${{ secrets.OKTETO_TOKEN }} + + - name: Destroy preview environment + uses: okteto/destroy-preview@latest + with: + name: pr-${{ github.event.number }}-syuilo diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d44717fc09..297f93b61e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -98,7 +98,7 @@ jobs: - name: ALSA Env run: echo -e 'pcm.!default {\n type hw\n card 0\n}\n\nctl.!default {\n type hw\n card 0\n}' > ~/.asoundrc - name: Cypress run - uses: cypress-io/github-action@v2 + uses: cypress-io/github-action@v4 with: install: false start: yarn start:test diff --git a/.node-version b/.node-version index c9b6b29e00..7fd023741b 100644 --- a/.node-version +++ b/.node-version @@ -1 +1 @@ -v16.0.0 +v16.15.0 diff --git a/okteto.yml b/.okteto/okteto-pipeline.yml similarity index 100% rename from okteto.yml rename to .okteto/okteto-pipeline.yml diff --git a/CHANGELOG.md b/CHANGELOG.md index b2e3d4d1f5..7869f68b00 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,14 +10,37 @@ You should also include the user name that made the change. --> ## 12.x.x (unreleased) + +### Improvements +- Server: Add rate limit to i/notifications @tamaina +- Client: Improve files page of control panel @syuilo + +### Bugfixes +- Server: Fix GenerateVideoThumbnail failed @mei23 +- Server: Ensure temp directory cleanup @Johann150 + +## 12.111.1 (2022/06/13) + +### Bugfixes +- some fixes of multiple notification read @tamaina +- some GenerateVideoThumbnail failed @Johann150 +- Client: デッキでウィジェットの情報が保存されない問題を修正 @syuilo +- Client: ギャラリーの投稿を開こうとすると編集画面が表示される @futchitwo + +## 12.111.0 (2022/06/11) +### Note +- Node.js 16.15.0 or later is required + ### Improvements - Supports Unicode Emoji 14.0 @mei23 - プッシュ通知を複数アカウント対応に #7667 @tamaina - プッシュ通知にクリックやactionを設定 #7667 @tamaina - ドライブに画像ファイルをアップロードするときオリジナル画像を破棄してwebpublicのみ保持するオプション @tamaina - Server: always remove completed tasks of job queue @Johann150 +- Client: アバターの設定で画像をクロップできるように @syuilo - Client: make emoji stand out more on reaction button @Johann150 - Client: display URL of QR code for TOTP registration @tamaina +- Client: render quote renote CWs as MFM @pixeldesu - API: notifications/readは配列でも受け付けるように #7667 @tamaina - API: ユーザー検索で、クエリがusernameの条件を満たす場合はusernameもLIKE検索するように @tamaina - MFM: Allow speed changes in all animated MFMs @Johann150 @@ -31,16 +54,14 @@ You should also include the user name that made the change. ### Bugfixes - Server: keep file order of note attachement @Johann150 -- Server: fix caching @Johann150 -- Server: await promises when following or unfollowing users @Johann150 - Server: fix missing foreign key for reports leading to reports page being unusable @Johann150 - Server: fix internal in-memory caching @Johann150 -- Server: use correct order of attachments on notes @Johann150 - Server: prevent crash when processing certain PNGs @syuilo - Server: Fix unable to generate video thumbnails @mei23 - Server: Fix `Cannot find module` issue @mei23 - Federation: Add rel attribute to host-meta @mei23 - Federation: add id for activitypub follows @Johann150 +- Federation: use `source` instead of `_misskey_content` @Johann150 - Federation: ensure resolver does not fetch local resources via HTTP(S) @Johann150 - Federation: correctly render empty note text @Johann150 - Federation: Fix quote renotes containing no text being federated correctly @Johann150 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a2e8319f45..c108bc8dfc 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -66,6 +66,13 @@ Be willing to comment on the good points and not just the things you want fixed - Are there any omissions or gaps? - Does it check for anomalies? +## Deploy +The `/deploy` command by issue comment can be used to deploy the contents of a PR to the preview environment. +``` +/deploy sha= +``` +An actual domain will be assigned so you can test the federation. + ## Merge For now, basically only @syuilo has the authority to merge PRs into develop because he is most familiar with the codebase. However, minor fixes, refactoring, and urgent changes may be merged at the discretion of a contributor. @@ -77,9 +84,9 @@ However, minor fixes, refactoring, and urgent changes may be merged at the discr - Into `master` from `develop` branch. - The title must be in the format `Release: x.y.z`. - `x.y.z` is the new version you are trying to release. - - Assign about 2~3 reviewers. -3. The release PR is approved, merge it. -4. Create a [release of GitHub](https://github.com/misskey-dev/misskey/releases) +3. Deploy and perform a simple QA check. Also verify that the tests passed. +4. Merge it. +5. Create a [release of GitHub](https://github.com/misskey-dev/misskey/releases) - The target branch must be `master` - The tag name must be the version diff --git a/cypress.config.ts b/cypress.config.ts new file mode 100644 index 0000000000..e390c41a54 --- /dev/null +++ b/cypress.config.ts @@ -0,0 +1,12 @@ +import { defineConfig } from 'cypress' + +export default defineConfig({ + e2e: { + // We've imported your old cypress plugins here. + // You may want to clean this up later by importing these. + setupNodeEvents(on, config) { + return require('./cypress/plugins/index.js')(on, config) + }, + baseUrl: 'http://localhost:61812', + }, +}) diff --git a/cypress.json b/cypress.json deleted file mode 100644 index e858e480b0..0000000000 --- a/cypress.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "baseUrl": "http://localhost:61812" -} diff --git a/cypress/integration/basic.js b/cypress/e2e/basic.cy.js similarity index 100% rename from cypress/integration/basic.js rename to cypress/e2e/basic.cy.js diff --git a/cypress/integration/widgets.js b/cypress/e2e/widgets.cy.js similarity index 100% rename from cypress/integration/widgets.js rename to cypress/e2e/widgets.cy.js diff --git a/cypress/support/index.js b/cypress/support/e2e.js similarity index 100% rename from cypress/support/index.js rename to cypress/support/e2e.js diff --git a/locales/ar-SA.yml b/locales/ar-SA.yml index efad7c954b..3bd8f1e506 100644 --- a/locales/ar-SA.yml +++ b/locales/ar-SA.yml @@ -1007,7 +1007,6 @@ _sfx: antenna: "الهوائيات" channel: "إشعارات القنات" _ago: - unknown: "مجهول" future: "المستقبَل" justNow: "اللحظة" secondsAgo: "منذ {n} ثوانٍ" diff --git a/locales/bn-BD.yml b/locales/bn-BD.yml index bcecb06253..d7753b6dcf 100644 --- a/locales/bn-BD.yml +++ b/locales/bn-BD.yml @@ -831,11 +831,18 @@ themeColor: "থিমের রং" size: "আকার" numberOfColumn: "কলামের সংখ্যা" searchByGoogle: "গুগল" +instanceDefaultLightTheme: "ইন্সট্যান্সের ডিফল্ট লাইট থিম" +instanceDefaultDarkTheme: "ইন্সট্যান্সের ডিফল্ট ডার্ক থিম" +instanceDefaultThemeDescription: "অবজেক্ট ফরম্যাটে থিম কোড লিখুন" +mutePeriod: "মিউটের সময়কাল" indefinitely: "অনির্দিষ্ট" tenMinutes: "১০ মিনিট" oneHour: "১ ঘণ্টা" oneDay: "একদিন" oneWeek: "এক সপ্তাহ" +reflectMayTakeTime: "এটির কাজ দেখা যেতে কিছুটা সময় লাগতে পারে।" +failedToFetchAccountInformation: "অ্যাকাউন্টের তথ্য উদ্ধার করা যায়নি" +rateLimitExceeded: "রেট লিমিট ছাড়িয়ে গেছে " _emailUnavailable: used: "এই ইমেইল ঠিকানাটি ইতোমধ্যে ব্যবহৃত হয়েছে" format: "এই ইমেল ঠিকানাটি সঠিকভাবে লিখা হয়নি" @@ -1081,7 +1088,6 @@ _sfx: antenna: "অ্যান্টেনাগুলি" channel: "চ্যানেলের বিজ্ঞপ্তি" _ago: - unknown: "অজানা" future: "ভবিষ্যৎ" justNow: "এইমাত্র" secondsAgo: "{n} সেকেন্ড আগে" @@ -1125,6 +1131,7 @@ _2fa: registerKey: "সিকিউরিটি কী নিবন্ধন করুন" step1: "প্রথমে, আপনার ডিভাইসে {a} বা {b} এর মতো একটি অথেনটিকেশন অ্যাপ ইনস্টল করুন৷" step2: "এরপরে, অ্যাপের সাহায্যে প্রদর্শিত QR কোডটি স্ক্যান করুন।" + step2Url: "ডেস্কটপ অ্যাপে, নিম্নলিখিত URL লিখুন:" step3: "অ্যাপে প্রদর্শিত টোকেনটি লিখুন এবং আপনার কাজ শেষ।" step4: "আপনাকে এখন থেকে লগ ইন করার সময়, এইভাবে টোকেন লিখতে হবে।" securityKeyInfo: "আপনি একটি হার্ডওয়্যার সিকিউরিটি কী ব্যবহার করে লগ ইন করতে পারেন যা FIDO2 বা ডিভাইসের ফিঙ্গারপ্রিন্ট সেন্সর বা পিন সমর্থন করে৷" @@ -1608,6 +1615,8 @@ _notification: youReceivedFollowRequest: "অনুসরণ করার জন্য অনুরোধ পাওয়া গেছে" yourFollowRequestAccepted: "আপনার অনুসরণ করার অনুরোধ গৃহীত হয়েছে" youWereInvitedToGroup: "আপনি একটি গ্রুপে আমন্ত্রিত হয়েছেন" + pollEnded: "পোলের ফলাফল দেখা যাবে" + emptyPushNotificationMessage: "আপডেট করা পুশ বিজ্ঞপ্তি" _types: all: "সকল" follow: "অনুসরণ করা হচ্ছে" @@ -1617,11 +1626,13 @@ _notification: quote: "উদ্ধৃতি" reaction: "প্রতিক্রিয়া" pollVote: "পোলে ভোট আছে" + pollEnded: "পোল শেষ" receiveFollowRequest: "প্রাপ্ত অনুসরণের অনুরোধসমূহ" followRequestAccepted: "গৃহীত অনুসরণের অনুরোধসমূহ" groupInvited: "গ্রুপের আমন্ত্রনসমূহ" app: "লিঙ্ক করা অ্যাপ থেকে বিজ্ঞপ্তি" _actions: + followBack: "ফলো ব্যাক করেছে" reply: "জবাব" renote: "রিনোট" _deck: diff --git a/locales/ca-ES.yml b/locales/ca-ES.yml index 577fbca2ae..74eab3603b 100644 --- a/locales/ca-ES.yml +++ b/locales/ca-ES.yml @@ -134,6 +134,8 @@ _theme: _sfx: note: "Notes" notification: "Notificacions" +_2fa: + step2Url: "També pots inserir aquest enllaç i utilitzes una aplicació d'escriptori:" _widgets: notifications: "Notificacions" timeline: "Línia de temps" diff --git a/locales/de-DE.yml b/locales/de-DE.yml index a3ff6c0a6c..5dfce28002 100644 --- a/locales/de-DE.yml +++ b/locales/de-DE.yml @@ -842,6 +842,9 @@ oneDay: "Einen Tag" oneWeek: "Eine Woche" reflectMayTakeTime: "Es kann etwas dauern, bis sich dies widerspiegelt." failedToFetchAccountInformation: "Benutzerkontoinformationen konnten nicht abgefragt werden" +rateLimitExceeded: "Versuchsanzahl überschritten" +cropImage: "Bild zuschneiden" +cropImageAsk: "Möchtest du das Bild zuschneiden?" _emailUnavailable: used: "Diese Email-Adresse wird bereits verwendet" format: "Das Format dieser Email-Adresse ist ungültig" @@ -1087,7 +1090,6 @@ _sfx: antenna: "Antennen" channel: "Kanalbenachrichtigung" _ago: - unknown: "Unbekannt" future: "Zukunft" justNow: "Gerade eben" secondsAgo: "vor {n} Sekunde(n)" @@ -1131,6 +1133,7 @@ _2fa: registerKey: "Neuen Sicherheitsschlüssel registrieren" step1: "Installiere zuerst eine Authentifizierungsapp (z.B. {a} oder {b}) auf deinem Gerät." step2: "Dann, scanne den angezeigten QR-Code mit deinem Gerät." + step2Url: "Nutzt du ein Desktopprogramm kannst du alternativ diese URL eingeben:" step3: "Gib zum Abschluss den Token ein, der von deiner App angezeigt wird." step4: "Alle folgenden Anmeldungsversuche werden ab sofort die Eingabe eines solchen Tokens benötigen." securityKeyInfo: "Du kannst neben Fingerabdruck- oder PIN-Authentifizierung auf deinem Gerät auch Anmeldung mit Hilfe eines FIDO2-kompatiblen Hardware-Sicherheitsschlüssels einrichten." diff --git a/locales/en-US.yml b/locales/en-US.yml index a7d69d6d4f..8bfea26b0f 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -842,6 +842,9 @@ oneDay: "One day" oneWeek: "One week" reflectMayTakeTime: "It may take some time for this to be reflected." failedToFetchAccountInformation: "Could not fetch account information" +rateLimitExceeded: "Rate limit exceeded" +cropImage: "Crop image" +cropImageAsk: "Do you want to crop this image?" _emailUnavailable: used: "This email address is already being used" format: "The format of this email address is invalid" @@ -1087,7 +1090,6 @@ _sfx: antenna: "Antennas" channel: "Channel notifications" _ago: - unknown: "Unknown" future: "Future" justNow: "Just now" secondsAgo: "{n} second(s) ago" @@ -1131,6 +1133,7 @@ _2fa: registerKey: "Register a security key" step1: "First, install an authentication app (such as {a} or {b}) on your device." step2: "Then, scan the QR code displayed on this screen." + step2Url: "You can also enter this URL if you're using a desktop program:" step3: "Enter the token provided by your app to finish setup." step4: "From now on, any future login attempts will ask for such a login token." securityKeyInfo: "Besides fingerprint or PIN authentication, you can also setup authentication via hardware security keys that support FIDO2 to further secure your account." diff --git a/locales/es-ES.yml b/locales/es-ES.yml index 8fff5ca4d9..6c10942b48 100644 --- a/locales/es-ES.yml +++ b/locales/es-ES.yml @@ -989,7 +989,6 @@ _sfx: antenna: "Antena receptora" channel: "Notificaciones del canal" _ago: - unknown: "Desconocido" future: "Futuro" justNow: "Recién ahora" secondsAgo: "Hace {n} segundos" diff --git a/locales/fr-FR.yml b/locales/fr-FR.yml index 3b5dc1b06c..7e225c2992 100644 --- a/locales/fr-FR.yml +++ b/locales/fr-FR.yml @@ -804,7 +804,7 @@ manageAccounts: "Gérer les comptes" 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" +muteThread: "Masquer cette discussion" unmuteThread: "Ne plus masquer le fil" ffVisibility: "Visibilité des abonnés/abonnements" ffVisibilityDescription: "Permet de configurer qui peut voir les personnes que tu suis et les personnes qui te suivent." @@ -1075,7 +1075,6 @@ _sfx: antenna: "Réception de l’antenne" channel: "Notifications de canal" _ago: - unknown: "Inconnu" future: "Futur" justNow: "à l’instant" secondsAgo: "Il y a {n}s" diff --git a/locales/id-ID.yml b/locales/id-ID.yml index a97ac819a9..39e2c1f661 100644 --- a/locales/id-ID.yml +++ b/locales/id-ID.yml @@ -842,6 +842,9 @@ oneDay: "1 Hari" oneWeek: "1 Bulan" reflectMayTakeTime: "Mungkin perlu beberapa saat untuk dicerminkan." failedToFetchAccountInformation: "Gagal untuk mendapatkan informasi akun" +rateLimitExceeded: "Batas sudah terlampaui" +cropImage: "potong gambar" +cropImageAsk: "Ingin memotong gambar?" _emailUnavailable: used: "Alamat surel ini telah digunakan" format: "Format tidak valid." @@ -1087,7 +1090,6 @@ _sfx: antenna: "Penerimaan Antenna" channel: "Pemberitahuan saluran" _ago: - unknown: "Tidak diketahui" future: "Masa depan" justNow: "Baru saja" secondsAgo: "{n} detik lalu" @@ -1131,6 +1133,7 @@ _2fa: registerKey: "Daftarkan kunci keamanan baru" step1: "Pertama, pasang aplikasi otentikasi (seperti {a} atau {b}) di perangkat kamu." step2: "Lalu, pindai kode QR yang ada di layar." + step2Url: "Di aplikasi desktop, masukkan URL berikut:" step3: "Masukkan token yang telah disediakan oleh aplikasimu untuk menyelesaikan pemasangan." step4: "Mulai sekarang, upaya login apapun akan meminta token login dari aplikasi otentikasi kamu." securityKeyInfo: "Kamu dapat memasang otentikasi WebAuthN untuk mengamankan proses login lebih lanjut dengan tidak hanya perangkat keras kunci keamanan yang mendukung FIDO2, namun juga sidik jari atau otentikasi PIN pada perangkatmu." diff --git a/locales/it-IT.yml b/locales/it-IT.yml index 4d10356698..8584ed6a8e 100644 --- a/locales/it-IT.yml +++ b/locales/it-IT.yml @@ -10,7 +10,7 @@ password: "Password" forgotPassword: "Hai dimenticato la tua password?" fetchingAsApObject: "Recuperando dal Fediverso..." ok: "OK" -gotIt: "Capito!" +gotIt: "Ho capito" cancel: "Annulla" enterUsername: "Inserisci un nome utente" renotedBy: "Rinotato da {user}" @@ -767,6 +767,7 @@ customCss: "CSS personalizzato" global: "Federata" squareAvatars: "Mostra l'immagine del profilo come quadrato" sent: "Inviare" +received: "Ricevuto" searchResult: "Risultati della Ricerca" hashtags: "Hashtag" troubleshooting: "Risoluzione problemi" @@ -804,6 +805,10 @@ welcomeBackWithName: "Bentornato/a, {name}" clickToFinishEmailVerification: "Fai click su [{ok}] per completare la verifica dell'indirizzo email." searchByGoogle: "Cerca" indefinitely: "Non scade" +tenMinutes: "10 minuti" +oneHour: "1 ora" +oneDay: "1 giorno" +oneWeek: "1 settimana" _emailUnavailable: used: "Email già in uso" format: "Formato email non valido" @@ -999,7 +1004,6 @@ _sfx: antenna: "Ricezione dell'antenna" channel: "Notifiche di canale" _ago: - unknown: "Sconosciuto" future: "Futuro" justNow: "Ora" secondsAgo: "{n}s fa" diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 9cd1d1eedb..43ab7f2d69 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -843,6 +843,8 @@ oneWeek: "1週間" reflectMayTakeTime: "反映されるまで時間がかかる場合があります。" failedToFetchAccountInformation: "アカウント情報の取得に失敗しました" rateLimitExceeded: "レート制限を超えました" +cropImage: "画像のクロップ" +cropImageAsk: "画像をクロップしますか?" _emailUnavailable: used: "既に使用されています" diff --git a/locales/ja-KS.yml b/locales/ja-KS.yml index fd6945160e..5458152dda 100644 --- a/locales/ja-KS.yml +++ b/locales/ja-KS.yml @@ -799,7 +799,6 @@ _sfx: notification: "通知" chat: "チャット" _ago: - unknown: "わからん" future: "未来" justNow: "たった今" secondsAgo: "{n}秒前" diff --git a/locales/ko-KR.yml b/locales/ko-KR.yml index 74d06185d9..e0a839a2c8 100644 --- a/locales/ko-KR.yml +++ b/locales/ko-KR.yml @@ -842,6 +842,9 @@ oneDay: "1일" oneWeek: "일주일" reflectMayTakeTime: "반영되기까지 시간이 걸릴 수 있습니다." failedToFetchAccountInformation: "계정 정보를 가져오지 못했습니다" +rateLimitExceeded: "요청 제한 횟수를 초과하였습니다" +cropImage: "이미지 자르기" +cropImageAsk: "이미지를 자르시겠습니까?" _emailUnavailable: used: "이 메일 주소는 사용중입니다" format: "형식이 올바르지 않습니다" @@ -1087,7 +1090,6 @@ _sfx: antenna: "안테나 수신" channel: "채널 알림" _ago: - unknown: "알 수 없음" future: "미래" justNow: "방금 전" secondsAgo: "{n}초 전" @@ -1131,6 +1133,7 @@ _2fa: registerKey: "키를 등록" step1: "먼저, {a}나 {b}등의 인증 앱을 사용 중인 디바이스에 설치합니다." step2: "그 후, 표시되어 있는 QR코드를 앱으로 스캔합니다." + step2Url: "데스크톱 앱에서는 다음 URL을 입력하세요:" step3: "앱에 표시된 토큰을 입력하시면 완료됩니다." step4: "다음 로그인부터는 토큰을 입력해야 합니다." securityKeyInfo: "FIDO2를 지원하는 하드웨어 보안 키 혹은 디바이스의 지문인식이나 화면잠금 PIN을 이용해서 로그인하도록 설정할 수 있습니다." diff --git a/locales/nl-NL.yml b/locales/nl-NL.yml index d0a83eb6a8..0ded573948 100644 --- a/locales/nl-NL.yml +++ b/locales/nl-NL.yml @@ -303,6 +303,8 @@ muteThread: "Discussies dempen " unmuteThread: "Dempen van discussie ongedaan maken" hide: "Verbergen" searchByGoogle: "Zoeken" +cropImage: "Afbeelding bijsnijden" +cropImageAsk: "Bijsnijdengevraagd" _email: _follow: title: "volgde jou" diff --git a/locales/pl-PL.yml b/locales/pl-PL.yml index 7fabab3b4f..fa1dad2173 100644 --- a/locales/pl-PL.yml +++ b/locales/pl-PL.yml @@ -946,7 +946,6 @@ _sfx: chatBg: "Rozmowy (tło)" channel: "Powiadomienia kanału" _ago: - unknown: "Nieznane" future: "W przyszłości" justNow: "Przed chwilą" secondsAgo: "{n} sek. temu" diff --git a/locales/pt-PT.yml b/locales/pt-PT.yml index c1afa5b56c..0dc15a27bb 100644 --- a/locales/pt-PT.yml +++ b/locales/pt-PT.yml @@ -81,18 +81,67 @@ somethingHappened: "Ocorreu um erro" retry: "Tentar novamente" pageLoadError: "Ocorreu um erro ao carregar a página." pageLoadErrorDescription: "Isto é normalmente causado por erros de rede ou pela cache do browser. Experimenta limpar a cache e tenta novamente após algum tempo." +serverIsDead: "O servidor não está respondendo. Por favor espere um pouco e tente novamente." +youShouldUpgradeClient: "Para visualizar essa página, por favor recarregue-a para atualizar seu cliente." +enterListName: "Insira um nome para a lista" +privacy: "Privacidade" +makeFollowManuallyApprove: "Pedidos de seguimento precisam ser aprovados" +defaultNoteVisibility: "Visibilidade padrão" follow: "Seguindo" +followRequest: "Mandar pedido de seguimento" +followRequests: "Pedidos de seguimento" +unfollow: "Deixar de seguir" +followRequestPending: "Pedido de seguimento pendente" enterEmoji: "Inserir emoji" renote: "Repostar" renoted: "Repostado" cantRenote: "Não pode repostar" cantReRenote: "Não pode repostar este repost" +quote: "Citar" pinnedNote: "Post fixado" pinned: "Afixar no perfil" +you: "Você" +clickToShow: "Clique para ver" sensitive: "Conteúdo sensível" +add: "Adicionar" +reaction: "Reações" +reactionSetting: "Quais reações a mostrar no selecionador de reações" +rememberNoteVisibility: "Lembrar das configurações de visibilidade de notas" +attachCancel: "Remover anexo" +markAsSensitive: "Marcar como sensível" +unmarkAsSensitive: "Desmarcar como sensível" +enterFileName: "Digite o nome do ficheiro" mute: "Silenciar" unmute: "Dessilenciar" +block: "Bloquear" +unblock: "Desbloquear" +suspend: "Suspender" +unsuspend: "Cancelar suspensão" +blockConfirm: "Tem certeza que gostaria de bloquear essa conta?" +unblockConfirm: "Tem certeza que gostaria de desbloquear essa conta?" +suspendConfirm: "Tem certeza que gostaria de suspender essa conta?" +unsuspendConfirm: "Tem certeza que gostaria de cancelar a suspensão dessa conta?" +selectList: "Escolhe uma lista" +selectAntenna: "Escolhe uma antena" +selectWidget: "Escolhe um widget" +editWidgets: "Editar widgets" +editWidgetsExit: "Pronto" +customEmojis: "Emoji personalizado" +emoji: "Emoji" +emojis: "Emojis" +emojiName: "Nome do Emoji" +emojiUrl: "URL do Emoji" +addEmoji: "Adicionar um Emoji" settingGuide: "Guia de configuração" +flagAsBot: "Marcar conta como robô" +flagAsCat: "Marcar conta como gato" +flagAsCatDescription: "Ative essa opção para marcar essa conta como gato." +flagShowTimelineReplies: "Mostrar respostas na linha de tempo" +general: "Geral" +wallpaper: "Papel de parede" +searchWith: "Buscar: {q}" +youHaveNoLists: "Não tem nenhuma lista" +followConfirm: "Tem certeza que quer deixar de seguir {name}?" instances: "Instância" registeredAt: "Registrado em" perHour: "por hora" @@ -107,6 +156,8 @@ nsfw: "Conteúdo sensível" monthX: "mês de {month}" pinnedNotes: "Post fixado" userList: "Listas" +none: "Nenhum" +output: "Resultado" smtpUser: "Nome de usuário" smtpPass: "Senha" user: "Usuários" @@ -116,6 +167,8 @@ _email: title: "Você tem um novo seguidor" _mfm: mention: "Menção" + quote: "Citar" + emoji: "Emoji personalizado" search: "Pesquisar" _theme: keys: @@ -136,38 +189,229 @@ _profile: _exportOrImport: followingList: "Seguindo" muteList: "Silenciar" + blockingList: "Bloquear" userLists: "Listas" _pages: + blocks: + _button: + _action: + _pushEvent: + event: "Nome do evento" + message: "Mostrar mensagem quando ativado" + variable: "Variável a mandar" + no-variable: "Nenhum" + callAiScript: "Invocar AiScript" + _callAiScript: + functionName: "Nome da função" + radioButton: "Escolha" + _radioButton: + values: "Lista de escolhas separadas por quebras de texto" script: categories: + logical: "Operação lógica" + operation: "Cálculos" + comparison: "Comparação" list: "Listas" blocks: + _strReplace: + arg2: "Texto que irá ser substituído" + arg3: "Substituir com" + strReverse: "Virar texto" + join: "Sequência de texto" _join: arg1: "Listas" + arg2: "Separador" + add: "Somar" + _add: + arg1: "A" + arg2: "B" + subtract: "Subtrair" + _subtract: + arg1: "A" + arg2: "B" + multiply: "Multiplicar" + _multiply: + arg1: "A" + arg2: "B" + divide: "Dividir" + _divide: + arg1: "A" + arg2: "B" + mod: "O resto de" + _mod: + arg1: "A" + arg2: "B" + round: "Arredondar decimal" + _round: + arg1: "Numérico" + eq: "A e B são iguais" + _eq: + arg1: "A" + arg2: "B" + notEq: "A e B são diferentes" + _notEq: + arg1: "A" + arg2: "B" + and: "A e B" + _and: + arg1: "A" + arg2: "B" + or: "A OU B" + _or: + arg1: "A" + arg2: "B" + lt: "< A é menor do que B" + _lt: + arg1: "A" + arg2: "B" + gt: "> A é maior do que B" + _gt: + arg1: "A" + arg2: "B" + ltEq: "<= A é maior ou igual a B" + _ltEq: + arg1: "A" + arg2: "B" + gtEq: ">= A é maior ou igual a B" + _gtEq: + arg1: "A" + arg2: "B" + if: "Galho" + _if: + arg1: "Se" + arg2: "Então" + arg3: "Se não" + not: "NÃO" + _not: + arg1: "NÃO" + random: "Aleatório" + _random: + arg1: "Probabilidade" + rannum: "Numeral aleatório" + _rannum: + arg1: "Valor mínimo" + arg2: "Valor máximo" + randomPick: "Escolher aleatoriamente de uma lista" _randomPick: arg1: "Listas" + dailyRandom: "Aleatório (Muda uma vez por dia para cada usuário)" + _dailyRandom: + arg1: "Probabilidade" + dailyRannum: "Numeral aleatório (Muda uma vez por dia para cada usuário)" + _dailyRannum: + arg1: "Valor mínimo" + arg2: "Valor máximo" + dailyRandomPick: "Escolher aleatoriamente de uma lista (Muda uma vez por dia para cada usuário)" _dailyRandomPick: arg1: "Listas" + seedRandom: "Aleatório (com semente)" + _seedRandom: + arg1: "Semente" + arg2: "Probabilidade" + seedRannum: "Número aleatório (com semente)" + _seedRannum: + arg1: "Semente" + arg2: "Valor mínimo" + arg3: "Valor máximo" + seedRandomPick: "Escolher aleatoriamente de uma lista (com uma semente)" _seedRandomPick: + arg1: "Semente" arg2: "Listas" + DRPWPM: "Escolher aleatoriamente de uma lista ponderada (Muda uma vez por dia para cada usuário)" + _DRPWPM: + arg1: "Lista de texto" + pick: "Escolhe a partir da lista" _pick: arg1: "Listas" + arg2: "Posição" + listLen: "Pegar comprimento da lista" _listLen: arg1: "Listas" + number: "Numérico" + stringToNumber: "Texto para numérico" + _stringToNumber: + arg1: "Texto" + numberToString: "Numérico para texto" + _numberToString: + arg1: "Numérico" + splitStrByLine: "Dividir texto por quebras" + _splitStrByLine: + arg1: "Texto" + ref: "Variável" + aiScriptVar: "Variável AiScript" + fn: "Função" + _fn: + slots: "Espaços" + slots-info: "Separar cada espaço com uma quebra de texto" + arg1: "Resultado" + for: "Repetição 'for'" + _for: + arg1: "Número de repetições" + arg2: "Ação" + typeError: "Espaço {slot} aceita valores de tipo \"{expect}\", mas o valor dado é do tipo \"{actual}\"!" + thereIsEmptySlot: "O espaço {slot} está vazio!" types: + string: "Texto" + number: "Numérico" array: "Listas" + stringArray: "Lista de texto" + emptySlot: "Espaço vazio" + enviromentVariables: "Variáveis de ambiente" + pageVariables: "Variáveis de página" +_relayStatus: + requesting: "Pendente" + accepted: "Aprovado" + rejected: "Recusado" _notification: + fileUploaded: "Carregamento de arquivo efetuado com sucesso" + youGotMention: "{name} te mencionou" + youGotReply: "{name} te respondeu" + youGotQuote: "{name} te citou" + youGotPoll: "{name} votou em sua enquete" + youGotMessagingMessageFromUser: "{name} te mandou uma mensagem de bate-papo" + youGotMessagingMessageFromGroup: "Uma mensagem foi mandada para o grupo {name}" youWereFollowed: "Você tem um novo seguidor" + youReceivedFollowRequest: "Você recebeu um pedido de seguimento" + yourFollowRequestAccepted: "Seu pedido de seguimento foi aceito" + youWereInvitedToGroup: "{userName} te convidou para um grupo" + pollEnded: "Os resultados da enquete agora estão disponíveis" + emptyPushNotificationMessage: "As notificações de alerta foram atualizadas" _types: + all: "Todos" follow: "Seguindo" mention: "Menção" + reply: "Respostas" renote: "Repostar" + quote: "Citar" + reaction: "Reações" + pollVote: "Votações em enquetes" + pollEnded: "Enquetes terminando" + receiveFollowRequest: "Recebeu pedidos de seguimento" + followRequestAccepted: "Aceitou pedidos de seguimento" + groupInvited: "Convites de grupo" + app: "Notificações de aplicativos conectados" _actions: + followBack: "te seguiu de volta" reply: "Responder" renote: "Repostar" _deck: + alwaysShowMainColumn: "Sempre mostrar a coluna principal" + columnAlign: "Alinhar colunas" + columnMargin: "Margem entre colunas" + columnHeaderHeight: "Altura do cabeçalho de coluna" + addColumn: "Adicionar coluna" + swapLeft: "Trocar de posição com a coluna à esquerda" + swapRight: "Trocar de posição com a coluna à direita" + swapUp: "Trocar de posição com a coluna acima" + swapDown: "Trocar de posição com a coluna abaixo" + popRight: "Acoplar coluna à direita" + profile: "Perfil" _columns: + main: "Principal" + widgets: "Widgets" notifications: "Notificações" tl: "Timeline" + antenna: "Antenas" list: "Listas" mentions: "Menções" + direct: "Notas diretas" diff --git a/locales/ru-RU.yml b/locales/ru-RU.yml index 7405c07e6c..c44589a7e5 100644 --- a/locales/ru-RU.yml +++ b/locales/ru-RU.yml @@ -141,6 +141,8 @@ flagAsBot: "Аккаунт бота" flagAsBotDescription: "Включите, если этот аккаунт управляется программой. Это позволит системе Misskey учитывать это, а также поможет разработчикам других ботов предотвратить бесконечные циклы взаимодействия." flagAsCat: "Аккаунт кота" flagAsCatDescription: "Включите, и этот аккаунт будет помечен как кошачий." +flagShowTimelineReplies: "Показывать ответы на заметки в ленте" +flagShowTimelineRepliesDescription: "Если этот параметр включен, то в ленте, в дополнение к заметкам пользователя, отображаются ответы на другие заметки пользователя." autoAcceptFollowed: "Принимать подписчиков автоматически" addAccount: "Добавить учётную запись" loginFailed: "Неудачная попытка входа" @@ -236,6 +238,7 @@ saved: "Сохранено" messaging: "Сообщения" upload: "Загрузить" keepOriginalUploading: "Сохранить исходное изображение" +keepOriginalUploadingDescription: "Сохраняет исходную версию при загрузке изображений. Если выключить, то при загрузке браузер генерирует изображение для публикации." fromDrive: "С «диска»" fromUrl: "По ссылке" uploadFromUrl: "Загрузить по ссылке" @@ -589,6 +592,7 @@ smtpSecure: "Использовать SSL/TLS для SMTP-соединений" smtpSecureInfo: "Выключите при использовании STARTTLS." testEmail: "Проверка доставки электронной почты" wordMute: "Скрытие слов" +regexpError: "Ошибка в регулярном выражении" instanceMute: "Глушение инстансов" userSaysSomething: "{name} что-то сообщает" makeActive: "Активировать" @@ -619,6 +623,8 @@ fillAbuseReportDescription: "Опишите, пожалуйста, причин abuseReported: "Жалоба отправлена. Большое спасибо за информацию." reporteeOrigin: "О ком сообщено" reporterOrigin: "Кто сообщил" +forwardReport: "Перенаправление отчета на инстант." +forwardReportIsAnonymous: "Удаленный инстант не сможет увидеть вашу информацию и будет отображаться как анонимная системная учетная запись." send: "Отправить" abuseMarkAsResolved: "Отметить жалобу как решённую" openInNewTab: "Открыть в новой вкладке" @@ -815,7 +821,16 @@ leaveGroupConfirm: "Покинуть группу «{name}»?" useDrawerReactionPickerForMobile: "Выдвижная палитра на мобильном устройстве" welcomeBackWithName: "С возвращением, {name}!" clickToFinishEmailVerification: "Пожалуйста, нажмите [{ok}], чтобы завершить подтверждение адреса электронной почты." +overridedDeviceKind: "Тип устройства" +smartphone: "Смартфон" +tablet: "Планшет" +auto: "Автоматически" +themeColor: "Цвет темы" +size: "Размер" +numberOfColumn: "Количество столбцов" searchByGoogle: "Поиск" +instanceDefaultLightTheme: "Светлая тема по умолчанию" +instanceDefaultDarkTheme: "Темная тема по умолчанию" indefinitely: "вечно" _emailUnavailable: used: "Уже используется" @@ -1059,7 +1074,6 @@ _sfx: antenna: "Антенна" channel: "Канал" _ago: - unknown: "Когда-то" future: "Из будущего" justNow: "Только что" secondsAgo: "{n} с назад" diff --git a/locales/sk-SK.yml b/locales/sk-SK.yml index 6818a64d30..dc1151522e 100644 --- a/locales/sk-SK.yml +++ b/locales/sk-SK.yml @@ -841,6 +841,7 @@ oneDay: "1 deň" oneWeek: "1 týždeň" reflectMayTakeTime: "Zmeny môžu chvíľu trvať kým sa prejavia." failedToFetchAccountInformation: "Nepodarilo sa načítať informácie o účte." +rateLimitExceeded: "Prekročený limit rýchlosti" _emailUnavailable: used: "Táto emailová adresa sa už používa" format: "Formát emailovej adresy je nesprávny" @@ -1086,7 +1087,6 @@ _sfx: antenna: "Antény" channel: "Upozornenia kanála" _ago: - unknown: "Neznáme" future: "Budúcnosť" justNow: "Teraz" secondsAgo: "pred {n} sekundami" @@ -1130,6 +1130,7 @@ _2fa: 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." + step2Url: "Do aplikácie zadajte nasledujúcu URL adresu:" 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." diff --git a/locales/sv-SE.yml b/locales/sv-SE.yml new file mode 100644 index 0000000000..42bfa45f25 --- /dev/null +++ b/locales/sv-SE.yml @@ -0,0 +1,319 @@ +--- +_lang_: "Svenska" +headlineMisskey: "Ett nätverk kopplat av noter" +introMisskey: "Välkommen! Misskey är en öppen och decentraliserad mikrobloggningstjänst.\nSkapa en \"not\" och dela dina tankar med alla runtomkring dig. 📡\nMed \"reaktioner\" kan du snabbt uttrycka dina känslor kring andras noter.👍\nLåt oss utforska en nya värld!🚀" +monthAndDay: "{day}/{month}" +search: "Sök" +notifications: "Notifikationer" +username: "Användarnamn" +password: "Lösenord" +forgotPassword: "Glömt lösenord" +fetchingAsApObject: "Hämtar från Fediversum..." +ok: "OK" +gotIt: "Uppfattat!" +cancel: "Avbryt" +enterUsername: "Ange användarnamn" +renotedBy: "Omnoterad av {user}" +noNotes: "Inga noteringar" +noNotifications: "Inga aviseringar" +instance: "Instanser" +settings: "Inställningar" +basicSettings: "Basinställningar" +otherSettings: "Andra inställningar" +openInWindow: "Öppna i ett fönster" +profile: "Profil" +timeline: "Tidslinje" +noAccountDescription: "Användaren har inte skrivit en biografi än." +login: "Logga in" +loggingIn: "Loggar in" +logout: "Logga ut" +signup: "Registrera" +uploading: "Uppladdning sker..." +save: "Spara" +users: "Användare" +addUser: "Lägg till användare" +favorite: "Lägg till i favoriter" +favorites: "Favoriter" +unfavorite: "Avfavorisera" +favorited: "Tillagd i favoriter." +alreadyFavorited: "Redan tillagd i favoriter." +cantFavorite: "Gick inte att lägga till i favoriter." +pin: "Fäst till profil" +unpin: "Lossa från profil" +copyContent: "Kopiera innehåll" +copyLink: "Kopiera länk" +delete: "Radera" +deleteAndEdit: "Radera och ändra" +deleteAndEditConfirm: "Är du säker att du vill radera denna not och ändra den? Du kommer förlora alla reaktioner, omnoteringar och svar till den." +addToList: "Lägg till i lista" +sendMessage: "Skicka ett meddelande" +copyUsername: "Kopiera användarnamn" +searchUser: "Sök användare" +reply: "Svara" +loadMore: "Ladda mer" +showMore: "Visa mer" +youGotNewFollower: "följde dig" +receiveFollowRequest: "Följarförfrågan mottagen" +followRequestAccepted: "Följarförfrågan accepterad" +mention: "Nämn" +mentions: "Omnämningar" +directNotes: "Direktnoter" +importAndExport: "Importera / Exportera" +import: "Importera" +export: "Exportera" +files: "Filer" +download: "Nedladdning" +driveFileDeleteConfirm: "Är du säker att du vill radera filen \"{name}\"? Noter med denna fil bifogad kommer också raderas." +unfollowConfirm: "Är du säker att du vill avfölja {name}?" +exportRequested: "Du har begärt en export. Detta kan ta lite tid. Den kommer läggas till i din Drive när den blir klar." +importRequested: "Du har begärt en import. Detta kan ta lite tid." +lists: "Listor" +noLists: "Du har inga listor" +note: "Not" +notes: "Noter" +following: "Följer" +followers: "Följare" +followsYou: "Följer dig" +createList: "Skapa lista" +manageLists: "Hantera lista" +error: "Fel!" +somethingHappened: "Ett fel har uppstått" +retry: "Försök igen" +pageLoadError: "Det gick inte att ladda sidan." +pageLoadErrorDescription: "Detta händer oftast p.g.a. nätverksfel eller din webbläsarcache. Försök tömma din cache och testa sedan igen efter en liten stund." +serverIsDead: "Servern svarar inte. Vänta ett litet tag och försök igen." +youShouldUpgradeClient: "För att kunna se denna sida, vänligen ladda om sidan för att uppdatera din klient." +enterListName: "Skriv ett namn till listan" +privacy: "Integritet" +makeFollowManuallyApprove: "Följarförfrågningar kräver manuellt godkännande" +defaultNoteVisibility: "Standardsynlighet" +follow: "Följ" +followRequest: "Skicka följarförfrågan" +followRequests: "Följarförfrågningar" +unfollow: "Avfölj" +followRequestPending: "Följarförfrågning avvaktar för svar" +enterEmoji: "Skriv en emoji" +renote: "Omnotera" +unrenote: "Ta tillbaka omnotering" +renoted: "Omnoterad." +cantRenote: "Inlägget kunde inte bli omnoterat." +cantReRenote: "En omnotering kan inte bli omnoterad." +quote: "Citat" +pinnedNote: "Fästad not" +pinned: "Fäst till profil" +you: "Du" +clickToShow: "Klicka för att visa" +sensitive: "Känsligt innehåll" +add: "Lägg till" +reaction: "Reaktioner" +reactionSetting: "Reaktioner som ska visas i reaktionsväljaren" +reactionSettingDescription2: "Dra för att omordna, klicka för att radera, tryck \"+\" för att lägga till." +rememberNoteVisibility: "Komihåg notvisningsinställningar" +attachCancel: "Ta bort bilaga" +markAsSensitive: "Markera som känsligt innehåll" +unmarkAsSensitive: "Avmarkera som känsligt innehåll" +enterFileName: "Ange filnamn" +mute: "Tysta" +unmute: "Avtysta" +block: "Blockera" +unblock: "Avblockera" +suspend: "Suspendera" +unsuspend: "Ta bort suspenderingen" +blockConfirm: "Är du säker att du vill blockera kontot?" +unblockConfirm: "Är du säkert att du vill avblockera kontot?" +suspendConfirm: "Är du säker att du vill suspendera detta konto?" +unsuspendConfirm: "Är du säker att du vill avsuspendera detta konto?" +selectList: "Välj lista" +selectAntenna: "Välj en antenn" +selectWidget: "Välj en widget" +editWidgets: "Redigera widgets" +editWidgetsExit: "Avsluta redigering" +customEmojis: "Anpassa emoji" +emoji: "Emoji" +emojis: "Emoji" +emojiName: "Emoji namn" +emojiUrl: "Emoji länk" +addEmoji: "Lägg till emoji" +settingGuide: "Rekommenderade inställningar" +cacheRemoteFiles: "Spara externa filer till cachen" +cacheRemoteFilesDescription: "När denna inställning är avstängd kommer externa filer laddas direkt från den externa instansen. Genom att stänga av detta kommer lagringsutrymme minska i användning men kommer öka datatrafiken eftersom miniatyrer inte kommer genereras." +flagAsBot: "Markera konto som bot" +flagAsBotDescription: "Aktivera det här alternativet om kontot är kontrollerat av ett program. Om aktiverat kommer den fungera som en flagga för andra utvecklare för att hindra ändlösa kedjor med andra bottar. Det kommer också få Misskeys interna system att hantera kontot som en bot." +flagAsCat: "Markera konto som katt" +flagAsCatDescription: "Aktivera denna inställning för att markera kontot som en katt." +flagShowTimelineReplies: "Visa svar i tidslinje" +flagShowTimelineRepliesDescription: "Visar användarsvar till andra användares noter i tidslinjen om påslagen." +autoAcceptFollowed: "Godkänn följarförfrågningar från användare du följer automatiskt" +addAccount: "Lägg till konto" +loginFailed: "Inloggningen misslyckades" +showOnRemote: "Se på extern instans" +general: "Allmänt" +wallpaper: "Bakgrundsbild" +setWallpaper: "Välj bakgrund" +removeWallpaper: "Ta bort bakgrund" +searchWith: "Sök: {q}" +youHaveNoLists: "Du har inga listor" +followConfirm: "Är du säker att du vill följa {name}?" +proxyAccount: "Proxykonto" +proxyAccountDescription: "Ett proxykonto är ett konto som agerar som en extern följare för användare under vissa villkor. Till exempel, när en användare lägger till en extern användare till en lista så kommer den externa användarens aktivitet inte levireras till instansen om ingen lokal användare följer det kontot, så proxykontot används istället." +host: "Värd" +selectUser: "Välj användare" +recipient: "Mottagare" +annotation: "Kommentarer" +federation: "Federation" +instances: "Instanser" +registeredAt: "Registrerad på" +latestRequestSentAt: "Senaste förfrågan skickad" +latestRequestReceivedAt: "Senaste begäran mottagen" +latestStatus: "Senaste status" +storageUsage: "Använt lagringsutrymme" +charts: "Diagram" +perHour: "Per timme" +perDay: "Per dag" +stopActivityDelivery: "Sluta skicka aktiviteter" +blockThisInstance: "Blockera instans" +operations: "Operationer" +software: "Mjukvara" +version: "Version" +metadata: "Metadata" +withNFiles: "{n} fil(er)" +monitor: "Övervakning" +jobQueue: "Jobbkö" +cpuAndMemory: "CPU och minne" +network: "Nätverk" +disk: "Disk" +instanceInfo: "Instansinformation" +statistics: "Statistik" +clearQueue: "Rensa kö" +clearQueueConfirmTitle: "Är du säker att du vill rensa kön?" +clearQueueConfirmText: "Om någon not är olevererad i kön kommer den inte federeras. Vanligtvis behövs inte denna handling." +clearCachedFiles: "Rensa cache" +clearCachedFilesConfirm: "Är du säker att du vill radera alla cachade externa filer?" +blockedInstances: "Blockerade instanser" +blockedInstancesDescription: "Lista adressnamn av instanser som du vill blockera. Listade instanser kommer inte längre kommunicera med denna instans." +muteAndBlock: "Tystningar och blockeringar" +mutedUsers: "Tystade användare" +blockedUsers: "Blockerade användare" +noUsers: "Det finns inga användare" +editProfile: "Redigera profil" +noteDeleteConfirm: "Är du säker på att du vill ta bort denna not?" +pinLimitExceeded: "Du kan inte fästa fler noter" +intro: "Misskey har installerats! Vänligen skapa en adminanvändare." +done: "Klar" +processing: "Bearbetar..." +preview: "Förhandsvisning" +default: "Standard" +noCustomEmojis: "Det finns ingen emoji" +noJobs: "Det finns inga jobb" +federating: "Federerar" +blocked: "Blockerad" +suspended: "Suspenderad" +all: "Allt" +subscribing: "Prenumererar" +publishing: "Publiceras" +notResponding: "Svarar inte" +instanceFollowing: "Följer på instans" +instanceFollowers: "Följare av instans" +instanceUsers: "Användare av denna instans" +changePassword: "Ändra lösenord" +security: "Säkerhet" +retypedNotMatch: "Inmatningen matchar inte" +currentPassword: "Nuvarande lösenord" +newPassword: "Nytt lösenord" +newPasswordRetype: "Bekräfta lösenord" +attachFile: "Bifoga filer" +more: "Mer!" +featured: "Utvalda" +usernameOrUserId: "Användarnamn eller användar-id" +noSuchUser: "Kan inte hitta användaren" +lookup: "Sökning" +announcements: "Nyheter" +imageUrl: "Bild-URL" +remove: "Radera" +removed: "Borttaget" +removeAreYouSure: "Är du säker att du vill radera \"{x}\"?" +deleteAreYouSure: "Är du säker att du vill radera \"{x}\"?" +resetAreYouSure: "Vill du återställa?" +saved: "Sparad" +messaging: "Chatt" +upload: "Ladda upp" +keepOriginalUploading: "Behåll originalbild" +nsfw: "Känsligt innehåll" +pinnedNotes: "Fästad not" +userList: "Listor" +smtpHost: "Värd" +smtpUser: "Användarnamn" +smtpPass: "Lösenord" +clearCache: "Rensa cache" +user: "Användare" +searchByGoogle: "Sök" +_email: + _follow: + title: "följde dig" +_mfm: + mention: "Nämn" + quote: "Citat" + emoji: "Anpassa emoji" + search: "Sök" +_theme: + keys: + mention: "Nämn" + renote: "Omnotera" +_sfx: + note: "Noter" + notification: "Notifikationer" + chat: "Chatt" +_widgets: + notifications: "Notifikationer" + timeline: "Tidslinje" + federation: "Federation" + jobQueue: "Jobbkö" +_cw: + show: "Ladda mer" +_visibility: + followers: "Följare" +_profile: + username: "Användarnamn" +_exportOrImport: + followingList: "Följer" + muteList: "Tysta" + blockingList: "Blockera" + userLists: "Listor" +_charts: + federation: "Federation" +_pages: + script: + categories: + list: "Listor" + blocks: + _join: + arg1: "Listor" + _randomPick: + arg1: "Listor" + _dailyRandomPick: + arg1: "Listor" + _seedRandomPick: + arg2: "Listor" + _pick: + arg1: "Listor" + _listLen: + arg1: "Listor" + types: + array: "Listor" +_notification: + youWereFollowed: "följde dig" + _types: + follow: "Följer" + mention: "Nämn" + renote: "Omnotera" + quote: "Citat" + reaction: "Reaktioner" + _actions: + reply: "Svara" + renote: "Omnotera" +_deck: + _columns: + notifications: "Notifikationer" + tl: "Tidslinje" + list: "Listor" + mentions: "Omnämningar" diff --git a/locales/uk-UA.yml b/locales/uk-UA.yml index 480526c134..7e7ef8685f 100644 --- a/locales/uk-UA.yml +++ b/locales/uk-UA.yml @@ -919,7 +919,6 @@ _sfx: antenna: "Прийом антени" channel: "Повідомлення каналу" _ago: - unknown: "Невідомо" future: "Майбутнє" justNow: "Щойно" secondsAgo: "{n}с тому" diff --git a/locales/vi-VN.yml b/locales/vi-VN.yml index ffe5ba1976..9919e0a0a4 100644 --- a/locales/vi-VN.yml +++ b/locales/vi-VN.yml @@ -842,6 +842,7 @@ oneDay: "1 ngày" oneWeek: "1 tuần" reflectMayTakeTime: "Có thể mất một thời gian để điều này được áp dụng." failedToFetchAccountInformation: "Không thể lấy thông tin tài khoản" +rateLimitExceeded: "Giới hạn quá mức" _emailUnavailable: used: "Địa chỉ email đã được sử dụng" format: "Địa chỉ email không hợp lệ" @@ -1087,7 +1088,6 @@ _sfx: antenna: "Trạm phát sóng" channel: "Kênh" _ago: - unknown: "Không rõ" future: "Tương lai" justNow: "Vừa xong" secondsAgo: "{n}s trước" @@ -1131,6 +1131,7 @@ _2fa: registerKey: "Đăng ký một mã bảo vệ" step1: "Trước tiên, hãy cài đặt một ứng dụng xác minh (chẳng hạn như {a} hoặc {b}) trên thiết bị của bạn." step2: "Sau đó, quét mã QR hiển thị trên màn hình này." + step2Url: "Bạn cũng có thể nhập URL này nếu sử dụng một chương trình máy tính:" step3: "Nhập mã token do ứng dụng của bạn cung cấp để hoàn tất thiết lập." step4: "Kể từ bây giờ, những lần đăng nhập trong tương lai sẽ yêu cầu mã token đăng nhập đó." securityKeyInfo: "Bên cạnh xác minh bằng vân tay hoặc mã PIN, bạn cũng có thể thiết lập xác minh thông qua khóa bảo mật phần cứng hỗ trợ FIDO2 để bảo mật hơn nữa cho tài khoản của mình." diff --git a/locales/zh-CN.yml b/locales/zh-CN.yml index c719dcb767..4953f55280 100644 --- a/locales/zh-CN.yml +++ b/locales/zh-CN.yml @@ -1087,7 +1087,6 @@ _sfx: antenna: "天线接收" channel: "频道通知" _ago: - unknown: "未知" future: "未来" justNow: "最近" secondsAgo: "{n}秒前" @@ -1131,6 +1130,7 @@ _2fa: registerKey: "注册密钥" step1: "首先,在您的设备上安装验证应用,例如{a}或{b}。" step2: "然后,扫描屏幕上显示的二维码。" + step2Url: "在桌面应用程序中输入以下URL:" step3: "输入您的应用提供的动态口令以完成设置。" step4: "从现在开始,任何登录操作都将要求您提供动态口令。" securityKeyInfo: "您可以设置使用支持FIDO2的硬件安全密钥、设备上的指纹或PIN来保护您的登录过程。" diff --git a/locales/zh-TW.yml b/locales/zh-TW.yml index e9b7ab6540..f088fdc0e9 100644 --- a/locales/zh-TW.yml +++ b/locales/zh-TW.yml @@ -1087,7 +1087,6 @@ _sfx: antenna: "天線接收" channel: "頻道通知" _ago: - unknown: "未知" future: "未來" justNow: "剛剛" secondsAgo: "{n}秒前" @@ -1131,6 +1130,7 @@ _2fa: registerKey: "註冊鍵" step1: "首先,在您的設備上安裝二步驗證程式,例如{a}或{b}。" step2: "然後,掃描螢幕上的QR code。" + step2Url: "在桌面版應用中,請輸入以下的URL:" step3: "輸入您的App提供的權杖以完成設定。" step4: "從現在開始,任何登入操作都將要求您提供權杖。" securityKeyInfo: "您可以設定使用支援FIDO2的硬體安全鎖、終端設備的指纹認證或者PIN碼來登入。" diff --git a/package.json b/package.json index e21c2c163b..f1c8c9f371 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "12.110.1", + "version": "12.111.1", "codename": "indigo", "repository": { "type": "git", @@ -24,7 +24,7 @@ "watch": "yarn dev", "dev": "node ./scripts/dev.js", "lint": "yarn workspaces foreach run lint", - "cy:open": "cypress open", + "cy:open": "cypress open --browser --e2e --config-file=cypress.config.ts", "cy:run": "cypress run", "e2e": "start-server-and-test start:test http://localhost:61812 cy:run", "mocha": "yarn workspace backend run mocha", @@ -48,13 +48,13 @@ "@types/gulp": "4.0.9", "@types/gulp-rename": "2.0.1", "@typescript-eslint/eslint-plugin": "latest", - "@typescript-eslint/parser": "5.27.0", + "@typescript-eslint/parser": "5.27.1", "cross-env": "7.0.3", - "cypress": "9.7.0", + "cypress": "10.0.3", "eslint-plugin-import": "^2.26.0", "eslint-plugin-vue": "latest", "start-server-and-test": "1.14.0", - "typescript": "4.7.2", + "typescript": "4.7.3", "vue-eslint-parser": "^9.0.2" } } diff --git a/packages/backend/.mocharc.json b/packages/backend/.mocharc.json index 589522216d..87c571cfd6 100644 --- a/packages/backend/.mocharc.json +++ b/packages/backend/.mocharc.json @@ -5,6 +5,6 @@ "loader=./test/loader.js" ], "slow": 1000, - "timeout": 3000, + "timeout": 10000, "exit": true } diff --git a/packages/backend/package.json b/packages/backend/package.json index 8d4fbed060..0e3e3ada82 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -35,10 +35,9 @@ "archiver": "5.3.1", "autobind-decorator": "2.4.0", "autwh": "0.1.0", - "aws-sdk": "2.1145.0", + "aws-sdk": "2.1152.0", "bcryptjs": "2.4.3", "blurhash": "1.1.5", - "broadcast-channel": "4.12.0", "bull": "4.8.3", "cacheable-lookup": "6.0.4", "cbor": "8.1.0", @@ -51,7 +50,7 @@ "deep-email-validator": "0.1.21", "escape-regexp": "0.0.1", "feed": "4.2.2", - "file-type": "17.1.1", + "file-type": "17.1.2", "fluent-ffmpeg": "2.1.2", "got": "12.1.0", "hpagent": "0.1.2", @@ -61,8 +60,8 @@ "jsdom": "19.0.0", "json5": "2.2.1", "json5-loader": "4.0.1", - "jsonld": "5.2.0", - "jsrsasign": "10.5.23", + "jsonld": "6.0.0", + "jsrsasign": "10.5.24", "koa": "2.13.4", "koa-bodyparser": "4.3.0", "koa-favicon": "2.1.0", @@ -79,7 +78,7 @@ "ms": "3.0.0-canary.1", "multer": "1.4.4", "nested-property": "4.0.0", - "node-fetch": "3.2.4", + "node-fetch": "3.2.6", "nodemailer": "6.7.5", "oauth": "^0.9.15", "os-utils": "0.0.14", @@ -115,8 +114,8 @@ "tinycolor2": "1.4.2", "tmp": "0.2.1", "ts-loader": "9.3.0", - "ts-node": "10.8.0", - "tsc-alias": "1.6.7", + "ts-node": "10.8.1", + "tsc-alias": "1.6.9", "tsconfig-paths": "4.0.0", "twemoji-parser": "14.0.0", "typeorm": "0.3.6", @@ -125,7 +124,7 @@ "uuid": "8.3.2", "web-push": "3.5.0", "websocket": "1.0.34", - "ws": "8.7.0", + "ws": "8.8.0", "xev": "3.0.2" }, "devDependencies": { @@ -152,7 +151,7 @@ "@types/koa__multer": "2.0.4", "@types/koa__router": "8.0.11", "@types/mocha": "9.1.1", - "@types/node": "17.0.36", + "@types/node": "17.0.41", "@types/node-fetch": "3.0.3", "@types/nodemailer": "6.4.4", "@types/oauth": "0.9.1", @@ -175,10 +174,10 @@ "@types/web-push": "3.3.2", "@types/websocket": "1.0.5", "@types/ws": "8.5.3", - "@typescript-eslint/eslint-plugin": "5.27.0", - "@typescript-eslint/parser": "5.27.0", + "@typescript-eslint/eslint-plugin": "5.27.1", + "@typescript-eslint/parser": "5.27.1", "cross-env": "7.0.3", - "eslint": "8.16.0", + "eslint": "8.17.0", "eslint-plugin-import": "2.26.0", "execa": "6.1.0", "form-data": "^4.0.0", diff --git a/packages/backend/src/db/postgre.ts b/packages/backend/src/db/postgre.ts index 50a85d6267..298f6713ea 100644 --- a/packages/backend/src/db/postgre.ts +++ b/packages/backend/src/db/postgre.ts @@ -208,7 +208,15 @@ export const db = new DataSource({ migrations: ['../../migration/*.js'], }); -export async function initDb() { +export async function initDb(force = false) { + if (force) { + if (db.isInitialized) { + await db.destroy(); + } + await db.initialize(); + return; + } + if (db.isInitialized) { // nop } else { diff --git a/packages/backend/src/misc/create-temp.ts b/packages/backend/src/misc/create-temp.ts index f07be634fb..fa88769de0 100644 --- a/packages/backend/src/misc/create-temp.ts +++ b/packages/backend/src/misc/create-temp.ts @@ -11,9 +11,14 @@ export function createTemp(): Promise<[string, () => void]> { export function createTempDir(): Promise<[string, () => void]> { return new Promise<[string, () => void]>((res, rej) => { - tmp.dir((e, path, cleanup) => { - if (e) return rej(e); - res([path, cleanup]); - }); + tmp.dir( + { + unsafeCleanup: true, + }, + (e, path, cleanup) => { + if (e) return rej(e); + res([path, cleanup]); + } + ); }); } diff --git a/packages/backend/src/models/repositories/note.ts b/packages/backend/src/models/repositories/note.ts index c0abbb4f93..3fefab0319 100644 --- a/packages/backend/src/models/repositories/note.ts +++ b/packages/backend/src/models/repositories/note.ts @@ -136,6 +136,7 @@ async function populateMyReaction(note: Note, meId: User['id'], _hint_?: { export const NoteRepository = db.getRepository(Note).extend({ async isVisibleForMe(note: Note, meId: User['id'] | null): Promise { + // This code must always be synchronized with the checks in generateVisibilityQuery. // visibility が specified かつ自分が指定されていなかったら非表示 if (note.visibility === 'specified') { if (meId == null) { diff --git a/packages/backend/src/remote/activitypub/models/note.ts b/packages/backend/src/remote/activitypub/models/note.ts index ad24bbcd65..5d63f2605a 100644 --- a/packages/backend/src/remote/activitypub/models/note.ts +++ b/packages/backend/src/remote/activitypub/models/note.ts @@ -197,7 +197,14 @@ export async function createNote(value: string | IObject, resolver?: Resolver, s const cw = note.summary === '' ? null : note.summary; // テキストのパース - const text = typeof note._misskey_content !== 'undefined' ? note._misskey_content : (note.content ? htmlToMfm(note.content, note.tag) : null); + let text: string | null = null; + if (note.source?.mediaType === 'text/x.misskeymarkdown' && typeof note.source?.content === 'string') { + text = note.source.content; + } else if (typeof note._misskey_content !== 'undefined') { + text = note._misskey_content; + } else if (typeof note.content === 'string') { + text = htmlToMfm(note.content, note.tag); + } // vote if (reply && reply.hasPoll) { diff --git a/packages/backend/src/remote/activitypub/renderer/note.ts b/packages/backend/src/remote/activitypub/renderer/note.ts index b7df0e9a39..b3bafaa3ab 100644 --- a/packages/backend/src/remote/activitypub/renderer/note.ts +++ b/packages/backend/src/remote/activitypub/renderer/note.ts @@ -82,15 +82,14 @@ export default async function renderNote(note: Note, dive = true, isTalk = false const files = await getPromisedFiles(note.fileIds); - // text should never be undefined - const text = note.text ?? null; + const text = note.text ?? ''; let poll: Poll | null = null; if (note.hasPoll) { poll = await Polls.findOneBy({ noteId: note.id }); } - let apText = text ?? ''; + let apText = text; if (quote) { apText += `\n\nRE: ${quote}`; @@ -138,6 +137,10 @@ export default async function renderNote(note: Note, dive = true, isTalk = false summary, content, _misskey_content: text, + source: { + content: text, + mediaType: "text/x.misskeymarkdown", + }, _misskey_quote: quote, quoteUrl: quote, published: note.createdAt.toISOString(), diff --git a/packages/backend/src/remote/activitypub/type.ts b/packages/backend/src/remote/activitypub/type.ts index ef5b98b59e..5d00481b75 100644 --- a/packages/backend/src/remote/activitypub/type.ts +++ b/packages/backend/src/remote/activitypub/type.ts @@ -106,7 +106,10 @@ export const isPost = (object: IObject): object is IPost => export interface IPost extends IObject { type: 'Note' | 'Question' | 'Article' | 'Audio' | 'Document' | 'Image' | 'Page' | 'Video' | 'Event'; - _misskey_content?: string; + source?: { + content: string; + mediaType: string; + }; _misskey_quote?: string; quoteUrl?: string; _misskey_talk: boolean; @@ -114,7 +117,10 @@ export interface IPost extends IObject { export interface IQuestion extends IObject { type: 'Note' | 'Question'; - _misskey_content?: string; + source?: { + content: string; + mediaType: string; + }; _misskey_quote?: string; quoteUrl?: string; oneOf?: IQuestionChoice[]; diff --git a/packages/backend/src/server/api/call.ts b/packages/backend/src/server/api/call.ts index cd3e0abc06..46afde4e47 100644 --- a/packages/backend/src/server/api/call.ts +++ b/packages/backend/src/server/api/call.ts @@ -1,12 +1,12 @@ -import Koa from 'koa'; import { performance } from 'perf_hooks'; -import { limiter } from './limiter.js'; +import Koa from 'koa'; import { CacheableLocalUser, User } from '@/models/entities/user.js'; +import { AccessToken } from '@/models/entities/access-token.js'; +import { getIpHash } from '@/misc/get-ip-hash.js'; +import { limiter } from './limiter.js'; import endpoints, { IEndpointMeta } from './endpoints.js'; import { ApiError } from './error.js'; import { apiLogger } from './logger.js'; -import { AccessToken } from '@/models/entities/access-token.js'; -import { getIpHash } from '@/misc/get-ip-hash.js'; const accessDenied = { message: 'Access denied.', @@ -33,7 +33,7 @@ export default async (endpoint: string, user: CacheableLocalUser | null | undefi throw new ApiError(accessDenied); } - if (ep.meta.limit && !isModerator) { + if (ep.meta.limit) { // koa will automatically load the `X-Forwarded-For` header if `proxy: true` is configured in the app. let limitActor: string; if (user) { @@ -120,20 +120,20 @@ export default async (endpoint: string, user: CacheableLocalUser | null | undefi if (e instanceof ApiError) { throw e; } else { - apiLogger.error(`Internal error occurred in ${ep.name}: ${e?.message}`, { + apiLogger.error(`Internal error occurred in ${ep.name}: ${e.message}`, { ep: ep.name, ps: data, e: { - message: e?.message, - code: e?.name, - stack: e?.stack, + message: e.message, + code: e.name, + stack: e.stack, }, }); throw new ApiError(null, { e: { - message: e?.message, - code: e?.name, - stack: e?.stack, + message: e.message, + code: e.name, + stack: e.stack, }, }); } diff --git a/packages/backend/src/server/api/common/generate-visibility-query.ts b/packages/backend/src/server/api/common/generate-visibility-query.ts index 715982934c..b50b6812f4 100644 --- a/packages/backend/src/server/api/common/generate-visibility-query.ts +++ b/packages/backend/src/server/api/common/generate-visibility-query.ts @@ -3,6 +3,7 @@ import { Followings } from '@/models/index.js'; import { Brackets, SelectQueryBuilder } from 'typeorm'; export function generateVisibilityQuery(q: SelectQueryBuilder, me?: { id: User['id'] } | null) { + // This code must always be synchronized with the checks in Notes.isVisibleForMe. if (me == null) { q.andWhere(new Brackets(qb => { qb .where(`note.visibility = 'public'`) @@ -11,7 +12,7 @@ export function generateVisibilityQuery(q: SelectQueryBuilder, me?: { id: U } else { const followingQuery = Followings.createQueryBuilder('following') .select('following.followeeId') - .where('following.followerId = :followerId', { followerId: me.id }); + .where('following.followerId = :meId'); q.andWhere(new Brackets(qb => { qb // 公開投稿である @@ -20,21 +21,22 @@ export function generateVisibilityQuery(q: SelectQueryBuilder, me?: { id: U .orWhere(`note.visibility = 'home'`); })) // または 自分自身 - .orWhere('note.userId = :userId1', { userId1: me.id }) + .orWhere('note.userId = :meId') // または 自分宛て - .orWhere(`'{"${me.id}"}' <@ note.visibleUserIds`) + .orWhere(':meId = ANY(note.visibleUserIds)') + .orWhere(':meId = ANY(note.mentions)') .orWhere(new Brackets(qb => { qb // または フォロワー宛ての投稿であり、 - .where('note.visibility = \'followers\'') + .where(`note.visibility = 'followers'`) .andWhere(new Brackets(qb => { qb // 自分がフォロワーである .where(`note.userId IN (${ followingQuery.getQuery() })`) // または 自分の投稿へのリプライ - .orWhere('note.replyUserId = :userId3', { userId3: me.id }); + .orWhere('note.replyUserId = :meId'); })); })); })); - q.setParameters(followingQuery.getParameters()); + q.setParameters({ meId: me.id }); } } diff --git a/packages/backend/src/server/api/common/read-notification.ts b/packages/backend/src/server/api/common/read-notification.ts index 0dad35bcc2..8c4ba41a36 100644 --- a/packages/backend/src/server/api/common/read-notification.ts +++ b/packages/backend/src/server/api/common/read-notification.ts @@ -9,6 +9,8 @@ export async function readNotification( userId: User['id'], notificationIds: Notification['id'][] ) { + if (notificationIds.length === 0) return; + // Update documents await Notifications.update({ id: In(notificationIds), diff --git a/packages/backend/src/server/api/endpoints/announcements.ts b/packages/backend/src/server/api/endpoints/announcements.ts index 222efdcef0..23cb93c9a5 100644 --- a/packages/backend/src/server/api/endpoints/announcements.ts +++ b/packages/backend/src/server/api/endpoints/announcements.ts @@ -1,5 +1,5 @@ -import define from '../define.js'; import { Announcements, AnnouncementReads } from '@/models/index.js'; +import define from '../define.js'; import { makePaginationQuery } from '../common/make-pagination-query.js'; export const meta = { diff --git a/packages/backend/src/server/api/endpoints/drive.ts b/packages/backend/src/server/api/endpoints/drive.ts index c599d96ca4..47e940cddd 100644 --- a/packages/backend/src/server/api/endpoints/drive.ts +++ b/packages/backend/src/server/api/endpoints/drive.ts @@ -1,6 +1,6 @@ -import define from '../define.js'; import { fetchMeta } from '@/misc/fetch-meta.js'; import { DriveFiles } from '@/models/index.js'; +import define from '../define.js'; export const meta = { tags: ['drive', 'account'], 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 7ffe89a1e5..415a8cc693 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 @@ -9,6 +9,8 @@ export const meta = { kind: 'read:drive', + description: 'Find the notes to which the given file is attached.', + res: { type: 'array', optional: false, nullable: false, 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 80293df5d9..bbae9bf4e4 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 @@ -8,6 +8,8 @@ export const meta = { kind: 'read:drive', + description: 'Check if a given file exists.', + res: { type: 'boolean', optional: false, nullable: false, 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 0939ae3365..7397fd9ce9 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/create.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/create.ts @@ -20,6 +20,8 @@ export const meta = { kind: 'write:drive', + description: 'Upload a new drive file.', + res: { type: 'object', optional: false, nullable: false, 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 61c56e6314..6108ae7da9 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/delete.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/delete.ts @@ -11,6 +11,8 @@ export const meta = { kind: 'write:drive', + description: 'Delete an existing drive file.', + errors: { noSuchFile: { message: 'No such file.', 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 0b74cb9f01..f2bc7348c6 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 @@ -8,6 +8,8 @@ export const meta = { kind: 'read:drive', + description: 'Search for a drive file by a hash of the contents.', + res: { type: 'array', optional: false, nullable: false, 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 4938a69d11..245fb45a65 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/find.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/find.ts @@ -9,6 +9,8 @@ export const meta = { kind: 'read:drive', + description: 'Search for a drive file by the given parameters.', + res: { type: 'array', optional: false, nullable: false, 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 fb19345fee..2c604c54c8 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/show.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/show.ts @@ -10,6 +10,8 @@ export const meta = { kind: 'read:drive', + description: 'Show the properties of a drive file.', + res: { type: 'object', optional: false, nullable: false, 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 4b3f5f2dc9..e3debe0b4f 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/update.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/update.ts @@ -11,6 +11,8 @@ export const meta = { kind: 'write:drive', + description: 'Update the properties of a drive file.', + errors: { invalidFileName: { message: 'Invalid file name.', 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 3bfecac802..53f2298f21 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 @@ -13,6 +13,8 @@ export const meta = { max: 60, }, + description: 'Request the server to download a new drive file from the specified URL.', + requireCredential: true, kind: 'write:drive', 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 bc8d2e2ac0..5fe622932d 100644 --- a/packages/backend/src/server/api/endpoints/export-custom-emojis.ts +++ b/packages/backend/src/server/api/endpoints/export-custom-emojis.ts @@ -1,6 +1,6 @@ -import define from '../define.js'; -import { createExportCustomEmojisJob } from '@/queue/index.js'; import ms from 'ms'; +import { createExportCustomEmojisJob } from '@/queue/index.js'; +import define from '../define.js'; export const meta = { secure: true, 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 b0c1225bee..56c5502978 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 @@ -1,6 +1,6 @@ +import { MoreThan } from 'typeorm'; import { USER_ONLINE_THRESHOLD } from '@/const.js'; import { Users } from '@/models/index.js'; -import { MoreThan } from 'typeorm'; import define from '../define.js'; export const meta = { diff --git a/packages/backend/src/server/api/endpoints/i.ts b/packages/backend/src/server/api/endpoints/i.ts index 5b1ad2b098..22aedfeee8 100644 --- a/packages/backend/src/server/api/endpoints/i.ts +++ b/packages/backend/src/server/api/endpoints/i.ts @@ -1,5 +1,5 @@ -import define from '../define.js'; import { Users } from '@/models/index.js'; +import define from '../define.js'; export const meta = { tags: ['account'], diff --git a/packages/backend/src/server/api/endpoints/i/notifications.ts b/packages/backend/src/server/api/endpoints/i/notifications.ts index 6ea8cb3574..1c31ce7a68 100644 --- a/packages/backend/src/server/api/endpoints/i/notifications.ts +++ b/packages/backend/src/server/api/endpoints/i/notifications.ts @@ -1,17 +1,22 @@ +import { Brackets } from 'typeorm'; +import { Notifications, Followings, Mutings, Users } from '@/models/index.js'; +import { notificationTypes } from '@/types.js'; +import read from '@/services/note/read.js'; import { readNotification } from '../../common/read-notification.js'; import define from '../../define.js'; import { makePaginationQuery } from '../../common/make-pagination-query.js'; import { generateMutedInstanceNotificationQuery } from '../../common/generate-muted-instance-query.js'; -import { Notifications, Followings, Mutings, Users } from '@/models/index.js'; -import { notificationTypes } from '@/types.js'; -import read from '@/services/note/read.js'; -import { Brackets } from 'typeorm'; export const meta = { tags: ['account', 'notifications'], requireCredential: true, + limit: { + duration: 60000, + max: 10, + }, + kind: 'read:notifications', res: { @@ -67,7 +72,7 @@ export default define(meta, paramDef, async (ps, user) => { .where('users.isSuspended = TRUE'); const query = makePaginationQuery(Notifications.createQueryBuilder('notification'), ps.sinceId, ps.untilId) - .andWhere(`notification.notifieeId = :meId`, { meId: user.id }) + .andWhere('notification.notifieeId = :meId', { meId: user.id }) .leftJoinAndSelect('notification.notifier', 'notifier') .leftJoinAndSelect('notification.note', 'note') .leftJoinAndSelect('notifier.avatar', 'notifierAvatar') @@ -103,13 +108,13 @@ export default define(meta, paramDef, async (ps, user) => { } if (ps.includeTypes && ps.includeTypes.length > 0) { - query.andWhere(`notification.type IN (:...includeTypes)`, { includeTypes: ps.includeTypes }); + query.andWhere('notification.type IN (:...includeTypes)', { includeTypes: ps.includeTypes }); } else if (ps.excludeTypes && ps.excludeTypes.length > 0) { - query.andWhere(`notification.type NOT IN (:...excludeTypes)`, { excludeTypes: ps.excludeTypes }); + query.andWhere('notification.type NOT IN (:...excludeTypes)', { excludeTypes: ps.excludeTypes }); } if (ps.unreadOnly) { - query.andWhere(`notification.isRead = false`); + query.andWhere('notification.isRead = false'); } const notifications = await query.take(ps.limit).getMany(); diff --git a/packages/backend/src/server/api/endpoints/meta.ts b/packages/backend/src/server/api/endpoints/meta.ts index e1ae282a97..5b624842c3 100644 --- a/packages/backend/src/server/api/endpoints/meta.ts +++ b/packages/backend/src/server/api/endpoints/meta.ts @@ -1,10 +1,10 @@ +import { IsNull, MoreThan } from 'typeorm'; import config from '@/config/index.js'; -import define from '../define.js'; import { fetchMeta } from '@/misc/fetch-meta.js'; import { Ads, Emojis, Users } from '@/models/index.js'; import { DB_MAX_NOTE_TEXT_LENGTH } from '@/misc/hard-limits.js'; -import { IsNull, MoreThan } from 'typeorm'; import { MAX_NOTE_TEXT_LENGTH } from '@/const.js'; +import define from '../define.js'; export const meta = { tags: ['meta'], diff --git a/packages/backend/src/server/api/endpoints/notes.ts b/packages/backend/src/server/api/endpoints/notes.ts index 2733c826e9..015b0338e3 100644 --- a/packages/backend/src/server/api/endpoints/notes.ts +++ b/packages/backend/src/server/api/endpoints/notes.ts @@ -1,6 +1,6 @@ +import { Notes } from '@/models/index.js'; import define from '../define.js'; import { makePaginationQuery } from '../common/make-pagination-query.js'; -import { Notes } from '@/models/index.js'; export const meta = { tags: ['notes'], @@ -34,8 +34,8 @@ export const paramDef = { // eslint-disable-next-line import/no-default-export 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`) + .andWhere('note.visibility = \'public\'') + .andWhere('note.localOnly = FALSE') .innerJoinAndSelect('note.user', 'user') .leftJoinAndSelect('user.avatar', 'avatar') .leftJoinAndSelect('user.banner', 'banner') @@ -61,7 +61,7 @@ export default define(meta, paramDef, async (ps) => { } if (ps.withFiles !== undefined) { - query.andWhere(ps.withFiles ? `note.fileIds != '{}'` : `note.fileIds = '{}'`); + query.andWhere(ps.withFiles ? 'note.fileIds != \'{}\'' : 'note.fileIds = \'{}\''); } if (ps.poll !== undefined) { diff --git a/packages/backend/src/server/api/endpoints/notes/children.ts b/packages/backend/src/server/api/endpoints/notes/children.ts index 86dde30d64..50ba293a58 100644 --- a/packages/backend/src/server/api/endpoints/notes/children.ts +++ b/packages/backend/src/server/api/endpoints/notes/children.ts @@ -1,9 +1,9 @@ +import { Brackets } from 'typeorm'; +import { Notes } from '@/models/index.js'; import define from '../../define.js'; import { makePaginationQuery } from '../../common/make-pagination-query.js'; import { generateVisibilityQuery } from '../../common/generate-visibility-query.js'; import { generateMutedUserQuery } from '../../common/generate-muted-user-query.js'; -import { Brackets } from 'typeorm'; -import { Notes } from '@/models/index.js'; import { generateBlockedUserQuery } from '../../common/generate-block-query.js'; import { generateMutedInstanceQuery } from '../../common/generate-muted-instance-query.js'; @@ -38,13 +38,13 @@ export const paramDef = { 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 }) + .where('note.replyId = :noteId', { noteId: ps.noteId }) .orWhere(new Brackets(qb => { qb - .where(`note.renoteId = :noteId`, { noteId: ps.noteId }) + .where('note.renoteId = :noteId', { noteId: ps.noteId }) .andWhere(new Brackets(qb => { qb - .where(`note.text IS NOT NULL`) - .orWhere(`note.fileIds != '{}'`) - .orWhere(`note.hasPoll = TRUE`); + .where('note.text IS NOT NULL') + .orWhere('note.fileIds != \'{}\'') + .orWhere('note.hasPoll = TRUE'); })); })); })) diff --git a/packages/backend/src/server/api/endpoints/notes/clips.ts b/packages/backend/src/server/api/endpoints/notes/clips.ts index 8683a7f75a..e79f8563e8 100644 --- a/packages/backend/src/server/api/endpoints/notes/clips.ts +++ b/packages/backend/src/server/api/endpoints/notes/clips.ts @@ -1,8 +1,8 @@ -import define from '../../define.js'; +import { In } from 'typeorm'; import { ClipNotes, Clips } from '@/models/index.js'; +import define from '../../define.js'; import { getNote } from '../../common/getters.js'; import { ApiError } from '../../error.js'; -import { In } from 'typeorm'; export const meta = { tags: ['clips', 'notes'], diff --git a/packages/backend/src/server/api/endpoints/notes/conversation.ts b/packages/backend/src/server/api/endpoints/notes/conversation.ts index b991a495f2..b731d18248 100644 --- a/packages/backend/src/server/api/endpoints/notes/conversation.ts +++ b/packages/backend/src/server/api/endpoints/notes/conversation.ts @@ -1,8 +1,8 @@ +import { Note } from '@/models/entities/note.js'; +import { Notes } from '@/models/index.js'; import define from '../../define.js'; import { ApiError } from '../../error.js'; import { getNote } from '../../common/getters.js'; -import { Note } from '@/models/entities/note.js'; -import { Notes } from '@/models/index.js'; export const meta = { tags: ['notes'], diff --git a/packages/backend/src/server/api/endpoints/notes/delete.ts b/packages/backend/src/server/api/endpoints/notes/delete.ts index 804e146fa4..c23ceeb5bf 100644 --- a/packages/backend/src/server/api/endpoints/notes/delete.ts +++ b/packages/backend/src/server/api/endpoints/notes/delete.ts @@ -1,9 +1,9 @@ -import deleteNote from '@/services/note/delete.js'; -import define from '../../define.js'; import ms from 'ms'; +import deleteNote from '@/services/note/delete.js'; +import { Users } from '@/models/index.js'; +import define from '../../define.js'; import { getNote } from '../../common/getters.js'; import { ApiError } from '../../error.js'; -import { Users } from '@/models/index.js'; export const meta = { tags: ['notes'], 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 41dc5ac8e1..097371a425 100644 --- a/packages/backend/src/server/api/endpoints/notes/favorites/create.ts +++ b/packages/backend/src/server/api/endpoints/notes/favorites/create.ts @@ -1,8 +1,8 @@ +import { NoteFavorites } from '@/models/index.js'; +import { genId } from '@/misc/gen-id.js'; import define from '../../../define.js'; import { ApiError } from '../../../error.js'; import { getNote } from '../../../common/getters.js'; -import { NoteFavorites } from '@/models/index.js'; -import { genId } from '@/misc/gen-id.js'; export const meta = { tags: ['notes', 'favorites'], 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 a48f7a0aa8..82ef4fa197 100644 --- a/packages/backend/src/server/api/endpoints/notes/favorites/delete.ts +++ b/packages/backend/src/server/api/endpoints/notes/favorites/delete.ts @@ -1,7 +1,7 @@ +import { NoteFavorites } from '@/models/index.js'; import define from '../../../define.js'; import { ApiError } from '../../../error.js'; import { getNote } from '../../../common/getters.js'; -import { NoteFavorites } from '@/models/index.js'; export const meta = { tags: ['notes', 'favorites'], diff --git a/packages/backend/src/server/api/endpoints/notes/featured.ts b/packages/backend/src/server/api/endpoints/notes/featured.ts index 6308d23696..dd9cc581aa 100644 --- a/packages/backend/src/server/api/endpoints/notes/featured.ts +++ b/packages/backend/src/server/api/endpoints/notes/featured.ts @@ -1,6 +1,6 @@ +import { Notes } from '@/models/index.js'; import define from '../../define.js'; import { generateMutedUserQuery } from '../../common/generate-muted-user-query.js'; -import { Notes } from '@/models/index.js'; import { generateBlockedUserQuery } from '../../common/generate-block-query.js'; export const meta = { @@ -36,9 +36,9 @@ export default define(meta, paramDef, async (ps, user) => { const query = Notes.createQueryBuilder('note') .addSelect('note.score') .where('note.userHost IS NULL') - .andWhere(`note.score > 0`) - .andWhere(`note.createdAt > :date`, { date: new Date(Date.now() - day) }) - .andWhere(`note.visibility = 'public'`) + .andWhere('note.score > 0') + .andWhere('note.createdAt > :date', { date: new Date(Date.now() - day) }) + .andWhere('note.visibility = \'public\'') .innerJoinAndSelect('note.user', 'user') .leftJoinAndSelect('user.avatar', 'avatar') .leftJoinAndSelect('user.banner', 'banner') 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 cb402ecaa1..418fc62c31 100644 --- a/packages/backend/src/server/api/endpoints/notes/global-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/global-timeline.ts @@ -1,11 +1,11 @@ -import define from '../../define.js'; import { fetchMeta } from '@/misc/fetch-meta.js'; +import { Notes, Users } from '@/models/index.js'; +import { activeUsersChart } from '@/services/chart/index.js'; +import define from '../../define.js'; import { ApiError } from '../../error.js'; import { makePaginationQuery } from '../../common/make-pagination-query.js'; -import { Notes, Users } from '@/models/index.js'; import { generateMutedUserQuery } from '../../common/generate-muted-user-query.js'; import { generateMutedInstanceQuery } from '../../common/generate-muted-instance-query.js'; -import { activeUsersChart } from '@/services/chart/index.js'; import { generateRepliesQuery } from '../../common/generate-replies-query.js'; import { generateMutedNoteQuery } from '../../common/generate-muted-note-query.js'; import { generateBlockedUserQuery } from '../../common/generate-block-query.js'; @@ -60,7 +60,7 @@ export default define(meta, paramDef, async (ps, user) => { //#region Construct query const query = makePaginationQuery(Notes.createQueryBuilder('note'), - ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) + ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere('note.visibility = \'public\'') .andWhere('note.channelId IS NULL') .innerJoinAndSelect('note.user', '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 f9893527e0..52ee817997 100644 --- a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts @@ -1,13 +1,13 @@ -import define from '../../define.js'; +import { Brackets } from 'typeorm'; import { fetchMeta } from '@/misc/fetch-meta.js'; +import { Followings, Notes, Users } from '@/models/index.js'; +import { activeUsersChart } from '@/services/chart/index.js'; +import define from '../../define.js'; import { ApiError } from '../../error.js'; import { makePaginationQuery } from '../../common/make-pagination-query.js'; -import { Followings, Notes, Users } from '@/models/index.js'; -import { Brackets } from 'typeorm'; import { generateVisibilityQuery } from '../../common/generate-visibility-query.js'; import { generateMutedUserQuery } from '../../common/generate-muted-user-query.js'; import { generateMutedInstanceQuery } from '../../common/generate-muted-instance-query.js'; -import { activeUsersChart } from '@/services/chart/index.js'; import { generateRepliesQuery } from '../../common/generate-replies-query.js'; import { generateMutedNoteQuery } from '../../common/generate-muted-note-query.js'; import { generateChannelQuery } from '../../common/generate-channel-query.js'; @@ -70,7 +70,7 @@ export default define(meta, paramDef, async (ps, user) => { .where('following.followerId = :followerId', { followerId: user.id }); const query = makePaginationQuery(Notes.createQueryBuilder('note'), - ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) + ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere(new Brackets(qb => { qb.where(`((note.userId IN (${ followingQuery.getQuery() })) OR (note.userId = :meId))`, { meId: user.id }) .orWhere('(note.visibility = \'public\') AND (note.userHost IS NULL)'); 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 03edf30b31..aac2a3749c 100644 --- a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts @@ -1,12 +1,12 @@ -import define from '../../define.js'; +import { Brackets } from 'typeorm'; import { fetchMeta } from '@/misc/fetch-meta.js'; -import { ApiError } from '../../error.js'; import { Notes, Users } from '@/models/index.js'; +import { activeUsersChart } from '@/services/chart/index.js'; +import define from '../../define.js'; +import { ApiError } from '../../error.js'; import { generateMutedUserQuery } from '../../common/generate-muted-user-query.js'; import { makePaginationQuery } from '../../common/make-pagination-query.js'; import { generateVisibilityQuery } from '../../common/generate-visibility-query.js'; -import { activeUsersChart } from '@/services/chart/index.js'; -import { Brackets } from 'typeorm'; import { generateRepliesQuery } from '../../common/generate-replies-query.js'; import { generateMutedNoteQuery } from '../../common/generate-muted-note-query.js'; import { generateChannelQuery } from '../../common/generate-channel-query.js'; @@ -66,7 +66,7 @@ export default define(meta, paramDef, async (ps, user) => { //#region Construct query const query = makePaginationQuery(Notes.createQueryBuilder('note'), - ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) + ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere('(note.visibility = \'public\') AND (note.userHost IS NULL)') .innerJoinAndSelect('note.user', 'user') .leftJoinAndSelect('user.avatar', 'avatar') diff --git a/packages/backend/src/server/api/endpoints/notes/mentions.ts b/packages/backend/src/server/api/endpoints/notes/mentions.ts index eafbba322d..9b41544523 100644 --- a/packages/backend/src/server/api/endpoints/notes/mentions.ts +++ b/packages/backend/src/server/api/endpoints/notes/mentions.ts @@ -1,10 +1,10 @@ -import define from '../../define.js'; +import { Brackets } from 'typeorm'; import read from '@/services/note/read.js'; import { Notes, Followings } from '@/models/index.js'; +import define from '../../define.js'; import { generateVisibilityQuery } from '../../common/generate-visibility-query.js'; import { generateMutedUserQuery } from '../../common/generate-muted-user-query.js'; import { makePaginationQuery } from '../../common/make-pagination-query.js'; -import { Brackets } from 'typeorm'; import { generateBlockedUserQuery } from '../../common/generate-block-query.js'; import { generateMutedNoteThreadQuery } from '../../common/generate-muted-note-thread-query.js'; 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 28bfade2f0..2150efaaf3 100644 --- a/packages/backend/src/server/api/endpoints/notes/polls/recommendation.ts +++ b/packages/backend/src/server/api/endpoints/notes/polls/recommendation.ts @@ -1,6 +1,6 @@ -import define from '../../../define.js'; -import { Polls, Mutings, Notes, PollVotes } from '@/models/index.js'; import { Brackets, In } from 'typeorm'; +import { Polls, Mutings, Notes, PollVotes } from '@/models/index.js'; +import define from '../../../define.js'; export const meta = { tags: ['notes'], @@ -31,8 +31,8 @@ export const paramDef = { 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 }) - .andWhere(`poll.noteVisibility = 'public'`) + .andWhere('poll.userId != :meId', { meId: user.id }) + .andWhere('poll.noteVisibility = \'public\'') .andWhere(new Brackets(qb => { qb .where('poll.expiresAt IS NULL') .orWhere('poll.expiresAt > :now', { now: new Date() }); 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 6244b55cf2..45a832cbd2 100644 --- a/packages/backend/src/server/api/endpoints/notes/polls/vote.ts +++ b/packages/backend/src/server/api/endpoints/notes/polls/vote.ts @@ -1,16 +1,16 @@ +import { Not } from 'typeorm'; import { publishNoteStream } from '@/services/stream.js'; import { createNotification } from '@/services/create-notification.js'; -import define from '../../../define.js'; -import { ApiError } from '../../../error.js'; -import { getNote } from '../../../common/getters.js'; import { deliver } from '@/queue/index.js'; import { renderActivity } from '@/remote/activitypub/renderer/index.js'; import renderVote from '@/remote/activitypub/renderer/vote.js'; import { deliverQuestionUpdate } from '@/services/note/polls/update.js'; import { PollVotes, NoteWatchings, Users, Polls, Blockings } from '@/models/index.js'; -import { Not } from 'typeorm'; import { IRemoteUser } from '@/models/entities/user.js'; import { genId } from '@/misc/gen-id.js'; +import { getNote } from '../../../common/getters.js'; +import { ApiError } from '../../../error.js'; +import define from '../../../define.js'; export const meta = { tags: ['notes'], 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 639ecae264..c13cafa21d 100644 --- a/packages/backend/src/server/api/endpoints/notes/reactions/delete.ts +++ b/packages/backend/src/server/api/endpoints/notes/reactions/delete.ts @@ -1,6 +1,6 @@ -import define from '../../../define.js'; import ms from 'ms'; import deleteReaction from '@/services/note/reaction/delete.js'; +import define from '../../../define.js'; import { getNote } from '../../../common/getters.js'; import { ApiError } from '../../../error.js'; diff --git a/packages/backend/src/server/api/endpoints/notes/renotes.ts b/packages/backend/src/server/api/endpoints/notes/renotes.ts index 87c855a5e8..28be360763 100644 --- a/packages/backend/src/server/api/endpoints/notes/renotes.ts +++ b/packages/backend/src/server/api/endpoints/notes/renotes.ts @@ -1,10 +1,10 @@ +import { Notes } from '@/models/index.js'; import define from '../../define.js'; import { getNote } from '../../common/getters.js'; import { ApiError } from '../../error.js'; import { generateVisibilityQuery } from '../../common/generate-visibility-query.js'; import { generateMutedUserQuery } from '../../common/generate-muted-user-query.js'; import { makePaginationQuery } from '../../common/make-pagination-query.js'; -import { Notes } from '@/models/index.js'; import { generateBlockedUserQuery } from '../../common/generate-block-query.js'; export const meta = { @@ -50,7 +50,7 @@ export default define(meta, paramDef, async (ps, user) => { }); const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId) - .andWhere(`note.renoteId = :renoteId`, { renoteId: note.id }) + .andWhere('note.renoteId = :renoteId', { renoteId: note.id }) .innerJoinAndSelect('note.user', 'user') .leftJoinAndSelect('user.avatar', 'avatar') .leftJoinAndSelect('user.banner', 'banner') diff --git a/packages/backend/src/server/api/endpoints/notes/replies.ts b/packages/backend/src/server/api/endpoints/notes/replies.ts index 3053eabe33..ab0018f58e 100644 --- a/packages/backend/src/server/api/endpoints/notes/replies.ts +++ b/packages/backend/src/server/api/endpoints/notes/replies.ts @@ -1,5 +1,5 @@ -import define from '../../define.js'; import { Notes } from '@/models/index.js'; +import define from '../../define.js'; import { makePaginationQuery } from '../../common/make-pagination-query.js'; import { generateVisibilityQuery } from '../../common/generate-visibility-query.js'; import { generateMutedUserQuery } from '../../common/generate-muted-user-query.js'; 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 bb85c92008..777de7221c 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,11 +1,11 @@ -import define from '../../define.js'; -import { makePaginationQuery } from '../../common/make-pagination-query.js'; -import { Notes } from '@/models/index.js'; -import { generateMutedUserQuery } from '../../common/generate-muted-user-query.js'; -import { generateVisibilityQuery } from '../../common/generate-visibility-query.js'; import { Brackets } from 'typeorm'; +import { Notes } from '@/models/index.js'; import { safeForSql } from '@/misc/safe-for-sql.js'; import { normalizeForSearch } from '@/misc/normalize-for-search.js'; +import define from '../../define.js'; +import { makePaginationQuery } from '../../common/make-pagination-query.js'; +import { generateMutedUserQuery } from '../../common/generate-muted-user-query.js'; +import { generateVisibilityQuery } from '../../common/generate-visibility-query.js'; import { generateBlockedUserQuery } from '../../common/generate-block-query.js'; export const meta = { diff --git a/packages/backend/src/server/api/endpoints/notes/search.ts b/packages/backend/src/server/api/endpoints/notes/search.ts index af9b5f0a10..4e2cdae801 100644 --- a/packages/backend/src/server/api/endpoints/notes/search.ts +++ b/packages/backend/src/server/api/endpoints/notes/search.ts @@ -1,8 +1,8 @@ +import { In } from 'typeorm'; +import { Notes } from '@/models/index.js'; +import config from '@/config/index.js'; import es from '../../../../db/elasticsearch.js'; import define from '../../define.js'; -import { Notes } from '@/models/index.js'; -import { In } from 'typeorm'; -import config from '@/config/index.js'; import { makePaginationQuery } from '../../common/make-pagination-query.js'; import { generateVisibilityQuery } from '../../common/generate-visibility-query.js'; import { generateMutedUserQuery } from '../../common/generate-muted-user-query.js'; @@ -99,7 +99,7 @@ export default define(meta, paramDef, async (ps, me) => { userHost: ps.host, }, }] : [] - : []; + : []; const result = await es.search({ index: config.elasticsearch.index || 'misskey_note', diff --git a/packages/backend/src/server/api/endpoints/notes/show.ts b/packages/backend/src/server/api/endpoints/notes/show.ts index d6692923c3..5cd74bd2ca 100644 --- a/packages/backend/src/server/api/endpoints/notes/show.ts +++ b/packages/backend/src/server/api/endpoints/notes/show.ts @@ -1,7 +1,7 @@ +import { Notes } from '@/models/index.js'; import define from '../../define.js'; import { getNote } from '../../common/getters.js'; import { ApiError } from '../../error.js'; -import { Notes } from '@/models/index.js'; export const meta = { tags: ['notes'], diff --git a/packages/backend/src/server/api/endpoints/notes/state.ts b/packages/backend/src/server/api/endpoints/notes/state.ts index 069f11fa4a..01afa5add2 100644 --- a/packages/backend/src/server/api/endpoints/notes/state.ts +++ b/packages/backend/src/server/api/endpoints/notes/state.ts @@ -1,5 +1,5 @@ -import define from '../../define.js'; import { NoteFavorites, Notes, NoteThreadMutings, NoteWatchings } from '@/models/index.js'; +import define from '../../define.js'; export const meta = { tags: ['notes'], 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 e48a2cf576..cf360526d3 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,9 +1,9 @@ -import define from '../../../define.js'; -import { getNote } from '../../../common/getters.js'; -import { ApiError } from '../../../error.js'; import { Notes, NoteThreadMutings } from '@/models/index.js'; import { genId } from '@/misc/gen-id.js'; import readNote from '@/services/note/read.js'; +import define from '../../../define.js'; +import { getNote } from '../../../common/getters.js'; +import { ApiError } from '../../../error.js'; export const meta = { tags: ['notes'], 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 4fb3137a5e..ac310d0fe6 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,7 +1,7 @@ +import { NoteThreadMutings } from '@/models/index.js'; import define from '../../../define.js'; import { getNote } from '../../../common/getters.js'; import { ApiError } from '../../../error.js'; -import { NoteThreadMutings } from '@/models/index.js'; export const meta = { tags: ['notes'], diff --git a/packages/backend/src/server/api/endpoints/notes/timeline.ts b/packages/backend/src/server/api/endpoints/notes/timeline.ts index 0f976d18be..d80940e950 100644 --- a/packages/backend/src/server/api/endpoints/notes/timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/timeline.ts @@ -1,11 +1,11 @@ +import { Brackets } from 'typeorm'; +import { Notes, Followings } from '@/models/index.js'; +import { activeUsersChart } from '@/services/chart/index.js'; import define from '../../define.js'; import { makePaginationQuery } from '../../common/make-pagination-query.js'; -import { Notes, Followings } from '@/models/index.js'; import { generateVisibilityQuery } from '../../common/generate-visibility-query.js'; import { generateMutedUserQuery } from '../../common/generate-muted-user-query.js'; import { generateMutedInstanceQuery } from '../../common/generate-muted-instance-query.js'; -import { activeUsersChart } from '@/services/chart/index.js'; -import { Brackets } from 'typeorm'; import { generateRepliesQuery } from '../../common/generate-replies-query.js'; import { generateMutedNoteQuery } from '../../common/generate-muted-note-query.js'; import { generateChannelQuery } from '../../common/generate-channel-query.js'; @@ -62,10 +62,10 @@ export default define(meta, paramDef, async (ps, user) => { .where('following.followerId = :followerId', { followerId: user.id }); const query = makePaginationQuery(Notes.createQueryBuilder('note'), - ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) + ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere(new Brackets(qb => { qb .where('note.userId = :meId', { meId: user.id }); - if (hasFollowing) qb.orWhere(`note.userId IN (${ followingQuery.getQuery() })`); + if (hasFollowing) qb.orWhere(`note.userId IN (${ followingQuery.getQuery() })`); })) .innerJoinAndSelect('note.user', 'user') .leftJoinAndSelect('user.avatar', 'avatar') diff --git a/packages/backend/src/server/api/endpoints/notes/unrenote.ts b/packages/backend/src/server/api/endpoints/notes/unrenote.ts index 5e8c31eaf8..3fba0efe0c 100644 --- a/packages/backend/src/server/api/endpoints/notes/unrenote.ts +++ b/packages/backend/src/server/api/endpoints/notes/unrenote.ts @@ -1,9 +1,9 @@ -import deleteNote from '@/services/note/delete.js'; -import define from '../../define.js'; import ms from 'ms'; +import deleteNote from '@/services/note/delete.js'; +import { Notes, Users } from '@/models/index.js'; +import define from '../../define.js'; import { getNote } from '../../common/getters.js'; import { ApiError } from '../../error.js'; -import { Notes, Users } from '@/models/index.js'; export const meta = { tags: ['notes'], 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 fd4a879035..e603a8f625 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,10 +1,10 @@ +import { Brackets } from 'typeorm'; +import { UserLists, UserListJoinings, Notes } from '@/models/index.js'; +import { activeUsersChart } from '@/services/chart/index.js'; import define from '../../define.js'; import { ApiError } from '../../error.js'; -import { UserLists, UserListJoinings, Notes } from '@/models/index.js'; import { makePaginationQuery } from '../../common/make-pagination-query.js'; import { generateVisibilityQuery } from '../../common/generate-visibility-query.js'; -import { activeUsersChart } from '@/services/chart/index.js'; -import { Brackets } from 'typeorm'; export const meta = { tags: ['notes', 'lists'], 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 8fdf84624e..7d482b0732 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,5 @@ -import define from '../../../define.js'; import watch from '@/services/note/watch.js'; +import define from '../../../define.js'; import { getNote } from '../../../common/getters.js'; import { ApiError } from '../../../error.js'; 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 d58f09797c..2c1a2e5fbd 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,5 @@ -import define from '../../../define.js'; import unwatch from '@/services/note/unwatch.js'; +import define from '../../../define.js'; import { getNote } from '../../../common/getters.js'; import { ApiError } from '../../../error.js'; diff --git a/packages/backend/src/server/api/endpoints/notifications/create.ts b/packages/backend/src/server/api/endpoints/notifications/create.ts index b339c8723d..80d513d8da 100644 --- a/packages/backend/src/server/api/endpoints/notifications/create.ts +++ b/packages/backend/src/server/api/endpoints/notifications/create.ts @@ -1,5 +1,5 @@ -import define from '../../define.js'; import { createNotification } from '@/services/create-notification.js'; +import define from '../../define.js'; export const meta = { tags: ['notifications'], 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 4575cba43f..d169afbb35 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 @@ -1,7 +1,7 @@ import { publishMainStream } from '@/services/stream.js'; import { pushNotification } from '@/services/push-notification.js'; -import define from '../../define.js'; import { Notifications } from '@/models/index.js'; +import define from '../../define.js'; export const meta = { tags: ['notifications', 'account'], diff --git a/packages/backend/src/server/api/endpoints/notifications/read.ts b/packages/backend/src/server/api/endpoints/notifications/read.ts index 65e96d4862..7bce525a55 100644 --- a/packages/backend/src/server/api/endpoints/notifications/read.ts +++ b/packages/backend/src/server/api/endpoints/notifications/read.ts @@ -2,17 +2,14 @@ import define from '../../define.js'; import { readNotification } from '../../common/read-notification.js'; export const meta = { - desc: { - 'ja-JP': '通知を既読にします。', - 'en-US': 'Mark a notification as read.' - }, - tags: ['notifications', 'account'], requireCredential: true, kind: 'write:notifications', + description: 'Mark a notification as read.', + errors: { noSuchNotification: { message: 'No such notification.', @@ -34,7 +31,11 @@ export const paramDef = { { type: 'object', properties: { - notificationIds: { type: 'array', items: { type: 'string', format: 'misskey:id' } }, + notificationIds: { + type: 'array', + items: { type: 'string', format: 'misskey:id' }, + maxItems: 100, + }, }, required: ['notificationIds'], }, diff --git a/packages/backend/src/server/api/endpoints/page-push.ts b/packages/backend/src/server/api/endpoints/page-push.ts index 7096aaa3d3..6dd3ede85a 100644 --- a/packages/backend/src/server/api/endpoints/page-push.ts +++ b/packages/backend/src/server/api/endpoints/page-push.ts @@ -1,6 +1,6 @@ -import define from '../define.js'; import { publishMainStream } from '@/services/stream.js'; import { Users, Pages } from '@/models/index.js'; +import define from '../define.js'; import { ApiError } from '../error.js'; export const meta = { diff --git a/packages/backend/src/server/api/endpoints/pages/create.ts b/packages/backend/src/server/api/endpoints/pages/create.ts index c171cd39f5..b008cde84e 100644 --- a/packages/backend/src/server/api/endpoints/pages/create.ts +++ b/packages/backend/src/server/api/endpoints/pages/create.ts @@ -1,8 +1,8 @@ import ms from 'ms'; -import define from '../../define.js'; import { Pages, DriveFiles } from '@/models/index.js'; import { genId } from '@/misc/gen-id.js'; import { Page } from '@/models/entities/page.js'; +import define from '../../define.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -51,7 +51,7 @@ export const paramDef = { } }, script: { type: 'string' }, eyeCatchingImageId: { type: 'string', format: 'misskey:id', nullable: true }, - font: { type: 'string', enum: ['serif', 'sans-serif'], default: "sans-serif" }, + font: { type: 'string', enum: ['serif', 'sans-serif'], default: 'sans-serif' }, alignCenter: { type: 'boolean', default: false }, hideTitleWhenPinned: { type: 'boolean', default: false }, }, diff --git a/packages/backend/src/server/api/endpoints/pages/delete.ts b/packages/backend/src/server/api/endpoints/pages/delete.ts index e35ad9ebf2..a7708e6585 100644 --- a/packages/backend/src/server/api/endpoints/pages/delete.ts +++ b/packages/backend/src/server/api/endpoints/pages/delete.ts @@ -1,6 +1,6 @@ +import { Pages } from '@/models/index.js'; import define from '../../define.js'; import { ApiError } from '../../error.js'; -import { Pages } from '@/models/index.js'; export const meta = { tags: ['pages'], diff --git a/packages/backend/src/server/api/endpoints/pages/featured.ts b/packages/backend/src/server/api/endpoints/pages/featured.ts index eeb6d509ca..5a149a626e 100644 --- a/packages/backend/src/server/api/endpoints/pages/featured.ts +++ b/packages/backend/src/server/api/endpoints/pages/featured.ts @@ -1,5 +1,5 @@ -import define from '../../define.js'; import { Pages } from '@/models/index.js'; +import define from '../../define.js'; export const meta = { tags: ['pages'], diff --git a/packages/backend/src/server/api/endpoints/pages/like.ts b/packages/backend/src/server/api/endpoints/pages/like.ts index 20793db988..269b539f74 100644 --- a/packages/backend/src/server/api/endpoints/pages/like.ts +++ b/packages/backend/src/server/api/endpoints/pages/like.ts @@ -1,7 +1,7 @@ -import define from '../../define.js'; -import { ApiError } from '../../error.js'; import { Pages, PageLikes } from '@/models/index.js'; import { genId } from '@/misc/gen-id.js'; +import define from '../../define.js'; +import { ApiError } from '../../error.js'; export const meta = { tags: ['pages'], diff --git a/packages/backend/src/server/api/endpoints/pages/unlike.ts b/packages/backend/src/server/api/endpoints/pages/unlike.ts index 636f3c7149..6b3a2bec10 100644 --- a/packages/backend/src/server/api/endpoints/pages/unlike.ts +++ b/packages/backend/src/server/api/endpoints/pages/unlike.ts @@ -1,6 +1,6 @@ +import { Pages, PageLikes } from '@/models/index.js'; import define from '../../define.js'; import { ApiError } from '../../error.js'; -import { Pages, PageLikes } from '@/models/index.js'; export const meta = { tags: ['pages'], diff --git a/packages/backend/src/server/api/endpoints/pages/update.ts b/packages/backend/src/server/api/endpoints/pages/update.ts index bf95ab36f2..d241f585aa 100644 --- a/packages/backend/src/server/api/endpoints/pages/update.ts +++ b/packages/backend/src/server/api/endpoints/pages/update.ts @@ -1,8 +1,8 @@ import ms from 'ms'; +import { Not } from 'typeorm'; +import { Pages, DriveFiles } from '@/models/index.js'; import define from '../../define.js'; import { ApiError } from '../../error.js'; -import { Pages, DriveFiles } from '@/models/index.js'; -import { Not } from 'typeorm'; export const meta = { tags: ['pages'], diff --git a/packages/backend/src/server/api/endpoints/pinned-users.ts b/packages/backend/src/server/api/endpoints/pinned-users.ts index 8d253c1f33..41595b47d9 100644 --- a/packages/backend/src/server/api/endpoints/pinned-users.ts +++ b/packages/backend/src/server/api/endpoints/pinned-users.ts @@ -1,9 +1,9 @@ -import define from '../define.js'; +import { IsNull } from 'typeorm'; import { Users } from '@/models/index.js'; import { fetchMeta } from '@/misc/fetch-meta.js'; import * as Acct from '@/misc/acct.js'; import { User } from '@/models/entities/user.js'; -import { IsNull } from 'typeorm'; +import define from '../define.js'; export const meta = { tags: ['users'], diff --git a/packages/backend/src/server/api/endpoints/promo/read.ts b/packages/backend/src/server/api/endpoints/promo/read.ts index cc602857de..c6a940c65e 100644 --- a/packages/backend/src/server/api/endpoints/promo/read.ts +++ b/packages/backend/src/server/api/endpoints/promo/read.ts @@ -1,8 +1,8 @@ +import { PromoReads } from '@/models/index.js'; +import { genId } from '@/misc/gen-id.js'; import define from '../../define.js'; import { ApiError } from '../../error.js'; import { getNote } from '../../common/getters.js'; -import { PromoReads } from '@/models/index.js'; -import { genId } from '@/misc/gen-id.js'; export const meta = { tags: ['notes'], 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 046337f040..511a6bbb53 100644 --- a/packages/backend/src/server/api/endpoints/request-reset-password.ts +++ b/packages/backend/src/server/api/endpoints/request-reset-password.ts @@ -1,17 +1,21 @@ -import { publishMainStream } from '@/services/stream.js'; -import define from '../define.js'; import rndstr from 'rndstr'; -import config from '@/config/index.js'; import ms from 'ms'; +import { IsNull } from 'typeorm'; +import { publishMainStream } from '@/services/stream.js'; +import config from '@/config/index.js'; import { Users, UserProfiles, PasswordResetRequests } from '@/models/index.js'; import { sendEmail } from '@/services/send-email.js'; -import { ApiError } from '../error.js'; import { genId } from '@/misc/gen-id.js'; -import { IsNull } from 'typeorm'; +import { ApiError } from '../error.js'; +import define from '../define.js'; export const meta = { + tags: ['reset password'], + requireCredential: false, + description: 'Request a users password to be reset.', + limit: { duration: ms('1hour'), max: 3, diff --git a/packages/backend/src/server/api/endpoints/reset-db.ts b/packages/backend/src/server/api/endpoints/reset-db.ts index dbe64e9a13..140f96d579 100644 --- a/packages/backend/src/server/api/endpoints/reset-db.ts +++ b/packages/backend/src/server/api/endpoints/reset-db.ts @@ -1,10 +1,14 @@ +import { resetDb } from '@/db/postgre.js'; import define from '../define.js'; import { ApiError } from '../error.js'; -import { resetDb } from '@/db/postgre.js'; export const meta = { + tags: ['non-productive'], + requireCredential: false, + description: 'Only available when running with NODE_ENV=testing. Reset the database and flush Redis.', + errors: { }, diff --git a/packages/backend/src/server/api/endpoints/reset-password.ts b/packages/backend/src/server/api/endpoints/reset-password.ts index 7acc545c40..797169c2c3 100644 --- a/packages/backend/src/server/api/endpoints/reset-password.ts +++ b/packages/backend/src/server/api/endpoints/reset-password.ts @@ -1,12 +1,16 @@ import bcrypt from 'bcryptjs'; import { publishMainStream } from '@/services/stream.js'; -import define from '../define.js'; import { Users, UserProfiles, PasswordResetRequests } from '@/models/index.js'; +import define from '../define.js'; import { ApiError } from '../error.js'; export const meta = { + tags: ['reset password'], + requireCredential: false, + description: 'Complete the password reset that was previously requested.', + errors: { }, diff --git a/packages/backend/src/server/api/endpoints/stats.ts b/packages/backend/src/server/api/endpoints/stats.ts index f8a1ee29de..cc94f8bf26 100644 --- a/packages/backend/src/server/api/endpoints/stats.ts +++ b/packages/backend/src/server/api/endpoints/stats.ts @@ -1,5 +1,5 @@ -import define from '../define.js'; import { Instances, NoteReactions, Notes, Users } from '@/models/index.js'; +import define from '../define.js'; import { } from '@/services/chart/index.js'; import { IsNull } from 'typeorm'; diff --git a/packages/backend/src/server/api/endpoints/sw/register.ts b/packages/backend/src/server/api/endpoints/sw/register.ts index a48973a0df..437f8874ff 100644 --- a/packages/backend/src/server/api/endpoints/sw/register.ts +++ b/packages/backend/src/server/api/endpoints/sw/register.ts @@ -1,13 +1,15 @@ -import define from '../../define.js'; import { fetchMeta } from '@/misc/fetch-meta.js'; import { genId } from '@/misc/gen-id.js'; import { SwSubscriptions } from '@/models/index.js'; +import define from '../../define.js'; export const meta = { tags: ['account'], requireCredential: true, + description: 'Register to receive push notifications.', + res: { type: 'object', optional: false, nullable: false, diff --git a/packages/backend/src/server/api/endpoints/sw/unregister.ts b/packages/backend/src/server/api/endpoints/sw/unregister.ts index 9748f2a222..c19e06b879 100644 --- a/packages/backend/src/server/api/endpoints/sw/unregister.ts +++ b/packages/backend/src/server/api/endpoints/sw/unregister.ts @@ -1,10 +1,12 @@ -import define from '../../define.js'; import { SwSubscriptions } from '@/models/index.js'; +import define from '../../define.js'; export const meta = { tags: ['account'], requireCredential: true, + + description: 'Unregister from receiving push notifications.', } as const; export const paramDef = { diff --git a/packages/backend/src/server/api/endpoints/test.ts b/packages/backend/src/server/api/endpoints/test.ts index 256da1a66f..9949237a7e 100644 --- a/packages/backend/src/server/api/endpoints/test.ts +++ b/packages/backend/src/server/api/endpoints/test.ts @@ -1,6 +1,10 @@ import define from '../define.js'; export const meta = { + tags: ['non-productive'], + + description: 'Endpoint for testing input validation.', + requireCredential: false, } as const; diff --git a/packages/backend/src/server/api/endpoints/username/available.ts b/packages/backend/src/server/api/endpoints/username/available.ts index 04b754f4ad..3e41aeaed8 100644 --- a/packages/backend/src/server/api/endpoints/username/available.ts +++ b/packages/backend/src/server/api/endpoints/username/available.ts @@ -1,6 +1,6 @@ -import define from '../../define.js'; -import { Users, UsedUsernames } from '@/models/index.js'; import { IsNull } from 'typeorm'; +import { Users, UsedUsernames } from '@/models/index.js'; +import define from '../../define.js'; export const meta = { tags: ['users'], diff --git a/packages/backend/src/server/api/endpoints/users.ts b/packages/backend/src/server/api/endpoints/users.ts index 10527d15cc..2377faebd3 100644 --- a/packages/backend/src/server/api/endpoints/users.ts +++ b/packages/backend/src/server/api/endpoints/users.ts @@ -1,5 +1,5 @@ -import define from '../define.js'; import { Users } from '@/models/index.js'; +import define from '../define.js'; import { generateMutedUserQueryForUsers } from '../common/generate-muted-user-query.js'; import { generateBlockQueryForUsers } from '../common/generate-block-query.js'; @@ -25,8 +25,8 @@ export const paramDef = { 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" }, + state: { type: 'string', enum: ['all', 'admin', 'moderator', 'adminOrModerator', 'alive'], default: 'all' }, + origin: { type: 'string', enum: ['combined', 'local', 'remote'], default: 'local' }, }, required: [], } as const; diff --git a/packages/backend/src/server/api/endpoints/users/clips.ts b/packages/backend/src/server/api/endpoints/users/clips.ts index 424c594749..09fdf27c23 100644 --- a/packages/backend/src/server/api/endpoints/users/clips.ts +++ b/packages/backend/src/server/api/endpoints/users/clips.ts @@ -1,9 +1,21 @@ -import define from '../../define.js'; import { Clips } from '@/models/index.js'; +import define from '../../define.js'; import { makePaginationQuery } from '../../common/make-pagination-query.js'; export const meta = { tags: ['users', 'clips'], + + description: 'Show all clips this user owns.', + + res: { + type: 'array', + optional: false, nullable: false, + items: { + type: 'object', + optional: false, nullable: false, + ref: 'Clip', + }, + }, } as const; export const paramDef = { @@ -20,7 +32,7 @@ export const paramDef = { // eslint-disable-next-line import/no-default-export 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.userId = :userId', { userId: ps.userId }) .andWhere('clip.isPublic = true'); const clips = await query diff --git a/packages/backend/src/server/api/endpoints/users/followers.ts b/packages/backend/src/server/api/endpoints/users/followers.ts index 26b1f20df0..7f9f980764 100644 --- a/packages/backend/src/server/api/endpoints/users/followers.ts +++ b/packages/backend/src/server/api/endpoints/users/followers.ts @@ -1,15 +1,17 @@ +import { IsNull } from 'typeorm'; +import { Users, Followings, UserProfiles } from '@/models/index.js'; +import { toPunyNullable } from '@/misc/convert-host.js'; import define from '../../define.js'; import { ApiError } from '../../error.js'; -import { Users, Followings, UserProfiles } from '@/models/index.js'; import { makePaginationQuery } from '../../common/make-pagination-query.js'; -import { toPunyNullable } from '@/misc/convert-host.js'; -import { IsNull } from 'typeorm'; export const meta = { tags: ['users'], requireCredential: false, + description: 'Show everyone that follows this user.', + res: { type: 'array', optional: false, nullable: false, @@ -94,7 +96,7 @@ export default define(meta, paramDef, async (ps, me) => { } const query = makePaginationQuery(Followings.createQueryBuilder('following'), ps.sinceId, ps.untilId) - .andWhere(`following.followeeId = :userId`, { userId: user.id }) + .andWhere('following.followeeId = :userId', { userId: user.id }) .innerJoinAndSelect('following.follower', 'follower'); const followings = await query diff --git a/packages/backend/src/server/api/endpoints/users/following.ts b/packages/backend/src/server/api/endpoints/users/following.ts index 42cf5216e8..0aaa810f76 100644 --- a/packages/backend/src/server/api/endpoints/users/following.ts +++ b/packages/backend/src/server/api/endpoints/users/following.ts @@ -1,15 +1,17 @@ +import { IsNull } from 'typeorm'; +import { Users, Followings, UserProfiles } from '@/models/index.js'; +import { toPunyNullable } from '@/misc/convert-host.js'; import define from '../../define.js'; import { ApiError } from '../../error.js'; -import { Users, Followings, UserProfiles } from '@/models/index.js'; import { makePaginationQuery } from '../../common/make-pagination-query.js'; -import { toPunyNullable } from '@/misc/convert-host.js'; -import { IsNull } from 'typeorm'; export const meta = { tags: ['users'], requireCredential: false, + description: 'Show everyone that this user is following.', + res: { type: 'array', optional: false, nullable: false, @@ -94,7 +96,7 @@ export default define(meta, paramDef, async (ps, me) => { } const query = makePaginationQuery(Followings.createQueryBuilder('following'), ps.sinceId, ps.untilId) - .andWhere(`following.followerId = :userId`, { userId: user.id }) + .andWhere('following.followerId = :userId', { userId: user.id }) .innerJoinAndSelect('following.followee', 'followee'); const followings = await query 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 d7c435256c..35bf2df598 100644 --- a/packages/backend/src/server/api/endpoints/users/gallery/posts.ts +++ b/packages/backend/src/server/api/endpoints/users/gallery/posts.ts @@ -4,6 +4,18 @@ import { makePaginationQuery } from '../../../common/make-pagination-query.js'; export const meta = { tags: ['users', 'gallery'], + + description: 'Show all gallery posts by the given user.', + + res: { + type: 'array', + optional: false, nullable: false, + items: { + type: 'object', + optional: false, nullable: false, + ref: 'GalleryPost', + }, + }, } as const; export const paramDef = { 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 73cadc0df7..56965d3066 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,15 +1,17 @@ -import define from '../../define.js'; +import { Not, In, IsNull } from 'typeorm'; import { maximum } from '@/prelude/array.js'; +import { Notes, Users } from '@/models/index.js'; +import define from '../../define.js'; import { ApiError } from '../../error.js'; import { getUser } from '../../common/getters.js'; -import { Not, In, IsNull } from 'typeorm'; -import { Notes, Users } from '@/models/index.js'; export const meta = { tags: ['users'], requireCredential: false, + description: 'Get a list of other users that the specified user frequently replies to.', + res: { type: 'array', optional: false, nullable: false, 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 fc775d7cc1..4a6362a3c6 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/create.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/create.ts @@ -1,8 +1,8 @@ -import define from '../../../define.js'; import { UserGroups, UserGroupJoinings } from '@/models/index.js'; import { genId } from '@/misc/gen-id.js'; import { UserGroup } from '@/models/entities/user-group.js'; import { UserGroupJoining } from '@/models/entities/user-group-joining.js'; +import define from '../../../define.js'; export const meta = { tags: ['groups'], @@ -11,6 +11,8 @@ export const meta = { kind: 'write:user-groups', + description: 'Create a new group.', + res: { type: 'object', optional: false, nullable: false, 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 f68006994c..2ff1f9aec1 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/delete.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/delete.ts @@ -1,6 +1,6 @@ +import { UserGroups } from '@/models/index.js'; import define from '../../../define.js'; import { ApiError } from '../../../error.js'; -import { UserGroups } from '@/models/index.js'; export const meta = { tags: ['groups'], @@ -9,6 +9,8 @@ export const meta = { kind: 'write:user-groups', + description: 'Delete an existing group.', + errors: { noSuchGroup: { message: 'No such group.', 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 75c1acc302..220fff5f3e 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,8 +1,8 @@ -import define from '../../../../define.js'; -import { ApiError } from '../../../../error.js'; import { UserGroupJoinings, UserGroupInvitations } from '@/models/index.js'; import { genId } from '@/misc/gen-id.js'; import { UserGroupJoining } from '@/models/entities/user-group-joining.js'; +import { ApiError } from '../../../../error.js'; +import define from '../../../../define.js'; export const meta = { tags: ['groups', 'users'], @@ -11,6 +11,8 @@ export const meta = { kind: 'write:user-groups', + description: 'Join a group the authenticated user has been invited to.', + errors: { noSuchInvitation: { message: 'No such invitation.', 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 46bc780ab0..8d1d3db734 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,6 +1,6 @@ +import { UserGroupInvitations } from '@/models/index.js'; import define from '../../../../define.js'; import { ApiError } from '../../../../error.js'; -import { UserGroupInvitations } from '@/models/index.js'; export const meta = { tags: ['groups', 'users'], @@ -9,6 +9,8 @@ export const meta = { kind: 'write:user-groups', + description: 'Delete an existing group invitation for the authenticated user without joining the group.', + errors: { noSuchInvitation: { message: 'No such invitation.', 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 30a5beb1d9..1a8d320f3a 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/invite.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/invite.ts @@ -1,10 +1,10 @@ -import define from '../../../define.js'; -import { ApiError } from '../../../error.js'; -import { getUser } from '../../../common/getters.js'; import { UserGroups, UserGroupJoinings, UserGroupInvitations } from '@/models/index.js'; import { genId } from '@/misc/gen-id.js'; import { UserGroupInvitation } from '@/models/entities/user-group-invitation.js'; import { createNotification } from '@/services/create-notification.js'; +import { getUser } from '../../../common/getters.js'; +import { ApiError } from '../../../error.js'; +import define from '../../../define.js'; export const meta = { tags: ['groups', 'users'], @@ -13,6 +13,8 @@ export const meta = { kind: 'write:user-groups', + description: 'Invite a user to an existing group.', + errors: { noSuchGroup: { message: 'No such group.', 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 77dc59d3e5..16c6e544e5 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/joined.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/joined.ts @@ -1,6 +1,6 @@ -import define from '../../../define.js'; -import { UserGroups, UserGroupJoinings } from '@/models/index.js'; import { Not, In } from 'typeorm'; +import { UserGroups, UserGroupJoinings } from '@/models/index.js'; +import define from '../../../define.js'; export const meta = { tags: ['groups', 'account'], @@ -9,6 +9,8 @@ export const meta = { kind: 'read:user-groups', + description: 'List the groups that the authenticated user is a member of.', + res: { type: 'array', optional: false, nullable: false, 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 33abd5439f..83dc757db1 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/leave.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/leave.ts @@ -1,6 +1,6 @@ +import { UserGroups, UserGroupJoinings } from '@/models/index.js'; import define from '../../../define.js'; import { ApiError } from '../../../error.js'; -import { UserGroups, UserGroupJoinings } from '@/models/index.js'; export const meta = { tags: ['groups', 'users'], @@ -9,6 +9,8 @@ export const meta = { kind: 'write:user-groups', + description: 'Leave a group. The owner of a group can not leave. They must transfer ownership or delete the group instead.', + errors: { noSuchGroup: { message: 'No such group.', 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 b1289e601f..d77cf1a52e 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/owned.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/owned.ts @@ -1,5 +1,5 @@ -import define from '../../../define.js'; import { UserGroups } from '@/models/index.js'; +import define from '../../../define.js'; export const meta = { tags: ['groups', 'account'], @@ -8,6 +8,8 @@ export const meta = { kind: 'read:user-groups', + description: 'List the groups that the authenticated user is the owner of.', + res: { type: 'array', optional: false, nullable: false, 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 b31990b2e3..ba67a1e5c9 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/pull.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/pull.ts @@ -1,7 +1,7 @@ +import { UserGroups, UserGroupJoinings } from '@/models/index.js'; import define from '../../../define.js'; import { ApiError } from '../../../error.js'; import { getUser } from '../../../common/getters.js'; -import { UserGroups, UserGroupJoinings } from '@/models/index.js'; export const meta = { tags: ['groups', 'users'], @@ -10,6 +10,8 @@ export const meta = { kind: 'write:user-groups', + description: 'Removes a specified user from a group. The owner can not be removed.', + errors: { noSuchGroup: { message: 'No such group.', 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 3ffb0f5ba9..21e3d9da26 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/show.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/show.ts @@ -1,6 +1,6 @@ +import { UserGroups, UserGroupJoinings } from '@/models/index.js'; import define from '../../../define.js'; import { ApiError } from '../../../error.js'; -import { UserGroups, UserGroupJoinings } from '@/models/index.js'; export const meta = { tags: ['groups', 'account'], @@ -9,6 +9,8 @@ export const meta = { kind: 'read:user-groups', + description: 'Show the properties of a group.', + res: { type: 'object', optional: false, nullable: false, 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 41ceee3b2e..6456e70dd5 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/transfer.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/transfer.ts @@ -1,7 +1,7 @@ +import { UserGroups, UserGroupJoinings } from '@/models/index.js'; import define from '../../../define.js'; import { ApiError } from '../../../error.js'; import { getUser } from '../../../common/getters.js'; -import { UserGroups, UserGroupJoinings } from '@/models/index.js'; export const meta = { tags: ['groups', 'users'], @@ -10,6 +10,8 @@ export const meta = { kind: 'write:user-groups', + description: 'Transfer ownership of a group from the authenticated user to another user.', + res: { type: 'object', optional: false, nullable: false, 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 1016aa8926..0a96165fc4 100644 --- a/packages/backend/src/server/api/endpoints/users/groups/update.ts +++ b/packages/backend/src/server/api/endpoints/users/groups/update.ts @@ -1,6 +1,6 @@ +import { UserGroups } from '@/models/index.js'; import define from '../../../define.js'; import { ApiError } from '../../../error.js'; -import { UserGroups } from '@/models/index.js'; export const meta = { tags: ['groups'], @@ -9,6 +9,8 @@ export const meta = { kind: 'write:user-groups', + description: 'Update the properties of a group.', + res: { type: 'object', optional: false, nullable: false, 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 d5260256d5..783e63f5de 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/create.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/create.ts @@ -1,7 +1,7 @@ -import define from '../../../define.js'; import { UserLists } from '@/models/index.js'; import { genId } from '@/misc/gen-id.js'; import { UserList } from '@/models/entities/user-list.js'; +import define from '../../../define.js'; export const meta = { tags: ['lists'], @@ -10,6 +10,8 @@ export const meta = { kind: 'write:account', + description: 'Create a new list of users.', + res: { type: 'object', optional: false, nullable: false, 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 b7ad96eef0..5a7613c98a 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/delete.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/delete.ts @@ -1,6 +1,6 @@ +import { UserLists } from '@/models/index.js'; import define from '../../../define.js'; import { ApiError } from '../../../error.js'; -import { UserLists } from '@/models/index.js'; export const meta = { tags: ['lists'], @@ -9,6 +9,8 @@ export const meta = { kind: 'write:account', + description: 'Delete an existing list of users.', + errors: { noSuchList: { message: 'No such list.', 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 78311292cb..889052fa30 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/list.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/list.ts @@ -1,5 +1,5 @@ -import define from '../../../define.js'; import { UserLists } from '@/models/index.js'; +import define from '../../../define.js'; export const meta = { tags: ['lists', 'account'], @@ -8,6 +8,8 @@ export const meta = { kind: 'read:account', + description: 'Show all lists that the authenticated user has created.', + res: { type: 'array', optional: false, nullable: false, 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 76863f07d1..d3d1d6555c 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/pull.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/pull.ts @@ -1,8 +1,8 @@ import { publishUserListStream } from '@/services/stream.js'; +import { UserLists, UserListJoinings, Users } from '@/models/index.js'; import define from '../../../define.js'; import { ApiError } from '../../../error.js'; import { getUser } from '../../../common/getters.js'; -import { UserLists, UserListJoinings, Users } from '@/models/index.js'; export const meta = { tags: ['lists', 'users'], @@ -11,6 +11,8 @@ export const meta = { kind: 'write:account', + description: 'Remove a user from a list.', + errors: { noSuchList: { message: 'No such list.', 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 260665c63a..12b7b86342 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/push.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/push.ts @@ -1,8 +1,8 @@ +import { pushUserToUserList } from '@/services/user-list/push.js'; +import { UserLists, UserListJoinings, Blockings } from '@/models/index.js'; import define from '../../../define.js'; import { ApiError } from '../../../error.js'; import { getUser } from '../../../common/getters.js'; -import { pushUserToUserList } from '@/services/user-list/push.js'; -import { UserLists, UserListJoinings, Blockings } from '@/models/index.js'; export const meta = { tags: ['lists', 'users'], @@ -11,6 +11,8 @@ export const meta = { kind: 'write:account', + description: 'Add a user to an existing list.', + errors: { noSuchList: { message: 'No such list.', 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 5f51980e95..fd0612f735 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/show.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/show.ts @@ -1,6 +1,6 @@ +import { UserLists } from '@/models/index.js'; import define from '../../../define.js'; import { ApiError } from '../../../error.js'; -import { UserLists } from '@/models/index.js'; export const meta = { tags: ['lists', 'account'], @@ -9,6 +9,8 @@ export const meta = { kind: 'read:account', + description: 'Show the properties of a list.', + res: { type: 'object', optional: false, nullable: false, 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 52353a14cc..65e708b959 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/update.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/update.ts @@ -1,6 +1,6 @@ +import { UserLists } from '@/models/index.js'; import define from '../../../define.js'; import { ApiError } from '../../../error.js'; -import { UserLists } from '@/models/index.js'; export const meta = { tags: ['lists'], @@ -9,6 +9,8 @@ export const meta = { kind: 'write:account', + description: 'Update the properties of a list.', + res: { type: 'object', optional: false, nullable: false, diff --git a/packages/backend/src/server/api/endpoints/users/notes.ts b/packages/backend/src/server/api/endpoints/users/notes.ts index 16318d2225..aec5c0ea99 100644 --- a/packages/backend/src/server/api/endpoints/users/notes.ts +++ b/packages/backend/src/server/api/endpoints/users/notes.ts @@ -1,17 +1,19 @@ +import { Brackets } from 'typeorm'; +import { Notes } from '@/models/index.js'; import define from '../../define.js'; import { ApiError } from '../../error.js'; import { getUser } from '../../common/getters.js'; import { makePaginationQuery } from '../../common/make-pagination-query.js'; import { generateVisibilityQuery } from '../../common/generate-visibility-query.js'; -import { Notes } from '@/models/index.js'; import { generateMutedUserQuery } from '../../common/generate-muted-user-query.js'; -import { Brackets } from 'typeorm'; import { generateBlockedUserQuery } from '../../common/generate-block-query.js'; import { generateMutedInstanceQuery } from '../../common/generate-muted-instance-query.js'; export const meta = { tags: ['users', 'notes'], + description: 'Show all notes that this user created.', + res: { type: 'array', optional: false, nullable: false, diff --git a/packages/backend/src/server/api/endpoints/users/pages.ts b/packages/backend/src/server/api/endpoints/users/pages.ts index b8b3e8192e..b1d28af845 100644 --- a/packages/backend/src/server/api/endpoints/users/pages.ts +++ b/packages/backend/src/server/api/endpoints/users/pages.ts @@ -1,9 +1,21 @@ -import define from '../../define.js'; import { Pages } from '@/models/index.js'; +import define from '../../define.js'; import { makePaginationQuery } from '../../common/make-pagination-query.js'; export const meta = { tags: ['users', 'pages'], + + description: 'Show all pages this user created.', + + res: { + type: 'array', + optional: false, nullable: false, + items: { + type: 'object', + optional: false, nullable: false, + ref: 'Page', + }, + }, } as const; export const paramDef = { @@ -20,7 +32,7 @@ export const paramDef = { // eslint-disable-next-line import/no-default-export 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.userId = :userId', { userId: ps.userId }) .andWhere('page.visibility = \'public\''); const pages = await query diff --git a/packages/backend/src/server/api/endpoints/users/reactions.ts b/packages/backend/src/server/api/endpoints/users/reactions.ts index c2d1994343..9668bd21b8 100644 --- a/packages/backend/src/server/api/endpoints/users/reactions.ts +++ b/packages/backend/src/server/api/endpoints/users/reactions.ts @@ -1,5 +1,5 @@ -import define from '../../define.js'; import { NoteReactions, UserProfiles } from '@/models/index.js'; +import define from '../../define.js'; import { makePaginationQuery } from '../../common/make-pagination-query.js'; import { generateVisibilityQuery } from '../../common/generate-visibility-query.js'; import { ApiError } from '../../error.js'; @@ -9,6 +9,8 @@ export const meta = { requireCredential: false, + description: 'Show all reactions this user made.', + res: { type: 'array', optional: false, nullable: false, @@ -50,8 +52,8 @@ export default define(meta, paramDef, async (ps, me) => { } const query = makePaginationQuery(NoteReactions.createQueryBuilder('reaction'), - ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) - .andWhere(`reaction.userId = :userId`, { userId: ps.userId }) + ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) + .andWhere('reaction.userId = :userId', { userId: ps.userId }) .leftJoinAndSelect('reaction.note', 'note'); generateVisibilityQuery(query, me); diff --git a/packages/backend/src/server/api/endpoints/users/recommendation.ts b/packages/backend/src/server/api/endpoints/users/recommendation.ts index a8f18de522..e7654e1714 100644 --- a/packages/backend/src/server/api/endpoints/users/recommendation.ts +++ b/packages/backend/src/server/api/endpoints/users/recommendation.ts @@ -1,6 +1,6 @@ import ms from 'ms'; -import define from '../../define.js'; import { Users, Followings } from '@/models/index.js'; +import define from '../../define.js'; import { generateMutedUserQueryForUsers } from '../../common/generate-muted-user-query.js'; import { generateBlockedUserQuery, generateBlockQueryForUsers } from '../../common/generate-block-query.js'; @@ -11,6 +11,8 @@ export const meta = { kind: 'read:account', + description: 'Show users that the authenticated user might be interested to follow.', + res: { type: 'array', optional: false, nullable: false, diff --git a/packages/backend/src/server/api/endpoints/users/relation.ts b/packages/backend/src/server/api/endpoints/users/relation.ts index c6262122d4..233a6a90b4 100644 --- a/packages/backend/src/server/api/endpoints/users/relation.ts +++ b/packages/backend/src/server/api/endpoints/users/relation.ts @@ -1,11 +1,13 @@ -import define from '../../define.js'; import { Users } from '@/models/index.js'; +import define from '../../define.js'; export const meta = { tags: ['users'], requireCredential: true, + description: 'Show the different kinds of relations between the authenticated user and the specified user(s).', + res: { optional: false, nullable: false, oneOf: [ 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 0be385dbbf..a9987eafa9 100644 --- a/packages/backend/src/server/api/endpoints/users/report-abuse.ts +++ b/packages/backend/src/server/api/endpoints/users/report-abuse.ts @@ -1,18 +1,20 @@ import * as sanitizeHtml from 'sanitize-html'; -import define from '../../define.js'; import { publishAdminStream } from '@/services/stream.js'; -import { ApiError } from '../../error.js'; -import { getUser } from '../../common/getters.js'; import { AbuseUserReports, Users } from '@/models/index.js'; import { genId } from '@/misc/gen-id.js'; import { sendEmail } from '@/services/send-email.js'; import { fetchMeta } from '@/misc/fetch-meta.js'; +import { getUser } from '../../common/getters.js'; +import { ApiError } from '../../error.js'; +import define from '../../define.js'; export const meta = { tags: ['users'], requireCredential: true, + description: 'File a report.', + errors: { noSuchUser: { message: 'No such user.', 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 f74d80e2ae..6e5bc46bb5 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,14 +1,16 @@ -import define from '../../define.js'; -import { Followings, Users } from '@/models/index.js'; import { Brackets } from 'typeorm'; +import { Followings, Users } from '@/models/index.js'; import { USER_ACTIVE_THRESHOLD } from '@/const.js'; import { User } from '@/models/entities/user.js'; +import define from '../../define.js'; export const meta = { tags: ['users'], requireCredential: false, + description: 'Search for a user by username and/or host.', + res: { type: 'array', optional: false, nullable: false, @@ -65,7 +67,7 @@ export default define(meta, paramDef, async (ps, me) => { const query = Users.createQueryBuilder('user') .where(`user.id IN (${ followingQuery.getQuery() })`) - .andWhere(`user.id != :meId`, { meId: me.id }) + .andWhere('user.id != :meId', { meId: me.id }) .andWhere('user.isSuspended = FALSE') .andWhere('user.usernameLower LIKE :username', { username: ps.username.toLowerCase() + '%' }) .andWhere(new Brackets(qb => { qb @@ -83,7 +85,7 @@ export default define(meta, paramDef, async (ps, me) => { 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 }) + .andWhere('user.id != :meId', { meId: me.id }) .andWhere('user.isSuspended = FALSE') .andWhere('user.usernameLower LIKE :username', { username: ps.username.toLowerCase() + '%' }) .andWhere('user.updatedAt IS NOT NULL'); diff --git a/packages/backend/src/server/api/endpoints/users/search.ts b/packages/backend/src/server/api/endpoints/users/search.ts index f93d4f718b..01729de667 100644 --- a/packages/backend/src/server/api/endpoints/users/search.ts +++ b/packages/backend/src/server/api/endpoints/users/search.ts @@ -1,13 +1,15 @@ -import define from '../../define.js'; +import { Brackets } from 'typeorm'; import { UserProfiles, Users } from '@/models/index.js'; import { User } from '@/models/entities/user.js'; -import { Brackets } from 'typeorm'; +import define from '../../define.js'; export const meta = { tags: ['users'], requireCredential: false, + description: 'Search for users.', + res: { type: 'array', optional: false, nullable: false, @@ -25,7 +27,7 @@ export const paramDef = { 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" }, + origin: { type: 'string', enum: ['local', 'remote', 'combined'], default: 'combined' }, detail: { type: 'boolean', default: true }, }, required: ['query'], @@ -111,7 +113,7 @@ export default define(meta, paramDef, async (ps, me) => { .orderBy('user.updatedAt', 'DESC', 'NULLS LAST') .take(ps.limit) .skip(ps.offset) - .getMany() + .getMany(), ); } } diff --git a/packages/backend/src/server/api/endpoints/users/show.ts b/packages/backend/src/server/api/endpoints/users/show.ts index 183ff1b8bb..846d83b49f 100644 --- a/packages/backend/src/server/api/endpoints/users/show.ts +++ b/packages/backend/src/server/api/endpoints/users/show.ts @@ -1,16 +1,18 @@ +import { FindOptionsWhere, In, IsNull } from 'typeorm'; import { resolveUser } from '@/remote/resolve-user.js'; +import { Users } from '@/models/index.js'; +import { User } from '@/models/entities/user.js'; import define from '../../define.js'; import { apiLogger } from '../../logger.js'; import { ApiError } from '../../error.js'; -import { Users } from '@/models/index.js'; -import { FindOptionsWhere, In, IsNull } from 'typeorm'; -import { User } from '@/models/entities/user.js'; export const meta = { tags: ['users'], requireCredential: false, + description: 'Show the properties of a user.', + res: { optional: false, nullable: false, oneOf: [ diff --git a/packages/backend/src/server/api/endpoints/users/stats.ts b/packages/backend/src/server/api/endpoints/users/stats.ts index d138019a72..47f322ee9b 100644 --- a/packages/backend/src/server/api/endpoints/users/stats.ts +++ b/packages/backend/src/server/api/endpoints/users/stats.ts @@ -1,12 +1,15 @@ +import { DriveFiles, Followings, NoteFavorites, NoteReactions, Notes, PageLikes, PollVotes, Users } from '@/models/index.js'; +import { awaitAll } from '@/prelude/await-all.js'; import define from '../../define.js'; import { ApiError } from '../../error.js'; -import { DriveFiles, Followings, NoteFavorites, NoteReactions, Notes, PageLikes, PollVotes, Users } from '@/models/index.js'; export const meta = { tags: ['users'], requireCredential: false, + description: 'Show statistics about a user.', + errors: { noSuchUser: { message: 'No such user.', @@ -14,6 +17,94 @@ export const meta = { id: '9e638e45-3b25-4ef7-8f95-07e8498f1819', }, }, + + res: { + type: 'object', + optional: false, nullable: false, + properties: { + notesCount: { + type: 'integer', + optional: false, nullable: false, + }, + repliesCount: { + type: 'integer', + optional: false, nullable: false, + }, + renotesCount: { + type: 'integer', + optional: false, nullable: false, + }, + repliedCount: { + type: 'integer', + optional: false, nullable: false, + }, + renotedCount: { + type: 'integer', + optional: false, nullable: false, + }, + pollVotesCount: { + type: 'integer', + optional: false, nullable: false, + }, + pollVotedCount: { + type: 'integer', + optional: false, nullable: false, + }, + localFollowingCount: { + type: 'integer', + optional: false, nullable: false, + }, + remoteFollowingCount: { + type: 'integer', + optional: false, nullable: false, + }, + localFollowersCount: { + type: 'integer', + optional: false, nullable: false, + }, + remoteFollowersCount: { + type: 'integer', + optional: false, nullable: false, + }, + followingCount: { + type: 'integer', + optional: false, nullable: false, + }, + followersCount: { + type: 'integer', + optional: false, nullable: false, + }, + sentReactionsCount: { + type: 'integer', + optional: false, nullable: false, + }, + receivedReactionsCount: { + type: 'integer', + optional: false, nullable: false, + }, + noteFavoritesCount: { + type: 'integer', + optional: false, nullable: false, + }, + pageLikesCount: { + type: 'integer', + optional: false, nullable: false, + }, + pageLikedCount: { + type: 'integer', + optional: false, nullable: false, + }, + driveFilesCount: { + type: 'integer', + optional: false, nullable: false, + }, + driveUsage: { + type: 'integer', + optional: false, nullable: false, + description: 'Drive usage in bytes', + }, + }, + }, } as const; export const paramDef = { @@ -31,109 +122,72 @@ export default define(meta, paramDef, async (ps, me) => { throw new ApiError(meta.errors.noSuchUser); } - const [ - notesCount, - repliesCount, - renotesCount, - repliedCount, - renotedCount, - pollVotesCount, - pollVotedCount, - localFollowingCount, - remoteFollowingCount, - localFollowersCount, - remoteFollowersCount, - sentReactionsCount, - receivedReactionsCount, - noteFavoritesCount, - pageLikesCount, - pageLikedCount, - driveFilesCount, - driveUsage, - ] = await Promise.all([ - Notes.createQueryBuilder('note') + const result = await awaitAll({ + notesCount: Notes.createQueryBuilder('note') .where('note.userId = :userId', { userId: user.id }) .getCount(), - Notes.createQueryBuilder('note') + repliesCount: Notes.createQueryBuilder('note') .where('note.userId = :userId', { userId: user.id }) .andWhere('note.replyId IS NOT NULL') .getCount(), - Notes.createQueryBuilder('note') + renotesCount: Notes.createQueryBuilder('note') .where('note.userId = :userId', { userId: user.id }) .andWhere('note.renoteId IS NOT NULL') .getCount(), - Notes.createQueryBuilder('note') + repliedCount: Notes.createQueryBuilder('note') .where('note.replyUserId = :userId', { userId: user.id }) .getCount(), - Notes.createQueryBuilder('note') + renotedCount: Notes.createQueryBuilder('note') .where('note.renoteUserId = :userId', { userId: user.id }) .getCount(), - PollVotes.createQueryBuilder('vote') + pollVotesCount: PollVotes.createQueryBuilder('vote') .where('vote.userId = :userId', { userId: user.id }) .getCount(), - PollVotes.createQueryBuilder('vote') + pollVotedCount: PollVotes.createQueryBuilder('vote') .innerJoin('vote.note', 'note') .where('note.userId = :userId', { userId: user.id }) .getCount(), - Followings.createQueryBuilder('following') + localFollowingCount: Followings.createQueryBuilder('following') .where('following.followerId = :userId', { userId: user.id }) .andWhere('following.followeeHost IS NULL') .getCount(), - Followings.createQueryBuilder('following') + remoteFollowingCount: Followings.createQueryBuilder('following') .where('following.followerId = :userId', { userId: user.id }) .andWhere('following.followeeHost IS NOT NULL') .getCount(), - Followings.createQueryBuilder('following') + localFollowersCount: Followings.createQueryBuilder('following') .where('following.followeeId = :userId', { userId: user.id }) .andWhere('following.followerHost IS NULL') .getCount(), - Followings.createQueryBuilder('following') + remoteFollowersCount: Followings.createQueryBuilder('following') .where('following.followeeId = :userId', { userId: user.id }) .andWhere('following.followerHost IS NOT NULL') .getCount(), - NoteReactions.createQueryBuilder('reaction') + sentReactionsCount: NoteReactions.createQueryBuilder('reaction') .where('reaction.userId = :userId', { userId: user.id }) .getCount(), - NoteReactions.createQueryBuilder('reaction') + receivedReactionsCount: NoteReactions.createQueryBuilder('reaction') .innerJoin('reaction.note', 'note') .where('note.userId = :userId', { userId: user.id }) .getCount(), - NoteFavorites.createQueryBuilder('favorite') + noteFavoritesCount: NoteFavorites.createQueryBuilder('favorite') .where('favorite.userId = :userId', { userId: user.id }) .getCount(), - PageLikes.createQueryBuilder('like') + pageLikesCount: PageLikes.createQueryBuilder('like') .where('like.userId = :userId', { userId: user.id }) .getCount(), - PageLikes.createQueryBuilder('like') + pageLikedCount: PageLikes.createQueryBuilder('like') .innerJoin('like.page', 'page') .where('page.userId = :userId', { userId: user.id }) .getCount(), - DriveFiles.createQueryBuilder('file') + driveFilesCount: DriveFiles.createQueryBuilder('file') .where('file.userId = :userId', { userId: user.id }) .getCount(), - DriveFiles.calcDriveUsageOf(user), - ]); + driveUsage: DriveFiles.calcDriveUsageOf(user), + }); - return { - notesCount, - repliesCount, - renotesCount, - repliedCount, - renotedCount, - pollVotesCount, - pollVotedCount, - localFollowingCount, - remoteFollowingCount, - localFollowersCount, - remoteFollowersCount, - followingCount: localFollowingCount + remoteFollowingCount, - followersCount: localFollowersCount + remoteFollowersCount, - sentReactionsCount, - receivedReactionsCount, - noteFavoritesCount, - pageLikesCount, - pageLikedCount, - driveFilesCount, - driveUsage, - }; + result.followingCount = result.localFollowingCount + result.remoteFollowingCount; + result.followersCount = result.localFollowersCount + result.remoteFollowersCount; + + return result; }); diff --git a/packages/backend/src/server/api/limiter.ts b/packages/backend/src/server/api/limiter.ts index 23430cf8b6..6ca3ebf18f 100644 --- a/packages/backend/src/server/api/limiter.ts +++ b/packages/backend/src/server/api/limiter.ts @@ -1,8 +1,8 @@ import Limiter from 'ratelimiter'; -import { redisClient } from '../../db/redis.js'; -import { IEndpointMeta } from './endpoints.js'; import { CacheableLocalUser, User } from '@/models/entities/user.js'; import Logger from '@/services/logger.js'; +import { redisClient } from '../../db/redis.js'; +import { IEndpointMeta } from './endpoints.js'; const logger = new Logger('limiter'); diff --git a/packages/backend/src/server/api/openapi/gen-spec.ts b/packages/backend/src/server/api/openapi/gen-spec.ts index 3929fff3f7..68fa814041 100644 --- a/packages/backend/src/server/api/openapi/gen-spec.ts +++ b/packages/backend/src/server/api/openapi/gen-spec.ts @@ -3,7 +3,7 @@ import config from '@/config/index.js'; import { errors as basicErrors } from './errors.js'; import { schemas, convertSchemaToOpenApiSchema } from './schemas.js'; -export function genOpenapiSpec(lang = 'ja-JP') { +export function genOpenapiSpec() { const spec = { openapi: '3.0.0', diff --git a/packages/backend/src/server/web/views/base.pug b/packages/backend/src/server/web/views/base.pug index 230ed1578a..5bb156f0f4 100644 --- a/packages/backend/src/server/web/views/base.pug +++ b/packages/backend/src/server/web/views/base.pug @@ -7,17 +7,15 @@ doctype html // - - _____ _ _ | |_|___ ___| |_ ___ _ _ - | | | | |_ -|_ -| \'_| -_| | | + | | | | |_ -|_ -| '_| -_| | | |_|_|_|_|___|___|_,_|___|_ | |___| Thank you for using Misskey! If you are reading this message... how about joining the development? https://github.com/misskey-dev/misskey - html diff --git a/packages/backend/src/services/chart/entities.ts b/packages/backend/src/services/chart/entities.ts index 13e994cb65..a9eeabd639 100644 --- a/packages/backend/src/services/chart/entities.ts +++ b/packages/backend/src/services/chart/entities.ts @@ -11,6 +11,11 @@ import { entity as PerUserFollowingChart } from './charts/entities/per-user-foll import { entity as PerUserDriveChart } from './charts/entities/per-user-drive.js'; import { entity as ApRequestChart } from './charts/entities/ap-request.js'; +import { entity as TestChart } from './charts/entities/test.js'; +import { entity as TestGroupedChart } from './charts/entities/test-grouped.js'; +import { entity as TestUniqueChart } from './charts/entities/test-unique.js'; +import { entity as TestIntersectionChart } from './charts/entities/test-intersection.js'; + export const entities = [ FederationChart.hour, FederationChart.day, NotesChart.hour, NotesChart.day, @@ -24,4 +29,11 @@ export const entities = [ PerUserFollowingChart.hour, PerUserFollowingChart.day, PerUserDriveChart.hour, PerUserDriveChart.day, ApRequestChart.hour, ApRequestChart.day, + + ...(process.env.NODE_ENV === 'test' ? [ + TestChart.hour, TestChart.day, + TestGroupedChart.hour, TestGroupedChart.day, + TestUniqueChart.hour, TestUniqueChart.day, + TestIntersectionChart.hour, TestIntersectionChart.day, + ] : []), ]; diff --git a/packages/backend/src/services/drive/generate-video-thumbnail.ts b/packages/backend/src/services/drive/generate-video-thumbnail.ts index ca12ab8d3d..6e6666481d 100644 --- a/packages/backend/src/services/drive/generate-video-thumbnail.ts +++ b/packages/backend/src/services/drive/generate-video-thumbnail.ts @@ -1,12 +1,10 @@ import * as fs from 'node:fs'; -import * as path from 'node:path'; -import { createTemp } from '@/misc/create-temp.js'; +import { createTempDir } from '@/misc/create-temp.js'; import { IImage, convertToJpeg } from './image-processor.js'; import FFmpeg from 'fluent-ffmpeg'; export async function GenerateVideoThumbnail(source: string): Promise { - const [file, cleanup] = await createTemp(); - const parsed = path.parse(file); + const [dir, cleanup] = await createTempDir(); try { await new Promise((res, rej) => { @@ -16,15 +14,15 @@ export async function GenerateVideoThumbnail(source: string): Promise { .on('end', res) .on('error', rej) .screenshot({ - folder: parsed.dir, - filename: parsed.base, + folder: dir, + filename: 'out.png', // must have .png extension count: 1, timestamps: ['5%'], }); }); // JPEGに変換 (Webpでもいいが、MastodonはWebpをサポートせず表示できなくなる) - return await convertToJpeg(498, 280); + return await convertToJpeg(`${dir}/out.png`, 498, 280); } finally { cleanup(); } diff --git a/packages/backend/test/chart.ts b/packages/backend/test/chart.ts index 823e388a82..ac0844679f 100644 --- a/packages/backend/test/chart.ts +++ b/packages/backend/test/chart.ts @@ -6,26 +6,17 @@ import TestChart from '../src/services/chart/charts/test.js'; import TestGroupedChart from '../src/services/chart/charts/test-grouped.js'; import TestUniqueChart from '../src/services/chart/charts/test-unique.js'; import TestIntersectionChart from '../src/services/chart/charts/test-intersection.js'; -import * as _TestChart from '../src/services/chart/charts/entities/test.js'; -import * as _TestGroupedChart from '../src/services/chart/charts/entities/test-grouped.js'; -import * as _TestUniqueChart from '../src/services/chart/charts/entities/test-unique.js'; -import * as _TestIntersectionChart from '../src/services/chart/charts/entities/test-intersection.js'; -import { async, initTestDb } from './utils.js'; +import { initDb } from '../src/db/postgre.js'; describe('Chart', () => { let testChart: TestChart; let testGroupedChart: TestGroupedChart; let testUniqueChart: TestUniqueChart; let testIntersectionChart: TestIntersectionChart; - let clock: lolex.Clock; + let clock: lolex.InstalledClock; - beforeEach(async(async () => { - await initTestDb(false, [ - _TestChart.entity.hour, _TestChart.entity.day, - _TestGroupedChart.entity.hour, _TestGroupedChart.entity.day, - _TestUniqueChart.entity.hour, _TestUniqueChart.entity.day, - _TestIntersectionChart.entity.hour, _TestIntersectionChart.entity.day, - ]); + beforeEach(async () => { + await initDb(true); testChart = new TestChart(); testGroupedChart = new TestGroupedChart(); @@ -34,14 +25,15 @@ describe('Chart', () => { clock = lolex.install({ now: new Date(Date.UTC(2000, 0, 1, 0, 0, 0)), + shouldClearNativeTimers: true, }); - })); + }); - afterEach(async(async () => { + afterEach(() => { clock.uninstall(); - })); + }); - it('Can updates', async(async () => { + it('Can updates', async () => { await testChart.increment(); await testChart.save(); @@ -63,9 +55,9 @@ describe('Chart', () => { total: [1, 0, 0], }, }); - })); + }); - it('Can updates (dec)', async(async () => { + it('Can updates (dec)', async () => { await testChart.decrement(); await testChart.save(); @@ -87,9 +79,9 @@ describe('Chart', () => { total: [-1, 0, 0], }, }); - })); + }); - it('Empty chart', async(async () => { + it('Empty chart', async () => { const chartHours = await testChart.getChart('hour', 3, null); const chartDays = await testChart.getChart('day', 3, null); @@ -108,9 +100,9 @@ describe('Chart', () => { total: [0, 0, 0], }, }); - })); + }); - it('Can updates at multiple times at same time', async(async () => { + it('Can updates at multiple times at same time', async () => { await testChart.increment(); await testChart.increment(); await testChart.increment(); @@ -134,9 +126,9 @@ describe('Chart', () => { total: [3, 0, 0], }, }); - })); + }); - it('複数回saveされてもデータの更新は一度だけ', async(async () => { + it('複数回saveされてもデータの更新は一度だけ', async () => { await testChart.increment(); await testChart.save(); await testChart.save(); @@ -160,9 +152,9 @@ describe('Chart', () => { total: [1, 0, 0], }, }); - })); + }); - it('Can updates at different times', async(async () => { + it('Can updates at different times', async () => { await testChart.increment(); await testChart.save(); @@ -189,11 +181,11 @@ describe('Chart', () => { total: [2, 0, 0], }, }); - })); + }); // 仕様上はこうなってほしいけど、実装は難しそうなのでskip /* - it('Can updates at different times without save', async(async () => { + it('Can updates at different times without save', async () => { await testChart.increment(); clock.tick('01:00:00'); @@ -219,10 +211,10 @@ describe('Chart', () => { total: [2, 0, 0] }, }); - })); + }); */ - it('Can padding', async(async () => { + it('Can padding', async () => { await testChart.increment(); await testChart.save(); @@ -249,10 +241,10 @@ describe('Chart', () => { total: [2, 0, 0], }, }); - })); + }); // 要求された範囲にログがひとつもない場合でもパディングできる - it('Can padding from past range', async(async () => { + it('Can padding from past range', async () => { await testChart.increment(); await testChart.save(); @@ -276,11 +268,11 @@ describe('Chart', () => { total: [1, 0, 0], }, }); - })); + }); // 要求された範囲の最も古い箇所に位置するログが存在しない場合でもパディングできる // Issue #3190 - it('Can padding from past range 2', async(async () => { + it('Can padding from past range 2', async () => { await testChart.increment(); await testChart.save(); @@ -307,9 +299,9 @@ describe('Chart', () => { total: [2, 0, 0], }, }); - })); + }); - it('Can specify offset', async(async () => { + it('Can specify offset', async () => { await testChart.increment(); await testChart.save(); @@ -336,9 +328,9 @@ describe('Chart', () => { total: [2, 0, 0], }, }); - })); + }); - it('Can specify offset (floor time)', async(async () => { + it('Can specify offset (floor time)', async () => { clock.tick('00:30:00'); await testChart.increment(); @@ -367,10 +359,10 @@ describe('Chart', () => { total: [2, 0, 0], }, }); - })); + }); describe('Grouped', () => { - it('Can updates', async(async () => { + it('Can updates', async () => { await testGroupedChart.increment('alice'); await testGroupedChart.save(); @@ -410,11 +402,11 @@ describe('Chart', () => { total: [0, 0, 0], }, }); - })); + }); }); describe('Unique increment', () => { - it('Can updates', async(async () => { + it('Can updates', async () => { await testUniqueChart.uniqueIncrement('alice'); await testUniqueChart.uniqueIncrement('alice'); await testUniqueChart.uniqueIncrement('bob'); @@ -430,10 +422,10 @@ describe('Chart', () => { assert.deepStrictEqual(chartDays, { foo: [2, 0, 0], }); - })); + }); describe('Intersection', () => { - it('条件が満たされていない場合はカウントされない', async(async () => { + it('条件が満たされていない場合はカウントされない', async () => { await testIntersectionChart.addA('alice'); await testIntersectionChart.addA('bob'); await testIntersectionChart.addB('carol'); @@ -453,9 +445,9 @@ describe('Chart', () => { b: [1, 0, 0], aAndB: [0, 0, 0], }); - })); + }); - it('条件が満たされている場合にカウントされる', async(async () => { + it('条件が満たされている場合にカウントされる', async () => { await testIntersectionChart.addA('alice'); await testIntersectionChart.addA('bob'); await testIntersectionChart.addB('carol'); @@ -476,12 +468,12 @@ describe('Chart', () => { b: [2, 0, 0], aAndB: [1, 0, 0], }); - })); + }); }); }); describe('Resync', () => { - it('Can resync', async(async () => { + it('Can resync', async () => { testChart.total = 1; await testChart.resync(); @@ -504,9 +496,9 @@ describe('Chart', () => { total: [1, 0, 0], }, }); - })); + }); - it('Can resync (2)', async(async () => { + it('Can resync (2)', async () => { await testChart.increment(); await testChart.save(); @@ -534,6 +526,6 @@ describe('Chart', () => { total: [100, 0, 0], }, }); - })); + }); }); }); diff --git a/packages/client/.eslintrc.js b/packages/client/.eslintrc.js index 1c2ab0a427..10f0e5a9cb 100644 --- a/packages/client/.eslintrc.js +++ b/packages/client/.eslintrc.js @@ -15,6 +15,12 @@ module.exports = { 'plugin:vue/vue3-recommended', ], rules: { + '@typescript-eslint/no-empty-interface': [ + 'error', + { + 'allowSingleExtends': true, + }, + ], // window の禁止理由: グローバルスコープと衝突し、予期せぬ結果を招くため // data の禁止理由: 抽象的すぎるため // e の禁止理由: error や event など、複数のキーワードの頭文字であり分かりにくいため diff --git a/packages/client/package.json b/packages/client/package.json index a53dc0cf5b..a891fbd0e9 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -19,13 +19,13 @@ "@rollup/pluginutils": "^4.2.1", "@syuilo/aiscript": "0.11.1", "@vitejs/plugin-vue": "2.3.3", - "@vue/compiler-sfc": "3.2.36", + "@vue/compiler-sfc": "3.2.37", "abort-controller": "3.0.0", "autobind-decorator": "2.4.0", "autosize": "5.0.1", "autwh": "0.1.0", "blurhash": "1.1.5", - "broadcast-channel": "4.12.0", + "broadcast-channel": "4.13.0", "browser-image-resizer": "misskey-dev/browser-image-resizer#tag=v2.2.1-misskey.2", "chart.js": "3.8.0", "chartjs-adapter-date-fns": "2.0.0", @@ -33,7 +33,8 @@ "chartjs-plugin-zoom": "1.2.1", "compare-versions": "4.1.3", "content-disposition": "0.5.4", - "date-fns": "^2.28.0", + "cropperjs": "2.0.0-beta", + "date-fns": "2.28.0", "escape-regexp": "0.0.1", "eventemitter3": "4.0.7", "feed": "4.2.2", @@ -58,9 +59,9 @@ "random-seed": "0.3.0", "reflect-metadata": "0.1.13", "rndstr": "1.0.0", - "rollup": "2.75.3", + "rollup": "2.75.6", "s-age": "1.1.2", - "sass": "1.52.1", + "sass": "1.52.3", "seedrandom": "3.0.5", "strict-event-emitter-types": "2.0.0", "stringz": "2.1.0", @@ -69,20 +70,20 @@ "three": "0.141.0", "throttle-debounce": "5.0.0", "tinycolor2": "1.4.2", - "tsc-alias": "1.6.7", + "tsc-alias": "1.6.9", "tsconfig-paths": "4.0.0", "twemoji-parser": "14.0.0", - "typescript": "4.7.2", + "typescript": "4.7.3", "uuid": "8.3.2", "v-debounce": "0.1.2", "vanilla-tilt": "1.7.2", - "vite": "2.9.9", - "vue": "3.2.36", + "vite": "2.9.10", + "vue": "3.2.37", "vue-prism-editor": "2.0.0-alpha.2", - "vue-router": "4.0.15", - "vuedraggable": "4.1.0", + "vue-router": "4.0.16", + "vuedraggable": "4.0.1", "websocket": "1.0.34", - "ws": "8.7.0" + "ws": "8.8.0" }, "devDependencies": { "@types/escape-regexp": "0.0.1", @@ -103,13 +104,13 @@ "@types/uuid": "8.3.4", "@types/websocket": "1.0.5", "@types/ws": "8.5.3", - "@typescript-eslint/eslint-plugin": "5.27.0", - "@typescript-eslint/parser": "5.27.0", + "@typescript-eslint/eslint-plugin": "5.27.1", + "@typescript-eslint/parser": "5.27.1", "cross-env": "7.0.3", - "cypress": "9.7.0", - "eslint": "8.16.0", + "cypress": "10.0.3", + "eslint": "8.17.0", "eslint-plugin-import": "2.26.0", - "eslint-plugin-vue": "9.0.1", + "eslint-plugin-vue": "9.1.0", "start-server-and-test": "1.14.0" } } diff --git a/packages/client/src/components/captcha.vue b/packages/client/src/components/captcha.vue index ccd8880df8..183658471b 100644 --- a/packages/client/src/components/captcha.vue +++ b/packages/client/src/components/captcha.vue @@ -27,8 +27,7 @@ type CaptchaContainer = { }; declare global { - interface Window extends CaptchaContainer { - } + interface Window extends CaptchaContainer { } } const props = defineProps<{ diff --git a/packages/client/src/components/cropper-dialog.vue b/packages/client/src/components/cropper-dialog.vue new file mode 100644 index 0000000000..a8bde6ea05 --- /dev/null +++ b/packages/client/src/components/cropper-dialog.vue @@ -0,0 +1,175 @@ + + + + + diff --git a/packages/client/src/components/drive-file-thumbnail.vue b/packages/client/src/components/drive-file-thumbnail.vue index dd24440e82..07cd565c58 100644 --- a/packages/client/src/components/drive-file-thumbnail.vue +++ b/packages/client/src/components/drive-file-thumbnail.vue @@ -1,6 +1,6 @@