Merge branch 'main' into fix-chat-continuation

This commit is contained in:
Norm 2022-07-19 07:08:51 +00:00
commit 7d4cbd6ecf
275 changed files with 3571 additions and 5420 deletions

View file

@ -72,24 +72,6 @@ redis:
# user: # user:
# pass: # pass:
# ┌───────────────┐
#───┘ ID generation └───────────────────────────────────────────
# You can select the ID generation method.
# You don't usually need to change this setting, but you can
# change it according to your preferences.
# Available methods:
# aid ... Short, Millisecond accuracy
# meid ... Similar to ObjectID, Millisecond accuracy
# ulid ... Millisecond accuracy
# objectid ... This is left for backward compatibility
# ONCE YOU HAVE STARTED THE INSTANCE, DO NOT CHANGE THE
# ID SETTINGS AFTER THAT!
id: 'aid'
# ┌─────────────────────┐ # ┌─────────────────────┐
#───┘ Other configuration └───────────────────────────────────── #───┘ Other configuration └─────────────────────────────────────

3
.github/FUNDING.yml vendored
View file

@ -1,3 +0,0 @@
# These are supported funding model platforms
patreon: syuilo

View file

@ -1,42 +0,0 @@
---
name: 🐛 Bug Report
about: Create a report to help us improve
title: ''
labels: ⚠bug?
assignees: ''
---
<!--
Thanks for reporting!
First, in order to avoid duplicate Issues, please search to see if the problem you found has already been reported.
-->
## 💡 Summary
<!-- Tell us what the bug is -->
## 🥰 Expected Behavior
<!--- Tell us what should happen -->
## 🤬 Actual Behavior
<!--
Tell us what happens instead of the expected behavior.
Please include errors from the developer console and/or server log files if you have access to them.
-->
## 📝 Steps to Reproduce
1.
2.
3.
## 📌 Environment
<!-- Tell us where on the platform it happens -->
Misskey version:
Your OS:
Your browser:

View file

@ -1,12 +0,0 @@
---
name: ✨ Feature Request
about: Suggest an idea for this project
title: ''
labels: ✨Feature
assignees: ''
---
## Summary
<!-- Tell us what the suggestion is -->

View file

@ -1,7 +0,0 @@
contact_links:
- name: 👪 Misskey Forum
url: https://forum.misskey.io/
about: Ask questions and share knowledge
- name: 💬 Misskey official Discord
url: https://discord.gg/Wp8gVStHW3
about: Chat freely about Misskey

View file

@ -1,17 +0,0 @@
<!-- お読みください / README
PRありがとうございます PRを作成する前に、コントリビューションガイドをご確認ください:
Thank you for your PR! Before creating a PR, please check the contribution guide:
https://github.com/misskey-dev/misskey/blob/develop/CONTRIBUTING.md
-->
# What
<!-- このPRで何をしたのか どう変わるのか? -->
<!-- What did you do with this PR? How will it change things? -->
# Why
<!-- なぜそうするのか? どういう意図なのか? 何が困っているのか? -->
<!-- Why do you do it? What are your intentions? What is the problem? -->
# Additional info (optional)
<!-- テスト観点など -->
<!-- Test perspective, etc -->

View file

@ -11,15 +11,25 @@ You should also include the user name that made the change.
## 12.x.x (unreleased) ## 12.x.x (unreleased)
### Changes
- ハイライトがみつけるに統合されました
- カスタム絵文字ページはインスタンス情報ページに統合されました
- 連合ページはインスタンス情報ページに統合されました
### Improvements ### Improvements
- Client: Fix URL-encoded routing
- Server: Allow GET method for some endpoints @syuilo - Server: Allow GET method for some endpoints @syuilo
- Server: Add rate limit to i/notifications @tamaina - Server: Add rate limit to i/notifications @tamaina
- Client: Improve control panel @syuilo - Client: Improve control panel @syuilo
- Client: Show warning in control panel when there is an unresolved abuse report @syuilo - Client: Show warning in control panel when there is an unresolved abuse report @syuilo
- Client: For notes with specified visibility, show recipients when hovering over visibility symbol. @Johann150
- Client: Add rss-ticker widget @syuilo
- Client: Removing entries from a clip @futchitwo
- Client: Poll highlights in explore page @syuilo
- Make possible to delete an account by admin @syuilo - Make possible to delete an account by admin @syuilo
- Improve player detection in URL preview @mei23 - Improve player detection in URL preview @mei23
- Add Badge Image to Push Notification #8012 @tamaina - Add Badge Image to Push Notification #8012 @tamaina
- Client: Removing entries from a clip @futchitwo - Server: Improve performance
- Server: Supports IPv6 on Redis transport. @mei23 - Server: Supports IPv6 on Redis transport. @mei23
IPv4/IPv6 is used by default. You can tune this behavior via `redis.family`. IPv4/IPv6 is used by default. You can tune this behavior via `redis.family`.

View file

@ -734,7 +734,6 @@ gallery: "المعرض"
recentPosts: "المشاركات الحديثة" recentPosts: "المشاركات الحديثة"
popularPosts: "المشاركات المتداولة" popularPosts: "المشاركات المتداولة"
shareWithNote: "شاركه في ملاحظة" shareWithNote: "شاركه في ملاحظة"
ads: "الإعلانات"
expiration: "ينتهي استطلاع الرأي في" expiration: "ينتهي استطلاع الرأي في"
memo: "تذكير" memo: "تذكير"
priority: "الأولوية" priority: "الأولوية"
@ -786,7 +785,6 @@ voteConfirm: "متيقِّن من تصويتك لـ {choice}؟"
hide: "إخفاء" hide: "إخفاء"
leaveGroup: "مغادرة الفريق" leaveGroup: "مغادرة الفريق"
leaveGroupConfirm: "متيقن من مغادرة \"{name}\"؟" leaveGroupConfirm: "متيقن من مغادرة \"{name}\"؟"
welcomeBackWithName: "مرحبًا بك مجددًا {name}"
clickToFinishEmailVerification: "انقر [{ok}] لاستيثاق بريدك الإلكتروني." clickToFinishEmailVerification: "انقر [{ok}] لاستيثاق بريدك الإلكتروني."
overridedDeviceKind: "نوع الجهاز" overridedDeviceKind: "نوع الجهاز"
smartphone: "هاتف ذكي" smartphone: "هاتف ذكي"

View file

@ -765,7 +765,6 @@ gallery: "গ্যালারী"
recentPosts: "নতুন পোস্ট" recentPosts: "নতুন পোস্ট"
popularPosts: "জনপ্রিয় পোস্ট" popularPosts: "জনপ্রিয় পোস্ট"
shareWithNote: "নোটের মাধ্যমে শেয়ার করুন" shareWithNote: "নোটের মাধ্যমে শেয়ার করুন"
ads: "বিজ্ঞাপন"
expiration: "নির্দিষ্ট সময়সীমা" expiration: "নির্দিষ্ট সময়সীমা"
memo: "মেমো" memo: "মেমো"
priority: "অগ্রাধিকার" priority: "অগ্রাধিকার"
@ -821,7 +820,6 @@ hide: "লুকান"
leaveGroup: "গ্রুপ ছেড়ে চলে যান" leaveGroup: "গ্রুপ ছেড়ে চলে যান"
leaveGroupConfirm: "\"{name}\" গ্রুপ ছেড়ে চলে যেতে চান?" leaveGroupConfirm: "\"{name}\" গ্রুপ ছেড়ে চলে যেতে চান?"
useDrawerReactionPickerForMobile: "মোবাইলে রিঅ্যাকশন পিকারকে ড্রয়ারে প্রদর্শন করুন" useDrawerReactionPickerForMobile: "মোবাইলে রিঅ্যাকশন পিকারকে ড্রয়ারে প্রদর্শন করুন"
welcomeBackWithName: "আবার স্বাগতম, {name}"
clickToFinishEmailVerification: " [{ok}] ক্লিক করার মাধ্যমে আপনার ইমেল ঠিকানা নিশ্চিত করুন।" clickToFinishEmailVerification: " [{ok}] ক্লিক করার মাধ্যমে আপনার ইমেল ঠিকানা নিশ্চিত করুন।"
overridedDeviceKind: "ডিভাইসের ধরন" overridedDeviceKind: "ডিভাইসের ধরন"
smartphone: "স্মার্টফোন" smartphone: "স্মার্টফোন"

View file

@ -766,7 +766,6 @@ gallery: "Galerie"
recentPosts: "Neue Beiträge" recentPosts: "Neue Beiträge"
popularPosts: "Beliebte Beiträge" popularPosts: "Beliebte Beiträge"
shareWithNote: "Mit Notiz teilen" shareWithNote: "Mit Notiz teilen"
ads: "Werbung"
expiration: "Frist" expiration: "Frist"
memo: "Merkzettel" memo: "Merkzettel"
priority: "Priorität" priority: "Priorität"
@ -822,7 +821,6 @@ hide: "Inhalt verbergen"
leaveGroup: "Gruppe verlassen" leaveGroup: "Gruppe verlassen"
leaveGroupConfirm: "Möchtest du „{name}“ wirklich verlassen?" leaveGroupConfirm: "Möchtest du „{name}“ wirklich verlassen?"
useDrawerReactionPickerForMobile: "Auf mobilen Geräten ausfahrbare Reaktionsauswahl anzeigen" useDrawerReactionPickerForMobile: "Auf mobilen Geräten ausfahrbare Reaktionsauswahl anzeigen"
welcomeBackWithName: "Willkommen zurück, {name}"
clickToFinishEmailVerification: "Drücke bitte auf [{ok}], um die Email-Bestätigung abzuschließen." clickToFinishEmailVerification: "Drücke bitte auf [{ok}], um die Email-Bestätigung abzuschließen."
overridedDeviceKind: "Gerätetyp" overridedDeviceKind: "Gerätetyp"
smartphone: "Smartphone" smartphone: "Smartphone"
@ -1659,3 +1657,17 @@ _deck:
list: "Listen" list: "Listen"
mentions: "Erwähnungen" mentions: "Erwähnungen"
direct: "Direktnachrichten" direct: "Direktnachrichten"
recentNHours: "Letzten {n} Stunden"
recentNDays: "Letzten {n} Tage"
isSystemAccount: "Ein Benutzerkonto, dass durch das System erstellt und automatisch kontrolliert wird."
typeToConfirm: "Bitte gib zur Bestätigung {x} ein"
deleteAccount: "Benutzerkonto löschen"
numberOfPageCache: "Seitencachegröße"
numberOfPageCacheDescription: "Das Erhöhen dieses Caches führt zu einer angenehmerern Benutzererfahrung, erhöht aber Serverlast und Arbeitsspeicherauslastung."
file: "Datei"
unclip: "Aus Clip entfernen"
confirmToUnclipAlreadyClippedNote: "Diese Notiz ist bereits im \"{name}\" Clip enthalten. Möchtest du sie aus diesem Clip entfernen?"
noEmailServerWarning: "Es ist kein Email-Server konfiguriert."
thereIsUnresolvedAbuseReportWarning: "Es liegen ungelöste Meldungen vor."
recommended: "Empfehlung"
check: "Check"

View file

@ -766,7 +766,6 @@ gallery: "Gallery"
recentPosts: "Recent posts" recentPosts: "Recent posts"
popularPosts: "Popular posts" popularPosts: "Popular posts"
shareWithNote: "Share with note" shareWithNote: "Share with note"
ads: "Advertisements"
expiration: "Deadline" expiration: "Deadline"
memo: "Memo" memo: "Memo"
priority: "Priority" priority: "Priority"
@ -822,7 +821,6 @@ hide: "Hide"
leaveGroup: "Leave group" leaveGroup: "Leave group"
leaveGroupConfirm: "Are you sure you want to leave \"{name}\"?" leaveGroupConfirm: "Are you sure you want to leave \"{name}\"?"
useDrawerReactionPickerForMobile: "Display reaction picker as drawer on mobile" useDrawerReactionPickerForMobile: "Display reaction picker as drawer on mobile"
welcomeBackWithName: "Welcome back, {name}"
clickToFinishEmailVerification: "Please click [{ok}] to complete email verification." clickToFinishEmailVerification: "Please click [{ok}] to complete email verification."
overridedDeviceKind: "Device type" overridedDeviceKind: "Device type"
smartphone: "Smartphone" smartphone: "Smartphone"
@ -852,6 +850,13 @@ typeToConfirm: "Please enter {x} to confirm"
deleteAccount: "Delete account" deleteAccount: "Delete account"
numberOfPageCache: "Number of cached pages" numberOfPageCache: "Number of cached pages"
numberOfPageCacheDescription: "Increasing this number will improve convenience for users but cause more server load as well as more memory to be used." numberOfPageCacheDescription: "Increasing this number will improve convenience for users but cause more server load as well as more memory to be used."
file: "File"
unclip: "Unclip"
confirmToUnclipAlreadyClippedNote: "This note is already part of the \"{name}\" clip. Do you want to remove it from this clip instead?"
noEmailServerWarning: "Email server not configured."
thereIsUnresolvedAbuseReportWarning: "There are unsolved reports."
recommended: "Recommended"
check: "Check"
_emailUnavailable: _emailUnavailable:
used: "This email address is already being used" used: "This email address is already being used"
format: "The format of this email address is invalid" format: "The format of this email address is invalid"
@ -1206,6 +1211,7 @@ _widgets:
trends: "Trending" trends: "Trending"
clock: "Clock" clock: "Clock"
rss: "RSS reader" rss: "RSS reader"
rssMarquee: "RSS ticker"
activity: "Activity" activity: "Activity"
photos: "Photos" photos: "Photos"
digitalClock: "Digital clock" digitalClock: "Digital clock"

View file

@ -761,7 +761,6 @@ gallery: "Galería"
recentPosts: "Posts recientes" recentPosts: "Posts recientes"
popularPosts: "Más vistos" popularPosts: "Más vistos"
shareWithNote: "Compartir con una nota" shareWithNote: "Compartir con una nota"
ads: "Anuncios"
expiration: "Termina el" expiration: "Termina el"
memo: "Notas" memo: "Notas"
priority: "Prioridad" priority: "Prioridad"

View file

@ -760,7 +760,6 @@ gallery: "Galerie"
recentPosts: "Les plus récentes" recentPosts: "Les plus récentes"
popularPosts: "Les plus consultées" popularPosts: "Les plus consultées"
shareWithNote: "Partager dans une note" shareWithNote: "Partager dans une note"
ads: "Publicité"
expiration: "Échéance" expiration: "Échéance"
memo: "Pense-bête" memo: "Pense-bête"
priority: "Priorité" priority: "Priorité"
@ -815,7 +814,6 @@ voteConfirm: "Confirmez-vous votre vote pour « {choice} » ?"
hide: "Masquer" hide: "Masquer"
leaveGroup: "Quitter le groupe" leaveGroup: "Quitter le groupe"
leaveGroupConfirm: "Êtes vous sûr de vouloir quitter \"{name}\" ?" leaveGroupConfirm: "Êtes vous sûr de vouloir quitter \"{name}\" ?"
welcomeBackWithName: "Heureux de vous revoir, {name}"
clickToFinishEmailVerification: "Veuillez cliquer sur [{ok}] afin de compléter la vérification par courriel." clickToFinishEmailVerification: "Veuillez cliquer sur [{ok}] afin de compléter la vérification par courriel."
overridedDeviceKind: "Type dappareil" overridedDeviceKind: "Type dappareil"
smartphone: "Smartphone" smartphone: "Smartphone"

View file

@ -765,7 +765,6 @@ gallery: "Galeri"
recentPosts: "Postingan terbaru" recentPosts: "Postingan terbaru"
popularPosts: "Postingan populer" popularPosts: "Postingan populer"
shareWithNote: "Bagikan dengan catatan" shareWithNote: "Bagikan dengan catatan"
ads: "Iklan"
expiration: "Batas akhir" expiration: "Batas akhir"
memo: "Memo" memo: "Memo"
priority: "Prioritas" priority: "Prioritas"
@ -821,7 +820,6 @@ hide: "Sembunyikan"
leaveGroup: "Keluar grup" leaveGroup: "Keluar grup"
leaveGroupConfirm: "Apakah kamu yakin untuk keluar dari \"{name}\"?" leaveGroupConfirm: "Apakah kamu yakin untuk keluar dari \"{name}\"?"
useDrawerReactionPickerForMobile: "Tampilkan bilah reaksi sebagai laci di ponsel" useDrawerReactionPickerForMobile: "Tampilkan bilah reaksi sebagai laci di ponsel"
welcomeBackWithName: "Selamat datang kembali, {name}."
clickToFinishEmailVerification: "Mohon klik [{ok}] untuk menyelesaikan verifikasi email." clickToFinishEmailVerification: "Mohon klik [{ok}] untuk menyelesaikan verifikasi email."
overridedDeviceKind: "Tipe perangkat" overridedDeviceKind: "Tipe perangkat"
smartphone: "Ponsel" smartphone: "Ponsel"

View file

@ -753,7 +753,6 @@ gallery: "Galleria"
recentPosts: "Le più recenti" recentPosts: "Le più recenti"
popularPosts: "Le più visualizzate" popularPosts: "Le più visualizzate"
shareWithNote: "Condividere in nota" shareWithNote: "Condividere in nota"
ads: "Pubblicità"
expiration: "Scadenza" expiration: "Scadenza"
memo: "Promemoria" memo: "Promemoria"
priority: "Priorità" priority: "Priorità"
@ -801,7 +800,6 @@ hide: "Nascondere"
leaveGroup: "Esci dal gruppo" leaveGroup: "Esci dal gruppo"
leaveGroupConfirm: "Uscire da「{name}」?" leaveGroupConfirm: "Uscire da「{name}」?"
useDrawerReactionPickerForMobile: "Mostra sul drawer da dispositivo mobile" useDrawerReactionPickerForMobile: "Mostra sul drawer da dispositivo mobile"
welcomeBackWithName: "Bentornato/a, {name}"
clickToFinishEmailVerification: "Fai click su [{ok}] per completare la verifica dell'indirizzo email." clickToFinishEmailVerification: "Fai click su [{ok}] per completare la verifica dell'indirizzo email."
indefinitely: "Non scade" indefinitely: "Non scade"
tenMinutes: "10 minuti" tenMinutes: "10 minuti"

View file

@ -768,7 +768,6 @@ gallery: "ギャラリー"
recentPosts: "最近の投稿" recentPosts: "最近の投稿"
popularPosts: "人気の投稿" popularPosts: "人気の投稿"
shareWithNote: "ノートで共有" shareWithNote: "ノートで共有"
ads: "広告"
expiration: "期限" expiration: "期限"
memo: "メモ" memo: "メモ"
priority: "優先度" priority: "優先度"
@ -824,7 +823,6 @@ hide: "隠す"
leaveGroup: "グループから抜ける" leaveGroup: "グループから抜ける"
leaveGroupConfirm: "「{name}」から抜けますか?" leaveGroupConfirm: "「{name}」から抜けますか?"
useDrawerReactionPickerForMobile: "モバイルデバイスのときドロワーで表示" useDrawerReactionPickerForMobile: "モバイルデバイスのときドロワーで表示"
welcomeBackWithName: "おかえりなさい、{name}さん"
clickToFinishEmailVerification: "[{ok}]を押して、メールアドレスの確認を完了してください。" clickToFinishEmailVerification: "[{ok}]を押して、メールアドレスの確認を完了してください。"
overridedDeviceKind: "デバイスタイプ" overridedDeviceKind: "デバイスタイプ"
smartphone: "スマートフォン" smartphone: "スマートフォン"
@ -1244,6 +1242,7 @@ _widgets:
trends: "トレンド" trends: "トレンド"
clock: "時計" clock: "時計"
rss: "RSSリーダー" rss: "RSSリーダー"
rssTicker: "RSSティッカー"
activity: "アクティビティ" activity: "アクティビティ"
photos: "フォト" photos: "フォト"
digitalClock: "デジタル時計" digitalClock: "デジタル時計"

View file

@ -645,7 +645,6 @@ goBack: "戻る"
info: "情報" info: "情報"
user: "ユーザー" user: "ユーザー"
administration: "管理" administration: "管理"
ads: "広告"
expiration: "期限" expiration: "期限"
memo: "メモ" memo: "メモ"
high: "高い" high: "高い"

View file

@ -765,7 +765,6 @@ gallery: "갤러리"
recentPosts: "최근 포스트" recentPosts: "최근 포스트"
popularPosts: "인기 포스트" popularPosts: "인기 포스트"
shareWithNote: "노트로 공유" shareWithNote: "노트로 공유"
ads: "광고"
expiration: "기한" expiration: "기한"
memo: "메모" memo: "메모"
priority: "우선순위" priority: "우선순위"
@ -821,7 +820,6 @@ hide: "숨기기"
leaveGroup: "그룹 나가기" leaveGroup: "그룹 나가기"
leaveGroupConfirm: "\"{name}\"에서 나갈까요?" leaveGroupConfirm: "\"{name}\"에서 나갈까요?"
useDrawerReactionPickerForMobile: "모바일에서 드로어 메뉴로 표시" useDrawerReactionPickerForMobile: "모바일에서 드로어 메뉴로 표시"
welcomeBackWithName: "환영합니다, {name}님"
clickToFinishEmailVerification: "[{ok}]를 눌러 이메일 인증을 완료하세요." clickToFinishEmailVerification: "[{ok}]를 눌러 이메일 인증을 완료하세요."
overridedDeviceKind: "장치 유형" overridedDeviceKind: "장치 유형"
smartphone: "스마트폰" smartphone: "스마트폰"

View file

@ -739,7 +739,6 @@ gallery: "Galeria"
recentPosts: "Ostatnie wpisy" recentPosts: "Ostatnie wpisy"
popularPosts: "Popularne wpisy" popularPosts: "Popularne wpisy"
shareWithNote: "Udostępnij z wpisem" shareWithNote: "Udostępnij z wpisem"
ads: "Reklamy"
expiration: "Ankieta kończy się" expiration: "Ankieta kończy się"
memo: "Notatki" memo: "Notatki"
priority: "Priorytet" priority: "Priorytet"

View file

@ -763,7 +763,6 @@ gallery: "Галерея"
recentPosts: "Недавние публикации" recentPosts: "Недавние публикации"
popularPosts: "Популярные публикации" popularPosts: "Популярные публикации"
shareWithNote: "Поделиться заметкой" shareWithNote: "Поделиться заметкой"
ads: "Реклама"
expiration: "Опрос длится" expiration: "Опрос длится"
memo: "Памятка" memo: "Памятка"
priority: "Приоритет" priority: "Приоритет"
@ -819,7 +818,6 @@ hide: "Спрятать"
leaveGroup: "Покинуть группу" leaveGroup: "Покинуть группу"
leaveGroupConfirm: "Покинуть группу «{name}»?" leaveGroupConfirm: "Покинуть группу «{name}»?"
useDrawerReactionPickerForMobile: "Выдвижная палитра на мобильном устройстве" useDrawerReactionPickerForMobile: "Выдвижная палитра на мобильном устройстве"
welcomeBackWithName: "С возвращением, {name}!"
clickToFinishEmailVerification: "Пожалуйста, нажмите [{ok}], чтобы завершить подтверждение адреса электронной почты." clickToFinishEmailVerification: "Пожалуйста, нажмите [{ok}], чтобы завершить подтверждение адреса электронной почты."
overridedDeviceKind: "Тип устройства" overridedDeviceKind: "Тип устройства"
smartphone: "Смартфон" smartphone: "Смартфон"

View file

@ -764,7 +764,6 @@ gallery: "Galéria"
recentPosts: "Najnovšie príspevky" recentPosts: "Najnovšie príspevky"
popularPosts: "Populárne príspevky" popularPosts: "Populárne príspevky"
shareWithNote: "Zdieľať s poznámkou" shareWithNote: "Zdieľať s poznámkou"
ads: "Reklamy"
expiration: "Ukončiť hlasovanie" expiration: "Ukončiť hlasovanie"
memo: "Memo" memo: "Memo"
priority: "Priorita" priority: "Priorita"
@ -820,7 +819,6 @@ hide: "Skryť"
leaveGroup: "Opustiť skupiny" leaveGroup: "Opustiť skupiny"
leaveGroupConfirm: "Naozaj chcete opustiť \"{name}\"?" leaveGroupConfirm: "Naozaj chcete opustiť \"{name}\"?"
useDrawerReactionPickerForMobile: "Zobraziť výber reakcií ako šuflík na mobile" useDrawerReactionPickerForMobile: "Zobraziť výber reakcií ako šuflík na mobile"
welcomeBackWithName: "Vitajte späť, {name}"
clickToFinishEmailVerification: "Kliknutím na [{ok}] dokončíte overeniu emailu." clickToFinishEmailVerification: "Kliknutím na [{ok}] dokončíte overeniu emailu."
overridedDeviceKind: "Typ zariadenia" overridedDeviceKind: "Typ zariadenia"
smartphone: "Smartfón" smartphone: "Smartfón"

View file

@ -765,7 +765,6 @@ gallery: "Thư viện ảnh"
recentPosts: "Tút gần đây" recentPosts: "Tút gần đây"
popularPosts: "Tút được xem nhiều nhất" popularPosts: "Tút được xem nhiều nhất"
shareWithNote: "Chia sẻ kèm với tút" shareWithNote: "Chia sẻ kèm với tút"
ads: "Quảng cáo"
expiration: "Thời hạn" expiration: "Thời hạn"
memo: "Lưu ý" memo: "Lưu ý"
priority: "Ưu tiên" priority: "Ưu tiên"
@ -821,7 +820,6 @@ hide: "Ẩn"
leaveGroup: "Rời khỏi nhóm" leaveGroup: "Rời khỏi nhóm"
leaveGroupConfirm: "Bạn có chắc muốn rời khỏi nhóm \"{name}\"?" leaveGroupConfirm: "Bạn có chắc muốn rời khỏi nhóm \"{name}\"?"
useDrawerReactionPickerForMobile: "Hiện bộ chọn biểu cảm dạng xổ ra trên điện thoại" useDrawerReactionPickerForMobile: "Hiện bộ chọn biểu cảm dạng xổ ra trên điện thoại"
welcomeBackWithName: "Chào mừng trở lại, {name}"
clickToFinishEmailVerification: "Vui lòng nhấn [{ok}] để hoàn tất việc đăng ký." clickToFinishEmailVerification: "Vui lòng nhấn [{ok}] để hoàn tất việc đăng ký."
overridedDeviceKind: "Loại thiết bị" overridedDeviceKind: "Loại thiết bị"
smartphone: "Điện thoại" smartphone: "Điện thoại"

View file

@ -765,7 +765,6 @@ gallery: "图库"
recentPosts: "最新发布" recentPosts: "最新发布"
popularPosts: "热门投稿" popularPosts: "热门投稿"
shareWithNote: "在帖子中分享" shareWithNote: "在帖子中分享"
ads: "广告"
expiration: "截止时间" expiration: "截止时间"
memo: "便笺" memo: "便笺"
priority: "优先级" priority: "优先级"
@ -821,7 +820,6 @@ hide: "隐藏"
leaveGroup: "离开群组" leaveGroup: "离开群组"
leaveGroupConfirm: "确定离开「{name}」?" leaveGroupConfirm: "确定离开「{name}」?"
useDrawerReactionPickerForMobile: "在移动设备上使用抽屉显示" useDrawerReactionPickerForMobile: "在移动设备上使用抽屉显示"
welcomeBackWithName: "欢迎回来,{name}"
clickToFinishEmailVerification: "点击 [{ok}] 完成电子邮件地址认证。" clickToFinishEmailVerification: "点击 [{ok}] 完成电子邮件地址认证。"
overridedDeviceKind: "设备类型" overridedDeviceKind: "设备类型"
smartphone: "智能手机" smartphone: "智能手机"

View file

@ -765,7 +765,6 @@ gallery: "相簿"
recentPosts: "最新貼文" recentPosts: "最新貼文"
popularPosts: "熱門的貼文" popularPosts: "熱門的貼文"
shareWithNote: "在貼文中分享" shareWithNote: "在貼文中分享"
ads: "廣告"
expiration: "期限" expiration: "期限"
memo: "備忘錄" memo: "備忘錄"
priority: "優先級" priority: "優先級"
@ -821,7 +820,6 @@ hide: "隱藏"
leaveGroup: "離開群組" leaveGroup: "離開群組"
leaveGroupConfirm: "確定離開「{name}」?" leaveGroupConfirm: "確定離開「{name}」?"
useDrawerReactionPickerForMobile: "在移動設備上使用抽屜顯示" useDrawerReactionPickerForMobile: "在移動設備上使用抽屜顯示"
welcomeBackWithName: "歡迎回來,{name}"
clickToFinishEmailVerification: "點擊 [{ok}] 完成電子郵件地址認證。" clickToFinishEmailVerification: "點擊 [{ok}] 完成電子郵件地址認證。"
overridedDeviceKind: "裝置類型" overridedDeviceKind: "裝置類型"
smartphone: "智慧型手機" smartphone: "智慧型手機"

View file

@ -1,10 +1,9 @@
{ {
"name": "misskey", "name": "foundkey",
"version": "12.111.1-test.1", "version": "12.111.1-test.1",
"codename": "indigo",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://github.com/misskey-dev/misskey.git" "url": "https://akkoma.dev/FoundKeyGang/FoundKey.git"
}, },
"private": true, "private": true,
"scripts": { "scripts": {
@ -41,10 +40,10 @@
"devDependencies": { "devDependencies": {
"@types/gulp": "4.0.9", "@types/gulp": "4.0.9",
"@types/gulp-rename": "2.0.1", "@types/gulp-rename": "2.0.1",
"@typescript-eslint/parser": "5.27.1", "@typescript-eslint/parser": "5.30.0",
"cross-env": "7.0.3", "cross-env": "7.0.3",
"cypress": "10.0.3", "cypress": "10.3.0",
"start-server-and-test": "1.14.0", "start-server-and-test": "1.14.0",
"typescript": "4.7.3" "typescript": "4.7.4"
} }
} }

View file

@ -0,0 +1,12 @@
export class removeAds1657570176749 {
name = 'removeAds1657570176749'
async up(queryRunner) {
await queryRunner.query(`DROP TABLE "ad"`);
}
async down(queryRunner) {
await queryRunner.query(`CREATE TABLE public.ad ("id" character varying(32) NOT NULL, "createdAt" timestamp with time zone NOT NULL, "expiresAt" timestamp with time zone NOT NULL, "place" character varying(32) NOT NULL, "priority" character varying(32) NOT NULL, "url" character varying(1024) NOT NULL, "imageUrl" character varying(1024) NOT NULL, "memo" character varying(8192) NOT NULL, "ratio" integer DEFAULT 1 NOT NULL)`);
}
}

View file

@ -0,0 +1,13 @@
export class removeRepoUrl1658146000392 {
name = 'removeRepoUrl1658146000392';
async up(queryRunner) {
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "repositoryUrl"`);
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "feedbackUrl"`);
}
async down(queryRunner) {
await queryRunner.query(`ALTER TABLE "meta" ADD "repositoryUrl" character varying(512) not null default 'https://github.com/misskey-dev/misskey'`);
await queryRunner.query(`ALTER TABLE "meta" ADD "feedbackUrl" character varying(512) default 'https://github.com/misskey-dev/misskey/issues/new'`);
}
}

View file

@ -5,7 +5,7 @@
"scripts": { "scripts": {
"build": "tsc -p tsconfig.json || echo done. && tsc-alias -p tsconfig.json", "build": "tsc -p tsconfig.json || echo done. && tsc-alias -p tsconfig.json",
"watch": "node watch.mjs", "watch": "node watch.mjs",
"lint": "eslint --quiet \"src/**/*.ts\"", "lint": "eslint --quiet src --ext .ts",
"mocha": "cross-env NODE_ENV=test TS_NODE_FILES=true TS_NODE_TRANSPILE_ONLY=true TS_NODE_PROJECT=\"./test/tsconfig.json\" mocha", "mocha": "cross-env NODE_ENV=test TS_NODE_FILES=true TS_NODE_TRANSPILE_ONLY=true TS_NODE_PROJECT=\"./test/tsconfig.json\" mocha",
"test": "npm run mocha" "test": "npm run mocha"
}, },
@ -14,7 +14,7 @@
"lodash": "^4.17.21" "lodash": "^4.17.21"
}, },
"dependencies": { "dependencies": {
"@bull-board/koa": "3.11.1", "@bull-board/koa": "4.0.0",
"@discordapp/twemoji": "14.0.2", "@discordapp/twemoji": "14.0.2",
"@elastic/elasticsearch": "7.11.0", "@elastic/elasticsearch": "7.11.0",
"@koa/cors": "3.1.0", "@koa/cors": "3.1.0",
@ -28,10 +28,10 @@
"archiver": "5.3.1", "archiver": "5.3.1",
"autobind-decorator": "2.4.0", "autobind-decorator": "2.4.0",
"autwh": "0.1.0", "autwh": "0.1.0",
"aws-sdk": "2.1152.0", "aws-sdk": "2.1165.0",
"bcryptjs": "2.4.3", "bcryptjs": "2.4.3",
"blurhash": "1.1.5", "blurhash": "1.1.5",
"bull": "4.8.3", "bull": "4.8.4",
"cacheable-lookup": "6.0.4", "cacheable-lookup": "6.0.4",
"cbor": "8.1.0", "cbor": "8.1.0",
"chalk": "5.0.1", "chalk": "5.0.1",
@ -51,7 +51,7 @@
"ip-cidr": "3.0.10", "ip-cidr": "3.0.10",
"is-svg": "4.3.2", "is-svg": "4.3.2",
"js-yaml": "4.1.0", "js-yaml": "4.1.0",
"jsdom": "19.0.0", "jsdom": "20.0.0",
"json5": "2.2.1", "json5": "2.2.1",
"json5-loader": "4.0.1", "json5-loader": "4.0.1",
"jsonld": "6.0.0", "jsonld": "6.0.0",
@ -69,29 +69,29 @@
"mime-types": "2.1.35", "mime-types": "2.1.35",
"misskey-js": "0.0.14", "misskey-js": "0.0.14",
"mocha": "10.0.0", "mocha": "10.0.0",
"ms": "3.0.0-canary.1",
"multer": "1.4.4", "multer": "1.4.4",
"nested-property": "4.0.0", "nested-property": "4.0.0",
"node-fetch": "3.2.6", "node-fetch": "3.2.6",
"nodemailer": "6.7.5", "nodemailer": "6.7.6",
"os-utils": "0.0.14", "os-utils": "0.0.14",
"parse5": "6.0.1", "parse5": "7.0.0",
"pg": "8.7.3", "pg": "8.7.3",
"private-ip": "2.3.3", "private-ip": "2.3.3",
"probe-image-size": "7.2.3", "probe-image-size": "7.2.3",
"promise-limit": "2.7.0", "promise-limit": "2.7.0",
"pug": "3.0.2", "pug": "3.0.2",
"punycode": "2.1.1", "punycode": "2.1.1",
"pureimage": "0.3.8", "pureimage": "0.3.14",
"qrcode": "1.5.0", "qrcode": "1.5.0",
"random-seed": "0.3.0", "random-seed": "0.3.0",
"ratelimiter": "3.4.1", "ratelimiter": "3.4.1",
"re2": "1.17.4", "re2": "1.17.7",
"redis-lock": "0.1.4", "redis-lock": "0.1.4",
"reflect-metadata": "0.1.13", "reflect-metadata": "0.1.13",
"rename": "1.0.4", "rename": "1.0.4",
"require-all": "3.0.0", "require-all": "3.0.0",
"rndstr": "1.0.0", "rndstr": "1.0.0",
"rss-parser": "3.12.0",
"s-age": "1.1.2", "s-age": "1.1.2",
"sanitize-html": "2.7.0", "sanitize-html": "2.7.0",
"semver": "7.3.7", "semver": "7.3.7",
@ -102,16 +102,15 @@
"style-loader": "3.3.1", "style-loader": "3.3.1",
"summaly": "2.6.0", "summaly": "2.6.0",
"syslog-pro": "1.0.0", "syslog-pro": "1.0.0",
"systeminformation": "5.11.16", "systeminformation": "5.11.22",
"tinycolor2": "1.4.2", "tinycolor2": "1.4.2",
"tmp": "0.2.1", "tmp": "0.2.1",
"ts-loader": "9.3.0", "ts-loader": "9.3.1",
"ts-node": "10.8.1", "ts-node": "10.8.1",
"tsc-alias": "1.6.9", "tsc-alias": "1.6.11",
"tsconfig-paths": "4.0.0", "tsconfig-paths": "4.0.0",
"twemoji-parser": "14.0.0", "twemoji-parser": "14.0.0",
"typeorm": "0.3.6", "typeorm": "0.3.7",
"ulid": "2.3.0",
"unzipper": "0.10.11", "unzipper": "0.10.11",
"uuid": "8.3.2", "uuid": "8.3.2",
"web-push": "3.5.0", "web-push": "3.5.0",
@ -121,7 +120,6 @@
}, },
"devDependencies": { "devDependencies": {
"@redocly/openapi-core": "1.0.0-beta.97", "@redocly/openapi-core": "1.0.0-beta.97",
"@types/semver": "7.3.9",
"@types/bcryptjs": "2.4.2", "@types/bcryptjs": "2.4.2",
"@types/bull": "3.15.8", "@types/bull": "3.15.8",
"@types/cbor": "6.0.0", "@types/cbor": "6.0.0",
@ -144,11 +142,10 @@
"@types/koa__multer": "2.0.4", "@types/koa__multer": "2.0.4",
"@types/koa__router": "8.0.11", "@types/koa__router": "8.0.11",
"@types/mocha": "9.1.1", "@types/mocha": "9.1.1",
"@types/node": "17.0.41", "@types/node": "18.0.0",
"@types/node-fetch": "3.0.3", "@types/node-fetch": "3.0.3",
"@types/nodemailer": "6.4.4", "@types/nodemailer": "6.4.4",
"@types/oauth": "0.9.1", "@types/oauth": "0.9.1",
"@types/parse5": "6.0.3",
"@types/pug": "2.0.6", "@types/pug": "2.0.6",
"@types/punycode": "2.1.0", "@types/punycode": "2.1.0",
"@types/qrcode": "1.4.2", "@types/qrcode": "1.4.2",
@ -157,7 +154,8 @@
"@types/redis": "4.0.11", "@types/redis": "4.0.11",
"@types/rename": "1.0.4", "@types/rename": "1.0.4",
"@types/sanitize-html": "2.6.2", "@types/sanitize-html": "2.6.2",
"@types/sharp": "0.30.2", "@types/semver": "7.3.10",
"@types/sharp": "0.30.4",
"@types/sinonjs__fake-timers": "8.1.2", "@types/sinonjs__fake-timers": "8.1.2",
"@types/speakeasy": "2.0.7", "@types/speakeasy": "2.0.7",
"@types/tinycolor2": "1.4.3", "@types/tinycolor2": "1.4.3",
@ -166,12 +164,12 @@
"@types/web-push": "3.3.2", "@types/web-push": "3.3.2",
"@types/websocket": "1.0.5", "@types/websocket": "1.0.5",
"@types/ws": "8.5.3", "@types/ws": "8.5.3",
"@typescript-eslint/eslint-plugin": "5.27.1", "@typescript-eslint/eslint-plugin": "^5.30.0",
"@typescript-eslint/parser": "5.27.1", "@typescript-eslint/parser": "^5.30.0",
"typescript": "4.7.3",
"eslint": "8.17.0",
"eslint-plugin-import": "2.26.0",
"cross-env": "7.0.3", "cross-env": "7.0.3",
"execa": "6.1.0" "eslint": "^8.20.0",
"eslint-plugin-import": "^2.26.0",
"execa": "6.1.0",
"typescript": "^4.7.4"
} }
} }

View file

@ -28,6 +28,8 @@ export default function load() {
const clientManifest = JSON.parse(fs.readFileSync(`${_dirname}/../../../../built/_client_dist_/manifest.json`, 'utf-8')); const clientManifest = JSON.parse(fs.readFileSync(`${_dirname}/../../../../built/_client_dist_/manifest.json`, 'utf-8'));
const config = yaml.load(fs.readFileSync(path, 'utf-8')) as Source; const config = yaml.load(fs.readFileSync(path, 'utf-8')) as Source;
if (config.id && config.id !== 'aid') throw new Error('Unsupported ID algorithm. Only "aid" is supported.');
const mixin = {} as Mixin; const mixin = {} as Mixin;
const url = tryCreateUrl(config.url); const url = tryCreateUrl(config.url);

View file

@ -1,5 +1,10 @@
export const MAX_NOTE_TEXT_LENGTH = 3000; export const MAX_NOTE_TEXT_LENGTH = 3000;
export const SECOND = 1000;
export const MINUTE = 60 * SECOND;
export const HOUR = 60 * MINUTE;
export const DAY = 24 * HOUR;
export const USER_ONLINE_THRESHOLD = 1000 * 60 * 10; // 10min export const USER_ONLINE_THRESHOLD = 1000 * 60 * 10; // 10min
export const USER_ACTIVE_THRESHOLD = 1000 * 60 * 60 * 24 * 3; // 3days export const USER_ACTIVE_THRESHOLD = 1000 * 60 * 60 * 24 * 3; // 3days

View file

@ -65,7 +65,6 @@ import { Channel } from '@/models/entities/channel.js';
import { ChannelFollowing } from '@/models/entities/channel-following.js'; import { ChannelFollowing } from '@/models/entities/channel-following.js';
import { ChannelNotePining } from '@/models/entities/channel-note-pining.js'; import { ChannelNotePining } from '@/models/entities/channel-note-pining.js';
import { RegistryItem } from '@/models/entities/registry-item.js'; import { RegistryItem } from '@/models/entities/registry-item.js';
import { Ad } from '@/models/entities/ad.js';
import { PasswordResetRequest } from '@/models/entities/password-reset-request.js'; import { PasswordResetRequest } from '@/models/entities/password-reset-request.js';
import { UserPending } from '@/models/entities/user-pending.js'; import { UserPending } from '@/models/entities/user-pending.js';
@ -168,7 +167,6 @@ export const entities = [
ChannelFollowing, ChannelFollowing,
ChannelNotePining, ChannelNotePining,
RegistryItem, RegistryItem,
Ad,
PasswordResetRequest, PasswordResetRequest,
UserPending, UserPending,
Webhook, Webhook,

View file

@ -1,6 +1,8 @@
import * as parse5 from 'parse5';
import treeAdapter from 'parse5/lib/tree-adapters/default.js';
import { URL } from 'node:url'; import { URL } from 'node:url';
import * as parse5 from 'parse5';
import * as TreeAdapter from '../../node_modules/parse5/dist/tree-adapters/default.js';
const treeAdapter = TreeAdapter.defaultTreeAdapter;
const urlRegex = /^https?:\/\/[\w\/:%#@$&?!()\[\]~.,=+\-]+/; const urlRegex = /^https?:\/\/[\w\/:%#@$&?!()\[\]~.,=+\-]+/;
const urlRegexFull = /^https?:\/\/[\w\/:%#@$&?!()\[\]~.,=+\-]+$/; const urlRegexFull = /^https?:\/\/[\w\/:%#@$&?!()\[\]~.,=+\-]+$/;
@ -19,7 +21,7 @@ export function fromHtml(html: string, hashtagNames?: string[]): string {
return text.trim(); return text.trim();
function getText(node: parse5.Node): string { function getText(node: TreeAdapter.Node): string {
if (treeAdapter.isTextNode(node)) return node.value; if (treeAdapter.isTextNode(node)) return node.value;
if (!treeAdapter.isElementNode(node)) return ''; if (!treeAdapter.isElementNode(node)) return '';
if (node.nodeName === 'br') return '\n'; if (node.nodeName === 'br') return '\n';
@ -31,7 +33,7 @@ export function fromHtml(html: string, hashtagNames?: string[]): string {
return ''; return '';
} }
function appendChildren(childNodes: parse5.ChildNode[]): void { function appendChildren(childNodes: TreeAdapter.ChildNode[]): void {
if (childNodes) { if (childNodes) {
for (const n of childNodes) { for (const n of childNodes) {
analyze(n); analyze(n);
@ -39,7 +41,7 @@ export function fromHtml(html: string, hashtagNames?: string[]): string {
} }
} }
function analyze(node: parse5.Node) { function analyze(node: TreeAdapter.Node) {
if (treeAdapter.isTextNode(node)) { if (treeAdapter.isTextNode(node)) {
text += node.value; text += node.value;
return; return;
@ -170,7 +172,7 @@ export function fromHtml(html: string, hashtagNames?: string[]): string {
const t = getText(node); const t = getText(node);
if (t) { if (t) {
text += '\n> '; text += '\n> ';
text += t.split('\n').join(`\n> `); text += t.split('\n').join('\n> ');
} }
break; break;
} }

View file

@ -1,21 +1,23 @@
import { ulid } from 'ulid'; import * as crypto from 'node:crypto';
import { genAid } from './id/aid.js';
import { genMeid } from './id/meid.js';
import { genMeidg } from './id/meidg.js';
import { genObjectId } from './id/object-id.js';
import config from '@/config/index.js';
const metohd = config.id.toLowerCase(); // AID generation
// 8 chars: milliseconds elapsed since 2000-01-01 00:00:00.000Z encoded as base36
// + 2 random chars
const TIME2000 = 946684800000;
let counter = crypto.randomBytes(2).readUInt16LE(0);
export function genId(date?: Date): string { export function genId(date?: Date): string {
if (!date || (date > new Date())) date = new Date(); if (!date || (date > new Date())) date = new Date();
switch (metohd) { let t = date.getTime();
case 'aid': return genAid(date); t -= TIME2000;
case 'meid': return genMeid(date); if (t < 0) t = 0;
case 'meidg': return genMeidg(date); if (isNaN(t)) throw 'Failed to create AID: Invalid Date';
case 'ulid': return ulid(date.getTime()); const time = t.toString(36).padStart(8, '0');
case 'objectid': return genObjectId(date);
default: throw new Error('unrecognized id generation method'); counter++;
} const noise = counter.toString(36).padStart(2, '0').slice(-2);
return time + noise;
} }

View file

@ -1,25 +0,0 @@
// AID
// 長さ8の[2000年1月1日からの経過ミリ秒をbase36でエンコードしたもの] + 長さ2の[ノイズ文字列]
import * as crypto from 'node:crypto';
const TIME2000 = 946684800000;
let counter = crypto.randomBytes(2).readUInt16LE(0);
function getTime(time: number) {
time = time - TIME2000;
if (time < 0) time = 0;
return time.toString(36).padStart(8, '0');
}
function getNoise() {
return counter.toString(36).padStart(2, '0').slice(-2);
}
export function genAid(date: Date): string {
const t = date.getTime();
if (isNaN(t)) throw 'Failed to create AID: Invalid Date';
counter++;
return getTime(t) + getNoise();
}

View file

@ -1,26 +0,0 @@
const CHARS = '0123456789abcdef';
function getTime(time: number) {
if (time < 0) time = 0;
if (time === 0) {
return CHARS[0];
}
time += 0x800000000000;
return time.toString(16).padStart(12, CHARS[0]);
}
function getRandom() {
let str = '';
for (let i = 0; i < 12; i++) {
str += CHARS[Math.floor(Math.random() * CHARS.length)];
}
return str;
}
export function genMeid(date: Date): string {
return getTime(date.getTime()) + getRandom();
}

View file

@ -1,28 +0,0 @@
const CHARS = '0123456789abcdef';
// 4bit Fixed hex value 'g'
// 44bit UNIX Time ms in Hex
// 48bit Random value in Hex
function getTime(time: number) {
if (time < 0) time = 0;
if (time === 0) {
return CHARS[0];
}
return time.toString(16).padStart(11, CHARS[0]);
}
function getRandom() {
let str = '';
for (let i = 0; i < 12; i++) {
str += CHARS[Math.floor(Math.random() * CHARS.length)];
}
return str;
}
export function genMeidg(date: Date): string {
return 'g' + getTime(date.getTime()) + getRandom();
}

View file

@ -1,26 +0,0 @@
const CHARS = '0123456789abcdef';
function getTime(time: number) {
if (time < 0) time = 0;
if (time === 0) {
return CHARS[0];
}
time = Math.floor(time / 1000);
return time.toString(16).padStart(8, CHARS[0]);
}
function getRandom() {
let str = '';
for (let i = 0; i < 16; i++) {
str += CHARS[Math.floor(Math.random() * CHARS.length)];
}
return str;
}
export function genObjectId(date: Date): string {
return getTime(date.getTime()) + getRandom();
}

View file

@ -1,59 +0,0 @@
import { Entity, Index, Column, PrimaryColumn } from 'typeorm';
import { id } from '../id.js';
@Entity()
export class Ad {
@PrimaryColumn(id())
public id: string;
@Index()
@Column('timestamp with time zone', {
comment: 'The created date of the Ad.',
})
public createdAt: Date;
@Index()
@Column('timestamp with time zone', {
comment: 'The expired date of the Ad.',
})
public expiresAt: Date;
@Column('varchar', {
length: 32, nullable: false,
})
public place: string;
// 今は使われていないが将来的に活用される可能性はある
@Column('varchar', {
length: 32, nullable: false,
})
public priority: string;
@Column('integer', {
default: 1, nullable: false,
})
public ratio: number;
@Column('varchar', {
length: 1024, nullable: false,
})
public url: string;
@Column('varchar', {
length: 1024, nullable: false,
})
public imageUrl: string;
@Column('varchar', {
length: 8192, nullable: false,
})
public memo: string;
constructor(data: Partial<Ad>) {
if (data == null) return;
for (const [k, v] of Object.entries(data)) {
(this as any)[k] = v;
}
}
}

View file

@ -316,20 +316,6 @@ export class Meta {
}) })
public ToSUrl: string | null; public ToSUrl: string | null;
@Column('varchar', {
length: 512,
default: 'https://github.com/misskey-dev/misskey',
nullable: false,
})
public repositoryUrl: string;
@Column('varchar', {
length: 512,
default: 'https://github.com/misskey-dev/misskey/issues/new',
nullable: true,
})
public feedbackUrl: string | null;
@Column('varchar', { @Column('varchar', {
length: 8192, length: 8192,
nullable: true, nullable: true,

View file

@ -59,7 +59,6 @@ import { MutedNote } from './entities/muted-note.js';
import { ChannelFollowing } from './entities/channel-following.js'; import { ChannelFollowing } from './entities/channel-following.js';
import { ChannelNotePining } from './entities/channel-note-pining.js'; import { ChannelNotePining } from './entities/channel-note-pining.js';
import { RegistryItem } from './entities/registry-item.js'; import { RegistryItem } from './entities/registry-item.js';
import { Ad } from './entities/ad.js';
import { PasswordResetRequest } from './entities/password-reset-request.js'; import { PasswordResetRequest } from './entities/password-reset-request.js';
import { UserPending } from './entities/user-pending.js'; import { UserPending } from './entities/user-pending.js';
import { InstanceRepository } from './repositories/instance.js'; import { InstanceRepository } from './repositories/instance.js';
@ -126,5 +125,4 @@ export const ChannelFollowings = db.getRepository(ChannelFollowing);
export const ChannelNotePinings = db.getRepository(ChannelNotePining); export const ChannelNotePinings = db.getRepository(ChannelNotePining);
export const RegistryItems = db.getRepository(RegistryItem); export const RegistryItems = db.getRepository(RegistryItem);
export const Webhooks = db.getRepository(Webhook); export const Webhooks = db.getRepository(Webhook);
export const Ads = db.getRepository(Ad);
export const PasswordResetRequests = db.getRepository(PasswordResetRequest); export const PasswordResetRequests = db.getRepository(PasswordResetRequest);

View file

@ -12,13 +12,15 @@ export async function readNotification(
if (notificationIds.length === 0) return; if (notificationIds.length === 0) return;
// Update documents // Update documents
await Notifications.update({ const result = await Notifications.update({
id: In(notificationIds), id: In(notificationIds),
isRead: false, isRead: false,
}, { }, {
isRead: true, isRead: true,
}); });
if (result.affected === 0) return;
if (!await Users.getHasUnreadNotification(userId)) return postReadAllNotifications(userId); if (!await Users.getHasUnreadNotification(userId)) return postReadAllNotifications(userId);
else return postReadNotifications(userId, notificationIds); else return postReadNotifications(userId, notificationIds);
} }
@ -27,7 +29,7 @@ export async function readNotificationByQuery(
userId: User['id'], userId: User['id'],
query: Record<string, any> query: Record<string, any>
) { ) {
const notificationIds = await Notifications.find({ const notificationIds = await Notifications.findBy({
...query, ...query,
notifieeId: userId, notifieeId: userId,
isRead: false, isRead: false,

View file

@ -4,10 +4,6 @@ import * as ep___admin_meta from './endpoints/admin/meta.js';
import * as ep___admin_abuseUserReports from './endpoints/admin/abuse-user-reports.js'; import * as ep___admin_abuseUserReports from './endpoints/admin/abuse-user-reports.js';
import * as ep___admin_accounts_create from './endpoints/admin/accounts/create.js'; import * as ep___admin_accounts_create from './endpoints/admin/accounts/create.js';
import * as ep___admin_accounts_delete from './endpoints/admin/accounts/delete.js'; import * as ep___admin_accounts_delete from './endpoints/admin/accounts/delete.js';
import * as ep___admin_ad_create from './endpoints/admin/ad/create.js';
import * as ep___admin_ad_delete from './endpoints/admin/ad/delete.js';
import * as ep___admin_ad_list from './endpoints/admin/ad/list.js';
import * as ep___admin_ad_update from './endpoints/admin/ad/update.js';
import * as ep___admin_announcements_create from './endpoints/admin/announcements/create.js'; import * as ep___admin_announcements_create from './endpoints/admin/announcements/create.js';
import * as ep___admin_announcements_delete from './endpoints/admin/announcements/delete.js'; import * as ep___admin_announcements_delete from './endpoints/admin/announcements/delete.js';
import * as ep___admin_announcements_list from './endpoints/admin/announcements/list.js'; import * as ep___admin_announcements_list from './endpoints/admin/announcements/list.js';
@ -311,16 +307,13 @@ import * as ep___users_searchByUsernameAndHost from './endpoints/users/search-by
import * as ep___users_search from './endpoints/users/search.js'; import * as ep___users_search from './endpoints/users/search.js';
import * as ep___users_show from './endpoints/users/show.js'; import * as ep___users_show from './endpoints/users/show.js';
import * as ep___users_stats from './endpoints/users/stats.js'; import * as ep___users_stats from './endpoints/users/stats.js';
import * as ep___fetchRss from './endpoints/fetch-rss.js';
const eps = [ const eps = [
['admin/meta', ep___admin_meta], ['admin/meta', ep___admin_meta],
['admin/abuse-user-reports', ep___admin_abuseUserReports], ['admin/abuse-user-reports', ep___admin_abuseUserReports],
['admin/accounts/create', ep___admin_accounts_create], ['admin/accounts/create', ep___admin_accounts_create],
['admin/accounts/delete', ep___admin_accounts_delete], ['admin/accounts/delete', ep___admin_accounts_delete],
['admin/ad/create', ep___admin_ad_create],
['admin/ad/delete', ep___admin_ad_delete],
['admin/ad/list', ep___admin_ad_list],
['admin/ad/update', ep___admin_ad_update],
['admin/announcements/create', ep___admin_announcements_create], ['admin/announcements/create', ep___admin_announcements_create],
['admin/announcements/delete', ep___admin_announcements_delete], ['admin/announcements/delete', ep___admin_announcements_delete],
['admin/announcements/list', ep___admin_announcements_list], ['admin/announcements/list', ep___admin_announcements_list],
@ -624,6 +617,7 @@ const eps = [
['users/search', ep___users_search], ['users/search', ep___users_search],
['users/show', ep___users_show], ['users/show', ep___users_show],
['users/stats', ep___users_stats], ['users/stats', ep___users_stats],
['fetch-rss', ep___fetchRss],
]; ];
export interface IEndpointMeta { export interface IEndpointMeta {

View file

@ -1,39 +0,0 @@
import define from '../../../define.js';
import { Ads } from '@/models/index.js';
import { genId } from '@/misc/gen-id.js';
export const meta = {
tags: ['admin'],
requireCredential: true,
requireModerator: true,
} as const;
export const paramDef = {
type: 'object',
properties: {
url: { type: 'string', minLength: 1 },
memo: { type: 'string' },
place: { type: 'string' },
priority: { type: 'string' },
ratio: { type: 'integer' },
expiresAt: { type: 'integer' },
imageUrl: { type: 'string', minLength: 1 },
},
required: ['url', 'memo', 'place', 'priority', 'ratio', 'expiresAt', 'imageUrl'],
} as const;
// eslint-disable-next-line import/no-default-export
export default define(meta, paramDef, async (ps) => {
await Ads.insert({
id: genId(),
createdAt: new Date(),
expiresAt: new Date(ps.expiresAt),
url: ps.url,
imageUrl: ps.imageUrl,
priority: ps.priority,
ratio: ps.ratio,
place: ps.place,
memo: ps.memo,
});
});

View file

@ -1,35 +0,0 @@
import define from '../../../define.js';
import { Ads } from '@/models/index.js';
import { ApiError } from '../../../error.js';
export const meta = {
tags: ['admin'],
requireCredential: true,
requireModerator: true,
errors: {
noSuchAd: {
message: 'No such ad.',
code: 'NO_SUCH_AD',
id: 'ccac9863-3a03-416e-b899-8a64041118b1',
},
},
} as const;
export const paramDef = {
type: 'object',
properties: {
id: { type: 'string', format: 'misskey:id' },
},
required: ['id'],
} as const;
// eslint-disable-next-line import/no-default-export
export default define(meta, paramDef, async (ps, me) => {
const ad = await Ads.findOneBy({ id: ps.id });
if (ad == null) throw new ApiError(meta.errors.noSuchAd);
await Ads.delete(ad.id);
});

View file

@ -1,30 +0,0 @@
import define from '../../../define.js';
import { Ads } from '@/models/index.js';
import { makePaginationQuery } from '../../../common/make-pagination-query.js';
export const meta = {
tags: ['admin'],
requireCredential: true,
requireModerator: true,
} as const;
export const paramDef = {
type: 'object',
properties: {
limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
sinceId: { type: 'string', format: 'misskey:id' },
untilId: { type: 'string', format: 'misskey:id' },
},
required: [],
} as const;
// eslint-disable-next-line import/no-default-export
export default define(meta, paramDef, async (ps) => {
const query = makePaginationQuery(Ads.createQueryBuilder('ad'), ps.sinceId, ps.untilId)
.andWhere('ad.expiresAt > :now', { now: new Date() });
const ads = await query.take(ps.limit).getMany();
return ads;
});

View file

@ -1,50 +0,0 @@
import define from '../../../define.js';
import { Ads } from '@/models/index.js';
import { ApiError } from '../../../error.js';
export const meta = {
tags: ['admin'],
requireCredential: true,
requireModerator: true,
errors: {
noSuchAd: {
message: 'No such ad.',
code: 'NO_SUCH_AD',
id: 'b7aa1727-1354-47bc-a182-3a9c3973d300',
},
},
} as const;
export const paramDef = {
type: 'object',
properties: {
id: { type: 'string', format: 'misskey:id' },
memo: { type: 'string' },
url: { type: 'string', minLength: 1 },
imageUrl: { type: 'string', minLength: 1 },
place: { type: 'string' },
priority: { type: 'string' },
ratio: { type: 'integer' },
expiresAt: { type: 'integer' },
},
required: ['id', 'memo', 'url', 'imageUrl', 'place', 'priority', 'ratio', 'expiresAt'],
} as const;
// eslint-disable-next-line import/no-default-export
export default define(meta, paramDef, async (ps, me) => {
const ad = await Ads.findOneBy({ id: ps.id });
if (ad == null) throw new ApiError(meta.errors.noSuchAd);
await Ads.update(ad.id, {
url: ps.url,
place: ps.place,
priority: ps.priority,
ratio: ps.ratio,
memo: ps.memo,
imageUrl: ps.imageUrl,
expiresAt: new Date(ps.expiresAt),
});
});

View file

@ -97,30 +97,6 @@ export const meta = {
}, },
}, },
}, },
ads: {
type: 'array',
optional: false, nullable: false,
items: {
type: 'object',
optional: false, nullable: false,
properties: {
place: {
type: 'string',
optional: false, nullable: false,
},
url: {
type: 'string',
optional: false, nullable: false,
format: 'url',
},
imageUrl: {
type: 'string',
optional: false, nullable: false,
format: 'url',
},
},
},
},
enableEmail: { enableEmail: {
type: 'boolean', type: 'boolean',
optional: false, nullable: false, optional: false, nullable: false,
@ -318,8 +294,6 @@ export default define(meta, paramDef, async (ps, me) => {
description: instance.description, description: instance.description,
langs: instance.langs, langs: instance.langs,
tosUrl: instance.ToSUrl, tosUrl: instance.ToSUrl,
repositoryUrl: instance.repositoryUrl,
feedbackUrl: instance.feedbackUrl,
disableRegistration: instance.disableRegistration, disableRegistration: instance.disableRegistration,
disableLocalTimeline: instance.disableLocalTimeline, disableLocalTimeline: instance.disableLocalTimeline,
disableGlobalTimeline: instance.disableGlobalTimeline, disableGlobalTimeline: instance.disableGlobalTimeline,

View file

@ -78,8 +78,6 @@ export const paramDef = {
swPublicKey: { type: 'string', nullable: true }, swPublicKey: { type: 'string', nullable: true },
swPrivateKey: { type: 'string', nullable: true }, swPrivateKey: { type: 'string', nullable: true },
tosUrl: { type: 'string', nullable: true }, tosUrl: { type: 'string', nullable: true },
repositoryUrl: { type: 'string' },
feedbackUrl: { type: 'string' },
useObjectStorage: { type: 'boolean' }, useObjectStorage: { type: 'boolean' },
objectStorageBaseUrl: { type: 'string', nullable: true }, objectStorageBaseUrl: { type: 'string', nullable: true },
objectStorageBucket: { type: 'string', nullable: true }, objectStorageBucket: { type: 'string', nullable: true },
@ -313,14 +311,6 @@ export default define(meta, paramDef, async (ps, me) => {
set.ToSUrl = ps.tosUrl; set.ToSUrl = ps.tosUrl;
} }
if (ps.repositoryUrl !== undefined) {
set.repositoryUrl = ps.repositoryUrl;
}
if (ps.feedbackUrl !== undefined) {
set.feedbackUrl = ps.feedbackUrl;
}
if (ps.useObjectStorage !== undefined) { if (ps.useObjectStorage !== undefined) {
set.useObjectStorage = ps.useObjectStorage; set.useObjectStorage = ps.useObjectStorage;
} }

View file

@ -1,6 +1,6 @@
import define from '../../define.js'; import define from '../../define.js';
import Resolver from '@/remote/activitypub/resolver.js'; import Resolver from '@/remote/activitypub/resolver.js';
import ms from 'ms'; import { HOUR } from '@/const.js';
export const meta = { export const meta = {
tags: ['federation'], tags: ['federation'],
@ -8,7 +8,7 @@ export const meta = {
requireCredential: true, requireCredential: true,
limit: { limit: {
duration: ms('1hour'), duration: HOUR,
max: 30, max: 30,
}, },

View file

@ -11,8 +11,8 @@ import { Note } from '@/models/entities/note.js';
import { CacheableLocalUser, User } from '@/models/entities/user.js'; import { CacheableLocalUser, User } from '@/models/entities/user.js';
import { fetchMeta } from '@/misc/fetch-meta.js'; import { fetchMeta } from '@/misc/fetch-meta.js';
import { isActor, isPost, getApId } from '@/remote/activitypub/type.js'; import { isActor, isPost, getApId } from '@/remote/activitypub/type.js';
import ms from 'ms';
import { SchemaType } from '@/misc/schema.js'; import { SchemaType } from '@/misc/schema.js';
import { HOUR } from '@/const.js';
export const meta = { export const meta = {
tags: ['federation'], tags: ['federation'],
@ -20,7 +20,7 @@ export const meta = {
requireCredential: true, requireCredential: true,
limit: { limit: {
duration: ms('1hour'), duration: HOUR,
max: 30, max: 30,
}, },

View file

@ -1,15 +1,15 @@
import ms from 'ms';
import create from '@/services/blocking/create.js'; import create from '@/services/blocking/create.js';
import define from '../../define.js'; import define from '../../define.js';
import { ApiError } from '../../error.js'; import { ApiError } from '../../error.js';
import { getUser } from '../../common/getters.js'; import { getUser } from '../../common/getters.js';
import { Blockings, NoteWatchings, Users } from '@/models/index.js'; import { Blockings, NoteWatchings, Users } from '@/models/index.js';
import { HOUR } from '@/const.js';
export const meta = { export const meta = {
tags: ['account'], tags: ['account'],
limit: { limit: {
duration: ms('1hour'), duration: HOUR,
max: 100, max: 100,
}, },

View file

@ -1,15 +1,15 @@
import ms from 'ms';
import deleteBlocking from '@/services/blocking/delete.js'; import deleteBlocking from '@/services/blocking/delete.js';
import define from '../../define.js'; import define from '../../define.js';
import { ApiError } from '../../error.js'; import { ApiError } from '../../error.js';
import { getUser } from '../../common/getters.js'; import { getUser } from '../../common/getters.js';
import { Blockings, Users } from '@/models/index.js'; import { Blockings, Users } from '@/models/index.js';
import { HOUR } from '@/const.js';
export const meta = { export const meta = {
tags: ['account'], tags: ['account'],
limit: { limit: {
duration: ms('1hour'), duration: HOUR,
max: 100, max: 100,
}, },

View file

@ -1,10 +1,10 @@
import ms from 'ms';
import { addFile } from '@/services/drive/add-file.js'; import { addFile } from '@/services/drive/add-file.js';
import define from '../../../define.js'; import define from '../../../define.js';
import { apiLogger } from '../../../logger.js'; import { apiLogger } from '../../../logger.js';
import { ApiError } from '../../../error.js'; import { ApiError } from '../../../error.js';
import { DriveFiles } from '@/models/index.js'; import { DriveFiles } from '@/models/index.js';
import { DB_MAX_IMAGE_COMMENT_LENGTH } from '@/misc/hard-limits.js'; import { DB_MAX_IMAGE_COMMENT_LENGTH } from '@/misc/hard-limits.js';
import { HOUR } from '@/const.js';
export const meta = { export const meta = {
tags: ['drive'], tags: ['drive'],
@ -12,7 +12,7 @@ export const meta = {
requireCredential: true, requireCredential: true,
limit: { limit: {
duration: ms('1hour'), duration: HOUR,
max: 120, max: 120,
}, },

View file

@ -1,14 +1,14 @@
import ms from 'ms';
import { uploadFromUrl } from '@/services/drive/upload-from-url.js'; import { uploadFromUrl } from '@/services/drive/upload-from-url.js';
import define from '../../../define.js'; import define from '../../../define.js';
import { DriveFiles } from '@/models/index.js'; import { DriveFiles } from '@/models/index.js';
import { publishMainStream } from '@/services/stream.js'; import { publishMainStream } from '@/services/stream.js';
import { HOUR } from '@/const.js';
export const meta = { export const meta = {
tags: ['drive'], tags: ['drive'],
limit: { limit: {
duration: ms('1hour'), duration: HOUR,
max: 60, max: 60,
}, },

View file

@ -1,12 +1,12 @@
import ms from 'ms';
import { createExportCustomEmojisJob } from '@/queue/index.js'; import { createExportCustomEmojisJob } from '@/queue/index.js';
import define from '../define.js'; import define from '../define.js';
import { HOUR } from '@/const.js';
export const meta = { export const meta = {
secure: true, secure: true,
requireCredential: true, requireCredential: true,
limit: { limit: {
duration: ms('1hour'), duration: HOUR,
max: 1, max: 1,
}, },
} as const; } as const;

View file

@ -0,0 +1,39 @@
import Parser from 'rss-parser';
import { getResponse } from '@/misc/fetch.js';
import config from '@/config/index.js';
import define from '../define.js';
const rssParser = new Parser();
export const meta = {
tags: ['meta'],
requireCredential: false,
allowGet: true,
cacheSec: 60 * 3,
} as const;
export const paramDef = {
type: 'object',
properties: {
url: { type: 'string' },
},
required: ['url'],
} as const;
// eslint-disable-next-line import/no-default-export
export default define(meta, paramDef, async (ps) => {
const res = await getResponse({
url: ps.url,
method: 'GET',
headers: Object.assign({
'User-Agent': config.userAgent,
Accept: 'application/rss+xml, */*',
}),
timeout: 5000,
});
const text = await res.text();
return rssParser.parseString(text);
});

View file

@ -1,16 +1,16 @@
import ms from 'ms';
import create from '@/services/following/create.js'; import create from '@/services/following/create.js';
import define from '../../define.js'; import define from '../../define.js';
import { ApiError } from '../../error.js'; import { ApiError } from '../../error.js';
import { getUser } from '../../common/getters.js'; import { getUser } from '../../common/getters.js';
import { Followings, Users } from '@/models/index.js'; import { Followings, Users } from '@/models/index.js';
import { IdentifiableError } from '@/misc/identifiable-error.js'; import { IdentifiableError } from '@/misc/identifiable-error.js';
import { HOUR } from '@/const.js';
export const meta = { export const meta = {
tags: ['following', 'users'], tags: ['following', 'users'],
limit: { limit: {
duration: ms('1hour'), duration: HOUR,
max: 100, max: 100,
}, },

View file

@ -1,15 +1,15 @@
import ms from 'ms';
import deleteFollowing from '@/services/following/delete.js'; import deleteFollowing from '@/services/following/delete.js';
import define from '../../define.js'; import define from '../../define.js';
import { ApiError } from '../../error.js'; import { ApiError } from '../../error.js';
import { getUser } from '../../common/getters.js'; import { getUser } from '../../common/getters.js';
import { Followings, Users } from '@/models/index.js'; import { Followings, Users } from '@/models/index.js';
import { HOUR } from '@/const.js';
export const meta = { export const meta = {
tags: ['following', 'users'], tags: ['following', 'users'],
limit: { limit: {
duration: ms('1hour'), duration: HOUR,
max: 100, max: 100,
}, },

View file

@ -1,15 +1,15 @@
import ms from 'ms';
import deleteFollowing from '@/services/following/delete.js'; import deleteFollowing from '@/services/following/delete.js';
import define from '../../define.js'; import define from '../../define.js';
import { ApiError } from '../../error.js'; import { ApiError } from '../../error.js';
import { getUser } from '../../common/getters.js'; import { getUser } from '../../common/getters.js';
import { Followings, Users } from '@/models/index.js'; import { Followings, Users } from '@/models/index.js';
import { HOUR } from '@/const.js';
export const meta = { export const meta = {
tags: ['following', 'users'], tags: ['following', 'users'],
limit: { limit: {
duration: ms('1hour'), duration: HOUR,
max: 100, max: 100,
}, },

View file

@ -1,9 +1,9 @@
import ms from 'ms';
import define from '../../../define.js'; import define from '../../../define.js';
import { DriveFiles, GalleryPosts } from '@/models/index.js'; import { DriveFiles, GalleryPosts } from '@/models/index.js';
import { genId } from '../../../../../misc/gen-id.js'; import { genId } from '../../../../../misc/gen-id.js';
import { GalleryPost } from '@/models/entities/gallery-post.js'; import { GalleryPost } from '@/models/entities/gallery-post.js';
import { DriveFile } from '@/models/entities/drive-file.js'; import { DriveFile } from '@/models/entities/drive-file.js';
import { HOUR } from '@/const.js';
export const meta = { export const meta = {
tags: ['gallery'], tags: ['gallery'],
@ -13,7 +13,7 @@ export const meta = {
kind: 'write:gallery', kind: 'write:gallery',
limit: { limit: {
duration: ms('1hour'), duration: HOUR,
max: 300, max: 300,
}, },

View file

@ -1,7 +1,7 @@
import ms from 'ms';
import define from '../../../define.js'; import define from '../../../define.js';
import { DriveFiles, GalleryPosts } from '@/models/index.js'; import { DriveFiles, GalleryPosts } from '@/models/index.js';
import { DriveFile } from '@/models/entities/drive-file.js'; import { DriveFile } from '@/models/entities/drive-file.js';
import { HOUR } from '@/const.js';
export const meta = { export const meta = {
tags: ['gallery'], tags: ['gallery'],
@ -11,7 +11,7 @@ export const meta = {
kind: 'write:gallery', kind: 'write:gallery',
limit: { limit: {
duration: ms('1hour'), duration: HOUR,
max: 300, max: 300,
}, },

View file

@ -1,12 +1,12 @@
import define from '../../define.js'; import define from '../../define.js';
import { createExportBlockingJob } from '@/queue/index.js'; import { createExportBlockingJob } from '@/queue/index.js';
import ms from 'ms'; import { HOUR } from '@/const.js';
export const meta = { export const meta = {
secure: true, secure: true,
requireCredential: true, requireCredential: true,
limit: { limit: {
duration: ms('1hour'), duration: HOUR,
max: 1, max: 1,
}, },
} as const; } as const;

View file

@ -1,12 +1,12 @@
import define from '../../define.js'; import define from '../../define.js';
import { createExportFollowingJob } from '@/queue/index.js'; import { createExportFollowingJob } from '@/queue/index.js';
import ms from 'ms'; import { HOUR } from '@/const.js';
export const meta = { export const meta = {
secure: true, secure: true,
requireCredential: true, requireCredential: true,
limit: { limit: {
duration: ms('1hour'), duration: HOUR,
max: 1, max: 1,
}, },
} as const; } as const;

View file

@ -1,12 +1,12 @@
import define from '../../define.js'; import define from '../../define.js';
import { createExportMuteJob } from '@/queue/index.js'; import { createExportMuteJob } from '@/queue/index.js';
import ms from 'ms'; import { HOUR } from '@/const.js';
export const meta = { export const meta = {
secure: true, secure: true,
requireCredential: true, requireCredential: true,
limit: { limit: {
duration: ms('1hour'), duration: HOUR,
max: 1, max: 1,
}, },
} as const; } as const;

View file

@ -1,12 +1,12 @@
import define from '../../define.js'; import define from '../../define.js';
import { createExportNotesJob } from '@/queue/index.js'; import { createExportNotesJob } from '@/queue/index.js';
import ms from 'ms'; import { DAY } from '@/const.js';
export const meta = { export const meta = {
secure: true, secure: true,
requireCredential: true, requireCredential: true,
limit: { limit: {
duration: ms('1day'), duration: DAY,
max: 1, max: 1,
}, },
} as const; } as const;

View file

@ -1,12 +1,12 @@
import define from '../../define.js'; import define from '../../define.js';
import { createExportUserListsJob } from '@/queue/index.js'; import { createExportUserListsJob } from '@/queue/index.js';
import ms from 'ms'; import { MINUTE } from '@/const.js';
export const meta = { export const meta = {
secure: true, secure: true,
requireCredential: true, requireCredential: true,
limit: { limit: {
duration: ms('1min'), duration: MINUTE,
max: 1, max: 1,
}, },
} as const; } as const;

View file

@ -1,15 +1,15 @@
import define from '../../define.js'; import define from '../../define.js';
import { createImportBlockingJob } from '@/queue/index.js'; import { createImportBlockingJob } from '@/queue/index.js';
import ms from 'ms';
import { ApiError } from '../../error.js'; import { ApiError } from '../../error.js';
import { DriveFiles } from '@/models/index.js'; import { DriveFiles } from '@/models/index.js';
import { HOUR } from '@/const.js';
export const meta = { export const meta = {
secure: true, secure: true,
requireCredential: true, requireCredential: true,
limit: { limit: {
duration: ms('1hour'), duration: HOUR,
max: 1, max: 1,
}, },

View file

@ -1,14 +1,14 @@
import define from '../../define.js'; import define from '../../define.js';
import { createImportFollowingJob } from '@/queue/index.js'; import { createImportFollowingJob } from '@/queue/index.js';
import ms from 'ms';
import { ApiError } from '../../error.js'; import { ApiError } from '../../error.js';
import { DriveFiles } from '@/models/index.js'; import { DriveFiles } from '@/models/index.js';
import { HOUR } from '@/const.js';
export const meta = { export const meta = {
secure: true, secure: true,
requireCredential: true, requireCredential: true,
limit: { limit: {
duration: ms('1hour'), duratition: HOUR,
max: 1, max: 1,
}, },

View file

@ -1,15 +1,15 @@
import define from '../../define.js'; import define from '../../define.js';
import { createImportMutingJob } from '@/queue/index.js'; import { createImportMutingJob } from '@/queue/index.js';
import ms from 'ms';
import { ApiError } from '../../error.js'; import { ApiError } from '../../error.js';
import { DriveFiles } from '@/models/index.js'; import { DriveFiles } from '@/models/index.js';
import { HOUR } from '@/const.js';
export const meta = { export const meta = {
secure: true, secure: true,
requireCredential: true, requireCredential: true,
limit: { limit: {
duration: ms('1hour'), duration: HOUR,
max: 1, max: 1,
}, },

View file

@ -1,14 +1,14 @@
import define from '../../define.js'; import define from '../../define.js';
import { createImportUserListsJob } from '@/queue/index.js'; import { createImportUserListsJob } from '@/queue/index.js';
import ms from 'ms';
import { ApiError } from '../../error.js'; import { ApiError } from '../../error.js';
import { DriveFiles } from '@/models/index.js'; import { DriveFiles } from '@/models/index.js';
import { HOUR } from '@/const.js';
export const meta = { export const meta = {
secure: true, secure: true,
requireCredential: true, requireCredential: true,
limit: { limit: {
duration: ms('1hour'), duration: HOUR,
max: 1, max: 1,
}, },

View file

@ -2,12 +2,12 @@ import { publishMainStream } from '@/services/stream.js';
import define from '../../define.js'; import define from '../../define.js';
import rndstr from 'rndstr'; import rndstr from 'rndstr';
import config from '@/config/index.js'; import config from '@/config/index.js';
import ms from 'ms';
import bcrypt from 'bcryptjs'; import bcrypt from 'bcryptjs';
import { Users, UserProfiles } from '@/models/index.js'; import { Users, UserProfiles } from '@/models/index.js';
import { sendEmail } from '@/services/send-email.js'; import { sendEmail } from '@/services/send-email.js';
import { ApiError } from '../../error.js'; import { ApiError } from '../../error.js';
import { validateEmailForAccount } from '@/services/validate-email-for-account.js'; import { validateEmailForAccount } from '@/services/validate-email-for-account.js';
import { HOUR } from '@/const.js';
export const meta = { export const meta = {
requireCredential: true, requireCredential: true,
@ -15,7 +15,7 @@ export const meta = {
secure: true, secure: true,
limit: { limit: {
duration: ms('1hour'), duration: HOUR,
max: 3, max: 3,
}, },

View file

@ -1,8 +1,8 @@
import define from '../../../define.js'; import define from '../../../define.js';
import ms from 'ms';
import { ApiError } from '../../../error.js'; import { ApiError } from '../../../error.js';
import { MessagingMessages } from '@/models/index.js'; import { MessagingMessages } from '@/models/index.js';
import { deleteMessage } from '@/services/messages/delete.js'; import { deleteMessage } from '@/services/messages/delete.js';
import { SECOND, HOUR } from '@/const.js';
export const meta = { export const meta = {
tags: ['messaging'], tags: ['messaging'],
@ -12,9 +12,9 @@ export const meta = {
kind: 'write:messaging', kind: 'write:messaging',
limit: { limit: {
duration: ms('1hour'), duration: HOUR,
max: 300, max: 300,
minInterval: ms('1sec'), minInterval: SECOND,
}, },
errors: { errors: {

View file

@ -1,7 +1,7 @@
import { IsNull, MoreThan } from 'typeorm'; import { IsNull, MoreThan } from 'typeorm';
import config from '@/config/index.js'; import config from '@/config/index.js';
import { fetchMeta } from '@/misc/fetch-meta.js'; import { fetchMeta } from '@/misc/fetch-meta.js';
import { Ads, Emojis, Users } from '@/models/index.js'; import { Emojis, Users } from '@/models/index.js';
import { MAX_NOTE_TEXT_LENGTH } from '@/const.js'; import { MAX_NOTE_TEXT_LENGTH } from '@/const.js';
import define from '../define.js'; import define from '../define.js';
@ -53,16 +53,6 @@ export const meta = {
type: 'string', type: 'string',
optional: false, nullable: true, optional: false, nullable: true,
}, },
repositoryUrl: {
type: 'string',
optional: false, nullable: false,
default: 'https://github.com/misskey-dev/misskey',
},
feedbackUrl: {
type: 'string',
optional: false, nullable: false,
default: 'https://github.com/misskey-dev/misskey/issues/new',
},
defaultDarkTheme: { defaultDarkTheme: {
type: 'string', type: 'string',
optional: false, nullable: true, optional: false, nullable: true,
@ -168,30 +158,6 @@ export const meta = {
}, },
}, },
}, },
ads: {
type: 'array',
optional: false, nullable: false,
items: {
type: 'object',
optional: false, nullable: false,
properties: {
place: {
type: 'string',
optional: false, nullable: false,
},
url: {
type: 'string',
optional: false, nullable: false,
format: 'url',
},
imageUrl: {
type: 'string',
optional: false, nullable: false,
format: 'url',
},
},
},
},
requireSetup: { requireSetup: {
type: 'boolean', type: 'boolean',
optional: false, nullable: false, optional: false, nullable: false,
@ -310,12 +276,6 @@ export default define(meta, paramDef, async (ps, me) => {
}, },
}); });
const ads = await Ads.find({
where: {
expiresAt: MoreThan(new Date()),
},
});
const response: any = { const response: any = {
maintainerName: instance.maintainerName, maintainerName: instance.maintainerName,
maintainerEmail: instance.maintainerEmail, maintainerEmail: instance.maintainerEmail,
@ -327,8 +287,6 @@ export default define(meta, paramDef, async (ps, me) => {
description: instance.description, description: instance.description,
langs: instance.langs, langs: instance.langs,
tosUrl: instance.ToSUrl, tosUrl: instance.ToSUrl,
repositoryUrl: instance.repositoryUrl,
feedbackUrl: instance.feedbackUrl,
disableRegistration: instance.disableRegistration, disableRegistration: instance.disableRegistration,
disableLocalTimeline: instance.disableLocalTimeline, disableLocalTimeline: instance.disableLocalTimeline,
disableGlobalTimeline: instance.disableGlobalTimeline, disableGlobalTimeline: instance.disableGlobalTimeline,
@ -349,13 +307,6 @@ export default define(meta, paramDef, async (ps, me) => {
emojis: await Emojis.packMany(emojis), emojis: await Emojis.packMany(emojis),
defaultLightTheme: instance.defaultLightTheme, defaultLightTheme: instance.defaultLightTheme,
defaultDarkTheme: instance.defaultDarkTheme, defaultDarkTheme: instance.defaultDarkTheme,
ads: ads.map(ad => ({
id: ad.id,
url: ad.url,
place: ad.place,
ratio: ad.ratio,
imageUrl: ad.imageUrl,
})),
enableEmail: instance.enableEmail, enableEmail: instance.enableEmail,
enableTwitterIntegration: instance.enableTwitterIntegration, enableTwitterIntegration: instance.enableTwitterIntegration,

View file

@ -1,4 +1,3 @@
import ms from 'ms';
import { In } from 'typeorm'; import { In } from 'typeorm';
import create from '@/services/note/create.js'; import create from '@/services/note/create.js';
import { User } from '@/models/entities/user.js'; import { User } from '@/models/entities/user.js';
@ -10,6 +9,7 @@ import { MAX_NOTE_TEXT_LENGTH } from '@/const.js';
import { noteVisibilities } from '../../../../types.js'; import { noteVisibilities } from '../../../../types.js';
import { ApiError } from '../../error.js'; import { ApiError } from '../../error.js';
import define from '../../define.js'; import define from '../../define.js';
import { HOUR } from '@/const.js';
export const meta = { export const meta = {
tags: ['notes'], tags: ['notes'],
@ -17,7 +17,7 @@ export const meta = {
requireCredential: true, requireCredential: true,
limit: { limit: {
duration: ms('1hour'), duration: HOUR,
max: 300, max: 300,
}, },

View file

@ -1,9 +1,9 @@
import ms from 'ms';
import deleteNote from '@/services/note/delete.js'; import deleteNote from '@/services/note/delete.js';
import { Users } from '@/models/index.js'; import { Users } from '@/models/index.js';
import define from '../../define.js'; import define from '../../define.js';
import { getNote } from '../../common/getters.js'; import { getNote } from '../../common/getters.js';
import { ApiError } from '../../error.js'; import { ApiError } from '../../error.js';
import { SECOND, HOUR } from '@/const.js';
export const meta = { export const meta = {
tags: ['notes'], tags: ['notes'],
@ -13,9 +13,9 @@ export const meta = {
kind: 'write:notes', kind: 'write:notes',
limit: { limit: {
duration: ms('1hour'), duration: HOUR,
max: 300, max: 300,
minInterval: ms('1sec'), minInterval: SECOND,
}, },
errors: { errors: {

View file

@ -60,12 +60,21 @@ export default define(meta, paramDef, async (ps, user) => {
query.setParameters(mutingQuery.getParameters()); query.setParameters(mutingQuery.getParameters());
//#endregion //#endregion
const polls = await query.take(ps.limit).skip(ps.offset).getMany(); const polls = await query
.orderBy('poll.noteId', 'DESC')
.take(ps.limit)
.skip(ps.offset)
.getMany();
if (polls.length === 0) return []; if (polls.length === 0) return [];
const notes = await Notes.findBy({ const notes = await Notes.find({
where: {
id: In(polls.map(poll => poll.noteId)), id: In(polls.map(poll => poll.noteId)),
},
order: {
createdAt: 'DESC',
},
}); });
return await Notes.packMany(notes, user, { return await Notes.packMany(notes, user, {

View file

@ -8,6 +8,9 @@ export const meta = {
requireCredential: false, requireCredential: false,
allowGet: true,
cacheSec: 60,
res: { res: {
type: 'array', type: 'array',
optional: false, nullable: false, optional: false, nullable: false,

View file

@ -1,8 +1,8 @@
import ms from 'ms';
import deleteReaction from '@/services/note/reaction/delete.js'; import deleteReaction from '@/services/note/reaction/delete.js';
import define from '../../../define.js'; import define from '../../../define.js';
import { getNote } from '../../../common/getters.js'; import { getNote } from '../../../common/getters.js';
import { ApiError } from '../../../error.js'; import { ApiError } from '../../../error.js';
import { SECOND, HOUR } from '@/const.js';
export const meta = { export const meta = {
tags: ['reactions', 'notes'], tags: ['reactions', 'notes'],
@ -12,9 +12,9 @@ export const meta = {
kind: 'write:reactions', kind: 'write:reactions',
limit: { limit: {
duration: ms('1hour'), duration: HOUR,
max: 60, max: 60,
minInterval: ms('3sec'), minInterval: 3 * SECOND,
}, },
errors: { errors: {

View file

@ -1,9 +1,9 @@
import ms from 'ms';
import deleteNote from '@/services/note/delete.js'; import deleteNote from '@/services/note/delete.js';
import { Notes, Users } from '@/models/index.js'; import { Notes, Users } from '@/models/index.js';
import define from '../../define.js'; import define from '../../define.js';
import { getNote } from '../../common/getters.js'; import { getNote } from '../../common/getters.js';
import { ApiError } from '../../error.js'; import { ApiError } from '../../error.js';
import { SECOND, HOUR } from '@/const.js';
export const meta = { export const meta = {
tags: ['notes'], tags: ['notes'],
@ -13,9 +13,9 @@ export const meta = {
kind: 'write:notes', kind: 'write:notes',
limit: { limit: {
duration: ms('1hour'), duration: HOUR,
max: 300, max: 300,
minInterval: ms('1sec'), minInterval: SECOND,
}, },
errors: { errors: {

View file

@ -1,9 +1,9 @@
import ms from 'ms';
import { Pages, DriveFiles } from '@/models/index.js'; import { Pages, DriveFiles } from '@/models/index.js';
import { genId } from '@/misc/gen-id.js'; import { genId } from '@/misc/gen-id.js';
import { Page } from '@/models/entities/page.js'; import { Page } from '@/models/entities/page.js';
import define from '../../define.js'; import define from '../../define.js';
import { ApiError } from '../../error.js'; import { ApiError } from '../../error.js';
import { HOUR } from '@/const.js';
export const meta = { export const meta = {
tags: ['pages'], tags: ['pages'],
@ -13,7 +13,7 @@ export const meta = {
kind: 'write:pages', kind: 'write:pages',
limit: { limit: {
duration: ms('1hour'), duration: HOUR,
max: 300, max: 300,
}, },

View file

@ -1,8 +1,8 @@
import ms from 'ms';
import { Not } from 'typeorm'; import { Not } from 'typeorm';
import { Pages, DriveFiles } from '@/models/index.js'; import { Pages, DriveFiles } from '@/models/index.js';
import define from '../../define.js'; import define from '../../define.js';
import { ApiError } from '../../error.js'; import { ApiError } from '../../error.js';
import { HOUR } from '@/const.js';
export const meta = { export const meta = {
tags: ['pages'], tags: ['pages'],
@ -12,7 +12,7 @@ export const meta = {
kind: 'write:pages', kind: 'write:pages',
limit: { limit: {
duration: ms('1hour'), duration: HOUR,
max: 300, max: 300,
}, },

View file

@ -1,11 +1,11 @@
import rndstr from 'rndstr'; import rndstr from 'rndstr';
import ms from 'ms';
import { IsNull } from 'typeorm'; import { IsNull } from 'typeorm';
import config from '@/config/index.js'; import config from '@/config/index.js';
import { Users, UserProfiles, PasswordResetRequests } from '@/models/index.js'; import { Users, UserProfiles, PasswordResetRequests } from '@/models/index.js';
import { sendEmail } from '@/services/send-email.js'; import { sendEmail } from '@/services/send-email.js';
import { genId } from '@/misc/gen-id.js'; import { genId } from '@/misc/gen-id.js';
import define from '../define.js'; import define from '../define.js';
import { HOUR } from '@/const.js';
export const meta = { export const meta = {
tags: ['reset password'], tags: ['reset password'],
@ -15,7 +15,7 @@ export const meta = {
description: 'Request a users password to be reset.', description: 'Request a users password to be reset.',
limit: { limit: {
duration: ms('1hour'), duration: HOUR,
max: 3, max: 3,
}, },

View file

@ -1,8 +1,8 @@
import ms from 'ms';
import { Users, Followings } from '@/models/index.js'; import { Users, Followings } from '@/models/index.js';
import define from '../../define.js'; import define from '../../define.js';
import { generateMutedUserQueryForUsers } from '../../common/generate-muted-user-query.js'; import { generateMutedUserQueryForUsers } from '../../common/generate-muted-user-query.js';
import { generateBlockedUserQuery, generateBlockQueryForUsers } from '../../common/generate-block-query.js'; import { generateBlockedUserQuery, generateBlockQueryForUsers } from '../../common/generate-block-query.js';
import { DAY } from '@/const.js';
export const meta = { export const meta = {
tags: ['users'], tags: ['users'],
@ -39,7 +39,7 @@ export default define(meta, paramDef, async (ps, me) => {
.where('user.isLocked = FALSE') .where('user.isLocked = FALSE')
.andWhere('user.isExplorable = TRUE') .andWhere('user.isExplorable = TRUE')
.andWhere('user.host IS NULL') .andWhere('user.host IS NULL')
.andWhere('user.updatedAt >= :date', { date: new Date(Date.now() - ms('7days')) }) .andWhere('user.updatedAt >= :date', { date: new Date(Date.now() - (7 * DAY)) })
.andWhere('user.id != :meId', { meId: me.id }) .andWhere('user.id != :meId', { meId: me.id })
.orderBy('user.followersCount', 'DESC'); .orderBy('user.followersCount', 'DESC');

View file

@ -51,7 +51,7 @@ export default class extends Channel {
} }
// 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する // 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する
if (iUserRelated(note, this.muting)) return; if (isUserRelated(note, this.muting)) return;
// 流れてきたNoteがブロックされているユーザーが関わるものだったら無視する // 流れてきたNoteがブロックされているユーザーが関わるものだったら無視する
if (isUserRelated(note, this.blocking)) return; if (isUserRelated(note, this.blocking)) return;

View file

@ -11,10 +11,10 @@ const router = new Router();
const nodeinfo2_1path = '/nodeinfo/2.1'; const nodeinfo2_1path = '/nodeinfo/2.1';
const nodeinfo2_0path = '/nodeinfo/2.0'; const nodeinfo2_0path = '/nodeinfo/2.0';
export const links = [/* (awaiting release) { export const links = [{
rel: 'http://nodeinfo.diaspora.software/ns/schema/2.1', rel: 'http://nodeinfo.diaspora.software/ns/schema/2.1',
href: config.url + nodeinfo2_1path href: config.url + nodeinfo2_1path
}, */{ }, {
rel: 'http://nodeinfo.diaspora.software/ns/schema/2.0', rel: 'http://nodeinfo.diaspora.software/ns/schema/2.0',
href: config.url + nodeinfo2_0path, href: config.url + nodeinfo2_0path,
}]; }];
@ -39,9 +39,9 @@ const nodeinfo2 = async () => {
return { return {
software: { software: {
name: 'misskey', name: 'foundkey',
version: config.version, version: config.version,
repository: meta.repositoryUrl, repository: 'https://akkoma.dev/FoundKeyGang/FoundKey',
}, },
protocols: ['activitypub'], protocols: ['activitypub'],
services: { services: {
@ -64,7 +64,7 @@ const nodeinfo2 = async () => {
langs: meta.langs, langs: meta.langs,
tosUrl: meta.ToSUrl, tosUrl: meta.ToSUrl,
repositoryUrl: meta.repositoryUrl, repositoryUrl: meta.repositoryUrl,
feedbackUrl: meta.feedbackUrl, feedbackUrl: 'ircs://irc.akkoma.dev/foundkey',
disableRegistration: meta.disableRegistration, disableRegistration: meta.disableRegistration,
disableLocalTimeline: meta.disableLocalTimeline, disableLocalTimeline: meta.disableLocalTimeline,
disableGlobalTimeline: meta.disableGlobalTimeline, disableGlobalTimeline: meta.disableGlobalTimeline,

View file

@ -106,15 +106,39 @@
function renderError(code, details) { function renderError(code, details) {
let errorsElement = document.getElementById('errors'); let errorsElement = document.getElementById('errors');
if (!errorsElement) { if (!errorsElement) {
document.getElementsByTagName("head")[0].insertAdjacentHTML(
"beforeend",
`<link rel="stylesheet" href="../error.css" />`);
document.documentElement.innerHTML = ` document.documentElement.innerHTML = `
<h1> An error has occurred. </h1> <svg class="icon-warning" xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-alert-triangle" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<p>If the problem persists, please contact the administrator. You may also try the following options:</p> <path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<ul> <path d="M12 9v2m0 4v.01"></path>
<li>Start <a href="/cli">the simple client</a></li> <path d="M5 19h14a2 2 0 0 0 1.84 -2.75l-7.1 -12.25a2 2 0 0 0 -3.5 0l-7.1 12.25a2 2 0 0 0 1.75 2.75"></path>
<li>Attempt to repair in <a href="/bios">BIOS</a></li> </svg>
<li><a href="/flush">Flush preferences and cache</a></li> <h1>An error has occurred!</h1>
</ul> <button class="button-big" onclick="location.reload(true);">
<hr> <span class="button-label-big">Refresh</span>
</button>
<p class="dont-worry">Don't worry, it's (probably) not your fault.</p>
<p>If the problem persists after refreshing, please contact your instance's administrator.<br>You may also try the following options:</p>
<a href="/flush">
<button class="button-small">
<span class="button-label-small">Flush preferences and cache</span>
</button>
</a>
<br>
<a href="/cli">
<button class="button-small">
<span class="button-label-small">Start the simple client</span>
</button>
</a>
<br>
<a href="/bios">
<button class="button-small">
<span class="button-label-small">Attempt to repair in Repair Tool</span>
</button>
</a>
<br>
<div id="errors"></div> <div id="errors"></div>
`; `;
@ -122,8 +146,7 @@
} }
const detailsElement = document.createElement('details'); const detailsElement = document.createElement('details');
detailsElement.innerHTML = `<summary><code>ERROR CODE: ${code}</code></summary>${JSON.stringify(details)}`; detailsElement.innerHTML = `<br><summary><code>ERROR CODE: ${code}</code></summary>${JSON.stringify(details)}`;
errorsElement.appendChild(detailsElement); errorsElement.appendChild(detailsElement);
} }

View file

@ -0,0 +1,98 @@
* {
font-family: BIZ UDGothic, Roboto, HelveticaNeue, Arial, sans-serif;
}
body,
html {
background-color: #222;
color: #dfddcc;
justify-content: center;
margin: auto;
width: 80%;
padding: 10px;
text-align: center;
}
button {
border-radius: 999px;
padding: 0px 12px 0px 12px;
border: none;
cursor: pointer;
margin-bottom: 12px;
}
.button-big {
background: linear-gradient(90deg, rgb(134, 179, 0), rgb(74, 179, 0));
line-height: 50px;
}
.button-big:hover {
background: rgb(153, 204, 0);
}
.button-small {
background: #444;
line-height: 40px;
}
.button-small:hover {
background: #555;
}
.button-label-big {
color: #222;
font-weight: bold;
font-size: 20px;
padding: 12px;
}
.button-label-small {
color: rgb(153, 204, 0);
font-size: 16px;
padding: 12px;
}
a {
color: rgb(134, 179, 0);
text-decoration: none;
}
p,
li {
font-size: 16px;
}
.dont-worry,
#msg {
font-size: 18px;
}
.icon-warning {
color: #dec340;
height: 4rem;
}
h1 {
font-size: 32px;
}
code {
font-family: Fira, FiraCode, monospace;
}
details {
background: #333;
margin-bottom: 2rem;
padding: 0.5rem 1rem;
border-radius: 5px;
justify-content: center;
margin: auto;
}
summary {
cursor: pointer;
}
summary > * {
display: inline;
}

View file

@ -5,7 +5,6 @@
import { dirname } from 'node:path'; import { dirname } from 'node:path';
import { fileURLToPath } from 'node:url'; import { fileURLToPath } from 'node:url';
import { readFileSync } from 'node:fs'; import { readFileSync } from 'node:fs';
import ms from 'ms';
import Koa from 'koa'; import Koa from 'koa';
import Router from '@koa/router'; import Router from '@koa/router';
import send from 'koa-send'; import send from 'koa-send';
@ -27,6 +26,7 @@ import { genOpenapiSpec } from '../api/openapi/gen-spec.js';
import { urlPreviewHandler } from './url-preview.js'; import { urlPreviewHandler } from './url-preview.js';
import { manifestHandler } from './manifest.js'; import { manifestHandler } from './manifest.js';
import packFeed from './feed.js'; import packFeed from './feed.js';
import { MINUTE, DAY } from '@/const.js';
const _filename = fileURLToPath(import.meta.url); const _filename = fileURLToPath(import.meta.url);
const _dirname = dirname(_filename); const _dirname = dirname(_filename);
@ -100,21 +100,21 @@ const router = new Router();
router.get('/static-assets/(.*)', async ctx => { router.get('/static-assets/(.*)', async ctx => {
await send(ctx as any, ctx.path.replace('/static-assets/', ''), { await send(ctx as any, ctx.path.replace('/static-assets/', ''), {
root: staticAssets, root: staticAssets,
maxage: ms('7 days'), maxage: 7 * DAY,
}); });
}); });
router.get('/client-assets/(.*)', async ctx => { router.get('/client-assets/(.*)', async ctx => {
await send(ctx as any, ctx.path.replace('/client-assets/', ''), { await send(ctx as any, ctx.path.replace('/client-assets/', ''), {
root: clientAssets, root: clientAssets,
maxage: ms('7 days'), maxage: 7 * DAY,
}); });
}); });
router.get('/assets/(.*)', async ctx => { router.get('/assets/(.*)', async ctx => {
await send(ctx as any, ctx.path.replace('/assets/', ''), { await send(ctx as any, ctx.path.replace('/assets/', ''), {
root: assets, root: assets,
maxage: ms('7 days'), maxage: 7 * DAY,
}); });
}); });
@ -137,7 +137,7 @@ router.get('/twemoji/(.*)', async ctx => {
await send(ctx as any, path, { await send(ctx as any, path, {
root: `${_dirname}/../../../node_modules/@discordapp/twemoji/dist/svg/`, root: `${_dirname}/../../../node_modules/@discordapp/twemoji/dist/svg/`,
maxage: ms('30 days'), maxage: 30 * DAY,
}); });
}); });
@ -188,7 +188,7 @@ router.get('/twemoji-badge/(.*)', async ctx => {
router.get(`/sw.js`, async ctx => { router.get(`/sw.js`, async ctx => {
await send(ctx as any, `/sw.js`, { await send(ctx as any, `/sw.js`, {
root: swAssets, root: swAssets,
maxage: ms('10 minutes'), maxage: 10 * MINUTE,
}); });
}); });

View file

@ -5,7 +5,7 @@ html
head head
meta(charset='utf-8') meta(charset='utf-8')
meta(name='application-name' content='Misskey') meta(name='application-name' content='Misskey')
title Misskey BIOS title Misskey Repair Tool
style style
include ../bios.css include ../bios.css
script script
@ -13,7 +13,7 @@ html
body body
header header
h1 Misskey BIOS #{version} h1 Misskey Repair Tool #{version}
main main
div.tabs div.tabs
button#ls edit local storage button#ls edit local storage

View file

@ -1,6 +1,12 @@
doctype html doctype html
html html
head
meta(charset='utf-8')
meta(name='application-name' content='Misskey')
title Flushing Misskey
style
include ../error.css
#msg #msg
script. script.
const msg = document.getElementById('msg'); const msg = document.getElementById('msg');

File diff suppressed because it is too large Load diff

View file

@ -186,7 +186,7 @@ export function connectStream(user: any, channel: string, listener: (message: Re
}); });
} }
export const waitFire = async (user: any, channel: string, trgr: () => any, cond: (msg: Record<string, any>) => boolean) => { export const waitFire = async (user: any, channel: string, trgr: () => any, cond: (msg: Record<string, any>) => boolean, params?: any) => {
return new Promise<boolean>(async (res, rej) => { return new Promise<boolean>(async (res, rej) => {
let timer: NodeJS.Timeout; let timer: NodeJS.Timeout;
@ -198,7 +198,7 @@ export const waitFire = async (user: any, channel: string, trgr: () => any, cond
if (timer) clearTimeout(timer); if (timer) clearTimeout(timer);
res(true); res(true);
} }
}); }, params);
} catch (e) { } catch (e) {
rej(e); rej(e);
} }
@ -208,7 +208,7 @@ export const waitFire = async (user: any, channel: string, trgr: () => any, cond
timer = setTimeout(() => { timer = setTimeout(() => {
ws.close(); ws.close();
res(false); res(false);
}, 5000); }, 3000);
try { try {
await trgr(); await trgr();

File diff suppressed because it is too large Load diff

View file

@ -3,7 +3,7 @@
"scripts": { "scripts": {
"watch": "vite build --watch --mode development", "watch": "vite build --watch --mode development",
"build": "vite build", "build": "vite build",
"lint": "eslint --quiet \"src/**/*.{ts,vue}\"" "lint": "eslint --quiet src --ext .ts,.vue"
}, },
"resolutions": { "resolutions": {
"chokidar": "^3.3.1", "chokidar": "^3.3.1",
@ -15,7 +15,7 @@
"@rollup/plugin-alias": "3.1.9", "@rollup/plugin-alias": "3.1.9",
"@rollup/plugin-json": "4.1.0", "@rollup/plugin-json": "4.1.0",
"@syuilo/aiscript": "0.11.1", "@syuilo/aiscript": "0.11.1",
"@vitejs/plugin-vue": "2.3.3", "@vitejs/plugin-vue": "^3.0.0",
"@vue/compiler-sfc": "3.2.37", "@vue/compiler-sfc": "3.2.37",
"abort-controller": "3.0.0", "abort-controller": "3.0.0",
"autobind-decorator": "2.4.0", "autobind-decorator": "2.4.0",
@ -35,7 +35,7 @@
"escape-regexp": "0.0.1", "escape-regexp": "0.0.1",
"eventemitter3": "4.0.7", "eventemitter3": "4.0.7",
"feed": "4.2.2", "feed": "4.2.2",
"idb-keyval": "6.1.0", "idb-keyval": "6.2.0",
"insert-text-at-cursor": "0.3.0", "insert-text-at-cursor": "0.3.0",
"json5": "2.2.1", "json5": "2.2.1",
"katex": "0.15.6", "katex": "0.15.6",
@ -45,36 +45,34 @@
"mocha": "10.0.0", "mocha": "10.0.0",
"ms": "2.1.3", "ms": "2.1.3",
"nested-property": "4.0.0", "nested-property": "4.0.0",
"photoswipe": "5.2.7", "photoswipe": "5.2.8",
"prismjs": "1.28.0", "prismjs": "1.28.0",
"private-ip": "2.3.3", "private-ip": "2.3.3",
"promise-limit": "2.7.0", "promise-limit": "2.7.0",
"pug": "3.0.2", "pug": "3.0.2",
"punycode": "2.1.1", "punycode": "2.1.1",
"qrcode": "1.5.0", "qrcode": "1.5.0",
"querystring": "0.2.1",
"random-seed": "0.3.0",
"reflect-metadata": "0.1.13", "reflect-metadata": "0.1.13",
"rndstr": "1.0.0", "rndstr": "1.0.0",
"rollup": "2.75.6", "rollup": "2.75.7",
"s-age": "1.1.2", "s-age": "1.1.2",
"sass": "1.52.3", "sass": "1.53.0",
"seedrandom": "3.0.5", "seedrandom": "3.0.5",
"strict-event-emitter-types": "2.0.0", "strict-event-emitter-types": "2.0.0",
"stringz": "2.1.0", "stringz": "2.1.0",
"syuilo-password-strength": "0.0.1", "syuilo-password-strength": "0.0.1",
"textarea-caret": "3.1.0", "textarea-caret": "3.1.0",
"three": "0.141.0", "three": "0.142.0",
"throttle-debounce": "5.0.0", "throttle-debounce": "5.0.0",
"tinycolor2": "1.4.2", "tinycolor2": "1.4.2",
"tsc-alias": "1.6.9", "tsc-alias": "1.6.11",
"tsconfig-paths": "4.0.0", "tsconfig-paths": "4.0.0",
"twemoji-parser": "14.0.0", "twemoji-parser": "14.0.0",
"typescript": "4.7.3", "typescript": "4.7.4",
"uuid": "8.3.2", "uuid": "8.3.2",
"v-debounce": "0.1.2", "v-debounce": "0.1.2",
"vanilla-tilt": "1.7.2", "vanilla-tilt": "1.7.2",
"vite": "2.9.10", "vite": "3.0.0",
"vue": "3.2.37", "vue": "3.2.37",
"vue-prism-editor": "2.0.0-alpha.2", "vue-prism-editor": "2.0.0-alpha.2",
"vuedraggable": "4.0.1", "vuedraggable": "4.0.1",
@ -93,20 +91,19 @@
"@types/oauth": "0.9.1", "@types/oauth": "0.9.1",
"@types/punycode": "2.1.0", "@types/punycode": "2.1.0",
"@types/qrcode": "1.4.2", "@types/qrcode": "1.4.2",
"@types/random-seed": "0.3.3",
"@types/seedrandom": "3.0.2", "@types/seedrandom": "3.0.2",
"@types/throttle-debounce": "5.0.0", "@types/throttle-debounce": "5.0.0",
"@types/tinycolor2": "1.4.3", "@types/tinycolor2": "1.4.3",
"@types/uuid": "8.3.4", "@types/uuid": "8.3.4",
"@types/websocket": "1.0.5", "@types/websocket": "1.0.5",
"@types/ws": "8.5.3", "@types/ws": "8.5.3",
"@typescript-eslint/eslint-plugin": "5.27.1", "@typescript-eslint/eslint-plugin": "^5.30.0",
"@typescript-eslint/parser": "5.27.1", "@typescript-eslint/parser": "^5.30.0",
"cross-env": "7.0.3", "cross-env": "7.0.3",
"cypress": "10.0.3", "cypress": "10.3.0",
"eslint": "8.17.0", "eslint": "^8.20.0",
"eslint-plugin-import": "2.26.0", "eslint-plugin-import": "^2.26.0",
"eslint-plugin-vue": "9.1.0", "eslint-plugin-vue": "^9.1.1",
"start-server-and-test": "1.14.0" "start-server-and-test": "1.14.0"
} }
} }

View file

@ -1,5 +1,5 @@
<template> <template>
<XWindow ref="window" :initial-width="400" :initial-height="500" :can-resize="true" @closed="emit('closed')"> <XWindow ref="uiWindow" :initial-width="400" :initial-height="500" :can-resize="true" @closed="emit('closed')">
<template #header> <template #header>
<i class="fas fa-exclamation-circle" style="margin-right: 0.5em;"></i> <i class="fas fa-exclamation-circle" style="margin-right: 0.5em;"></i>
<I18n :src="i18n.ts.reportAbuseOf" tag="span"> <I18n :src="i18n.ts.reportAbuseOf" tag="span">
@ -40,19 +40,19 @@ const emit = defineEmits<{
(ev: 'closed'): void; (ev: 'closed'): void;
}>(); }>();
const window = ref<InstanceType<typeof XWindow>>(); const uiWindow = ref<InstanceType<typeof XWindow>>();
const comment = ref(props.initialComment || ''); const comment = ref(props.initialComment || '');
function send() { function send() {
os.apiWithDialog('users/report-abuse', { os.apiWithDialog('users/report-abuse', {
userId: props.user.id, userId: props.user.id,
comment: comment.value, comment: comment.value,
}, undefined).then(res => { }).then(res => {
os.alert({ os.alert({
type: 'success', type: 'success',
text: i18n.ts.abuseReported text: i18n.ts.abuseReported
}); });
window.value?.close(); uiWindow.value?.close();
emit('closed'); emit('closed');
}); });
} }

View file

@ -20,6 +20,7 @@
<span v-if="emoji.isCustomEmoji" class="emoji"><img :src="defaultStore.state.disableShowingAnimatedImages ? getStaticImageUrl(emoji.url) : emoji.url" :alt="emoji.emoji"/></span> <span v-if="emoji.isCustomEmoji" class="emoji"><img :src="defaultStore.state.disableShowingAnimatedImages ? getStaticImageUrl(emoji.url) : emoji.url" :alt="emoji.emoji"/></span>
<span v-else-if="!defaultStore.state.useOsNativeEmojis" class="emoji"><img :src="emoji.url" :alt="emoji.emoji"/></span> <span v-else-if="!defaultStore.state.useOsNativeEmojis" class="emoji"><img :src="emoji.url" :alt="emoji.emoji"/></span>
<span v-else class="emoji">{{ emoji.emoji }}</span> <span v-else class="emoji">{{ emoji.emoji }}</span>
<!-- eslint-disable-next-line vue/no-v-html -->
<span class="name" v-html="emoji.name.replace(q, `<b>${q}</b>`)"></span> <span class="name" v-html="emoji.name.replace(q, `<b>${q}</b>`)"></span>
<span v-if="emoji.aliasOf" class="alias">({{ emoji.aliasOf }})</span> <span v-if="emoji.aliasOf" class="alias">({{ emoji.aliasOf }})</span>
</li> </li>

View file

@ -1,3 +1,4 @@
<!-- eslint-disable vue/no-v-html -->
<template> <template>
<code v-if="inline" :class="`language-${prismLang}`" v-html="html"></code> <code v-if="inline" :class="`language-${prismLang}`" v-html="html"></code>
<pre v-else :class="`language-${prismLang}`"><code :class="`language-${prismLang}`" v-html="html"></code></pre> <pre v-else :class="`language-${prismLang}`"><code :class="`language-${prismLang}`" v-html="html"></code></pre>
@ -5,7 +6,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import { computed } from 'vue'; import { computed } from 'vue';
import 'prismjs'; import Prism from 'prismjs';
import 'prismjs/themes/prism-okaidia.css'; import 'prismjs/themes/prism-okaidia.css';
const props = defineProps<{ const props = defineProps<{

View file

@ -1,13 +1,12 @@
<script lang="ts"> <script lang="ts">
import { defineComponent, h, PropType, TransitionGroup } from 'vue'; import { defineComponent, h, PropType, TransitionGroup } from 'vue';
import MkAd from '@/components/global/ad.vue';
import { i18n } from '@/i18n'; import { i18n } from '@/i18n';
import { defaultStore } from '@/store'; import { defaultStore } from '@/store';
export default defineComponent({ export default defineComponent({
props: { props: {
items: { items: {
type: Array as PropType<{ id: string; createdAt: string; _shouldInsertAd_: boolean; }[]>, type: Array as PropType<{ id: string; createdAt: string; }[]>,
required: true, required: true,
}, },
direction: { direction: {
@ -25,11 +24,6 @@ export default defineComponent({
required: false, required: false,
default: false default: false
}, },
ad: {
type: Boolean,
required: false,
default: false
},
}, },
setup(props, { slots, expose }) { setup(props, { slots, expose }) {
@ -77,17 +71,9 @@ export default defineComponent({
])); ]));
return [el, separator]; return [el, separator];
} else {
if (props.ad && item._shouldInsertAd_) {
return [h(MkAd, {
class: 'a', // advertise()
key: item.id + ':ad',
prefer: ['horizontal', 'horizontal-big'],
}), el];
} else { } else {
return el; return el;
} }
}
}); });
return () => h( return () => h(

View file

@ -9,12 +9,12 @@
<form v-if="instance.enableEmail" class="bafeceda" @submit.prevent="onSubmit"> <form v-if="instance.enableEmail" class="bafeceda" @submit.prevent="onSubmit">
<div class="main _formRoot"> <div class="main _formRoot">
<MkInput v-model="username" class="_formBlock" type="text" pattern="^[a-zA-Z0-9_]+$" spellcheck="false" autofocus required> <MkInput v-model="username" class="_formBlock" type="text" pattern="^[a-zA-Z0-9_]+$" :spellcheck="false" autofocus required>
<template #label>{{ i18n.ts.username }}</template> <template #label>{{ i18n.ts.username }}</template>
<template #prefix>@</template> <template #prefix>@</template>
</MkInput> </MkInput>
<MkInput v-model="email" class="_formBlock" type="email" spellcheck="false" required> <MkInput v-model="email" class="_formBlock" type="email" :spellcheck="false" required>
<template #label>{{ i18n.ts.emailAddress }}</template> <template #label>{{ i18n.ts.emailAddress }}</template>
<template #caption>{{ i18n.ts._forgotPassword.enterEmail }}</template> <template #caption>{{ i18n.ts._forgotPassword.enterEmail }}</template>
</MkInput> </MkInput>

View file

@ -1,5 +1,6 @@
<template> <template>
<XModalWindow ref="dialog" <XModalWindow
ref="dialog"
:width="450" :width="450"
:can-close="false" :can-close="false"
:with-ok-button="true" :with-ok-button="true"
@ -37,10 +38,10 @@
<option v-for="item in form[item].enum" :key="item.value" :value="item.value">{{ item.label }}</option> <option v-for="item in form[item].enum" :key="item.value" :value="item.value">{{ item.label }}</option>
</FormSelect> </FormSelect>
<FormRadios v-else-if="form[item].type === 'radio'" v-model="values[item]" class="_formBlock"> <FormRadios v-else-if="form[item].type === 'radio'" v-model="values[item]" class="_formBlock">
<template #caption><span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ $ts.optional }})</span></template> <template #label><span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ $ts.optional }})</span></template>
<option v-for="item in form[item].options" :key="item.value" :value="item.value">{{ item.label }}</option> <option v-for="item in form[item].options" :key="item.value" :value="item.value">{{ item.label }}</option>
</FormRadios> </FormRadios>
<FormRange v-else-if="form[item].type === 'range'" v-model="values[item]" :min="form[item].mim" :max="form[item].max" :step="form[item].step" :text-converter="form[item].textConverter" class="_formBlock"> <FormRange v-else-if="form[item].type === 'range'" v-model="values[item]" :min="form[item].min" :max="form[item].max" :step="form[item].step" :text-converter="form[item].textConverter" class="_formBlock">
<template #label><span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ $ts.optional }})</span></template> <template #label><span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ $ts.optional }})</span></template>
<template v-if="form[item].description" #caption>{{ form[item].description }}</template> <template v-if="form[item].description" #caption>{{ form[item].description }}</template>
</FormRange> </FormRange>
@ -55,7 +56,6 @@
<script lang="ts"> <script lang="ts">
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import XModalWindow from '@/components/ui/modal-window.vue';
import FormInput from './form/input.vue'; import FormInput from './form/input.vue';
import FormTextarea from './form/textarea.vue'; import FormTextarea from './form/textarea.vue';
import FormSwitch from './form/switch.vue'; import FormSwitch from './form/switch.vue';
@ -63,6 +63,7 @@ import FormSelect from './form/select.vue';
import FormRange from './form/range.vue'; import FormRange from './form/range.vue';
import MkButton from './ui/button.vue'; import MkButton from './ui/button.vue';
import FormRadios from './form/radios.vue'; import FormRadios from './form/radios.vue';
import XModalWindow from '@/components/ui/modal-window.vue';
export default defineComponent({ export default defineComponent({
components: { components: {
@ -91,31 +92,31 @@ export default defineComponent({
data() { data() {
return { return {
values: {} values: {},
}; };
}, },
created() { created() {
for (const item in this.form) { for (const item in this.form) {
this.values[item] = this.form[item].hasOwnProperty('default') ? this.form[item].default : null; this.values[item] = this.form[item].default ?? null;
} }
}, },
methods: { methods: {
ok() { ok() {
this.$emit('done', { this.$emit('done', {
result: this.values result: this.values,
}); });
this.$refs.dialog.close(); this.$refs.dialog.close();
}, },
cancel() { cancel() {
this.$emit('done', { this.$emit('done', {
canceled: true canceled: true,
}); });
this.$refs.dialog.close(); this.$refs.dialog.close();
} },
} },
}); });
</script> </script>

Some files were not shown because too many files have changed in this diff Show more