Merge branch 'develop' into sw-notification-action
This commit is contained in:
commit
5b52417673
81 changed files with 2712 additions and 625 deletions
|
@ -1 +1 @@
|
||||||
v16.0.0
|
v16.2.0
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
FROM node:16.0.0-alpine3.13 AS base
|
FROM node:16.2.0-alpine3.13 AS base
|
||||||
|
|
||||||
ENV NODE_ENV=production
|
ENV NODE_ENV=production
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
---
|
---
|
||||||
_lang_: "Čeština"
|
_lang_: "Čeština"
|
||||||
|
headlineMisskey: "Síť propojená poznámkami"
|
||||||
introMisskey: "Vítejte! Misskey je otevřený a decentralizovaný microblogový servis.\n\"Poznámkami\" můžete sdílet co se zrovna děje se všemi ve Vašem okolí. 📡\nPomocí \"reakcí\" můžete sdílet své názory a pocity na ostatní poznámky. 👍\nPojďte objevovat nový svět! 🚀"
|
introMisskey: "Vítejte! Misskey je otevřený a decentralizovaný microblogový servis.\n\"Poznámkami\" můžete sdílet co se zrovna děje se všemi ve Vašem okolí. 📡\nPomocí \"reakcí\" můžete sdílet své názory a pocity na ostatní poznámky. 👍\nPojďte objevovat nový svět! 🚀"
|
||||||
monthAndDay: "{day}. {month}."
|
monthAndDay: "{day}. {month}."
|
||||||
search: "Vyhledávání"
|
search: "Vyhledávání"
|
||||||
notifications: "Oznámení"
|
notifications: "Oznámení"
|
||||||
username: "Uživatelské jméno"
|
username: "Uživatelské jméno"
|
||||||
password: "Heslo"
|
password: "Heslo"
|
||||||
|
forgotPassword: "Zapomenuté heslo"
|
||||||
fetchingAsApObject: "Načítám data z Fediversu..."
|
fetchingAsApObject: "Načítám data z Fediversu..."
|
||||||
ok: "Potvrdit"
|
ok: "Potvrdit"
|
||||||
gotIt: "Rozumím!"
|
gotIt: "Rozumím!"
|
||||||
|
@ -72,6 +74,7 @@ error: "Chyba"
|
||||||
somethingHappened: "Jejda. Něco se nepovedlo."
|
somethingHappened: "Jejda. Něco se nepovedlo."
|
||||||
retry: "Opakovat"
|
retry: "Opakovat"
|
||||||
pageLoadError: "Nepodařilo se načíst stránku"
|
pageLoadError: "Nepodařilo se načíst stránku"
|
||||||
|
enterListName: "Jméno seznamu"
|
||||||
privacy: "Soukromí"
|
privacy: "Soukromí"
|
||||||
follow: "Sledovaní"
|
follow: "Sledovaní"
|
||||||
unfollow: "Přestat sledovat"
|
unfollow: "Přestat sledovat"
|
||||||
|
@ -112,16 +115,24 @@ emojiName: "Jméno emoji"
|
||||||
emojiUrl: "URL obrázku"
|
emojiUrl: "URL obrázku"
|
||||||
addEmoji: "Přidat emoji"
|
addEmoji: "Přidat emoji"
|
||||||
settingGuide: "Doporučené nastavení"
|
settingGuide: "Doporučené nastavení"
|
||||||
|
cacheRemoteFiles: "Ukládání vzdálených souborů do mezipaměti"
|
||||||
|
cacheRemoteFilesDescription: "Zakázání tohoto nastavení způsobí, že vzdálené soubory budou odkazovány přímo, místo aby byly ukládány do mezipaměti. Tím se ušetří úložiště na serveru, ale zvýší se provoz, protože se negenerují miniatury."
|
||||||
flagAsBot: "Tento účet je bot"
|
flagAsBot: "Tento účet je bot"
|
||||||
flagAsBotDescription: "Pokud je tento účet kontrolován programem zaškrtněte tuto možnost. To označí tento účet jako bot pro ostatní vývojáře a zabrání tak nekonečným interakcím s ostatními boty a upraví Misskey systém aby se choval k tomuhle účtu jako bot."
|
flagAsBotDescription: "Pokud je tento účet kontrolován programem zaškrtněte tuto možnost. To označí tento účet jako bot pro ostatní vývojáře a zabrání tak nekonečným interakcím s ostatními boty a upraví Misskey systém aby se choval k tomuhle účtu jako bot."
|
||||||
flagAsCat: "Tenhle účet je kočka"
|
flagAsCat: "Tenhle účet je kočka"
|
||||||
flagAsCatDescription: "Vyberte tuto možnost aby tento účet byl označen jako kočka."
|
flagAsCatDescription: "Vyberte tuto možnost aby tento účet byl označen jako kočka."
|
||||||
autoAcceptFollowed: "Automaticky akceptovat následování od účtů které sledujete"
|
autoAcceptFollowed: "Automaticky akceptovat následování od účtů které sledujete"
|
||||||
|
addAccount: "Přidat účet"
|
||||||
loginFailed: "Přihlášení se nezdařilo."
|
loginFailed: "Přihlášení se nezdařilo."
|
||||||
|
showOnRemote: "Více na původním profilu"
|
||||||
general: "Obecně"
|
general: "Obecně"
|
||||||
wallpaper: "Obrázek na pozadí"
|
wallpaper: "Obrázek na pozadí"
|
||||||
setWallpaper: "Nastavení obrázku na pozadí"
|
setWallpaper: "Nastavení obrázku na pozadí"
|
||||||
removeWallpaper: "Odstranit pozadí"
|
removeWallpaper: "Odstranit pozadí"
|
||||||
|
youHaveNoLists: "Nemáte žádné seznamy"
|
||||||
|
proxyAccount: "Proxy účet"
|
||||||
|
proxyAccountDescription: "Proxy účet je účet, který za určitých podmínek sleduje uživatele na dálku vaším jménem. Například když uživatel zařadí vzdáleného uživatele do seznamu, pokud nikdo nesleduje uživatele na seznamu, aktivita nebude doručena instanci, takže místo toho bude uživatele sledovat účet proxy."
|
||||||
|
host: "Hostitel"
|
||||||
selectUser: "Vyberte uživatele"
|
selectUser: "Vyberte uživatele"
|
||||||
recipient: "Pro"
|
recipient: "Pro"
|
||||||
annotation: "Komentáře"
|
annotation: "Komentáře"
|
||||||
|
@ -139,6 +150,8 @@ operations: "Operace"
|
||||||
software: "Software"
|
software: "Software"
|
||||||
version: "Verze"
|
version: "Verze"
|
||||||
metadata: "Metadata"
|
metadata: "Metadata"
|
||||||
|
withNFiles: "{n} soubor(ů)"
|
||||||
|
monitor: "Monitorovat"
|
||||||
jobQueue: "Fronta úloh"
|
jobQueue: "Fronta úloh"
|
||||||
cpuAndMemory: "CPU a paměť"
|
cpuAndMemory: "CPU a paměť"
|
||||||
network: "Síť"
|
network: "Síť"
|
||||||
|
@ -204,8 +217,12 @@ remoteUserCaution: "Tyto informace nemusí být aktuální jelikož uživatel je
|
||||||
activity: "Aktivita"
|
activity: "Aktivita"
|
||||||
images: "Obrázky"
|
images: "Obrázky"
|
||||||
birthday: "Datum narození"
|
birthday: "Datum narození"
|
||||||
|
yearsOld: "{age} let"
|
||||||
registeredDate: "Datum registrace"
|
registeredDate: "Datum registrace"
|
||||||
location: "Lokace"
|
location: "Lokace"
|
||||||
|
theme: "Vzhled"
|
||||||
|
themeForLightMode: "Vzhled pro použití ve světlém režimu"
|
||||||
|
themeForDarkMode: "Vzhled k použití v tmavém režimu"
|
||||||
light: "Světlý"
|
light: "Světlý"
|
||||||
dark: "Tmavý"
|
dark: "Tmavý"
|
||||||
lightThemes: "Světlý vzhled"
|
lightThemes: "Světlý vzhled"
|
||||||
|
@ -319,6 +336,10 @@ retype: "Zadejte znovu"
|
||||||
noteOf: "{user} poznámky"
|
noteOf: "{user} poznámky"
|
||||||
inviteToGroup: "Pozvat do skupiny"
|
inviteToGroup: "Pozvat do skupiny"
|
||||||
invitations: "Pozvat"
|
invitations: "Pozvat"
|
||||||
|
checking: "Ověřuji"
|
||||||
|
available: "K dispozici"
|
||||||
|
unavailable: "Není k dispozici"
|
||||||
|
usernameInvalidFormat: "Písmena, čísla a _ jsou povolená."
|
||||||
tooShort: "Příliš krátké"
|
tooShort: "Příliš krátké"
|
||||||
tooLong: "Příliš dlouhé"
|
tooLong: "Příliš dlouhé"
|
||||||
weakPassword: "Slabé heslo"
|
weakPassword: "Slabé heslo"
|
||||||
|
@ -330,7 +351,13 @@ signinWith: "Přihlásit se s {x}"
|
||||||
signinFailed: "Nelze se přihlásit. Zkontrolujte prosím své uživatelské jméno a heslo."
|
signinFailed: "Nelze se přihlásit. Zkontrolujte prosím své uživatelské jméno a heslo."
|
||||||
or: "Nebo"
|
or: "Nebo"
|
||||||
language: "Jazyk"
|
language: "Jazyk"
|
||||||
|
uiLanguage: "Jazyk uživatelského rozhraní"
|
||||||
|
groupInvited: "Pozvat do skupiny"
|
||||||
|
aboutX: "O {x}"
|
||||||
|
useOsNativeEmojis: "Použití nativních emoji operačního systému"
|
||||||
youHaveNoGroups: "Nemáte žádné skupiny"
|
youHaveNoGroups: "Nemáte žádné skupiny"
|
||||||
|
joinOrCreateGroup: "Můžete požádat o pozvání do stávající skupiny nebo vytvořit novou."
|
||||||
|
noHistory: "Žádná historie"
|
||||||
signinHistory: "Historie přihlášení"
|
signinHistory: "Historie přihlášení"
|
||||||
category: "Kategorie"
|
category: "Kategorie"
|
||||||
tags: "Štítky"
|
tags: "Štítky"
|
||||||
|
@ -371,6 +398,7 @@ rooms: "Místnost"
|
||||||
inboxUrl: "Inbox URL"
|
inboxUrl: "Inbox URL"
|
||||||
deletedNote: "Odstraněné příspěvky"
|
deletedNote: "Odstraněné příspěvky"
|
||||||
invisibleNote: "Skryté příspěvky"
|
invisibleNote: "Skryté příspěvky"
|
||||||
|
smtpHost: "Hostitel"
|
||||||
smtpUser: "Uživatelské jméno"
|
smtpUser: "Uživatelské jméno"
|
||||||
smtpPass: "Heslo"
|
smtpPass: "Heslo"
|
||||||
clearCache: "Vyprázdnit mezipaměť"
|
clearCache: "Vyprázdnit mezipaměť"
|
||||||
|
@ -416,6 +444,8 @@ _timelines:
|
||||||
_rooms:
|
_rooms:
|
||||||
_roomType:
|
_roomType:
|
||||||
default: "Výchozí"
|
default: "Výchozí"
|
||||||
|
_furnitures:
|
||||||
|
monitor: "Monitorovat"
|
||||||
_pages:
|
_pages:
|
||||||
blocks:
|
blocks:
|
||||||
image: "Obrázky"
|
image: "Obrázky"
|
||||||
|
@ -438,6 +468,7 @@ _pages:
|
||||||
types:
|
types:
|
||||||
array: "Seznamy"
|
array: "Seznamy"
|
||||||
_notification:
|
_notification:
|
||||||
|
youWereInvitedToGroup: "Pozvat do skupiny"
|
||||||
_types:
|
_types:
|
||||||
follow: "Sledovaní"
|
follow: "Sledovaní"
|
||||||
mention: "Zmínění"
|
mention: "Zmínění"
|
||||||
|
|
|
@ -731,9 +731,11 @@ active: "Aktiv"
|
||||||
offline: "Offline"
|
offline: "Offline"
|
||||||
notRecommended: "Nicht empfohlen"
|
notRecommended: "Nicht empfohlen"
|
||||||
botProtection: "Bot-Schutz"
|
botProtection: "Bot-Schutz"
|
||||||
|
instanceBlocking: "Blockierte Instanzen"
|
||||||
selectAccount: "Benutzerkonto auswählen"
|
selectAccount: "Benutzerkonto auswählen"
|
||||||
enabled: "Aktiviert"
|
enabled: "Aktiviert"
|
||||||
disabled: "Deaktiviert"
|
disabled: "Deaktiviert"
|
||||||
|
quickAction: "Schnellaktionen"
|
||||||
user: "Benutzer"
|
user: "Benutzer"
|
||||||
administration: "Verwaltung"
|
administration: "Verwaltung"
|
||||||
accounts: "Benutzerkonten"
|
accounts: "Benutzerkonten"
|
||||||
|
@ -754,6 +756,10 @@ high: "Hoch"
|
||||||
middle: "Mittel"
|
middle: "Mittel"
|
||||||
low: "Niedrig"
|
low: "Niedrig"
|
||||||
emailNotConfiguredWarning: "Keine Email-Adresse hinterlegt"
|
emailNotConfiguredWarning: "Keine Email-Adresse hinterlegt"
|
||||||
|
ratio: "Verhältnis"
|
||||||
|
_ad:
|
||||||
|
back: "Zurück"
|
||||||
|
reduceFrequencyOfThisAd: "Diese Werbung weniger anzeigen"
|
||||||
_forgotPassword:
|
_forgotPassword:
|
||||||
enterEmail: "Gib die Email-Adresse ein, mit der du dich registriert hast. An diese wird ein Link gesendet, mit der du dein Passwort zurücksetzen kannst."
|
enterEmail: "Gib die Email-Adresse ein, mit der du dich registriert hast. An diese wird ein Link gesendet, mit der du dein Passwort zurücksetzen kannst."
|
||||||
ifNoEmail: "Solltest du bei der Registrierung keine Email-Adresse angegeben haben, wende dich bitte an den Administrator."
|
ifNoEmail: "Solltest du bei der Registrierung keine Email-Adresse angegeben haben, wende dich bitte an den Administrator."
|
||||||
|
|
|
@ -756,6 +756,10 @@ high: "High"
|
||||||
middle: "Medium"
|
middle: "Medium"
|
||||||
low: "Low"
|
low: "Low"
|
||||||
emailNotConfiguredWarning: "Email address not set"
|
emailNotConfiguredWarning: "Email address not set"
|
||||||
|
ratio: "Ratio"
|
||||||
|
_ad:
|
||||||
|
back: "Back"
|
||||||
|
reduceFrequencyOfThisAd: "Show this ad less"
|
||||||
_forgotPassword:
|
_forgotPassword:
|
||||||
enterEmail: "Enter the email address you used to register. A link with which you can reset your password will then be sent to it."
|
enterEmail: "Enter the email address you used to register. A link with which you can reset your password will then be sent to it."
|
||||||
ifNoEmail: "If you did not use an email during registration, please contact the administrator instead."
|
ifNoEmail: "If you did not use an email during registration, please contact the administrator instead."
|
||||||
|
|
|
@ -667,6 +667,8 @@ user: "Usuarios"
|
||||||
administration: "Administrar"
|
administration: "Administrar"
|
||||||
expiration: "Termina el"
|
expiration: "Termina el"
|
||||||
middle: "Mediano"
|
middle: "Mediano"
|
||||||
|
_ad:
|
||||||
|
back: "Deseleccionar"
|
||||||
_gallery:
|
_gallery:
|
||||||
unlike: "Quitar me gusta"
|
unlike: "Quitar me gusta"
|
||||||
_email:
|
_email:
|
||||||
|
|
|
@ -607,7 +607,7 @@ chatOpenBehavior: "Comportement de la fenêtre de discussion lors de son ouvertu
|
||||||
behavior: "Comportement"
|
behavior: "Comportement"
|
||||||
sample: "Exemple"
|
sample: "Exemple"
|
||||||
abuseReports: "Signalements"
|
abuseReports: "Signalements"
|
||||||
reportAbuse: "Signalements"
|
reportAbuse: "Signaler"
|
||||||
reportAbuseOf: "Signaler {name}"
|
reportAbuseOf: "Signaler {name}"
|
||||||
fillAbuseReportDescription: "Veuillez expliquer les raisons du signalement. S'il s'agit d'une note précise, veuillez en donner le lien."
|
fillAbuseReportDescription: "Veuillez expliquer les raisons du signalement. S'il s'agit d'une note précise, veuillez en donner le lien."
|
||||||
abuseReported: "Le rapport est envoyé. Merci."
|
abuseReported: "Le rapport est envoyé. Merci."
|
||||||
|
@ -750,12 +750,16 @@ popularPosts: "Les plus consultées"
|
||||||
shareWithNote: "Partager dans une note"
|
shareWithNote: "Partager dans une note"
|
||||||
ads: "Publicité"
|
ads: "Publicité"
|
||||||
expiration: "Échéance"
|
expiration: "Échéance"
|
||||||
memo: "Mémo"
|
memo: "Pense-bête"
|
||||||
priority: "Priorité"
|
priority: "Priorité"
|
||||||
high: "Haute"
|
high: "Haute"
|
||||||
middle: "Moyen"
|
middle: "Moyen"
|
||||||
low: "Basse"
|
low: "Basse"
|
||||||
emailNotConfiguredWarning: "Vous n'avez pas configuré d'adresse e-mail."
|
emailNotConfiguredWarning: "Vous n'avez pas configuré d'adresse e-mail."
|
||||||
|
ratio: "Ratio"
|
||||||
|
_ad:
|
||||||
|
back: "Retour"
|
||||||
|
reduceFrequencyOfThisAd: "Voir cette publicité moins souvent"
|
||||||
_forgotPassword:
|
_forgotPassword:
|
||||||
enterEmail: "Entrez ici l'adresse e-mail que vous avez enregistrée pour votre compte. Un lien vous permettant de réinitialiser votre mot de passe sera envoyé à cette adresse."
|
enterEmail: "Entrez ici l'adresse e-mail que vous avez enregistrée pour votre compte. Un lien vous permettant de réinitialiser votre mot de passe sera envoyé à cette adresse."
|
||||||
ifNoEmail: "Si vous n'avez pas enregistré d'adresse e-mail, merci de contacter l'administrateur·rice de votre instance."
|
ifNoEmail: "Si vous n'avez pas enregistré d'adresse e-mail, merci de contacter l'administrateur·rice de votre instance."
|
||||||
|
|
1382
locales/id-ID.yml
1382
locales/id-ID.yml
File diff suppressed because it is too large
Load diff
|
@ -7,6 +7,7 @@ search: "Cerca"
|
||||||
notifications: "Notifiche"
|
notifications: "Notifiche"
|
||||||
username: "Nome utente"
|
username: "Nome utente"
|
||||||
password: "Password"
|
password: "Password"
|
||||||
|
forgotPassword: "Hai dimenticato la tua password?"
|
||||||
fetchingAsApObject: "Recuperando dal Fediverso..."
|
fetchingAsApObject: "Recuperando dal Fediverso..."
|
||||||
ok: "OK"
|
ok: "OK"
|
||||||
gotIt: "Capito!"
|
gotIt: "Capito!"
|
||||||
|
@ -589,10 +590,13 @@ regenerateLoginTokenDescription: "Genera un nuovo token di autenticazione. Solit
|
||||||
fileIdOrUrl: "ID o URL del file"
|
fileIdOrUrl: "ID o URL del file"
|
||||||
chatOpenBehavior: "Comportamento della finestra di chat quando viene aperta"
|
chatOpenBehavior: "Comportamento della finestra di chat quando viene aperta"
|
||||||
behavior: "Comportamento"
|
behavior: "Comportamento"
|
||||||
abuseReports: "Segnala"
|
abuseReports: "Segnalazioni"
|
||||||
reportAbuse: "Segnala"
|
reportAbuse: "Segnalazioni"
|
||||||
reportAbuseOf: "Segnala {name}"
|
reportAbuseOf: "Segnala {name}"
|
||||||
|
fillAbuseReportDescription: "Si prega di spiegare il motivo della segnalazione. Se riguarda una nota precisa, si prega di collegare anche l'URL della nota."
|
||||||
|
abuseReported: "La segnalazione è stata inviata. Grazie."
|
||||||
send: "Inviare"
|
send: "Inviare"
|
||||||
|
abuseMarkAsResolved: "Contrassegna la segnalazione come risolta"
|
||||||
openInNewTab: "Apri in una nuova scheda"
|
openInNewTab: "Apri in una nuova scheda"
|
||||||
openInSideView: "Apri in vista laterale"
|
openInSideView: "Apri in vista laterale"
|
||||||
defaultNavigationBehaviour: "Navigazione preimpostata"
|
defaultNavigationBehaviour: "Navigazione preimpostata"
|
||||||
|
@ -704,6 +708,7 @@ onlineStatus: "Stato di connessione"
|
||||||
hideOnlineStatus: "Stato invisibile"
|
hideOnlineStatus: "Stato invisibile"
|
||||||
hideOnlineStatusDescription: "Abilitare l'opzione di stato invisibile può guastare la praticità di singole funzioni, come la ricerca."
|
hideOnlineStatusDescription: "Abilitare l'opzione di stato invisibile può guastare la praticità di singole funzioni, come la ricerca."
|
||||||
online: "Online"
|
online: "Online"
|
||||||
|
active: "Attiv@"
|
||||||
offline: "Offline"
|
offline: "Offline"
|
||||||
notRecommended: "Sconsigliato"
|
notRecommended: "Sconsigliato"
|
||||||
botProtection: "Protezione contro i bot"
|
botProtection: "Protezione contro i bot"
|
||||||
|
@ -724,8 +729,22 @@ 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"
|
||||||
middle: "Predefinito"
|
memo: "Promemoria"
|
||||||
|
priority: "Priorità"
|
||||||
|
high: "Alta"
|
||||||
|
middle: "Media"
|
||||||
|
low: "Bassa"
|
||||||
|
emailNotConfiguredWarning: "Non hai impostato nessun indirizzo e-mail."
|
||||||
|
ratio: "Rapporto"
|
||||||
|
_ad:
|
||||||
|
back: "Indietro"
|
||||||
|
reduceFrequencyOfThisAd: "Visualizza questa pubblicità meno spesso"
|
||||||
|
_forgotPassword:
|
||||||
|
enterEmail: "Inserisci l'indirizzo di posta elettronica che hai registrato nel tuo profilo. Il collegamento necessario per ripristinare la password verrà inviato a questo indirizzo."
|
||||||
|
ifNoEmail: "Se nessun indirizzo e-mail è stato registrato, si prega di contattare l'amministratore·trice dell'istanza."
|
||||||
|
contactAdmin: "Poiché questa istanza non permette l'utilizzo di una mail, si prega di contattare l'amministratore·trice dell'istanza per poter ripristinare la password."
|
||||||
_gallery:
|
_gallery:
|
||||||
my: "Le mie pubblicazioni"
|
my: "Le mie pubblicazioni"
|
||||||
liked: "Pubblicazioni che mi piacciono"
|
liked: "Pubblicazioni che mi piacciono"
|
||||||
|
|
|
@ -756,6 +756,11 @@ high: "高"
|
||||||
middle: "中"
|
middle: "中"
|
||||||
low: "低"
|
low: "低"
|
||||||
emailNotConfiguredWarning: "メールアドレスの設定がされていません。"
|
emailNotConfiguredWarning: "メールアドレスの設定がされていません。"
|
||||||
|
ratio: "比率"
|
||||||
|
|
||||||
|
_ad:
|
||||||
|
back: "戻る"
|
||||||
|
reduceFrequencyOfThisAd: "この広告の表示頻度を下げる"
|
||||||
|
|
||||||
_forgotPassword:
|
_forgotPassword:
|
||||||
enterEmail: "アカウントに登録したメールアドレスを入力してください。そのアドレス宛てに、パスワードリセット用のリンクが送信されます。"
|
enterEmail: "アカウントに登録したメールアドレスを入力してください。そのアドレス宛てに、パスワードリセット用のリンクが送信されます。"
|
||||||
|
|
|
@ -649,6 +649,8 @@ memo: "メモ"
|
||||||
high: "高い"
|
high: "高い"
|
||||||
middle: "中"
|
middle: "中"
|
||||||
low: "低い"
|
low: "低い"
|
||||||
|
_ad:
|
||||||
|
back: "戻る"
|
||||||
_gallery:
|
_gallery:
|
||||||
unlike: "良くないわ"
|
unlike: "良くないわ"
|
||||||
_email:
|
_email:
|
||||||
|
|
|
@ -7,6 +7,7 @@ search: "검색"
|
||||||
notifications: "알림"
|
notifications: "알림"
|
||||||
username: "유저명"
|
username: "유저명"
|
||||||
password: "비밀번호"
|
password: "비밀번호"
|
||||||
|
forgotPassword: "비밀번호 재설정"
|
||||||
fetchingAsApObject: "연합에서 조회 중"
|
fetchingAsApObject: "연합에서 조회 중"
|
||||||
ok: "OK"
|
ok: "OK"
|
||||||
gotIt: "알겠어요"
|
gotIt: "알겠어요"
|
||||||
|
@ -747,8 +748,22 @@ gallery: "갤러리"
|
||||||
recentPosts: "최근 포스트"
|
recentPosts: "최근 포스트"
|
||||||
popularPosts: "인기 포스트"
|
popularPosts: "인기 포스트"
|
||||||
shareWithNote: "노트로 공유"
|
shareWithNote: "노트로 공유"
|
||||||
expiration: "투표 기한"
|
ads: "광고"
|
||||||
|
expiration: "기한"
|
||||||
|
memo: "메모"
|
||||||
|
priority: "우선순위"
|
||||||
|
high: "높음"
|
||||||
middle: "보통"
|
middle: "보통"
|
||||||
|
low: "낮음"
|
||||||
|
emailNotConfiguredWarning: "메일 주소가 설정되어 있지 않습니다."
|
||||||
|
ratio: "비율"
|
||||||
|
_ad:
|
||||||
|
back: "뒤로"
|
||||||
|
reduceFrequencyOfThisAd: "이 광고의 표시 빈도 낮추기"
|
||||||
|
_forgotPassword:
|
||||||
|
enterEmail: "여기에 계정에 등록한 메일 주소를 입력해 주세요. 입력한 메일 주소로 비밀번호 재설정 링크를 발송합니다."
|
||||||
|
ifNoEmail: "메일 주소를 등록하지 않은 경우, 관리자에 문의해 주십시오."
|
||||||
|
contactAdmin: "이 인스턴스에서는 메일 기능이 지원되지 않습니다. 비밀번호를 재설정하려면 관리자에게 문의해 주십시오."
|
||||||
_gallery:
|
_gallery:
|
||||||
my: "내 갤러리"
|
my: "내 갤러리"
|
||||||
liked: "좋아요 한 갤러리"
|
liked: "좋아요 한 갤러리"
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
---
|
---
|
||||||
_lang_: "Nederlands"
|
_lang_: "Nederlands"
|
||||||
|
headlineMisskey: "Netwerk verbonden door notities"
|
||||||
|
|
|
@ -7,6 +7,7 @@ search: "Szukaj"
|
||||||
notifications: "Powiadomienia"
|
notifications: "Powiadomienia"
|
||||||
username: "Nazwa użytkownika"
|
username: "Nazwa użytkownika"
|
||||||
password: "Hasło"
|
password: "Hasło"
|
||||||
|
forgotPassword: "Nie pamiętam hasła"
|
||||||
fetchingAsApObject: "Pobieranie z Fediwersum…"
|
fetchingAsApObject: "Pobieranie z Fediwersum…"
|
||||||
ok: "OK"
|
ok: "OK"
|
||||||
gotIt: "Rozumiem!"
|
gotIt: "Rozumiem!"
|
||||||
|
@ -65,6 +66,7 @@ download: "Pobierz"
|
||||||
driveFileDeleteConfirm: "Czy chcesz usunąć plik \"{name}\"? Zniknie również notatka, do której dołączony jest ten plik."
|
driveFileDeleteConfirm: "Czy chcesz usunąć plik \"{name}\"? Zniknie również notatka, do której dołączony jest ten plik."
|
||||||
unfollowConfirm: "Czy na pewno chcesz przestać obserwować {name}?"
|
unfollowConfirm: "Czy na pewno chcesz przestać obserwować {name}?"
|
||||||
exportRequested: "Zażądałeś eksportu. Może to zająć trochę czasu. Po zakończeniu eksportu zostanie on dodany do Twoich \"dysków\"."
|
exportRequested: "Zażądałeś eksportu. Może to zająć trochę czasu. Po zakończeniu eksportu zostanie on dodany do Twoich \"dysków\"."
|
||||||
|
importRequested: "Zażądano importu. Może to zająć chwilę."
|
||||||
lists: "Listy"
|
lists: "Listy"
|
||||||
noLists: "Nie masz żadnych list"
|
noLists: "Nie masz żadnych list"
|
||||||
note: "Utwórz wpis"
|
note: "Utwórz wpis"
|
||||||
|
@ -136,6 +138,7 @@ flagAsBot: "To konto jest botem"
|
||||||
flagAsCat: "To konto jest kotem"
|
flagAsCat: "To konto jest kotem"
|
||||||
flagAsCatDescription: "Przełącz tę opcję, aby konto było oznaczone jako kot."
|
flagAsCatDescription: "Przełącz tę opcję, aby konto było oznaczone jako kot."
|
||||||
autoAcceptFollowed: "Automatycznie przyjmuj prośby o możliwość obserwacji od użytkowników, których obserwujesz"
|
autoAcceptFollowed: "Automatycznie przyjmuj prośby o możliwość obserwacji od użytkowników, których obserwujesz"
|
||||||
|
addAccount: "Dodaj konto"
|
||||||
loginFailed: "Nie udało się zalogować"
|
loginFailed: "Nie udało się zalogować"
|
||||||
showOnRemote: "Zobacz na zdalnej instancji"
|
showOnRemote: "Zobacz na zdalnej instancji"
|
||||||
general: "Ogólne"
|
general: "Ogólne"
|
||||||
|
@ -291,12 +294,15 @@ reject: "Odrzuć"
|
||||||
normal: "Normalny"
|
normal: "Normalny"
|
||||||
instanceName: "Nazwa instancji"
|
instanceName: "Nazwa instancji"
|
||||||
instanceDescription: "Opis instancji"
|
instanceDescription: "Opis instancji"
|
||||||
|
maintainerName: "Administrator"
|
||||||
|
maintainerEmail: "E-mail administratora"
|
||||||
tosUrl: "Adres URL regulaminu"
|
tosUrl: "Adres URL regulaminu"
|
||||||
thisYear: "Rok"
|
thisYear: "Rok"
|
||||||
thisMonth: "Miesiąc"
|
thisMonth: "Miesiąc"
|
||||||
today: "Dziś"
|
today: "Dziś"
|
||||||
dayX: "{day}"
|
dayX: "{day}"
|
||||||
monthX: "{month}"
|
monthX: "{month}"
|
||||||
|
yearX: "{year}"
|
||||||
pages: "Strony"
|
pages: "Strony"
|
||||||
integration: "Integracja"
|
integration: "Integracja"
|
||||||
connectSerice: "Połącz"
|
connectSerice: "Połącz"
|
||||||
|
@ -332,6 +338,7 @@ manageAntennas: "Zarządzaj Antenami"
|
||||||
name: "Nazwa"
|
name: "Nazwa"
|
||||||
antennaSource: "Źródło Anteny"
|
antennaSource: "Źródło Anteny"
|
||||||
antennaExcludeKeywords: "Wykluczone słowa kluczowe"
|
antennaExcludeKeywords: "Wykluczone słowa kluczowe"
|
||||||
|
notifyAntenna: "Powiadamiaj o nowych wpisach"
|
||||||
withFileAntenna: "Filtruj tylko wpisy z załączonym plikiem"
|
withFileAntenna: "Filtruj tylko wpisy z załączonym plikiem"
|
||||||
enableServiceworker: "Włącz ServiceWorker"
|
enableServiceworker: "Włącz ServiceWorker"
|
||||||
antennaUsersDescription: "Wypisz po jednej nazwie użytkownika w linii"
|
antennaUsersDescription: "Wypisz po jednej nazwie użytkownika w linii"
|
||||||
|
@ -358,6 +365,7 @@ administrator: "Admin"
|
||||||
token: "Token"
|
token: "Token"
|
||||||
twoStepAuthentication: "Uwierzytelnianie dwuskładnikowe"
|
twoStepAuthentication: "Uwierzytelnianie dwuskładnikowe"
|
||||||
moderator: "Moderator"
|
moderator: "Moderator"
|
||||||
|
nUsersMentioned: "{n} wspomnianych użytkowników"
|
||||||
securityKey: "Klucz bezpieczeństwa"
|
securityKey: "Klucz bezpieczeństwa"
|
||||||
securityKeyName: "Nazwa klucza"
|
securityKeyName: "Nazwa klucza"
|
||||||
registerSecurityKey: "Zarejestruj klucz bezpieczeństwa"
|
registerSecurityKey: "Zarejestruj klucz bezpieczeństwa"
|
||||||
|
@ -397,6 +405,7 @@ retype: "Wprowadź ponownie"
|
||||||
noteOf: "Wpisy {user}"
|
noteOf: "Wpisy {user}"
|
||||||
inviteToGroup: "Zaproś do grupy"
|
inviteToGroup: "Zaproś do grupy"
|
||||||
maxNoteTextLength: "Limit znaków dla wpisów"
|
maxNoteTextLength: "Limit znaków dla wpisów"
|
||||||
|
quoteAttached: "Zacytowano"
|
||||||
quoteQuestion: "Czy na pewno chcesz umieścić cytat?"
|
quoteQuestion: "Czy na pewno chcesz umieścić cytat?"
|
||||||
noMessagesYet: "Nie napisano jeszcze wiadomości"
|
noMessagesYet: "Nie napisano jeszcze wiadomości"
|
||||||
newMessageExists: "Masz nową wiadomość"
|
newMessageExists: "Masz nową wiadomość"
|
||||||
|
@ -419,6 +428,7 @@ signinWith: "Zaloguj się z {x}"
|
||||||
signinFailed: "Nie udało się zalogować. Wprowadzona nazwa użytkownika lub hasło są nieprawidłowe."
|
signinFailed: "Nie udało się zalogować. Wprowadzona nazwa użytkownika lub hasło są nieprawidłowe."
|
||||||
tapSecurityKey: "Wybierz swój klucz bezpieczeństwa"
|
tapSecurityKey: "Wybierz swój klucz bezpieczeństwa"
|
||||||
or: "Lub"
|
or: "Lub"
|
||||||
|
language: "Język"
|
||||||
uiLanguage: "Język wyświetlania UI"
|
uiLanguage: "Język wyświetlania UI"
|
||||||
groupInvited: "Zaproszony(-a) do grupy"
|
groupInvited: "Zaproszony(-a) do grupy"
|
||||||
aboutX: "O {x}"
|
aboutX: "O {x}"
|
||||||
|
@ -432,6 +442,7 @@ category: "Kategoria"
|
||||||
tags: "Tagi"
|
tags: "Tagi"
|
||||||
docSource: "Źródło tego dokumentu"
|
docSource: "Źródło tego dokumentu"
|
||||||
createAccount: "Utwórz konto"
|
createAccount: "Utwórz konto"
|
||||||
|
existingAccount: "Istniejące konto"
|
||||||
regenerate: "Wygeneruj ponownie"
|
regenerate: "Wygeneruj ponownie"
|
||||||
fontSize: "Rozmiar czcionki"
|
fontSize: "Rozmiar czcionki"
|
||||||
noFollowRequests: "Nie masz żadnych oczekujących próśb o możliwość obserwacji"
|
noFollowRequests: "Nie masz żadnych oczekujących próśb o możliwość obserwacji"
|
||||||
|
@ -453,6 +464,8 @@ showFeaturedNotesInTimeline: "Pokazuj wyróżnione wpisy w osi czasu"
|
||||||
objectStorage: "Pamięć obiektowa"
|
objectStorage: "Pamięć obiektowa"
|
||||||
useObjectStorage: "Używaj pamięci obiektowej"
|
useObjectStorage: "Używaj pamięci obiektowej"
|
||||||
objectStorageBaseUrl: "Podstawowy URL"
|
objectStorageBaseUrl: "Podstawowy URL"
|
||||||
|
objectStorageBucket: "Bucket"
|
||||||
|
objectStorageBucketDesc: "Podaj nazwę „wiadra” używaną przez konfigurowaną usługę."
|
||||||
objectStoragePrefix: "Prefiks"
|
objectStoragePrefix: "Prefiks"
|
||||||
objectStoragePrefixDesc: "Pliki będą przechowywane w katalogu z tym prefiksem."
|
objectStoragePrefixDesc: "Pliki będą przechowywane w katalogu z tym prefiksem."
|
||||||
objectStorageEndpoint: "Punkt końcowy"
|
objectStorageEndpoint: "Punkt końcowy"
|
||||||
|
@ -540,6 +553,7 @@ pluginTokenRequestedDescription: "Ta wtyczka będzie mogła korzystać z ustawio
|
||||||
notificationType: "Rodzaj powiadomień"
|
notificationType: "Rodzaj powiadomień"
|
||||||
edit: "Edytuj"
|
edit: "Edytuj"
|
||||||
useStarForReactionFallback: "Użyj ★ jako zapasowego emoji, gdy emoji reakcji jest nieznane"
|
useStarForReactionFallback: "Użyj ★ jako zapasowego emoji, gdy emoji reakcji jest nieznane"
|
||||||
|
emailServer: "Serwer poczty e-mail"
|
||||||
enableEmail: "Włącz dostarczanie wiadomości e-mail"
|
enableEmail: "Włącz dostarczanie wiadomości e-mail"
|
||||||
emailConfigInfo: "Wykorzystywany do potwierdzenia adresu e-mail w trakcie rejestracji, lub gdy zapomnisz hasła"
|
emailConfigInfo: "Wykorzystywany do potwierdzenia adresu e-mail w trakcie rejestracji, lub gdy zapomnisz hasła"
|
||||||
email: "Adres e-mail"
|
email: "Adres e-mail"
|
||||||
|
@ -552,6 +566,7 @@ smtpPass: "Hasło"
|
||||||
emptyToDisableSmtpAuth: "Pozostaw adres e-mail i hasło puste, aby wyłączyć weryfikację SMTP"
|
emptyToDisableSmtpAuth: "Pozostaw adres e-mail i hasło puste, aby wyłączyć weryfikację SMTP"
|
||||||
smtpSecureInfo: "Wyłącz, jeżeli używasz STARTTLS"
|
smtpSecureInfo: "Wyłącz, jeżeli używasz STARTTLS"
|
||||||
testEmail: "Przetestuj dostarczanie wiadomości e-mail"
|
testEmail: "Przetestuj dostarczanie wiadomości e-mail"
|
||||||
|
wordMute: "Wyciszenie słowa"
|
||||||
userSaysSomething: "{name} powiedział(-a) coś"
|
userSaysSomething: "{name} powiedział(-a) coś"
|
||||||
makeActive: "Aktywuj"
|
makeActive: "Aktywuj"
|
||||||
display: "Wyświetlanie"
|
display: "Wyświetlanie"
|
||||||
|
@ -622,6 +637,7 @@ emailVerified: "Adres e-mail został potwierdzony"
|
||||||
noteFavoritesCount: "Liczba polubionych wpisów"
|
noteFavoritesCount: "Liczba polubionych wpisów"
|
||||||
pageLikesCount: "Liczba otrzymanych polubień stron"
|
pageLikesCount: "Liczba otrzymanych polubień stron"
|
||||||
pageLikedCount: "Liczba polubionych stron"
|
pageLikedCount: "Liczba polubionych stron"
|
||||||
|
reversiCount: "Liczba rozgrywek Reversi"
|
||||||
contact: "Kontakt"
|
contact: "Kontakt"
|
||||||
useSystemFont: "Używaj domyślnej czcionki systemu"
|
useSystemFont: "Używaj domyślnej czcionki systemu"
|
||||||
experimentalFeatures: "Eksperymentalne funkcje"
|
experimentalFeatures: "Eksperymentalne funkcje"
|
||||||
|
@ -630,6 +646,7 @@ makeExplorable: "Pokazuj konto na stronie „Eksploruj”"
|
||||||
makeExplorableDescription: "Jeżeli wyłączysz tę opcję, Twoje konto nie będzie wyświetlać się w sekcji „Eksploruj”."
|
makeExplorableDescription: "Jeżeli wyłączysz tę opcję, Twoje konto nie będzie wyświetlać się w sekcji „Eksploruj”."
|
||||||
showGapBetweenNotesInTimeline: "Pokazuj odstęp między wpisami na osi czasu."
|
showGapBetweenNotesInTimeline: "Pokazuj odstęp między wpisami na osi czasu."
|
||||||
duplicate: "Duplikuj"
|
duplicate: "Duplikuj"
|
||||||
|
left: "Lewo"
|
||||||
center: "Wyśsrodkuj"
|
center: "Wyśsrodkuj"
|
||||||
wide: "Szerokie"
|
wide: "Szerokie"
|
||||||
narrow: "Wąskie"
|
narrow: "Wąskie"
|
||||||
|
@ -639,24 +656,107 @@ clearCache: "Wyczyść pamięć podręczną"
|
||||||
onlineUsersCount: "{n} osób jest online"
|
onlineUsersCount: "{n} osób jest online"
|
||||||
nUsers: "{n} użytkowników"
|
nUsers: "{n} użytkowników"
|
||||||
nNotes: "{n} wpisów"
|
nNotes: "{n} wpisów"
|
||||||
|
sendErrorReports: "Wyślij raporty o błędach"
|
||||||
|
myTheme: "Mój motyw"
|
||||||
backgroundColor: "Tło"
|
backgroundColor: "Tło"
|
||||||
accentColor: "Akcent"
|
accentColor: "Akcent"
|
||||||
textColor: "Tekst"
|
textColor: "Tekst"
|
||||||
|
saveAs: "Zapisz jako…"
|
||||||
|
advanced: "Zaawansowane"
|
||||||
value: "Wartość"
|
value: "Wartość"
|
||||||
|
createdAt: "Utworzono"
|
||||||
|
updatedAt: "Zaktualizowano"
|
||||||
|
saveConfirm: "Zapisać zmiany?"
|
||||||
|
deleteConfirm: "Na pewno usunąć?"
|
||||||
|
invalidValue: "Nieprawidłowa wartość."
|
||||||
|
registry: "Rejestr"
|
||||||
|
closeAccount: "Zamknij konto"
|
||||||
|
currentVersion: "Bieżąca wersja"
|
||||||
|
latestVersion: "Najnowsza wersja"
|
||||||
|
youAreRunningUpToDateClient: "Korzystasz z najnowszej wersji klienta."
|
||||||
|
newVersionOfClientAvailable: "Nowsza wersja klienta jest dostępna."
|
||||||
|
usageAmount: "Użycie"
|
||||||
|
capacity: "Pojemność"
|
||||||
|
inUse: "Użyto"
|
||||||
|
editCode: "Edytuj kod"
|
||||||
|
apply: "Zastosuj"
|
||||||
|
receiveAnnouncementFromInstance: "Otrzymuj powiadomienia e-mail z tej instancji"
|
||||||
|
emailNotification: "Powiadomienia e-mail"
|
||||||
|
publish: "Publikuj"
|
||||||
|
inChannelSearch: "Szukaj na kanale"
|
||||||
|
useReactionPickerForContextMenu: "Otwórz wybornik reakcji prawym kliknięciem"
|
||||||
|
typingUsers: "{users} pisze(-ą)..."
|
||||||
|
jumpToSpecifiedDate: "Przejdź do określonej daty"
|
||||||
|
showingPastTimeline: "Obecnie wyświetla starą oś czasu"
|
||||||
|
clear: "Wróć"
|
||||||
|
markAllAsRead: "Oznacz wszystkie jako przeczytane"
|
||||||
goBack: "Wróć"
|
goBack: "Wróć"
|
||||||
|
unlikeConfirm: "Na pewno chcesz usunąć polubienie?"
|
||||||
|
fullView: "Pełny widok"
|
||||||
|
quitFullView: "Opuść pełny widok"
|
||||||
|
addDescription: "Dodaj opis"
|
||||||
info: "Informacje"
|
info: "Informacje"
|
||||||
|
userInfo: "Informacje o użykowniku"
|
||||||
|
unknown: "Nieznane"
|
||||||
|
onlineStatus: "Status online"
|
||||||
|
hideOnlineStatus: "Ukryj status online"
|
||||||
|
hideOnlineStatusDescription: "Ukrywanie statusu online ogranicza wygody niektórych funkcji, tj. wyszukiwanie"
|
||||||
|
online: "Online"
|
||||||
|
active: "Aktywny"
|
||||||
|
offline: "Offline"
|
||||||
|
notRecommended: "Nie zalecane"
|
||||||
|
botProtection: "Zabezpieczenie przed botami"
|
||||||
|
instanceBlocking: "Zablokowane instancje"
|
||||||
|
selectAccount: "Wybierz konto"
|
||||||
|
enabled: "Właczono"
|
||||||
|
disabled: "Wyłączono"
|
||||||
|
quickAction: "Szybkie działania"
|
||||||
user: "Użytkownicy"
|
user: "Użytkownicy"
|
||||||
administration: "Zarządzanie"
|
administration: "Zarządzanie"
|
||||||
|
accounts: "Konta"
|
||||||
|
switch: "Przełącz"
|
||||||
|
noMaintainerInformationWarning: "Informacje o administratorze nie są skonfigurowane."
|
||||||
|
noBotProtectionWarning: "Zabezpieczenie przed botami nie jest skonfigurowane."
|
||||||
|
configure: "Skonfiguruj"
|
||||||
|
postToGallery: "Opublikuj w galerii"
|
||||||
|
gallery: "Galeria"
|
||||||
|
recentPosts: "Ostatnie wpisy"
|
||||||
|
popularPosts: "Popularne wpisy"
|
||||||
|
shareWithNote: "Udostępnij z wpisem"
|
||||||
|
ads: "Reklamy"
|
||||||
expiration: "Ankieta kończy się"
|
expiration: "Ankieta kończy się"
|
||||||
|
memo: "Notatki"
|
||||||
|
priority: "Priorytet"
|
||||||
|
high: "Wysoki"
|
||||||
middle: "Średnie"
|
middle: "Średnie"
|
||||||
|
low: "Niski"
|
||||||
|
emailNotConfiguredWarning: "Nie podano adresu e-mail"
|
||||||
|
ratio: "Stosunek"
|
||||||
|
_ad:
|
||||||
|
back: "Wróć"
|
||||||
|
reduceFrequencyOfThisAd: "Pokazuj tę reklamę rzadziej"
|
||||||
|
_forgotPassword:
|
||||||
|
ifNoEmail: "Jeżeli nie podano adresu e-mail podczas rejestracji, skontaktuj się z administratorem zamiast tego."
|
||||||
|
contactAdmin: "Jeżeli Twoja instancja nie obsługuje adresów e-mail, skontaktuj się zamiast tego z administratorem, aby zresetować hasło."
|
||||||
_gallery:
|
_gallery:
|
||||||
|
my: "Moja galeria"
|
||||||
|
liked: "Polubione wpisy"
|
||||||
|
like: "Polub"
|
||||||
unlike: "Cofnij polubienie"
|
unlike: "Cofnij polubienie"
|
||||||
_email:
|
_email:
|
||||||
_follow:
|
_follow:
|
||||||
title: "Zaobserwował(a) Cię"
|
title: "Zaobserwował(a) Cię"
|
||||||
|
_receiveFollowRequest:
|
||||||
|
title: "Otrzymano prośbę o możliwość obserwacji"
|
||||||
|
_plugin:
|
||||||
|
install: "Zainstaluj wtyczki"
|
||||||
|
installWarn: "Nie instaluj niezaufanych wtyczek."
|
||||||
|
manage: "Zarządzanie wtyczkami"
|
||||||
_registry:
|
_registry:
|
||||||
|
scope: "Zakres"
|
||||||
key: "Klucz"
|
key: "Klucz"
|
||||||
keys: "Klucz"
|
keys: "Klucz"
|
||||||
|
domain: "Domena"
|
||||||
createKey: "Utwórz klucz"
|
createKey: "Utwórz klucz"
|
||||||
_aboutMisskey:
|
_aboutMisskey:
|
||||||
about: "Misskey jest oprogramowanie open source rozwijanym przez syuilo od 2014."
|
about: "Misskey jest oprogramowanie open source rozwijanym przez syuilo od 2014."
|
||||||
|
@ -706,6 +806,7 @@ _mfm:
|
||||||
x4Description: "Czyni treść jeszcze większą niż jeszcze większa."
|
x4Description: "Czyni treść jeszcze większą niż jeszcze większa."
|
||||||
blur: "Rozmycie"
|
blur: "Rozmycie"
|
||||||
font: "Czcionka"
|
font: "Czcionka"
|
||||||
|
fontDescription: "Wybiera czcionkę do wyświetlania treści."
|
||||||
_reversi:
|
_reversi:
|
||||||
reversi: "Reversi"
|
reversi: "Reversi"
|
||||||
gameSettings: "Ustawienia gry"
|
gameSettings: "Ustawienia gry"
|
||||||
|
@ -970,16 +1071,21 @@ _profile:
|
||||||
username: "Nazwa użytkownika"
|
username: "Nazwa użytkownika"
|
||||||
description: "Opis"
|
description: "Opis"
|
||||||
youCanIncludeHashtags: "Możesz umieścić hashtagi w swoim opisie."
|
youCanIncludeHashtags: "Możesz umieścić hashtagi w swoim opisie."
|
||||||
|
metadata: "Dodatkowe informacje"
|
||||||
|
metadataEdit: "Edytuj dodatkowe informacje"
|
||||||
|
metadataDescription: "Możesz wyświetlać do czterech sekcji dodatkowych informacji na swoim profilu."
|
||||||
metadataLabel: "Etykieta"
|
metadataLabel: "Etykieta"
|
||||||
metadataContent: "Treść"
|
metadataContent: "Treść"
|
||||||
changeAvatar: "Zmień awatar"
|
changeAvatar: "Zmień awatar"
|
||||||
changeBanner: "Zmień baner"
|
changeBanner: "Zmień baner"
|
||||||
_exportOrImport:
|
_exportOrImport:
|
||||||
|
allNotes: "Wszystkie wpisy"
|
||||||
followingList: "Obserwowani"
|
followingList: "Obserwowani"
|
||||||
muteList: "Wycisz"
|
muteList: "Wycisz"
|
||||||
blockingList: "Zablokuj"
|
blockingList: "Zablokuj"
|
||||||
userLists: "Listy"
|
userLists: "Listy"
|
||||||
_charts:
|
_charts:
|
||||||
|
federationInstancesTotal: "Łącznie sfederowanych instancji"
|
||||||
usersTotal: "Łącznie # użytkowników"
|
usersTotal: "Łącznie # użytkowników"
|
||||||
activeUsers: "Aktywni użytkownicy"
|
activeUsers: "Aktywni użytkownicy"
|
||||||
_instanceCharts:
|
_instanceCharts:
|
||||||
|
@ -994,6 +1100,7 @@ _instanceCharts:
|
||||||
_timelines:
|
_timelines:
|
||||||
home: "Strona główna"
|
home: "Strona główna"
|
||||||
local: "Lokalne"
|
local: "Lokalne"
|
||||||
|
social: "Społeczność"
|
||||||
global: "Globalna"
|
global: "Globalna"
|
||||||
_rooms:
|
_rooms:
|
||||||
roomOf: "Pokój {user}"
|
roomOf: "Pokój {user}"
|
||||||
|
@ -1364,6 +1471,8 @@ _deck:
|
||||||
swapRight: "Przesuń w prawo"
|
swapRight: "Przesuń w prawo"
|
||||||
swapUp: "Zamień z powyższym"
|
swapUp: "Zamień z powyższym"
|
||||||
swapDown: "Zamień z poniższym"
|
swapDown: "Zamień z poniższym"
|
||||||
|
stackLeft: "Przypnij do lewej"
|
||||||
|
popRight: "Odepnij w prawo"
|
||||||
profile: "Profil"
|
profile: "Profil"
|
||||||
_columns:
|
_columns:
|
||||||
main: "Główna"
|
main: "Główna"
|
||||||
|
|
|
@ -749,6 +749,8 @@ popularPosts: "Популярные публикации"
|
||||||
shareWithNote: "Поделиться заметкой"
|
shareWithNote: "Поделиться заметкой"
|
||||||
expiration: "Опрос длится"
|
expiration: "Опрос длится"
|
||||||
middle: "Средне"
|
middle: "Средне"
|
||||||
|
_ad:
|
||||||
|
back: "Выход"
|
||||||
_gallery:
|
_gallery:
|
||||||
my: "Личная"
|
my: "Личная"
|
||||||
liked: "Понравившееся"
|
liked: "Понравившееся"
|
||||||
|
|
|
@ -691,6 +691,8 @@ user: "Користувачі"
|
||||||
administration: "Управління"
|
administration: "Управління"
|
||||||
expiration: "Опитування закінчується"
|
expiration: "Опитування закінчується"
|
||||||
middle: "Середній"
|
middle: "Середній"
|
||||||
|
_ad:
|
||||||
|
back: "Назад"
|
||||||
_gallery:
|
_gallery:
|
||||||
unlike: "Не вподобати"
|
unlike: "Не вподобати"
|
||||||
_email:
|
_email:
|
||||||
|
|
|
@ -7,6 +7,7 @@ search: "搜索"
|
||||||
notifications: "通知"
|
notifications: "通知"
|
||||||
username: "用户名"
|
username: "用户名"
|
||||||
password: "密码"
|
password: "密码"
|
||||||
|
forgotPassword: "忘记密码"
|
||||||
fetchingAsApObject: "联合查询中"
|
fetchingAsApObject: "联合查询中"
|
||||||
ok: "OK"
|
ok: "OK"
|
||||||
gotIt: "我明白了"
|
gotIt: "我明白了"
|
||||||
|
@ -173,7 +174,7 @@ metadata: "元数据"
|
||||||
withNFiles: "{n}个文件"
|
withNFiles: "{n}个文件"
|
||||||
monitor: "监视器"
|
monitor: "监视器"
|
||||||
jobQueue: "作业队列"
|
jobQueue: "作业队列"
|
||||||
cpuAndMemory: "CPU使用量"
|
cpuAndMemory: "CPU和内存"
|
||||||
network: "网络"
|
network: "网络"
|
||||||
disk: "存储"
|
disk: "存储"
|
||||||
instanceInfo: "实例信息"
|
instanceInfo: "实例信息"
|
||||||
|
@ -747,8 +748,22 @@ gallery: "图库"
|
||||||
recentPosts: "最新发布"
|
recentPosts: "最新发布"
|
||||||
popularPosts: "热门投稿"
|
popularPosts: "热门投稿"
|
||||||
shareWithNote: "在帖子中分享"
|
shareWithNote: "在帖子中分享"
|
||||||
|
ads: "广告"
|
||||||
expiration: "截止时间"
|
expiration: "截止时间"
|
||||||
|
memo: "便笺"
|
||||||
|
priority: "优先级"
|
||||||
|
high: "高"
|
||||||
middle: "中"
|
middle: "中"
|
||||||
|
low: "低"
|
||||||
|
emailNotConfiguredWarning: "电子邮件地址未设置。"
|
||||||
|
ratio: "比率"
|
||||||
|
_ad:
|
||||||
|
back: "返回"
|
||||||
|
reduceFrequencyOfThisAd: "减少此广告的频率"
|
||||||
|
_forgotPassword:
|
||||||
|
enterEmail: "请输入您用来注册帐户的电子邮件地址。密码重置链接将发送到该地址。"
|
||||||
|
ifNoEmail: "如果您没有使用电子邮件地址注册,请联系管理员。"
|
||||||
|
contactAdmin: "该实例不支持电子邮件。如果您想重设密码,请联系管理员。"
|
||||||
_gallery:
|
_gallery:
|
||||||
my: "我的图库"
|
my: "我的图库"
|
||||||
liked: "喜欢的图片"
|
liked: "喜欢的图片"
|
||||||
|
|
|
@ -733,6 +733,8 @@ noBotProtectionWarning: "尚未設定Bot防護。"
|
||||||
configure: "設定"
|
configure: "設定"
|
||||||
expiration: "期限"
|
expiration: "期限"
|
||||||
middle: "中"
|
middle: "中"
|
||||||
|
_ad:
|
||||||
|
back: "返回"
|
||||||
_gallery:
|
_gallery:
|
||||||
unlike: "收回喜歡"
|
unlike: "收回喜歡"
|
||||||
_email:
|
_email:
|
||||||
|
|
14
migration/1620364649428-ad2.ts
Normal file
14
migration/1620364649428-ad2.ts
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
import {MigrationInterface, QueryRunner} from "typeorm";
|
||||||
|
|
||||||
|
export class ad21620364649428 implements MigrationInterface {
|
||||||
|
name = 'ad21620364649428'
|
||||||
|
|
||||||
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(`ALTER TABLE "ad" ADD "ratio" integer NOT NULL DEFAULT '1'`);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(`ALTER TABLE "ad" DROP COLUMN "ratio"`);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
16
migration/1621479946000-add-note-indexes.ts
Normal file
16
migration/1621479946000-add-note-indexes.ts
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
import {MigrationInterface, QueryRunner} from "typeorm";
|
||||||
|
|
||||||
|
export class addNoteIndexes1621479946000 implements MigrationInterface {
|
||||||
|
name = 'addNoteIndexes1621479946000'
|
||||||
|
|
||||||
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(`CREATE INDEX "IDX_NOTE_MENTIONS" ON "note" USING gin ("mentions")`, undefined);
|
||||||
|
await queryRunner.query(`CREATE INDEX "IDX_NOTE_VISIBLE_USER_IDS" ON "note" USING gin ("visibleUserIds")`, undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(`DROP INDEX "IDX_NOTE_MENTIONS"`, undefined);
|
||||||
|
await queryRunner.query(`DROP INDEX "IDX_NOTE_VISIBLE_USER_IDS"`, undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
74
package.json
74
package.json
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "misskey",
|
"name": "misskey",
|
||||||
"author": "syuilo <syuilotan@yahoo.co.jp>",
|
"author": "syuilo <syuilotan@yahoo.co.jp>",
|
||||||
"version": "12.80.3",
|
"version": "12.81.2",
|
||||||
"codename": "indigo",
|
"codename": "indigo",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
@ -36,7 +36,7 @@
|
||||||
"lodash": "^4.17.20"
|
"lodash": "^4.17.20"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/plugin-transform-runtime": "7.13.15",
|
"@babel/plugin-transform-runtime": "7.14.3",
|
||||||
"@elastic/elasticsearch": "7.11.0",
|
"@elastic/elasticsearch": "7.11.0",
|
||||||
"@koa/cors": "3.1.0",
|
"@koa/cors": "3.1.0",
|
||||||
"@koa/multer": "3.0.0",
|
"@koa/multer": "3.0.0",
|
||||||
|
@ -47,13 +47,13 @@
|
||||||
"@syuilo/aiscript": "0.11.1",
|
"@syuilo/aiscript": "0.11.1",
|
||||||
"@types/bcryptjs": "2.4.2",
|
"@types/bcryptjs": "2.4.2",
|
||||||
"@types/bull": "3.15.1",
|
"@types/bull": "3.15.1",
|
||||||
"@types/cbor": "5.0.1",
|
"@types/cbor": "6.0.0",
|
||||||
"@types/dateformat": "3.0.1",
|
"@types/dateformat": "3.0.1",
|
||||||
"@types/escape-regexp": "0.0.0",
|
"@types/escape-regexp": "0.0.0",
|
||||||
"@types/glob": "7.1.3",
|
"@types/glob": "7.1.3",
|
||||||
"@types/gulp": "4.0.8",
|
"@types/gulp": "4.0.8",
|
||||||
"@types/gulp-rename": "2.0.0",
|
"@types/gulp-rename": "2.0.0",
|
||||||
"@types/is-url": "1.2.28",
|
"@types/is-url": "1.2.29",
|
||||||
"@types/js-yaml": "4.0.1",
|
"@types/js-yaml": "4.0.1",
|
||||||
"@types/jsdom": "16.2.10",
|
"@types/jsdom": "16.2.10",
|
||||||
"@types/jsonld": "1.5.5",
|
"@types/jsonld": "1.5.5",
|
||||||
|
@ -70,9 +70,9 @@
|
||||||
"@types/koa__multer": "2.0.2",
|
"@types/koa__multer": "2.0.2",
|
||||||
"@types/koa__router": "8.0.4",
|
"@types/koa__router": "8.0.4",
|
||||||
"@types/markdown-it": "12.0.1",
|
"@types/markdown-it": "12.0.1",
|
||||||
"@types/matter-js": "0.14.11",
|
"@types/matter-js": "0.14.12",
|
||||||
"@types/mocha": "8.2.2",
|
"@types/mocha": "8.2.2",
|
||||||
"@types/node": "14.14.41",
|
"@types/node": "15.3.1",
|
||||||
"@types/node-fetch": "2.5.10",
|
"@types/node-fetch": "2.5.10",
|
||||||
"@types/nodemailer": "6.4.1",
|
"@types/nodemailer": "6.4.1",
|
||||||
"@types/nprogress": "0.2.0",
|
"@types/nprogress": "0.2.0",
|
||||||
|
@ -86,11 +86,11 @@
|
||||||
"@types/random-seed": "0.3.3",
|
"@types/random-seed": "0.3.3",
|
||||||
"@types/ratelimiter": "3.4.1",
|
"@types/ratelimiter": "3.4.1",
|
||||||
"@types/redis": "2.8.28",
|
"@types/redis": "2.8.28",
|
||||||
"@types/rename": "1.0.2",
|
"@types/rename": "1.0.3",
|
||||||
"@types/request-stats": "3.0.0",
|
"@types/request-stats": "3.0.0",
|
||||||
"@types/rimraf": "3.0.0",
|
"@types/rimraf": "3.0.0",
|
||||||
"@types/seedrandom": "2.4.28",
|
"@types/seedrandom": "2.4.28",
|
||||||
"@types/sharp": "0.28.0",
|
"@types/sharp": "0.28.1",
|
||||||
"@types/sinonjs__fake-timers": "6.0.2",
|
"@types/sinonjs__fake-timers": "6.0.2",
|
||||||
"@types/speakeasy": "2.0.5",
|
"@types/speakeasy": "2.0.5",
|
||||||
"@types/throttle-debounce": "2.1.0",
|
"@types/throttle-debounce": "2.1.0",
|
||||||
|
@ -101,46 +101,46 @@
|
||||||
"@types/webpack": "5.28.0",
|
"@types/webpack": "5.28.0",
|
||||||
"@types/webpack-stream": "3.2.12",
|
"@types/webpack-stream": "3.2.12",
|
||||||
"@types/websocket": "1.0.2",
|
"@types/websocket": "1.0.2",
|
||||||
"@types/ws": "7.4.1",
|
"@types/ws": "7.4.4",
|
||||||
"@typescript-eslint/parser": "4.22.0",
|
"@typescript-eslint/parser": "4.24.0",
|
||||||
"@vue/compiler-sfc": "3.0.11",
|
"@vue/compiler-sfc": "3.0.11",
|
||||||
"abort-controller": "3.0.0",
|
"abort-controller": "3.0.0",
|
||||||
"apexcharts": "3.26.1",
|
"apexcharts": "3.26.3",
|
||||||
"autobind-decorator": "2.4.0",
|
"autobind-decorator": "2.4.0",
|
||||||
"autosize": "4.0.2",
|
"autosize": "4.0.4",
|
||||||
"autwh": "0.1.0",
|
"autwh": "0.1.0",
|
||||||
"aws-sdk": "2.892.0",
|
"aws-sdk": "2.910.0",
|
||||||
"bcryptjs": "2.4.3",
|
"bcryptjs": "2.4.3",
|
||||||
"blurhash": "1.1.3",
|
"blurhash": "1.1.3",
|
||||||
"broadcast-channel": "3.5.3",
|
"broadcast-channel": "3.6.0",
|
||||||
"bull": "3.22.3",
|
"bull": "3.22.6",
|
||||||
"cafy": "15.2.1",
|
"cafy": "15.2.1",
|
||||||
"cbor": "7.0.5",
|
"cbor": "7.0.5",
|
||||||
"chalk": "4.1.1",
|
"chalk": "4.1.1",
|
||||||
"chart.js": "2.9.4",
|
"chart.js": "2.9.4",
|
||||||
"cli-highlight": "2.1.11",
|
"cli-highlight": "2.1.11",
|
||||||
"commander": "7.2.0",
|
"commander": "7.2.0",
|
||||||
"concurrently": "6.0.2",
|
"concurrently": "6.1.0",
|
||||||
"content-disposition": "0.5.3",
|
"content-disposition": "0.5.3",
|
||||||
"core-js": "3.11.0",
|
"core-js": "3.12.1",
|
||||||
"crc-32": "1.2.0",
|
"crc-32": "1.2.0",
|
||||||
"css-loader": "5.2.4",
|
"css-loader": "5.2.4",
|
||||||
"cssnano": "5.0.1",
|
"cssnano": "5.0.3",
|
||||||
"dateformat": "4.5.1",
|
"dateformat": "4.5.1",
|
||||||
"diskusage": "1.1.3",
|
"diskusage": "1.1.3",
|
||||||
"escape-regexp": "0.0.1",
|
"escape-regexp": "0.0.1",
|
||||||
"eslint": "7.25.0",
|
"eslint": "7.26.0",
|
||||||
"eslint-plugin-vue": "7.9.0",
|
"eslint-plugin-vue": "7.9.0",
|
||||||
"eventemitter3": "4.0.7",
|
"eventemitter3": "4.0.7",
|
||||||
"feed": "4.2.2",
|
"feed": "4.2.2",
|
||||||
"file-type": "16.3.0",
|
"file-type": "16.4.0",
|
||||||
"fluent-ffmpeg": "2.1.2",
|
"fluent-ffmpeg": "2.1.2",
|
||||||
"glob": "7.1.6",
|
"glob": "7.1.7",
|
||||||
"got": "11.8.2",
|
"got": "11.8.2",
|
||||||
"gulp": "4.0.2",
|
"gulp": "4.0.2",
|
||||||
"gulp-cssnano": "2.1.3",
|
"gulp-cssnano": "2.1.3",
|
||||||
"gulp-rename": "2.0.0",
|
"gulp-rename": "2.0.0",
|
||||||
"gulp-replace": "1.1.1",
|
"gulp-replace": "1.1.3",
|
||||||
"gulp-terser": "2.0.1",
|
"gulp-terser": "2.0.1",
|
||||||
"gulp-tslint": "8.1.4",
|
"gulp-tslint": "8.1.4",
|
||||||
"hard-source-webpack-plugin": "0.13.1",
|
"hard-source-webpack-plugin": "0.13.1",
|
||||||
|
@ -158,7 +158,7 @@
|
||||||
"json5-loader": "4.0.1",
|
"json5-loader": "4.0.1",
|
||||||
"jsonld": "4.0.1",
|
"jsonld": "4.0.1",
|
||||||
"jsrsasign": "8.0.20",
|
"jsrsasign": "8.0.20",
|
||||||
"katex": "0.13.3",
|
"katex": "0.13.11",
|
||||||
"koa": "2.13.1",
|
"koa": "2.13.1",
|
||||||
"koa-bodyparser": "4.3.0",
|
"koa-bodyparser": "4.3.0",
|
||||||
"koa-favicon": "2.1.0",
|
"koa-favicon": "2.1.0",
|
||||||
|
@ -173,21 +173,21 @@
|
||||||
"markdown-it": "12.0.6",
|
"markdown-it": "12.0.6",
|
||||||
"markdown-it-anchor": "7.1.0",
|
"markdown-it-anchor": "7.1.0",
|
||||||
"matter-js": "0.17.1",
|
"matter-js": "0.17.1",
|
||||||
"mfm-js": "0.16.3",
|
"mfm-js": "0.16.4",
|
||||||
"mocha": "8.3.2",
|
"mocha": "8.4.0",
|
||||||
"moji": "0.5.1",
|
"moji": "0.5.1",
|
||||||
"ms": "2.1.3",
|
"ms": "2.1.3",
|
||||||
"multer": "1.4.2",
|
"multer": "1.4.2",
|
||||||
"nested-property": "4.0.0",
|
"nested-property": "4.0.0",
|
||||||
"node-fetch": "2.6.1",
|
"node-fetch": "2.6.1",
|
||||||
"nodemailer": "6.5.0",
|
"nodemailer": "6.6.0",
|
||||||
"object-assign-deep": "0.4.0",
|
"object-assign-deep": "0.4.0",
|
||||||
"os-utils": "0.0.14",
|
"os-utils": "0.0.14",
|
||||||
"parse5": "6.0.1",
|
"parse5": "6.0.1",
|
||||||
"pg": "8.6.0",
|
"pg": "8.6.0",
|
||||||
"portscanner": "2.2.0",
|
"portscanner": "2.2.0",
|
||||||
"postcss": "8.2.12",
|
"postcss": "8.2.15",
|
||||||
"postcss-loader": "5.2.0",
|
"postcss-loader": "5.3.0",
|
||||||
"prismjs": "1.23.0",
|
"prismjs": "1.23.0",
|
||||||
"probe-image-size": "7.1.0",
|
"probe-image-size": "7.1.0",
|
||||||
"promise-limit": "2.7.0",
|
"promise-limit": "2.7.0",
|
||||||
|
@ -198,7 +198,7 @@
|
||||||
"qrcode": "1.4.4",
|
"qrcode": "1.4.4",
|
||||||
"random-seed": "0.3.0",
|
"random-seed": "0.3.0",
|
||||||
"ratelimiter": "3.4.1",
|
"ratelimiter": "3.4.1",
|
||||||
"re2": "1.15.9",
|
"re2": "1.16.0",
|
||||||
"reconnecting-websocket": "4.4.0",
|
"reconnecting-websocket": "4.4.0",
|
||||||
"redis": "3.1.2",
|
"redis": "3.1.2",
|
||||||
"redis-lock": "0.1.4",
|
"redis-lock": "0.1.4",
|
||||||
|
@ -210,25 +210,25 @@
|
||||||
"rimraf": "3.0.2",
|
"rimraf": "3.0.2",
|
||||||
"rndstr": "1.0.0",
|
"rndstr": "1.0.0",
|
||||||
"s-age": "1.1.2",
|
"s-age": "1.1.2",
|
||||||
"sass": "1.32.11",
|
"sass": "1.32.13",
|
||||||
"sass-loader": "11.0.1",
|
"sass-loader": "11.1.1",
|
||||||
"seedrandom": "3.0.5",
|
"seedrandom": "3.0.5",
|
||||||
"sharp": "0.28.1",
|
"sharp": "0.28.2",
|
||||||
"speakeasy": "2.0.0",
|
"speakeasy": "2.0.0",
|
||||||
"stringz": "2.1.0",
|
"stringz": "2.1.0",
|
||||||
"style-loader": "2.0.0",
|
"style-loader": "2.0.0",
|
||||||
"summaly": "2.4.0",
|
"summaly": "2.4.0",
|
||||||
"syslog-pro": "1.0.0",
|
"syslog-pro": "1.0.0",
|
||||||
"systeminformation": "5.6.12",
|
"systeminformation": "5.6.22",
|
||||||
"syuilo-password-strength": "0.0.1",
|
"syuilo-password-strength": "0.0.1",
|
||||||
"textarea-caret": "3.1.0",
|
"textarea-caret": "3.1.0",
|
||||||
"three": "0.117.1",
|
"three": "0.117.1",
|
||||||
"throttle-debounce": "3.0.1",
|
"throttle-debounce": "3.0.1",
|
||||||
"tinycolor2": "1.4.2",
|
"tinycolor2": "1.4.2",
|
||||||
"tmp": "0.2.1",
|
"tmp": "0.2.1",
|
||||||
"ts-loader": "9.1.1",
|
"ts-loader": "9.2.1",
|
||||||
"ts-node": "9.1.1",
|
"ts-node": "9.1.1",
|
||||||
"tsc-alias": "1.2.10",
|
"tsc-alias": "1.2.11",
|
||||||
"tsconfig-paths": "3.9.0",
|
"tsconfig-paths": "3.9.0",
|
||||||
"tslint": "6.1.3",
|
"tslint": "6.1.3",
|
||||||
"tslint-sonarts": "1.9.0",
|
"tslint-sonarts": "1.9.0",
|
||||||
|
@ -248,8 +248,8 @@
|
||||||
"vue-svg-loader": "0.17.0-beta.2",
|
"vue-svg-loader": "0.17.0-beta.2",
|
||||||
"vuedraggable": "4.0.1",
|
"vuedraggable": "4.0.1",
|
||||||
"web-push": "3.4.4",
|
"web-push": "3.4.4",
|
||||||
"webpack": "5.35.1",
|
"webpack": "5.37.1",
|
||||||
"webpack-cli": "4.6.0",
|
"webpack-cli": "4.7.0",
|
||||||
"websocket": "1.0.34",
|
"websocket": "1.0.34",
|
||||||
"ws": "7.4.5",
|
"ws": "7.4.5",
|
||||||
"xev": "2.0.1"
|
"xev": "2.0.1"
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<MkModal ref="modal" :manual-showing="manualShowing" :src="src" @click="$refs.modal.close()" @opening="opening" @close="$emit('close')" @closed="$emit('closed')">
|
<MkModal ref="modal" :manual-showing="manualShowing" :src="src" :front="true" @click="$refs.modal.close()" @opening="opening" @close="$emit('close')" @closed="$emit('closed')">
|
||||||
<MkEmojiPicker :show-pinned="showPinned" :as-reaction-picker="asReactionPicker" @chosen="chosen" ref="picker"/>
|
<MkEmojiPicker :show-pinned="showPinned" :as-reaction-picker="asReactionPicker" @chosen="chosen" ref="picker"/>
|
||||||
</MkModal>
|
</MkModal>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -9,8 +9,9 @@
|
||||||
<div class="menu" v-else>
|
<div class="menu" v-else>
|
||||||
<div class="body">
|
<div class="body">
|
||||||
<div>Ads by {{ host }}</div>
|
<div>Ads by {{ host }}</div>
|
||||||
<!--<MkButton>{{ $ts.stopThisAd }}</MkButton>-->
|
<!--<MkButton class="button" primary>{{ $ts._ad.like }}</MkButton>-->
|
||||||
<button class="_textButton" @click="toggleMenu">{{ $ts.close }}</button>
|
<MkButton v-if="ad.ratio !== 0" class="button" @click="reduceFrequency">{{ $ts._ad.reduceFrequencyOfThisAd }}</MkButton>
|
||||||
|
<button class="_textButton" @click="toggleMenu">{{ $ts._ad.back }}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -19,9 +20,11 @@
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, ref } from 'vue';
|
import { defineComponent, ref } from 'vue';
|
||||||
import { instance } from '@client/instance';
|
import { Instance, instance } from '@client/instance';
|
||||||
import { host } from '@client/config';
|
import { host } from '@client/config';
|
||||||
import MkButton from '@client/components/ui/button.vue';
|
import MkButton from '@client/components/ui/button.vue';
|
||||||
|
import { defaultStore } from '@client/store';
|
||||||
|
import * as os from '@client/os';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
|
@ -45,35 +48,65 @@ export default defineComponent({
|
||||||
showMenu.value = !showMenu.value;
|
showMenu.value = !showMenu.value;
|
||||||
};
|
};
|
||||||
|
|
||||||
let ad = null;
|
const choseAd = (): Instance['ads'][number] | null => {
|
||||||
|
if (props.specify) {
|
||||||
|
return props.specify as Instance['ads'][number];
|
||||||
|
}
|
||||||
|
|
||||||
if (props.specify) {
|
const allAds = instance.ads.map(ad => defaultStore.state.mutedAds.includes(ad.id) ? {
|
||||||
ad = props.specify;
|
...ad,
|
||||||
} else {
|
ratio: 0
|
||||||
let ads = instance.ads.filter(ad => props.prefer.includes(ad.place));
|
} : ad);
|
||||||
|
|
||||||
|
let ads = allAds.filter(ad => props.prefer.includes(ad.place));
|
||||||
|
|
||||||
if (ads.length === 0) {
|
if (ads.length === 0) {
|
||||||
ads = instance.ads.filter(ad => ad.place === 'square');
|
ads = allAds.filter(ad => ad.place === 'square');
|
||||||
}
|
}
|
||||||
|
|
||||||
const high = ads.filter(ad => ad.priority === 'high');
|
const lowPriorityAds = ads.filter(ad => ad.ratio === 0);
|
||||||
const middle = ads.filter(ad => ad.priority === 'middle');
|
ads = ads.filter(ad => ad.ratio !== 0);
|
||||||
const low = ads.filter(ad => ad.priority === 'low');
|
|
||||||
|
|
||||||
if (high.length > 0) {
|
if (ads.length === 0) {
|
||||||
ad = high[Math.floor(Math.random() * high.length)];
|
if (lowPriorityAds.length !== 0) {
|
||||||
} else if (middle.length > 0) {
|
return lowPriorityAds[Math.floor(Math.random() * lowPriorityAds.length)];
|
||||||
ad = middle[Math.floor(Math.random() * middle.length)];
|
} else {
|
||||||
} else if (low.length > 0) {
|
return null;
|
||||||
ad = low[Math.floor(Math.random() * low.length)];
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
const totalFactor = ads.reduce((a, b) => a + b.ratio, 0);
|
||||||
|
const r = Math.random() * totalFactor;
|
||||||
|
|
||||||
|
let stackedFactor = 0;
|
||||||
|
for (const ad of ads) {
|
||||||
|
if (r >= stackedFactor && r <= stackedFactor + ad.ratio) {
|
||||||
|
return ad;
|
||||||
|
} else {
|
||||||
|
stackedFactor += ad.ratio;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const chosen = ref(choseAd());
|
||||||
|
|
||||||
|
const reduceFrequency = () => {
|
||||||
|
if (chosen.value == null) return;
|
||||||
|
if (defaultStore.state.mutedAds.includes(chosen.value.id)) return;
|
||||||
|
defaultStore.push('mutedAds', chosen.value.id);
|
||||||
|
os.success();
|
||||||
|
chosen.value = choseAd();
|
||||||
|
showMenu.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
ad,
|
ad: chosen,
|
||||||
showMenu,
|
showMenu,
|
||||||
toggleMenu,
|
toggleMenu,
|
||||||
host,
|
host,
|
||||||
|
reduceFrequency,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -157,6 +190,10 @@ export default defineComponent({
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
max-width: 400px;
|
max-width: 400px;
|
||||||
border: solid 1px var(--divider);
|
border: solid 1px var(--divider);
|
||||||
|
|
||||||
|
> .button {
|
||||||
|
margin: 8px auto;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,6 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="gqnyydlz" :style="{ background: color }" v-else>
|
<div class="gqnyydlz" :style="{ background: color }" v-else>
|
||||||
<i class="fas fa-eye-slash" @click="hide = true"></i>
|
|
||||||
<a
|
<a
|
||||||
:href="image.url"
|
:href="image.url"
|
||||||
:title="image.name"
|
:title="image.name"
|
||||||
|
@ -18,6 +17,7 @@
|
||||||
<ImgWithBlurhash :hash="image.blurhash" :src="url" :alt="image.name" :title="image.name" :cover="false"/>
|
<ImgWithBlurhash :hash="image.blurhash" :src="url" :alt="image.name" :title="image.name" :cover="false"/>
|
||||||
<div class="gif" v-if="image.type === 'image/gif'">GIF</div>
|
<div class="gif" v-if="image.type === 'image/gif'">GIF</div>
|
||||||
</a>
|
</a>
|
||||||
|
<i class="fas fa-eye-slash" @click="hide = true"></i>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<transition :name="$store.state.animation ? popup ? 'modal-popup' : 'modal' : ''" :duration="$store.state.animation ? popup ? 500 : 300 : 0" appear @after-leave="onClosed" @enter="$emit('opening')" @after-enter="childRendered">
|
<transition :name="$store.state.animation ? popup ? 'modal-popup' : 'modal' : ''" :duration="$store.state.animation ? popup ? 500 : 300 : 0" appear @after-leave="onClosed" @enter="$emit('opening')" @after-enter="childRendered">
|
||||||
<div v-show="manualShowing != null ? manualShowing : showing" class="mk-modal" v-hotkey.global="keymap" :style="{ pointerEvents: (manualShowing != null ? manualShowing : showing) ? 'auto' : 'none', '--transformOrigin': transformOrigin }">
|
<div v-show="manualShowing != null ? manualShowing : showing" class="qzhlnise" :class="{ front }" v-hotkey.global="keymap" :style="{ pointerEvents: (manualShowing != null ? manualShowing : showing) ? 'auto' : 'none', '--transformOrigin': transformOrigin }">
|
||||||
<div class="bg _modalBg" @click="onBgClick" @contextmenu.prevent.stop="() => {}"></div>
|
<div class="bg _modalBg" @click="onBgClick" @contextmenu.prevent.stop="() => {}"></div>
|
||||||
<div class="content" :class="{ popup, fixed, top: position === 'top' }" @click.self="onBgClick" ref="content">
|
<div class="content" :class="{ popup, fixed, top: position === 'top' }" @click.self="onBgClick" ref="content">
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
|
@ -41,6 +41,11 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
position: {
|
position: {
|
||||||
required: false
|
required: false
|
||||||
|
},
|
||||||
|
front: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: false,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
emits: ['opening', 'click', 'esc', 'close', 'closed'],
|
emits: ['opening', 'click', 'esc', 'close', 'closed'],
|
||||||
|
@ -224,7 +229,7 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.mk-modal {
|
.qzhlnise {
|
||||||
> .bg {
|
> .bg {
|
||||||
z-index: 10000;
|
z-index: 10000;
|
||||||
}
|
}
|
||||||
|
@ -269,5 +274,19 @@ export default defineComponent({
|
||||||
position: fixed;
|
position: fixed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.front {
|
||||||
|
> .bg {
|
||||||
|
z-index: 20000;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .content:not(.popup) {
|
||||||
|
z-index: 20000;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .content.popup {
|
||||||
|
z-index: 20000;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -3,10 +3,17 @@ import { api } from './os';
|
||||||
|
|
||||||
// TODO: 他のタブと永続化されたstateを同期
|
// TODO: 他のタブと永続化されたstateを同期
|
||||||
|
|
||||||
type Instance = {
|
export type Instance = {
|
||||||
emojis: {
|
emojis: {
|
||||||
category: string;
|
category: string;
|
||||||
}[];
|
}[];
|
||||||
|
ads: {
|
||||||
|
id: string;
|
||||||
|
ratio: number;
|
||||||
|
place: string;
|
||||||
|
url: string;
|
||||||
|
imageUrl: string;
|
||||||
|
}[];
|
||||||
};
|
};
|
||||||
|
|
||||||
const data = localStorage.getItem('instance');
|
const data = localStorage.getItem('instance');
|
||||||
|
|
|
@ -15,12 +15,17 @@
|
||||||
<MkRadio v-model="ad.place" value="horizontal">horizontal</MkRadio>
|
<MkRadio v-model="ad.place" value="horizontal">horizontal</MkRadio>
|
||||||
<MkRadio v-model="ad.place" value="horizontal-big">horizontal-big</MkRadio>
|
<MkRadio v-model="ad.place" value="horizontal-big">horizontal-big</MkRadio>
|
||||||
</div>
|
</div>
|
||||||
|
<!--
|
||||||
<div style="margin: 32px 0;">
|
<div style="margin: 32px 0;">
|
||||||
{{ $ts.priority }}
|
{{ $ts.priority }}
|
||||||
<MkRadio v-model="ad.priority" value="high">{{ $ts.high }}</MkRadio>
|
<MkRadio v-model="ad.priority" value="high">{{ $ts.high }}</MkRadio>
|
||||||
<MkRadio v-model="ad.priority" value="middle">{{ $ts.middle }}</MkRadio>
|
<MkRadio v-model="ad.priority" value="middle">{{ $ts.middle }}</MkRadio>
|
||||||
<MkRadio v-model="ad.priority" value="low">{{ $ts.low }}</MkRadio>
|
<MkRadio v-model="ad.priority" value="low">{{ $ts.low }}</MkRadio>
|
||||||
</div>
|
</div>
|
||||||
|
-->
|
||||||
|
<MkInput v-model:value="ad.ratio" type="number">
|
||||||
|
<span>{{ $ts.ratio }}</span>
|
||||||
|
</MkInput>
|
||||||
<MkInput v-model:value="ad.expiresAt" type="date">
|
<MkInput v-model:value="ad.expiresAt" type="date">
|
||||||
<span>{{ $ts.expiration }}</span>
|
<span>{{ $ts.expiration }}</span>
|
||||||
</MkInput>
|
</MkInput>
|
||||||
|
@ -82,6 +87,7 @@ export default defineComponent({
|
||||||
memo: '',
|
memo: '',
|
||||||
place: 'square',
|
place: 'square',
|
||||||
priority: 'middle',
|
priority: 'middle',
|
||||||
|
ratio: 1,
|
||||||
url: '',
|
url: '',
|
||||||
imageUrl: null,
|
imageUrl: null,
|
||||||
expiresAt: null,
|
expiresAt: null,
|
||||||
|
|
|
@ -43,6 +43,7 @@
|
||||||
<FormGroup>
|
<FormGroup>
|
||||||
<template #label>{{ $ts.info }}</template>
|
<template #label>{{ $ts.info }}</template>
|
||||||
<FormLink :active="page === 'database'" replace to="/instance/database"><template #icon><i class="fas fa-database"></i></template>{{ $ts.database }}</FormLink>
|
<FormLink :active="page === 'database'" replace to="/instance/database"><template #icon><i class="fas fa-database"></i></template>{{ $ts.database }}</FormLink>
|
||||||
|
<FormLink :active="page === 'logs'" replace to="/instance/logs"><template #icon><i class="fas fa-stream"></i></template>{{ $ts.logs }}</FormLink>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
</FormBase>
|
</FormBase>
|
||||||
</div>
|
</div>
|
||||||
|
@ -105,6 +106,7 @@ export default defineComponent({
|
||||||
case 'announcements': return defineAsyncComponent(() => import('./announcements.vue'));
|
case 'announcements': return defineAsyncComponent(() => import('./announcements.vue'));
|
||||||
case 'ads': return defineAsyncComponent(() => import('./ads.vue'));
|
case 'ads': return defineAsyncComponent(() => import('./ads.vue'));
|
||||||
case 'database': return defineAsyncComponent(() => import('./database.vue'));
|
case 'database': return defineAsyncComponent(() => import('./database.vue'));
|
||||||
|
case 'logs': return defineAsyncComponent(() => import('./logs.vue'));
|
||||||
case 'abuses': return defineAsyncComponent(() => import('./abuses.vue'));
|
case 'abuses': return defineAsyncComponent(() => import('./abuses.vue'));
|
||||||
case 'settings': return defineAsyncComponent(() => import('./settings.vue'));
|
case 'settings': return defineAsyncComponent(() => import('./settings.vue'));
|
||||||
case 'files-settings': return defineAsyncComponent(() => import('./files-settings.vue'));
|
case 'files-settings': return defineAsyncComponent(() => import('./files-settings.vue'));
|
||||||
|
|
|
@ -123,7 +123,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent } from 'vue';
|
import { defineComponent, markRaw } from 'vue';
|
||||||
import Chart from 'chart.js';
|
import Chart from 'chart.js';
|
||||||
import XModalWindow from '@client/components/ui/modal-window.vue';
|
import XModalWindow from '@client/components/ui/modal-window.vue';
|
||||||
import MkUsersDialog from '@client/components/users-dialog.vue';
|
import MkUsersDialog from '@client/components/users-dialog.vue';
|
||||||
|
@ -280,7 +280,7 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
|
|
||||||
Chart.defaults.global.defaultFontColor = getComputedStyle(document.documentElement).getPropertyValue('--fg');
|
Chart.defaults.global.defaultFontColor = getComputedStyle(document.documentElement).getPropertyValue('--fg');
|
||||||
this.chartInstance = new Chart(this.canvas, {
|
this.chartInstance = markRaw(new Chart(this.canvas, {
|
||||||
type: 'line',
|
type: 'line',
|
||||||
data: {
|
data: {
|
||||||
labels: new Array(chartLimit).fill(0).map((_, i) => this.getDate(i).toLocaleString()).slice().reverse(),
|
labels: new Array(chartLimit).fill(0).map((_, i) => this.getDate(i).toLocaleString()).slice().reverse(),
|
||||||
|
@ -331,7 +331,7 @@ export default defineComponent({
|
||||||
mode: 'index',
|
mode: 'index',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
}));
|
||||||
},
|
},
|
||||||
|
|
||||||
getDate(ago: number) {
|
getDate(ago: number) {
|
||||||
|
|
|
@ -5,13 +5,13 @@
|
||||||
<span>{{ $ts.domain }}</span>
|
<span>{{ $ts.domain }}</span>
|
||||||
</MkInput>
|
</MkInput>
|
||||||
<MkSelect v-model:value="logLevel">
|
<MkSelect v-model:value="logLevel">
|
||||||
<template #label>{{ $ts.level }}</template>
|
<template #label>Level</template>
|
||||||
<option value="all">{{ $ts.levels.all }}</option>
|
<option value="all">All</option>
|
||||||
<option value="info">{{ $ts.levels.info }}</option>
|
<option value="info">Info</option>
|
||||||
<option value="success">{{ $ts.levels.success }}</option>
|
<option value="success">Success</option>
|
||||||
<option value="warning">{{ $ts.levels.warning }}</option>
|
<option value="warning">Warning</option>
|
||||||
<option value="error">{{ $ts.levels.error }}</option>
|
<option value="error">Error</option>
|
||||||
<option value="debug">{{ $ts.levels.debug }}</option>
|
<option value="debug">Debug</option>
|
||||||
</MkSelect>
|
</MkSelect>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -45,6 +45,8 @@ export default defineComponent({
|
||||||
MkTextarea,
|
MkTextarea,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
emits: ['info'],
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
[symbols.PAGE_INFO]: {
|
[symbols.PAGE_INFO]: {
|
||||||
|
@ -72,6 +74,10 @@ export default defineComponent({
|
||||||
this.fetchLogs();
|
this.fetchLogs();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
this.$emit('info', this[symbols.PAGE_INFO]);
|
||||||
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
fetchLogs() {
|
fetchLogs() {
|
||||||
os.api('admin/logs', {
|
os.api('admin/logs', {
|
||||||
|
|
|
@ -27,7 +27,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent } from 'vue';
|
import { defineComponent, markRaw } from 'vue';
|
||||||
import Chart from 'chart.js';
|
import Chart from 'chart.js';
|
||||||
import number from '../../filters/number';
|
import number from '../../filters/number';
|
||||||
|
|
||||||
|
@ -69,7 +69,7 @@ export default defineComponent({
|
||||||
|
|
||||||
Chart.defaults.global.defaultFontColor = getComputedStyle(document.documentElement).getPropertyValue('--fg');
|
Chart.defaults.global.defaultFontColor = getComputedStyle(document.documentElement).getPropertyValue('--fg');
|
||||||
|
|
||||||
this.chart = new Chart(this.$refs.chart, {
|
this.chart = markRaw(new Chart(this.$refs.chart, {
|
||||||
type: 'line',
|
type: 'line',
|
||||||
data: {
|
data: {
|
||||||
labels: [],
|
labels: [],
|
||||||
|
@ -152,7 +152,7 @@ export default defineComponent({
|
||||||
mode: 'index',
|
mode: 'index',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
}));
|
||||||
|
|
||||||
this.connection.on('stats', this.onStats);
|
this.connection.on('stats', this.onStats);
|
||||||
this.connection.on('statsLog', this.onStatsLog);
|
this.connection.on('statsLog', this.onStatsLog);
|
||||||
|
|
|
@ -55,6 +55,10 @@ export const defaultStore = markRaw(new Storage('base', {
|
||||||
where: 'account',
|
where: 'account',
|
||||||
default: []
|
default: []
|
||||||
},
|
},
|
||||||
|
mutedAds: {
|
||||||
|
where: 'account',
|
||||||
|
default: [] as string[]
|
||||||
|
},
|
||||||
|
|
||||||
menu: {
|
menu: {
|
||||||
where: 'deviceAccount',
|
where: 'deviceAccount',
|
||||||
|
|
|
@ -64,11 +64,8 @@ export default defineComponent({
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.wtdtxvec {
|
.wtdtxvec {
|
||||||
--margin: 8px;
|
--margin: 8px;
|
||||||
|
--panelShadow: none;
|
||||||
|
|
||||||
padding: 0 var(--margin);
|
padding: 0 var(--margin);
|
||||||
|
|
||||||
::v-deep(._panel) {
|
|
||||||
box-shadow: none;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -114,6 +114,7 @@ export default defineComponent({
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
font-size: 0.9em;
|
font-size: 0.9em;
|
||||||
color: var(--fg);
|
color: var(--fg);
|
||||||
|
padding-right: 8px;
|
||||||
|
|
||||||
> .a {
|
> .a {
|
||||||
display: block;
|
display: block;
|
||||||
|
@ -129,6 +130,9 @@ export default defineComponent({
|
||||||
font-size: 75%;
|
font-size: 75%;
|
||||||
opacity: 0.7;
|
opacity: 0.7;
|
||||||
line-height: $bodyInfoHieght;
|
line-height: $bodyInfoHieght;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# AiScript
|
# AiScript
|
||||||
|
|
||||||
## 関数
|
## Fungsi
|
||||||
デフォルトで値渡しです。
|
Secara bawaan, berjalan sebagai pass dari value.
|
||||||
|
|
|
@ -1,19 +1,19 @@
|
||||||
# Misskey API
|
# Misskey API
|
||||||
|
|
||||||
MisskeyAPIを使ってMisskeyクライアント、Misskey連携Webサービス、Bot等(以下「アプリケーション」と呼びます)を開発できます。 ストリーミングAPIもあるので、リアルタイム性のあるアプリケーションを作ることも可能です。
|
Dengan menggunakan Misskey API kamu dapat mengembangkan klien Misskey, Webservice yang terintegrasi dengan Misskey, Bots (nantinya akan disebut "Aplikasi" disini), dll. Terdapat juga streaming API, yang memungkinan untuk membuat aplikasi real-time.
|
||||||
|
|
||||||
APIを使い始めるには、まずアクセストークンを取得する必要があります。 このドキュメントでは、アクセストークンを取得する手順を説明した後、基本的なAPIの使い方を説明します。
|
Untuk memulai menggunakin API, kamu harus memiliki access token terlebih dahulu. Halaman ini akan menjelaskan bagaimana untuk mendapatkan access token dan menjelaskan instruksi penggunaan dasar dari Misskey API.
|
||||||
|
|
||||||
## アクセストークンの取得
|
## Mendapatkan access token
|
||||||
基本的に、APIはリクエストにはアクセストークンが必要となります。 APIにリクエストするのが自分自身なのか、不特定の利用者に使ってもらうアプリケーションなのかによって取得手順は異なります。
|
Pada dasarnya, semua request API membutuhkan access token. Metode untuk mendapatkan sebuah access token bermacam-macam bergantung pada kamu sendiri yang mengirim request API atau request tersebut dikirim melalui aplikasi yang dipakai oleh pengguna akhir.
|
||||||
|
|
||||||
* 前者の場合: [「自分自身のアクセストークンを手動発行する」](#自分自身のアクセストークンを手動発行する)に進む
|
* Apabila kamu pengguna lama: Langsung saja menuju [ "Menerbitkan access token untuk akun kamu sendiri secara manual" ](#自分自身のアクセストークンを手動発行する)
|
||||||
* 後者の場合: [「アプリケーション利用者にアクセストークンの発行をリクエストする」](#アプリケーション利用者にアクセストークンの発行をリクエストする)に進む
|
* Apabila kamu pengguna baru: Langsung saja menuju [ "Meminta aplikasi pengguna untuk menghasilkan access token" ](#アプリケーション利用者にアクセストークンの発行をリクエストする)
|
||||||
|
|
||||||
### 自分自身のアクセストークンを手動発行する
|
### Menerbitkan access token untuk akun kamu sendiri secara manual
|
||||||
「設定 > API」で、自分のアクセストークンを発行できます。
|
Kamu dapat membuat access token untuk akun milikmu di Pengaturan > API.
|
||||||
|
|
||||||
[「APIの使い方」へ進む](#APIの使い方)
|
[Lanjutkan untuk menggunakan API.](#APIの使い方)
|
||||||
|
|
||||||
### アプリケーション利用者にアクセストークンの発行をリクエストする
|
### アプリケーション利用者にアクセストークンの発行をリクエストする
|
||||||
アプリケーション利用者のアクセストークンを取得するには、以下の手順で発行をリクエストします。
|
アプリケーション利用者のアクセストークンを取得するには、以下の手順で発行をリクエストします。
|
||||||
|
@ -49,7 +49,7 @@ UUIDを生成する。以後これをセッションIDと呼びます。
|
||||||
* `token` ... ユーザーのアクセストークン
|
* `token` ... ユーザーのアクセストークン
|
||||||
* `user` ... ユーザーの情報
|
* `user` ... ユーザーの情報
|
||||||
|
|
||||||
[「APIの使い方」へ進む](#APIの使い方)
|
[Lanjutkan untuk menggunakan API.](#APIの使い方)
|
||||||
|
|
||||||
## APIの使い方
|
## APIの使い方
|
||||||
**APIはすべてPOSTで、リクエスト/レスポンスともにJSON形式です。RESTではありません。** アクセストークンは、`i`というパラメータ名でリクエストに含めます。
|
**APIはすべてPOSTで、リクエスト/レスポンスともにJSON形式です。RESTではありません。** アクセストークンは、`i`というパラメータ名でリクエストに含めます。
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# プラグインの作成
|
# プラグインの作成
|
||||||
Misskey Webクライアントのプラグイン機能を使うと、クライアントを拡張し、様々な機能を追加できます。 ここではプラグインの作成にあたってのメタデータ定義や、AiScript APIリファレンスを掲載します。
|
Misskey Webクライアントのプラグイン機能を使うと、クライアントを拡張し、様々な機能を追加できます。 ここではプラグインの作成にあたってのメタデータ定義や、AiScript APIリファレンスを掲載します。
|
||||||
|
|
||||||
## メタデータ
|
## Metadata
|
||||||
プラグインは、AiScriptのメタデータ埋め込み機能を使って、デフォルトとしてプラグインのメタデータを定義する必要があります。 メタデータは次のプロパティを含むオブジェクトです。
|
プラグインは、AiScriptのメタデータ埋め込み機能を使って、デフォルトとしてプラグインのメタデータを定義する必要があります。 メタデータは次のプロパティを含むオブジェクトです。
|
||||||
|
|
||||||
### name
|
### name
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
# Emoji kustom
|
# Emoji kustom
|
||||||
カスタム絵文字は、インスタンスで用意された画像を絵文字のように使える機能です。 ノート、リアクション、チャット、自己紹介、名前などの場所で使うことができます。 カスタム絵文字をそれらの場所で使うには、絵文字ピッカーボタン(ある場合)を押すか、`:`を入力して絵文字サジェストを表示します。 テキスト内に`:foo:`のような形式の文字列が見つかると、`foo`の部分がカスタム絵文字名と解釈され、表示時には対応したカスタム絵文字に置き換わります。
|
Emoji kustom merupakan fungsi yang menyediakan gambar terunggah ke server untuk digunakan seperti emoji. Emoji kustom ini dapt digunakan pada note, reaksi, obrolan, profil, dan bahkan username serta tempat-tempat lainnya. Untuk menggunakan emoji kustom pada tempat yang disebutkan di atas, tekan tombol pemilih Emoji (bila ada) atau ketik sebuah `:` dan jendela saran emoji akan muncul. Jika sebuah string yang terlihat seperti `:foo:` ditemukan dalam text apapun, maka sebagian dari `foo` diinterpretaskan sebagai nama emoji kustom dan akan digantikan dengan gambar emoji kustom ketika ditampilkan.
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# デッキ
|
# Dek
|
||||||
|
|
||||||
デッキは利用可能なUIのひとつです。「カラム」と呼ばれるビューを複数並べて表示させることで、カスタマイズ性が高く、情報量の多いUIが構築できることが特徴です。
|
デッキは利用可能なUIのひとつです。「カラム」と呼ばれるビューを複数並べて表示させることで、カスタマイズ性が高く、情報量の多いUIが構築できることが特徴です。
|
||||||
|
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
# Ikuti
|
# Ikuti
|
||||||
ユーザーをフォローすると、タイムラインにそのユーザーの投稿が表示されるようになります。ただし、他のユーザーに対する返信は含まれません。 ユーザーをフォローするには、ユーザーページの「フォロー」ボタンをクリックします。フォローを解除するには、もう一度クリックします。
|
Jika kamu mengikuti seorang pengguna, postingan dari pengguna tersebut akan muncul pada linimasa kamu.Akan tetapi, balasan dari mereka kepada pengguna lain tidak akan ditampilkan. Untuk mengikuti seorang pengguna, klik tombol "Ikuti" pada halaman pengguna mereka.Untuk berhenti mengikuti seorang pengguna, klik tombol tersebut sekali lagi.
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# キーボードショートカット
|
# キーボードショートカット
|
||||||
|
|
||||||
## グローバル
|
## Global
|
||||||
これらのショートカットは基本的にどこでも使えます。
|
これらのショートカットは基本的にどこでも使えます。
|
||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# Pages
|
# Pages
|
||||||
|
|
||||||
## 変数
|
## Variabel
|
||||||
変数を使うことで動的なページを作成できます。テキスト内で <b>{ 変数名 }</b> と書くとそこに変数の値を埋め込めます。例えば <b>Hello { thing } world!</b> というテキストで、変数(thing)の値が <b>ai</b> だった場合、テキストは <b>Hello ai world!</b> になります。
|
変数を使うことで動的なページを作成できます。テキスト内で <b>{ 変数名 }</b> と書くとそこに変数の値を埋め込めます。例えば <b>Hello { thing } world!</b> というテキストで、変数(thing)の値が <b>ai</b> だった場合、テキストは <b>Hello ai world!</b> になります。
|
||||||
|
|
||||||
変数の評価(値を算出すること)は上から下に行われるので、ある変数の中で自分より下の変数を参照することはできません。例えば上から <b>A、B、C</b> と3つの変数を定義したとき、<b>C</b>の中で<b>A</b>や<b>B</b>を参照することはできますが、<b>A</b>の中で<b>B</b>や<b>C</b>を参照することはできません。
|
変数の評価(値を算出すること)は上から下に行われるので、ある変数の中で自分より下の変数を参照することはできません。例えば上から <b>A、B、C</b> と3つの変数を定義したとき、<b>C</b>の中で<b>A</b>や<b>B</b>を参照することはできますが、<b>A</b>の中で<b>B</b>や<b>C</b>を参照することはできません。
|
||||||
|
|
|
@ -110,7 +110,7 @@ y = Math.floor(pos / mapWidth)
|
||||||
```
|
```
|
||||||
|
|
||||||
### フォームコントロールの種類
|
### フォームコントロールの種類
|
||||||
#### スイッチ
|
#### Beralih
|
||||||
type: `switch` スイッチを表示します。何かの機能をオン/オフさせたい場合に有用です。
|
type: `switch` スイッチを表示します。何かの機能をオン/オフさせたい場合に有用です。
|
||||||
|
|
||||||
##### プロパティ
|
##### プロパティ
|
||||||
|
|
|
@ -31,7 +31,7 @@
|
||||||
|
|
||||||
**ストリームでのやり取りはすべてJSONです。**
|
**ストリームでのやり取りはすべてJSONです。**
|
||||||
|
|
||||||
## チャンネル
|
## Kanal
|
||||||
MisskeyのストリーミングAPIにはチャンネルという概念があります。これは、送受信する情報を分離するための仕組みです。 Misskeyのストリームに接続しただけでは、まだリアルタイムでタイムラインの投稿を受信したりはできません。 ストリーム上でチャンネルに接続することで、様々な情報を受け取ったり情報を送信したりすることができるようになります。
|
MisskeyのストリーミングAPIにはチャンネルという概念があります。これは、送受信する情報を分離するための仕組みです。 Misskeyのストリームに接続しただけでは、まだリアルタイムでタイムラインの投稿を受信したりはできません。 ストリーム上でチャンネルに接続することで、様々な情報を受け取ったり情報を送信したりすることができるようになります。
|
||||||
|
|
||||||
### チャンネルに接続する
|
### チャンネルに接続する
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# テーマ
|
# Tema
|
||||||
|
|
||||||
テーマを設定して、Misskeyクライアントの見た目を変更できます。
|
テーマを設定して、Misskeyクライアントの見た目を変更できます。
|
||||||
|
|
||||||
|
@ -61,8 +61,8 @@
|
||||||
* 関数(後述)
|
* 関数(後述)
|
||||||
* `:{関数名}<{引数}<{色}`
|
* `:{関数名}<{引数}<{色}`
|
||||||
|
|
||||||
#### 定数
|
#### Konstanta
|
||||||
「CSS変数として出力はしたくないが、他のCSS変数の値として使いまわしたい」値があるときは、定数を使うと便利です。 キー名を`$`で始めると、そのキーはCSS変数として出力されません。
|
「CSS変数として出力はしたくないが、他のCSS変数の値として使いまわしたい」値があるときは、定数を使うと便利です。 キー名を`$`で始めると、そのキーはCSS変数として出力されません。
|
||||||
|
|
||||||
#### 関数
|
#### Fungsi
|
||||||
wip
|
wip
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
# タイムラインの比較
|
# Perbandingan Linimasa
|
||||||
|
|
||||||
https://docs.google.com/spreadsheets/d/1lxQ2ugKrhz58Bg96HTDK_2F98BUritkMyIiBkOByjHA/edit?usp=sharing
|
https://docs.google.com/spreadsheets/d/1lxQ2ugKrhz58Bg96HTDK_2F98BUritkMyIiBkOByjHA/edit?usp=sharing
|
||||||
|
|
||||||
## ホーム
|
## Beranda
|
||||||
自分のフォローしているユーザーの投稿
|
Postingan dari pengguna yang kamu ikuti
|
||||||
|
|
||||||
## ローカル
|
## Lokal
|
||||||
全てのローカルユーザーの「ホーム」指定されていない投稿
|
Seluruh postingan dari pengguna lokal yang tidak ditandai sebagai "Hanya Beranda"
|
||||||
|
|
||||||
## ソーシャル
|
## Sosial
|
||||||
自分のフォローしているユーザーの投稿と、全てのローカルユーザーの「ホーム」指定されていない投稿
|
Postingan dari pengguna yang kamu ikuti beserta dengan semua postingan dari pengguna lokal yang tidak ditandai sebagai "Hanya Beranda"
|
||||||
|
|
||||||
## グローバル
|
## Global
|
||||||
全てのローカルユーザーの「ホーム」指定されていない投稿と、サーバーに届いた全てのリモートユーザーの「ホーム」指定されていない投稿
|
Seluruh postingan dari pengguna lokal yang tidak ditandai sebagai "Hanya Beranda" dan juga pesan yang diterima oleh server yang tidak ditandai sebagai "Hanya Beranda"
|
||||||
|
|
|
@ -8,7 +8,7 @@ Wpisy użytkowników, których obserwujesz
|
||||||
## Lokalne
|
## Lokalne
|
||||||
全てのローカルユーザーの「ホーム」指定されていない投稿
|
全てのローカルユーザーの「ホーム」指定されていない投稿
|
||||||
|
|
||||||
## ソーシャル
|
## Społeczność
|
||||||
自分のフォローしているユーザーの投稿と、全てのローカルユーザーの「ホーム」指定されていない投稿
|
自分のフォローしているユーザーの投稿と、全てのローカルユーザーの「ホーム」指定されていない投稿
|
||||||
|
|
||||||
## Globalne
|
## Globalne
|
||||||
|
|
|
@ -55,7 +55,8 @@ export async function getHtml(url: string, accept = 'text/html, */*', timeout =
|
||||||
const _http = new http.Agent({
|
const _http = new http.Agent({
|
||||||
keepAlive: true,
|
keepAlive: true,
|
||||||
keepAliveMsecs: 30 * 1000,
|
keepAliveMsecs: 30 * 1000,
|
||||||
});
|
lookup: cache.lookup,
|
||||||
|
} as http.AgentOptions);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get https non-proxy agent
|
* Get https non-proxy agent
|
||||||
|
@ -70,14 +71,14 @@ const _https = new https.Agent({
|
||||||
* Get http proxy or non-proxy agent
|
* Get http proxy or non-proxy agent
|
||||||
*/
|
*/
|
||||||
export const httpAgent = config.proxy
|
export const httpAgent = config.proxy
|
||||||
? new HttpProxyAgent(config.proxy) as unknown as http.Agent
|
? new HttpProxyAgent(config.proxy)
|
||||||
: _http;
|
: _http;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get https proxy or non-proxy agent
|
* Get https proxy or non-proxy agent
|
||||||
*/
|
*/
|
||||||
export const httpsAgent = config.proxy
|
export const httpsAgent = config.proxy
|
||||||
? new HttpsProxyAgent(config.proxy) as unknown as https.Agent
|
? new HttpsProxyAgent(config.proxy)
|
||||||
: _https;
|
: _https;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -23,11 +23,17 @@ export class Ad {
|
||||||
})
|
})
|
||||||
public place: string;
|
public place: string;
|
||||||
|
|
||||||
|
// 今は使われていないが将来的に活用される可能性はある
|
||||||
@Column('varchar', {
|
@Column('varchar', {
|
||||||
length: 32, nullable: false
|
length: 32, nullable: false
|
||||||
})
|
})
|
||||||
public priority: string;
|
public priority: string;
|
||||||
|
|
||||||
|
@Column('integer', {
|
||||||
|
default: 1, nullable: false
|
||||||
|
})
|
||||||
|
public ratio: number;
|
||||||
|
|
||||||
@Column('varchar', {
|
@Column('varchar', {
|
||||||
length: 1024, nullable: false
|
length: 1024, nullable: false
|
||||||
})
|
})
|
||||||
|
|
|
@ -7,6 +7,8 @@ import { Channel } from './channel';
|
||||||
|
|
||||||
@Entity()
|
@Entity()
|
||||||
@Index('IDX_NOTE_TAGS', { synchronize: false })
|
@Index('IDX_NOTE_TAGS', { synchronize: false })
|
||||||
|
@Index('IDX_NOTE_MENTIONS', { synchronize: false })
|
||||||
|
@Index('IDX_NOTE_VISIBLE_USER_IDS', { synchronize: false })
|
||||||
export class Note {
|
export class Note {
|
||||||
@PrimaryColumn(id())
|
@PrimaryColumn(id())
|
||||||
public id: string;
|
public id: string;
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import * as httpSignature from 'http-signature';
|
import * as httpSignature from 'http-signature';
|
||||||
|
|
||||||
import config from '@/config';
|
import config from '@/config';
|
||||||
import { User } from '../models/entities/user';
|
|
||||||
import { program } from '../argv';
|
import { program } from '../argv';
|
||||||
|
|
||||||
import processDeliver from './processors/deliver';
|
import processDeliver from './processors/deliver';
|
||||||
|
@ -11,14 +10,9 @@ import procesObjectStorage from './processors/object-storage';
|
||||||
import { queueLogger } from './logger';
|
import { queueLogger } from './logger';
|
||||||
import { DriveFile } from '../models/entities/drive-file';
|
import { DriveFile } from '../models/entities/drive-file';
|
||||||
import { getJobInfo } from './get-job-info';
|
import { getJobInfo } from './get-job-info';
|
||||||
import { IActivity } from '../remote/activitypub/type';
|
|
||||||
import { dbQueue, deliverQueue, inboxQueue, objectStorageQueue } from './queues';
|
import { dbQueue, deliverQueue, inboxQueue, objectStorageQueue } from './queues';
|
||||||
|
import { ThinUser } from './types';
|
||||||
export type InboxJobData = {
|
import { IActivity } from '@/remote/activitypub/type';
|
||||||
activity: IActivity,
|
|
||||||
/** HTTP-Signature */
|
|
||||||
signature: httpSignature.IParsedSignature
|
|
||||||
};
|
|
||||||
|
|
||||||
function renderError(e: Error): any {
|
function renderError(e: Error): any {
|
||||||
return {
|
return {
|
||||||
|
@ -65,8 +59,9 @@ objectStorageQueue
|
||||||
.on('error', (job: any, err: Error) => objectStorageLogger.error(`error ${err}`, { job, e: renderError(err) }))
|
.on('error', (job: any, err: Error) => objectStorageLogger.error(`error ${err}`, { job, e: renderError(err) }))
|
||||||
.on('stalled', (job) => objectStorageLogger.warn(`stalled id=${job.id}`));
|
.on('stalled', (job) => objectStorageLogger.warn(`stalled id=${job.id}`));
|
||||||
|
|
||||||
export function deliver(user: { id: User['id']; host: null; }, content: any, to: any) {
|
export function deliver(user: ThinUser, content: unknown, to: string | null) {
|
||||||
if (content == null) return null;
|
if (content == null) return null;
|
||||||
|
if (to == null) return null;
|
||||||
|
|
||||||
const data = {
|
const data = {
|
||||||
user,
|
user,
|
||||||
|
@ -76,6 +71,7 @@ export function deliver(user: { id: User['id']; host: null; }, content: any, to:
|
||||||
|
|
||||||
return deliverQueue.add(data, {
|
return deliverQueue.add(data, {
|
||||||
attempts: config.deliverJobMaxAttempts || 12,
|
attempts: config.deliverJobMaxAttempts || 12,
|
||||||
|
timeout: 1 * 60 * 1000, // 1min
|
||||||
backoff: {
|
backoff: {
|
||||||
type: 'exponential',
|
type: 'exponential',
|
||||||
delay: 60 * 1000
|
delay: 60 * 1000
|
||||||
|
@ -85,7 +81,7 @@ export function deliver(user: { id: User['id']; host: null; }, content: any, to:
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function inbox(activity: any, signature: httpSignature.IParsedSignature) {
|
export function inbox(activity: IActivity, signature: httpSignature.IParsedSignature) {
|
||||||
const data = {
|
const data = {
|
||||||
activity: activity,
|
activity: activity,
|
||||||
signature
|
signature
|
||||||
|
@ -93,6 +89,7 @@ export function inbox(activity: any, signature: httpSignature.IParsedSignature)
|
||||||
|
|
||||||
return inboxQueue.add(data, {
|
return inboxQueue.add(data, {
|
||||||
attempts: config.inboxJobMaxAttempts || 8,
|
attempts: config.inboxJobMaxAttempts || 8,
|
||||||
|
timeout: 5 * 60 * 1000, // 5min
|
||||||
backoff: {
|
backoff: {
|
||||||
type: 'exponential',
|
type: 'exponential',
|
||||||
delay: 60 * 1000
|
delay: 60 * 1000
|
||||||
|
@ -102,7 +99,7 @@ export function inbox(activity: any, signature: httpSignature.IParsedSignature)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createDeleteDriveFilesJob(user: { id: User['id'] }) {
|
export function createDeleteDriveFilesJob(user: ThinUser) {
|
||||||
return dbQueue.add('deleteDriveFiles', {
|
return dbQueue.add('deleteDriveFiles', {
|
||||||
user: user
|
user: user
|
||||||
}, {
|
}, {
|
||||||
|
@ -111,7 +108,7 @@ export function createDeleteDriveFilesJob(user: { id: User['id'] }) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createExportNotesJob(user: { id: User['id'] }) {
|
export function createExportNotesJob(user: ThinUser) {
|
||||||
return dbQueue.add('exportNotes', {
|
return dbQueue.add('exportNotes', {
|
||||||
user: user
|
user: user
|
||||||
}, {
|
}, {
|
||||||
|
@ -120,7 +117,7 @@ export function createExportNotesJob(user: { id: User['id'] }) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createExportFollowingJob(user: { id: User['id'] }) {
|
export function createExportFollowingJob(user: ThinUser) {
|
||||||
return dbQueue.add('exportFollowing', {
|
return dbQueue.add('exportFollowing', {
|
||||||
user: user
|
user: user
|
||||||
}, {
|
}, {
|
||||||
|
@ -129,7 +126,7 @@ export function createExportFollowingJob(user: { id: User['id'] }) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createExportMuteJob(user: { id: User['id'] }) {
|
export function createExportMuteJob(user: ThinUser) {
|
||||||
return dbQueue.add('exportMute', {
|
return dbQueue.add('exportMute', {
|
||||||
user: user
|
user: user
|
||||||
}, {
|
}, {
|
||||||
|
@ -138,7 +135,7 @@ export function createExportMuteJob(user: { id: User['id'] }) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createExportBlockingJob(user: { id: User['id'] }) {
|
export function createExportBlockingJob(user: ThinUser) {
|
||||||
return dbQueue.add('exportBlocking', {
|
return dbQueue.add('exportBlocking', {
|
||||||
user: user
|
user: user
|
||||||
}, {
|
}, {
|
||||||
|
@ -147,7 +144,7 @@ export function createExportBlockingJob(user: { id: User['id'] }) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createExportUserListsJob(user: { id: User['id'] }) {
|
export function createExportUserListsJob(user: ThinUser) {
|
||||||
return dbQueue.add('exportUserLists', {
|
return dbQueue.add('exportUserLists', {
|
||||||
user: user
|
user: user
|
||||||
}, {
|
}, {
|
||||||
|
@ -156,7 +153,7 @@ export function createExportUserListsJob(user: { id: User['id'] }) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createImportFollowingJob(user: { id: User['id'] }, fileId: DriveFile['id']) {
|
export function createImportFollowingJob(user: ThinUser, fileId: DriveFile['id']) {
|
||||||
return dbQueue.add('importFollowing', {
|
return dbQueue.add('importFollowing', {
|
||||||
user: user,
|
user: user,
|
||||||
fileId: fileId
|
fileId: fileId
|
||||||
|
@ -166,7 +163,7 @@ export function createImportFollowingJob(user: { id: User['id'] }, fileId: Drive
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createImportUserListsJob(user: { id: User['id'] }, fileId: DriveFile['id']) {
|
export function createImportUserListsJob(user: ThinUser, fileId: DriveFile['id']) {
|
||||||
return dbQueue.add('importUserLists', {
|
return dbQueue.add('importUserLists', {
|
||||||
user: user,
|
user: user,
|
||||||
fileId: fileId
|
fileId: fileId
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import * as Queue from 'bull';
|
import * as Bull from 'bull';
|
||||||
import config from '@/config';
|
import config from '@/config';
|
||||||
|
|
||||||
export function initialize(name: string, limitPerSec = -1) {
|
export function initialize<T>(name: string, limitPerSec = -1) {
|
||||||
return new Queue(name, {
|
return new Bull<T>(name, {
|
||||||
redis: {
|
redis: {
|
||||||
port: config.redis.port,
|
port: config.redis.port,
|
||||||
host: config.redis.host,
|
host: config.redis.host,
|
||||||
|
|
|
@ -4,10 +4,11 @@ import { queueLogger } from '../../logger';
|
||||||
import { deleteFileSync } from '../../../services/drive/delete-file';
|
import { deleteFileSync } from '../../../services/drive/delete-file';
|
||||||
import { Users, DriveFiles } from '../../../models';
|
import { Users, DriveFiles } from '../../../models';
|
||||||
import { MoreThan } from 'typeorm';
|
import { MoreThan } from 'typeorm';
|
||||||
|
import { DbUserJobData } from '@/queue/types';
|
||||||
|
|
||||||
const logger = queueLogger.createSubLogger('delete-drive-files');
|
const logger = queueLogger.createSubLogger('delete-drive-files');
|
||||||
|
|
||||||
export async function deleteDriveFiles(job: Bull.Job, done: any): Promise<void> {
|
export async function deleteDriveFiles(job: Bull.Job<DbUserJobData>, done: any): Promise<void> {
|
||||||
logger.info(`Deleting drive files of ${job.data.user.id} ...`);
|
logger.info(`Deleting drive files of ${job.data.user.id} ...`);
|
||||||
|
|
||||||
const user = await Users.findOne(job.data.user.id);
|
const user = await Users.findOne(job.data.user.id);
|
||||||
|
|
|
@ -8,10 +8,11 @@ import dateFormat = require('dateformat');
|
||||||
import { getFullApAccount } from '@/misc/convert-host';
|
import { getFullApAccount } from '@/misc/convert-host';
|
||||||
import { Users, Blockings } from '../../../models';
|
import { Users, Blockings } from '../../../models';
|
||||||
import { MoreThan } from 'typeorm';
|
import { MoreThan } from 'typeorm';
|
||||||
|
import { DbUserJobData } from '@/queue/types';
|
||||||
|
|
||||||
const logger = queueLogger.createSubLogger('export-blocking');
|
const logger = queueLogger.createSubLogger('export-blocking');
|
||||||
|
|
||||||
export async function exportBlocking(job: Bull.Job, done: any): Promise<void> {
|
export async function exportBlocking(job: Bull.Job<DbUserJobData>, done: any): Promise<void> {
|
||||||
logger.info(`Exporting blocking of ${job.data.user.id} ...`);
|
logger.info(`Exporting blocking of ${job.data.user.id} ...`);
|
||||||
|
|
||||||
const user = await Users.findOne(job.data.user.id);
|
const user = await Users.findOne(job.data.user.id);
|
||||||
|
@ -61,7 +62,7 @@ export async function exportBlocking(job: Bull.Job, done: any): Promise<void> {
|
||||||
}
|
}
|
||||||
|
|
||||||
const content = getFullApAccount(u.username, u.host);
|
const content = getFullApAccount(u.username, u.host);
|
||||||
await new Promise((res, rej) => {
|
await new Promise<void>((res, rej) => {
|
||||||
stream.write(content + '\n', err => {
|
stream.write(content + '\n', err => {
|
||||||
if (err) {
|
if (err) {
|
||||||
logger.error(err);
|
logger.error(err);
|
||||||
|
|
|
@ -8,10 +8,11 @@ import dateFormat = require('dateformat');
|
||||||
import { getFullApAccount } from '@/misc/convert-host';
|
import { getFullApAccount } from '@/misc/convert-host';
|
||||||
import { Users, Followings } from '../../../models';
|
import { Users, Followings } from '../../../models';
|
||||||
import { MoreThan } from 'typeorm';
|
import { MoreThan } from 'typeorm';
|
||||||
|
import { DbUserJobData } from '@/queue/types';
|
||||||
|
|
||||||
const logger = queueLogger.createSubLogger('export-following');
|
const logger = queueLogger.createSubLogger('export-following');
|
||||||
|
|
||||||
export async function exportFollowing(job: Bull.Job, done: any): Promise<void> {
|
export async function exportFollowing(job: Bull.Job<DbUserJobData>, done: any): Promise<void> {
|
||||||
logger.info(`Exporting following of ${job.data.user.id} ...`);
|
logger.info(`Exporting following of ${job.data.user.id} ...`);
|
||||||
|
|
||||||
const user = await Users.findOne(job.data.user.id);
|
const user = await Users.findOne(job.data.user.id);
|
||||||
|
@ -61,7 +62,7 @@ export async function exportFollowing(job: Bull.Job, done: any): Promise<void> {
|
||||||
}
|
}
|
||||||
|
|
||||||
const content = getFullApAccount(u.username, u.host);
|
const content = getFullApAccount(u.username, u.host);
|
||||||
await new Promise((res, rej) => {
|
await new Promise<void>((res, rej) => {
|
||||||
stream.write(content + '\n', err => {
|
stream.write(content + '\n', err => {
|
||||||
if (err) {
|
if (err) {
|
||||||
logger.error(err);
|
logger.error(err);
|
||||||
|
|
|
@ -8,10 +8,11 @@ import dateFormat = require('dateformat');
|
||||||
import { getFullApAccount } from '@/misc/convert-host';
|
import { getFullApAccount } from '@/misc/convert-host';
|
||||||
import { Users, Mutings } from '../../../models';
|
import { Users, Mutings } from '../../../models';
|
||||||
import { MoreThan } from 'typeorm';
|
import { MoreThan } from 'typeorm';
|
||||||
|
import { DbUserJobData } from '@/queue/types';
|
||||||
|
|
||||||
const logger = queueLogger.createSubLogger('export-mute');
|
const logger = queueLogger.createSubLogger('export-mute');
|
||||||
|
|
||||||
export async function exportMute(job: Bull.Job, done: any): Promise<void> {
|
export async function exportMute(job: Bull.Job<DbUserJobData>, done: any): Promise<void> {
|
||||||
logger.info(`Exporting mute of ${job.data.user.id} ...`);
|
logger.info(`Exporting mute of ${job.data.user.id} ...`);
|
||||||
|
|
||||||
const user = await Users.findOne(job.data.user.id);
|
const user = await Users.findOne(job.data.user.id);
|
||||||
|
@ -61,7 +62,7 @@ export async function exportMute(job: Bull.Job, done: any): Promise<void> {
|
||||||
}
|
}
|
||||||
|
|
||||||
const content = getFullApAccount(u.username, u.host);
|
const content = getFullApAccount(u.username, u.host);
|
||||||
await new Promise((res, rej) => {
|
await new Promise<void>((res, rej) => {
|
||||||
stream.write(content + '\n', err => {
|
stream.write(content + '\n', err => {
|
||||||
if (err) {
|
if (err) {
|
||||||
logger.error(err);
|
logger.error(err);
|
||||||
|
|
|
@ -9,10 +9,11 @@ import { Users, Notes, Polls } from '../../../models';
|
||||||
import { MoreThan } from 'typeorm';
|
import { MoreThan } from 'typeorm';
|
||||||
import { Note } from '../../../models/entities/note';
|
import { Note } from '../../../models/entities/note';
|
||||||
import { Poll } from '../../../models/entities/poll';
|
import { Poll } from '../../../models/entities/poll';
|
||||||
|
import { DbUserJobData } from '@/queue/types';
|
||||||
|
|
||||||
const logger = queueLogger.createSubLogger('export-notes');
|
const logger = queueLogger.createSubLogger('export-notes');
|
||||||
|
|
||||||
export async function exportNotes(job: Bull.Job, done: any): Promise<void> {
|
export async function exportNotes(job: Bull.Job<DbUserJobData>, done: any): Promise<void> {
|
||||||
logger.info(`Exporting notes of ${job.data.user.id} ...`);
|
logger.info(`Exporting notes of ${job.data.user.id} ...`);
|
||||||
|
|
||||||
const user = await Users.findOne(job.data.user.id);
|
const user = await Users.findOne(job.data.user.id);
|
||||||
|
@ -33,7 +34,7 @@ export async function exportNotes(job: Bull.Job, done: any): Promise<void> {
|
||||||
|
|
||||||
const stream = fs.createWriteStream(path, { flags: 'a' });
|
const stream = fs.createWriteStream(path, { flags: 'a' });
|
||||||
|
|
||||||
await new Promise((res, rej) => {
|
await new Promise<void>((res, rej) => {
|
||||||
stream.write('[', err => {
|
stream.write('[', err => {
|
||||||
if (err) {
|
if (err) {
|
||||||
logger.error(err);
|
logger.error(err);
|
||||||
|
@ -72,7 +73,7 @@ export async function exportNotes(job: Bull.Job, done: any): Promise<void> {
|
||||||
poll = await Polls.findOneOrFail({ noteId: note.id });
|
poll = await Polls.findOneOrFail({ noteId: note.id });
|
||||||
}
|
}
|
||||||
const content = JSON.stringify(serialize(note, poll));
|
const content = JSON.stringify(serialize(note, poll));
|
||||||
await new Promise((res, rej) => {
|
await new Promise<void>((res, rej) => {
|
||||||
stream.write(exportedNotesCount === 0 ? content : ',\n' + content, err => {
|
stream.write(exportedNotesCount === 0 ? content : ',\n' + content, err => {
|
||||||
if (err) {
|
if (err) {
|
||||||
logger.error(err);
|
logger.error(err);
|
||||||
|
@ -92,7 +93,7 @@ export async function exportNotes(job: Bull.Job, done: any): Promise<void> {
|
||||||
job.progress(exportedNotesCount / total);
|
job.progress(exportedNotesCount / total);
|
||||||
}
|
}
|
||||||
|
|
||||||
await new Promise((res, rej) => {
|
await new Promise<void>((res, rej) => {
|
||||||
stream.write(']', err => {
|
stream.write(']', err => {
|
||||||
if (err) {
|
if (err) {
|
||||||
logger.error(err);
|
logger.error(err);
|
||||||
|
|
|
@ -8,10 +8,11 @@ import dateFormat = require('dateformat');
|
||||||
import { getFullApAccount } from '@/misc/convert-host';
|
import { getFullApAccount } from '@/misc/convert-host';
|
||||||
import { Users, UserLists, UserListJoinings } from '../../../models';
|
import { Users, UserLists, UserListJoinings } from '../../../models';
|
||||||
import { In } from 'typeorm';
|
import { In } from 'typeorm';
|
||||||
|
import { DbUserJobData } from '@/queue/types';
|
||||||
|
|
||||||
const logger = queueLogger.createSubLogger('export-user-lists');
|
const logger = queueLogger.createSubLogger('export-user-lists');
|
||||||
|
|
||||||
export async function exportUserLists(job: Bull.Job, done: any): Promise<void> {
|
export async function exportUserLists(job: Bull.Job<DbUserJobData>, done: any): Promise<void> {
|
||||||
logger.info(`Exporting user lists of ${job.data.user.id} ...`);
|
logger.info(`Exporting user lists of ${job.data.user.id} ...`);
|
||||||
|
|
||||||
const user = await Users.findOne(job.data.user.id);
|
const user = await Users.findOne(job.data.user.id);
|
||||||
|
@ -45,7 +46,7 @@ export async function exportUserLists(job: Bull.Job, done: any): Promise<void> {
|
||||||
for (const u of users) {
|
for (const u of users) {
|
||||||
const acct = getFullApAccount(u.username, u.host);
|
const acct = getFullApAccount(u.username, u.host);
|
||||||
const content = `${list.name},${acct}`;
|
const content = `${list.name},${acct}`;
|
||||||
await new Promise((res, rej) => {
|
await new Promise<void>((res, rej) => {
|
||||||
stream.write(content + '\n', err => {
|
stream.write(content + '\n', err => {
|
||||||
if (err) {
|
if (err) {
|
||||||
logger.error(err);
|
logger.error(err);
|
||||||
|
|
|
@ -7,10 +7,11 @@ import { resolveUser } from '../../../remote/resolve-user';
|
||||||
import { downloadTextFile } from '@/misc/download-text-file';
|
import { downloadTextFile } from '@/misc/download-text-file';
|
||||||
import { isSelfHost, toPuny } from '@/misc/convert-host';
|
import { isSelfHost, toPuny } from '@/misc/convert-host';
|
||||||
import { Users, DriveFiles } from '../../../models';
|
import { Users, DriveFiles } from '../../../models';
|
||||||
|
import { DbUserImportJobData } from '@/queue/types';
|
||||||
|
|
||||||
const logger = queueLogger.createSubLogger('import-following');
|
const logger = queueLogger.createSubLogger('import-following');
|
||||||
|
|
||||||
export async function importFollowing(job: Bull.Job, done: any): Promise<void> {
|
export async function importFollowing(job: Bull.Job<DbUserImportJobData>, done: any): Promise<void> {
|
||||||
logger.info(`Importing following of ${job.data.user.id} ...`);
|
logger.info(`Importing following of ${job.data.user.id} ...`);
|
||||||
|
|
||||||
const user = await Users.findOne(job.data.user.id);
|
const user = await Users.findOne(job.data.user.id);
|
||||||
|
|
|
@ -8,10 +8,11 @@ import { downloadTextFile } from '@/misc/download-text-file';
|
||||||
import { isSelfHost, toPuny } from '@/misc/convert-host';
|
import { isSelfHost, toPuny } from '@/misc/convert-host';
|
||||||
import { DriveFiles, Users, UserLists, UserListJoinings } from '../../../models';
|
import { DriveFiles, Users, UserLists, UserListJoinings } from '../../../models';
|
||||||
import { genId } from '@/misc/gen-id';
|
import { genId } from '@/misc/gen-id';
|
||||||
|
import { DbUserImportJobData } from '@/queue/types';
|
||||||
|
|
||||||
const logger = queueLogger.createSubLogger('import-user-lists');
|
const logger = queueLogger.createSubLogger('import-user-lists');
|
||||||
|
|
||||||
export async function importUserLists(job: Bull.Job, done: any): Promise<void> {
|
export async function importUserLists(job: Bull.Job<DbUserImportJobData>, done: any): Promise<void> {
|
||||||
logger.info(`Importing user lists of ${job.data.user.id} ...`);
|
logger.info(`Importing user lists of ${job.data.user.id} ...`);
|
||||||
|
|
||||||
const user = await Users.findOne(job.data.user.id);
|
const user = await Users.findOne(job.data.user.id);
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import * as Bull from 'bull';
|
import * as Bull from 'bull';
|
||||||
|
import { DbJobData } from '@/queue/types';
|
||||||
import { deleteDriveFiles } from './delete-drive-files';
|
import { deleteDriveFiles } from './delete-drive-files';
|
||||||
import { exportNotes } from './export-notes';
|
import { exportNotes } from './export-notes';
|
||||||
import { exportFollowing } from './export-following';
|
import { exportFollowing } from './export-following';
|
||||||
|
@ -17,10 +18,10 @@ const jobs = {
|
||||||
exportUserLists,
|
exportUserLists,
|
||||||
importFollowing,
|
importFollowing,
|
||||||
importUserLists
|
importUserLists
|
||||||
} as any;
|
} as Record<string, Bull.ProcessCallbackFunction<DbJobData> | Bull.ProcessPromiseFunction<DbJobData>>;
|
||||||
|
|
||||||
export default function(dbQueue: Bull.Queue) {
|
export default function(dbQueue: Bull.Queue<DbJobData>) {
|
||||||
for (const [k, v] of Object.entries(jobs)) {
|
for (const [k, v] of Object.entries(jobs)) {
|
||||||
dbQueue.process(k, v as any);
|
dbQueue.process(k, v);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ import { fetchMeta } from '@/misc/fetch-meta';
|
||||||
import { toPuny } from '@/misc/convert-host';
|
import { toPuny } from '@/misc/convert-host';
|
||||||
import { Cache } from '@/misc/cache';
|
import { Cache } from '@/misc/cache';
|
||||||
import { Instance } from '../../models/entities/instance';
|
import { Instance } from '../../models/entities/instance';
|
||||||
|
import { DeliverJobData } from '../types';
|
||||||
|
|
||||||
const logger = new Logger('deliver');
|
const logger = new Logger('deliver');
|
||||||
|
|
||||||
|
@ -17,7 +18,7 @@ let latest: string | null = null;
|
||||||
|
|
||||||
const suspendedHostsCache = new Cache<Instance[]>(1000 * 60 * 60);
|
const suspendedHostsCache = new Cache<Instance[]>(1000 * 60 * 60);
|
||||||
|
|
||||||
export default async (job: Bull.Job) => {
|
export default async (job: Bull.Job<DeliverJobData>) => {
|
||||||
const { host } = new URL(job.data.to);
|
const { host } = new URL(job.data.to);
|
||||||
|
|
||||||
// ブロックしてたら中断
|
// ブロックしてたら中断
|
||||||
|
|
|
@ -10,7 +10,7 @@ import { fetchMeta } from '@/misc/fetch-meta';
|
||||||
import { toPuny, extractDbHost } from '@/misc/convert-host';
|
import { toPuny, extractDbHost } from '@/misc/convert-host';
|
||||||
import { getApId } from '../../remote/activitypub/type';
|
import { getApId } from '../../remote/activitypub/type';
|
||||||
import { fetchInstanceMetadata } from '../../services/fetch-instance-metadata';
|
import { fetchInstanceMetadata } from '../../services/fetch-instance-metadata';
|
||||||
import { InboxJobData } from '..';
|
import { InboxJobData } from '../types';
|
||||||
import DbResolver from '../../remote/activitypub/db-resolver';
|
import DbResolver from '../../remote/activitypub/db-resolver';
|
||||||
import { resolvePerson } from '../../remote/activitypub/models/person';
|
import { resolvePerson } from '../../remote/activitypub/models/person';
|
||||||
import { LdSignature } from '../../remote/activitypub/misc/ld-signature';
|
import { LdSignature } from '../../remote/activitypub/misc/ld-signature';
|
||||||
|
@ -23,7 +23,7 @@ export default async (job: Bull.Job<InboxJobData>): Promise<string> => {
|
||||||
const activity = job.data.activity;
|
const activity = job.data.activity;
|
||||||
|
|
||||||
//#region Log
|
//#region Log
|
||||||
const info = Object.assign({}, activity);
|
const info = Object.assign({}, activity) as any;
|
||||||
delete info['@context'];
|
delete info['@context'];
|
||||||
logger.debug(JSON.stringify(info, null, 2));
|
logger.debug(JSON.stringify(info, null, 2));
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
|
@ -7,7 +7,7 @@ import { MoreThan, Not, IsNull } from 'typeorm';
|
||||||
|
|
||||||
const logger = queueLogger.createSubLogger('clean-remote-files');
|
const logger = queueLogger.createSubLogger('clean-remote-files');
|
||||||
|
|
||||||
export default async function cleanRemoteFiles(job: Bull.Job, done: any): Promise<void> {
|
export default async function cleanRemoteFiles(job: Bull.Job<{}>, done: any): Promise<void> {
|
||||||
logger.info(`Deleting cached remote files...`);
|
logger.info(`Deleting cached remote files...`);
|
||||||
|
|
||||||
let deletedCount = 0;
|
let deletedCount = 0;
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
|
import { ObjectStorageFileJobData } from '@/queue/types';
|
||||||
import * as Bull from 'bull';
|
import * as Bull from 'bull';
|
||||||
import { deleteObjectStorageFile } from '../../../services/drive/delete-file';
|
import { deleteObjectStorageFile } from '../../../services/drive/delete-file';
|
||||||
|
|
||||||
export default async (job: Bull.Job) => {
|
export default async (job: Bull.Job<ObjectStorageFileJobData>) => {
|
||||||
const key: string = job.data.key;
|
const key: string = job.data.key;
|
||||||
|
|
||||||
await deleteObjectStorageFile(key);
|
await deleteObjectStorageFile(key);
|
||||||
|
|
|
@ -1,14 +1,15 @@
|
||||||
import * as Bull from 'bull';
|
import * as Bull from 'bull';
|
||||||
|
import { ObjectStorageJobData } from '@/queue/types';
|
||||||
import deleteFile from './delete-file';
|
import deleteFile from './delete-file';
|
||||||
import cleanRemoteFiles from './clean-remote-files';
|
import cleanRemoteFiles from './clean-remote-files';
|
||||||
|
|
||||||
const jobs = {
|
const jobs = {
|
||||||
deleteFile,
|
deleteFile,
|
||||||
cleanRemoteFiles,
|
cleanRemoteFiles,
|
||||||
} as any;
|
} as Record<string, Bull.ProcessCallbackFunction<ObjectStorageJobData> | Bull.ProcessPromiseFunction<ObjectStorageJobData>>;
|
||||||
|
|
||||||
export default function(q: Bull.Queue) {
|
export default function(q: Bull.Queue) {
|
||||||
for (const [k, v] of Object.entries(jobs)) {
|
for (const [k, v] of Object.entries(jobs)) {
|
||||||
q.process(k, 16, v as any);
|
q.process(k, 16, v);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import config from '@/config';
|
import config from '@/config';
|
||||||
import { initialize as initializeQueue } from './initialize';
|
import { initialize as initializeQueue } from './initialize';
|
||||||
|
import { DeliverJobData, InboxJobData, DbJobData, ObjectStorageJobData } from './types';
|
||||||
|
|
||||||
export const deliverQueue = initializeQueue('deliver', config.deliverJobPerSec || 128);
|
export const deliverQueue = initializeQueue<DeliverJobData>('deliver', config.deliverJobPerSec || 128);
|
||||||
export const inboxQueue = initializeQueue('inbox', config.inboxJobPerSec || 16);
|
export const inboxQueue = initializeQueue<InboxJobData>('inbox', config.inboxJobPerSec || 16);
|
||||||
export const dbQueue = initializeQueue('db');
|
export const dbQueue = initializeQueue<DbJobData>('db');
|
||||||
export const objectStorageQueue = initializeQueue('objectStorage');
|
export const objectStorageQueue = initializeQueue<ObjectStorageJobData>('objectStorage');
|
||||||
|
|
39
src/queue/types.ts
Normal file
39
src/queue/types.ts
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
import { DriveFile } from '@/models/entities/drive-file';
|
||||||
|
import { User } from '@/models/entities/user';
|
||||||
|
import { IActivity } from '@/remote/activitypub/type';
|
||||||
|
import * as httpSignature from 'http-signature';
|
||||||
|
|
||||||
|
export type DeliverJobData = {
|
||||||
|
/** Actor */
|
||||||
|
user: ThinUser;
|
||||||
|
/** Activity */
|
||||||
|
content: unknown;
|
||||||
|
/** inbox URL to deliver */
|
||||||
|
to: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type InboxJobData = {
|
||||||
|
activity: IActivity;
|
||||||
|
signature: httpSignature.IParsedSignature;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type DbJobData = DbUserJobData | DbUserImportJobData;
|
||||||
|
|
||||||
|
export type DbUserJobData = {
|
||||||
|
user: ThinUser;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type DbUserImportJobData = {
|
||||||
|
user: ThinUser;
|
||||||
|
fileId: DriveFile['id'];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ObjectStorageJobData = ObjectStorageFileJobData | {};
|
||||||
|
|
||||||
|
export type ObjectStorageFileJobData = {
|
||||||
|
key: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ThinUser = {
|
||||||
|
id: User['id'];
|
||||||
|
};
|
|
@ -11,6 +11,11 @@ export default async (actor: IRemoteUser, activity: ILike) => {
|
||||||
|
|
||||||
await extractEmojis(activity.tag || [], actor.host).catch(() => null);
|
await extractEmojis(activity.tag || [], actor.host).catch(() => null);
|
||||||
|
|
||||||
await create(actor, note, activity._misskey_reaction || activity.content || activity.name);
|
return await create(actor, note, activity._misskey_reaction || activity.content || activity.name).catch(e => {
|
||||||
return `ok`;
|
if (e.id === '51c42bb4-931a-456b-bff7-e5a8a70dd298') {
|
||||||
|
return 'skip: already reacted';
|
||||||
|
} else {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}).then(() => 'ok');
|
||||||
};
|
};
|
||||||
|
|
|
@ -511,6 +511,10 @@ const activitystreams = {
|
||||||
"shares": {
|
"shares": {
|
||||||
"@id": "as:shares",
|
"@id": "as:shares",
|
||||||
"@type": "@id"
|
"@type": "@id"
|
||||||
|
},
|
||||||
|
"alsoKnownAs": {
|
||||||
|
"@id": "as:alsoKnownAs",
|
||||||
|
"@type": "@id"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -15,7 +15,33 @@ export const renderActivity = (x: any): IActivity | null => {
|
||||||
return Object.assign({
|
return Object.assign({
|
||||||
'@context': [
|
'@context': [
|
||||||
'https://www.w3.org/ns/activitystreams',
|
'https://www.w3.org/ns/activitystreams',
|
||||||
'https://w3id.org/security/v1'
|
'https://w3id.org/security/v1',
|
||||||
|
{
|
||||||
|
// as non-standards
|
||||||
|
manuallyApprovesFollowers: 'as:manuallyApprovesFollowers',
|
||||||
|
sensitive: 'as:sensitive',
|
||||||
|
Hashtag: 'as:Hashtag',
|
||||||
|
quoteUrl: 'as:quoteUrl',
|
||||||
|
// Mastodon
|
||||||
|
toot: 'http://joinmastodon.org/ns#',
|
||||||
|
Emoji: 'toot:Emoji',
|
||||||
|
featured: 'toot:featured',
|
||||||
|
discoverable: 'toot:discoverable',
|
||||||
|
// schema
|
||||||
|
schema: 'http://schema.org#',
|
||||||
|
PropertyValue: 'schema:PropertyValue',
|
||||||
|
value: 'schema:value',
|
||||||
|
// Misskey
|
||||||
|
misskey: `${config.url}/ns#`,
|
||||||
|
'_misskey_content': 'misskey:_misskey_content',
|
||||||
|
'_misskey_quote': 'misskey:_misskey_quote',
|
||||||
|
'_misskey_reaction': 'misskey:_misskey_reaction',
|
||||||
|
'_misskey_votes': 'misskey:_misskey_votes',
|
||||||
|
'_misskey_talk': 'misskey:_misskey_talk',
|
||||||
|
'isCat': 'misskey:isCat',
|
||||||
|
// vcard
|
||||||
|
vcard: 'http://www.w3.org/2006/vcard/ns#',
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}, x);
|
}, x);
|
||||||
};
|
};
|
||||||
|
@ -25,35 +51,6 @@ export const attachLdSignature = async (activity: any, user: { id: User['id']; h
|
||||||
|
|
||||||
const keypair = await getUserKeypair(user.id);
|
const keypair = await getUserKeypair(user.id);
|
||||||
|
|
||||||
const obj = {
|
|
||||||
// as non-standards
|
|
||||||
manuallyApprovesFollowers: 'as:manuallyApprovesFollowers',
|
|
||||||
sensitive: 'as:sensitive',
|
|
||||||
Hashtag: 'as:Hashtag',
|
|
||||||
quoteUrl: 'as:quoteUrl',
|
|
||||||
// Mastodon
|
|
||||||
toot: 'http://joinmastodon.org/ns#',
|
|
||||||
Emoji: 'toot:Emoji',
|
|
||||||
featured: 'toot:featured',
|
|
||||||
discoverable: 'toot:discoverable',
|
|
||||||
// schema
|
|
||||||
schema: 'http://schema.org#',
|
|
||||||
PropertyValue: 'schema:PropertyValue',
|
|
||||||
value: 'schema:value',
|
|
||||||
// Misskey
|
|
||||||
misskey: `${config.url}/ns#`,
|
|
||||||
'_misskey_content': 'misskey:_misskey_content',
|
|
||||||
'_misskey_quote': 'misskey:_misskey_quote',
|
|
||||||
'_misskey_reaction': 'misskey:_misskey_reaction',
|
|
||||||
'_misskey_votes': 'misskey:_misskey_votes',
|
|
||||||
'_misskey_talk': 'misskey:_misskey_talk',
|
|
||||||
'isCat': 'misskey:isCat',
|
|
||||||
// vcard
|
|
||||||
vcard: 'http://www.w3.org/2006/vcard/ns#',
|
|
||||||
};
|
|
||||||
|
|
||||||
activity['@context'].push(obj);
|
|
||||||
|
|
||||||
const ldSignature = new LdSignature();
|
const ldSignature = new LdSignature();
|
||||||
ldSignature.debug = false;
|
ldSignature.debug = false;
|
||||||
activity = await ldSignature.signRsaSignature2017(activity, keypair.privateKey, `${config.url}/users/${user.id}#main-key`);
|
activity = await ldSignature.signRsaSignature2017(activity, keypair.privateKey, `${config.url}/users/${user.id}#main-key`);
|
||||||
|
|
|
@ -1,8 +1,12 @@
|
||||||
import config from '@/config';
|
import config from '@/config';
|
||||||
import { ILocalUser, User } from '../../../models/entities/user';
|
import { ILocalUser, User } from '../../../models/entities/user';
|
||||||
|
|
||||||
export default (object: any, user: { id: User['id'] }) => ({
|
export default (object: any, user: { id: User['id'] }) => {
|
||||||
type: 'Undo',
|
if (object == null) return null;
|
||||||
actor: `${config.url}/users/${user.id}`,
|
|
||||||
object
|
return {
|
||||||
});
|
type: 'Undo',
|
||||||
|
actor: `${config.url}/users/${user.id}`,
|
||||||
|
object
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
|
@ -22,7 +22,7 @@ export function generateVisibilityQuery(q: SelectQueryBuilder<any>, me?: { id: U
|
||||||
// または 自分自身
|
// または 自分自身
|
||||||
.orWhere('note.userId = :userId1', { userId1: me.id })
|
.orWhere('note.userId = :userId1', { userId1: me.id })
|
||||||
// または 自分宛て
|
// または 自分宛て
|
||||||
.orWhere(':userId2 = ANY(note.visibleUserIds)', { userId2: me.id })
|
.orWhere(`'{"${me.id}"}' <@ note.visibleUserIds`)
|
||||||
.orWhere(new Brackets(qb => { qb
|
.orWhere(new Brackets(qb => { qb
|
||||||
// または フォロワー宛ての投稿であり、
|
// または フォロワー宛ての投稿であり、
|
||||||
.where('note.visibility = \'followers\'')
|
.where('note.visibility = \'followers\'')
|
||||||
|
|
|
@ -22,6 +22,9 @@ export const meta = {
|
||||||
priority: {
|
priority: {
|
||||||
validator: $.str
|
validator: $.str
|
||||||
},
|
},
|
||||||
|
ratio: {
|
||||||
|
validator: $.num.int().min(0)
|
||||||
|
},
|
||||||
expiresAt: {
|
expiresAt: {
|
||||||
validator: $.num.int()
|
validator: $.num.int()
|
||||||
},
|
},
|
||||||
|
@ -39,6 +42,7 @@ export default define(meta, async (ps) => {
|
||||||
url: ps.url,
|
url: ps.url,
|
||||||
imageUrl: ps.imageUrl,
|
imageUrl: ps.imageUrl,
|
||||||
priority: ps.priority,
|
priority: ps.priority,
|
||||||
|
ratio: ps.ratio,
|
||||||
place: ps.place,
|
place: ps.place,
|
||||||
memo: ps.memo,
|
memo: ps.memo,
|
||||||
});
|
});
|
||||||
|
|
|
@ -29,6 +29,9 @@ export const meta = {
|
||||||
priority: {
|
priority: {
|
||||||
validator: $.str
|
validator: $.str
|
||||||
},
|
},
|
||||||
|
ratio: {
|
||||||
|
validator: $.num.int().min(0)
|
||||||
|
},
|
||||||
expiresAt: {
|
expiresAt: {
|
||||||
validator: $.num.int()
|
validator: $.num.int()
|
||||||
},
|
},
|
||||||
|
@ -52,6 +55,7 @@ export default define(meta, async (ps, me) => {
|
||||||
url: ps.url,
|
url: ps.url,
|
||||||
place: ps.place,
|
place: ps.place,
|
||||||
priority: ps.priority,
|
priority: ps.priority,
|
||||||
|
ratio: ps.ratio,
|
||||||
memo: ps.memo,
|
memo: ps.memo,
|
||||||
imageUrl: ps.imageUrl,
|
imageUrl: ps.imageUrl,
|
||||||
expiresAt: new Date(ps.expiresAt),
|
expiresAt: new Date(ps.expiresAt),
|
||||||
|
|
26
src/server/api/endpoints/admin/get-index-stats.ts
Normal file
26
src/server/api/endpoints/admin/get-index-stats.ts
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
import define from '../../define';
|
||||||
|
import { getConnection } from 'typeorm';
|
||||||
|
|
||||||
|
export const meta = {
|
||||||
|
requireCredential: true as const,
|
||||||
|
requireModerator: true,
|
||||||
|
|
||||||
|
tags: ['admin'],
|
||||||
|
|
||||||
|
params: {
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default define(meta, async () => {
|
||||||
|
const stats = await
|
||||||
|
getConnection().query(`SELECT * FROM pg_indexes;`)
|
||||||
|
.then(recs => {
|
||||||
|
const res = [] as { tablename: string; indexname: string; }[];
|
||||||
|
for (const rec of recs) {
|
||||||
|
res.push(rec);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
});
|
||||||
|
|
||||||
|
return stats;
|
||||||
|
});
|
|
@ -509,9 +509,10 @@ export default define(meta, async (ps, me) => {
|
||||||
maxNoteTextLength: Math.min(instance.maxNoteTextLength, DB_MAX_NOTE_TEXT_LENGTH),
|
maxNoteTextLength: Math.min(instance.maxNoteTextLength, DB_MAX_NOTE_TEXT_LENGTH),
|
||||||
emojis: await Emojis.packMany(emojis),
|
emojis: await Emojis.packMany(emojis),
|
||||||
ads: ads.map(ad => ({
|
ads: ads.map(ad => ({
|
||||||
|
id: ad.id,
|
||||||
url: ad.url,
|
url: ad.url,
|
||||||
place: ad.place,
|
place: ad.place,
|
||||||
priority: ad.priority,
|
ratio: ad.ratio,
|
||||||
imageUrl: ad.imageUrl,
|
imageUrl: ad.imageUrl,
|
||||||
})),
|
})),
|
||||||
enableEmail: instance.enableEmail,
|
enableEmail: instance.enableEmail,
|
||||||
|
|
|
@ -60,8 +60,8 @@ export default define(meta, async (ps, user) => {
|
||||||
|
|
||||||
const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId)
|
const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId)
|
||||||
.andWhere(new Brackets(qb => { qb
|
.andWhere(new Brackets(qb => { qb
|
||||||
.where(`:meId = ANY(note.mentions)`, { meId: user.id })
|
.where(`'{"${user.id}"}' <@ note.mentions`)
|
||||||
.orWhere(`:meId = ANY(note.visibleUserIds)`, { meId: user.id });
|
.orWhere(`'{"${user.id}"}' <@ note.visibleUserIds`);
|
||||||
}))
|
}))
|
||||||
.innerJoinAndSelect('note.user', 'user')
|
.innerJoinAndSelect('note.user', 'user')
|
||||||
.leftJoinAndSelect('note.reply', 'reply')
|
.leftJoinAndSelect('note.reply', 'reply')
|
||||||
|
|
|
@ -35,7 +35,7 @@ export default async function(user: User, note: Note, quiet = false) {
|
||||||
});
|
});
|
||||||
|
|
||||||
//#region ローカルの投稿なら削除アクティビティを配送
|
//#region ローカルの投稿なら削除アクティビティを配送
|
||||||
if (Users.isLocalUser(user)) {
|
if (Users.isLocalUser(user) && !note.localOnly) {
|
||||||
let renote: Note | undefined;
|
let renote: Note | undefined;
|
||||||
|
|
||||||
// if deletd note is renote
|
// if deletd note is renote
|
||||||
|
|
|
@ -13,12 +13,13 @@ import { createNotification } from '../../create-notification';
|
||||||
import deleteReaction from './delete';
|
import deleteReaction from './delete';
|
||||||
import { isDuplicateKeyValueError } from '@/misc/is-duplicate-key-value-error';
|
import { isDuplicateKeyValueError } from '@/misc/is-duplicate-key-value-error';
|
||||||
import { NoteReaction } from '../../../models/entities/note-reaction';
|
import { NoteReaction } from '../../../models/entities/note-reaction';
|
||||||
|
import { IdentifiableError } from '@/misc/identifiable-error';
|
||||||
|
|
||||||
export default async (user: { id: User['id']; host: User['host']; }, note: Note, reaction?: string) => {
|
export default async (user: { id: User['id']; host: User['host']; }, note: Note, reaction?: string) => {
|
||||||
// TODO: cache
|
// TODO: cache
|
||||||
reaction = await toDbReaction(reaction, user.host);
|
reaction = await toDbReaction(reaction, user.host);
|
||||||
|
|
||||||
let record: NoteReaction = {
|
const record: NoteReaction = {
|
||||||
id: genId(),
|
id: genId(),
|
||||||
createdAt: new Date(),
|
createdAt: new Date(),
|
||||||
noteId: note.id,
|
noteId: note.id,
|
||||||
|
@ -31,17 +32,18 @@ export default async (user: { id: User['id']; host: User['host']; }, note: Note,
|
||||||
await NoteReactions.insert(record);
|
await NoteReactions.insert(record);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (isDuplicateKeyValueError(e)) {
|
if (isDuplicateKeyValueError(e)) {
|
||||||
record = await NoteReactions.findOneOrFail({
|
const exists = await NoteReactions.findOneOrFail({
|
||||||
noteId: note.id,
|
noteId: note.id,
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (record.reaction !== reaction) {
|
if (exists.reaction !== reaction) {
|
||||||
// 別のリアクションがすでにされていたら置き換える
|
// 別のリアクションがすでにされていたら置き換える
|
||||||
await deleteReaction(user, note);
|
await deleteReaction(user, note);
|
||||||
|
await NoteReactions.insert(record);
|
||||||
} else {
|
} else {
|
||||||
// 同じリアクションがすでにされていたら何もしない
|
// 同じリアクションがすでにされていたらエラー
|
||||||
return;
|
throw new IdentifiableError('51c42bb4-931a-456b-bff7-e5a8a70dd298');
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw e;
|
throw e;
|
||||||
|
|
Loading…
Reference in a new issue