Merge branch 'develop' into gh-fa55fa5e/10452/unknown/rtl

This commit is contained in:
naskya 2023-12-23 05:33:21 +09:00
commit 8d3f3bd198
No known key found for this signature in database
GPG key ID: 712D413B3A9FED5C
885 changed files with 16189 additions and 22321 deletions

View file

@ -192,6 +192,9 @@ reservedUsernames: [
# Proxy remote files (default: false)
#proxyRemoteFiles: true
# Use authorized fetch for outgoing requests
signToActivityPubGet: true
#allowedPrivateNetworks: [
# '127.0.0.1/32'
#]

3
.gitignore vendored
View file

@ -57,6 +57,9 @@ packages/backend/assets/LICENSE
!/packages/backend/src/db
!/packages/backend/src/server/api/endpoints/drive/files
packages/megalodon/lib
packages/megalodon/.idea
# blender backups
*.blend1
*.blend2

View file

@ -1 +0,0 @@
v18.16.0

1
.npmrc
View file

@ -1 +0,0 @@
use-lockfile-v6=true

View file

@ -1,4 +0,0 @@
{
"$schema": "http://json.schemastore.org/vsls",
"gitignore": "exclude"
}

File diff suppressed because it is too large Load diff

View file

@ -94,9 +94,9 @@ An actual domain will be assigned so you can test the federation.
- The tag name must be the version
## Development
During development, it is useful to use the `yarn dev` command.
During development, it is useful to use the `pnpm run dev` command.
This command monitors the server-side and client-side source files and automatically builds them if they are modified.
In addition, it will also automatically start the Misskey server process.
In addition, it will also automatically start the Firefish server process.
## Testing
- Test codes are located in [`/test`](/test).
@ -119,20 +119,13 @@ yarn test
#### Run specify test
```
TS_NODE_FILES=true TS_NODE_TRANSPILE_ONLY=true TS_NODE_PROJECT="./test/tsconfig.json" yarn dlx mocha test/foo.ts --require ts-node/register
TS_NODE_FILES=true TS_NODE_TRANSPILE_ONLY=true TS_NODE_PROJECT="./test/tsconfig.json" pnpx mocha test/foo.ts --require ts-node/register
```
### e2e tests
TODO
## Continuous integration
Misskey uses GitHub Actions for executing automated tests.
Configuration files are located in [`/.github/workflows`](/.github/workflows).
## Vue
Misskey uses Vue(v3) as its front-end framework.
Firefish uses Vue(v3) as its front-end framework.
- Use TypeScript.
- **When creating a new component, please use the Composition API (with [setup sugar](https://v3.vuejs.org/api/sfc-script-setup.html) and [ref sugar](https://github.com/vuejs/rfcs/discussions/369)) instead of the Options API.**
- **When creating a new component, please use the Composition API (with [setup syntax](https://v3.vuejs.org/api/sfc-script-setup.html) and [ref syntax](https://github.com/vuejs/rfcs/discussions/369)) instead of the Options API.**
- Some of the existing components are implemented in the Options API, but it is an old implementation. Refactors that migrate those components to the Composition API are also welcome.
## nirax

View file

@ -1,5 +1,5 @@
## Install dev and compilation dependencies, build files
FROM node:latest as build
FROM node:20-slim as build
WORKDIR /firefish
# Install compilation dependencies
@ -26,6 +26,7 @@ COPY packages/backend/package.json packages/backend/package.json
COPY packages/client/package.json packages/client/package.json
COPY packages/sw/package.json packages/sw/package.json
COPY packages/firefish-js/package.json packages/firefish-js/package.json
COPY packages/megalodon/package.json packages/megalodon/package.json
COPY packages/backend/native-utils/package.json packages/backend/native-utils/package.json
COPY packages/backend/native-utils/npm/linux-x64-musl/package.json packages/backend/native-utils/npm/linux-x64-musl/package.json
COPY packages/backend/native-utils/npm/linux-arm64-musl/package.json packages/backend/native-utils/npm/linux-arm64-musl/package.json
@ -47,14 +48,16 @@ RUN env NODE_ENV=production sh -c "pnpm run --filter '!native-utils' build && pn
RUN pnpm i --prod --frozen-lockfile
## Runtime container
FROM node:latest
FROM node:20-slim
WORKDIR /firefish
# Install runtime dependencies
RUN apt-get update && apt-get install -y libvips-dev zip unzip tini ffmpeg
RUN apt-get update && apt-get install -y --no-install-recommends libvips-dev zip unzip tini ffmpeg
COPY . ./
COPY --from=build /firefish/packages/megalodon /firefish/packages/megalodon
# Copy node modules
COPY --from=build /firefish/node_modules /firefish/node_modules
COPY --from=build /firefish/packages/backend/node_modules /firefish/packages/backend/node_modules

View file

@ -98,11 +98,11 @@ url: "https://{{ .Values.firefish.domain }}/"
#───┘ Port and TLS settings └───────────────────────────────────
#
# Misskey requires a reverse proxy to support HTTPS connections.
# Firefish requires a reverse proxy to support HTTPS connections.
#
# +----- https://example.tld/ ------------+
# +------+ |+-------------+ +----------------+|
# | User | ---> || Proxy (443) | ---> | Misskey (3000) ||
# | User | ---> || Proxy (443) | ---> | Firefish (3000) ||
# +------+ |+-------------+ +----------------+|
# +---------------------------------------+
#
@ -110,7 +110,7 @@ url: "https://{{ .Values.firefish.domain }}/"
# An encrypted connection with HTTPS is highly recommended
# because tokens may be transferred in GET requests.
# The port that your Misskey server should listen on.
# The port that your Firefish server should listen on.
port: 3000
# ┌──────────────────────────┐

View file

@ -139,10 +139,10 @@ describe("After user singed in", () => {
it("note", () => {
cy.get("[data-cy-open-post-form]").click();
cy.get("[data-cy-post-form-text]").type("Hello, Misskey!");
cy.get("[data-cy-post-form-text]").type("Hello, Firefish!");
cy.get("[data-cy-open-post-form-submit]").click();
cy.contains("Hello, Misskey!");
cy.contains("Hello, Firefish!");
});
});

View file

@ -2,7 +2,9 @@ version: "3"
services:
web:
image: registry.joinfirefish.org/firefish/firefish
# Choose one of these tags:
# stable-amd64, stable-arm64, beta-amd64, beta-arm64
image: registry.joinfirefish.org/firefish/firefish:stable-amd64
container_name: firefish_web
restart: unless-stopped
depends_on:

View file

@ -4,6 +4,11 @@ Breaking changes are indicated by the :warning: icon.
## v1.0.5 (unreleased)
### dev21
- `admin/update-meta` can now take `moreUrls` parameter, and response of `admin/meta` now includes `moreUrls`
- These URLs are used for the help menu ([related merge request](https://git.joinfirefish.org/firefish/firefish/-/merge_requests/10640))
### dev18
- :warning: response of `meta` no longer includes the following:

View file

@ -52,7 +52,7 @@ gulp.task("build:backend:script", () => {
"./packages/backend/src/server/web/bios.js",
"./packages/backend/src/server/web/cli.js",
])
.pipe(replace("LANGS", JSON.stringify(Object.keys(locales))))
.pipe(replace("SUPPORTED_LANGS", JSON.stringify(Object.keys(locales))))
.pipe(
terser({
toplevel: true,

View file

@ -475,3 +475,5 @@ _preferencesBackups:
editWidgetsExit: Готово
done: Готово
emailRequiredForSignup: Изискване за адрес на е-поща за регистриране
preview: Преглед
privacy: Поверителност

View file

@ -112,7 +112,7 @@ you: "Tu"
clickToShow: "Fes clic per a mostrar"
sensitive: "NSFW"
add: "Afegeix"
reaction: "Reaccions"
reaction: "Reacció"
reactionSetting: "Reaccions a mostrar al selector de reaccions"
reactionSettingDescription2: "Arrossega per reordenar, fes clic per suprimir, prem
\"+\" per afegir."
@ -792,11 +792,11 @@ customEmojis: Emojis personalitzats
cacheRemoteFilesDescription: Quan aquesta opció està desactivada, els fitxers remots
es carreguen directament del servidor remot. Desactivar-la farà que baixi l'ús d'emmagatzematge,
però incrementa el tràfic, perquè les miniatures no es generaran.
flagAsBot: Marca aquest compte com a bot
flagAsBot: Marca aquest compte com automatitzat
flagAsBotDescription: Activa aquesta opció si aquest compte és controlat per un programa.
Si s'activa, això actuarà com una bandera per a altres desenvolupadors i ajuda a
prevenir cadenes de interaccions infinites amb altres bots a més d'ajustar els sistemes
interns de Firefish per tractar aquest compte com un bot.
prevenir cadenes de interaccions infinites amb altres comptes automatitzats a més
d'ajustar els sistemes interns de Firefish per tractar aquest compte com automatitzat.
flagAsCat: Ets un gat? 🐱
flagShowTimelineReplies: Mostra respostes a la línia de temps
flagAsCatDescription: Guanyaràs unes orelles de gat i parlares com un gat!
@ -895,7 +895,7 @@ nUsersRead: llegit per {n}
agreeTo: Estic d'acord amb {0}
activity: Activitat
home: Inici
remoteUserCaution: La informació dels usuaris remots pot estar incompleta.
remoteUserCaution: La informació dels usuaris remots és incompleta.
themeForDarkMode: Tema a fer servir en mode fosc
light: Clar
registeredDate: Data de registre
@ -1051,7 +1051,7 @@ popularTags: Etiquetes populars
about: Sobre
recentlyUpdatedUsers: Usuaris actius fa poc
recentlyRegisteredUsers: Usuaris registrats fa poc
recentlyDiscoveredUsers: Nous suaris descoberts
recentlyDiscoveredUsers: Nous usuaris descoberts
administrator: Administrador
token: Token
registerSecurityKey: Registreu una clau de seguretat
@ -1550,7 +1550,7 @@ troubleshooting: Resolució de problemes
learnMore: Més informació
misskeyUpdated: Firefish s'ha actualitzat!
translate: Tradueix
translatedFrom: Traduït desde {x}
translatedFrom: Traduït del {x}
aiChanMode: Ai-chan a la interfície d'usuari clàssica
keepCw: Mantenir els avisos de contingut
pubSub: Comptes Pub/Sub
@ -2127,7 +2127,7 @@ clipsDesc: Els clips són com marcadors categoritzats que es poden compartir. Po
selectChannel: Selecciona un canal
isLocked: Aquest compte té les següents aprovacions
isPatron: Mecenes de Firefish
isBot: Aquest compte és un bot
isBot: Aquest es un compte automatitzat
isModerator: Moderador
isAdmin: Administrador
_filters:
@ -2199,3 +2199,22 @@ openServerInfo: Mostra la informació del servidor fent clic al símbol del serv
en un missatge
vibrate: Activar vibracions
clickToShowPatterns: Fes clic per veure patrons de mòduls
iconSet: Conjunt d'Icones
_iconSets:
fill: Omplerts
regular: Normals
bold: Negreta
duotone: Bitó
light: Prims
showAttachedNotes: Mostrar publicacions que contenen aquest fitxer
reactions: Reaccions
attachedToNotes: Publicacions que contenen aquest fitxer
replies: Respostes
quotes: Cites
renotes: Impulsos
moreUrls: Pàgines fixades
moreUrlsDescription: "Introdueix les pàgines que vols fixar al menú d'ajuda a la part
inferior esquerra fent servir aquesta notació:\n\"Nom a mostrar\": https://example.com/"
useEmojiCdn: Aconsegueix Twemoji des d'un CDN
useEmojiCdnDescription: Fes servir Twemoji des del CDN de JSDeliver en lloc de fer
servir el del propi servidor.

View file

@ -402,6 +402,8 @@ withReplies: "Antworten beinhalten"
connectedTo: "Mit folgenden Nutzerkonten verknüpft"
notesAndReplies: "Beiträge und Antworten"
withFiles: "Beiträge mit Dateien"
attachedToNotes: "Beiträge mit dieser Datei"
showAttachedNotes: "Zeige Beiträge mit dieser Datei"
silence: "stummschalten"
silenceConfirm: "Sind Sie sicher, dass Sie diesen Benutzer Stummschalten möchten?"
unsilence: "Stummschaltung aufheben"
@ -2216,3 +2218,4 @@ openServerInfo: Anzeigen von Serverinformationen durch Anklicken des Server-Tick
in einem Beitrag
vibrate: Vibrationen abspielen
clickToShowPatterns: Klicken um Modul-Muster anzuzeigen
replies: Antworten

View file

@ -168,11 +168,11 @@ cacheRemoteFiles: "Cache remote files"
cacheRemoteFilesDescription: "When this setting is disabled, remote files are loaded
directly from the remote server. Disabling this will decrease storage usage, but
increase traffic, as thumbnails will not be generated."
flagAsBot: "Mark this account as a bot"
flagAsBot: "Mark this account as automated"
flagAsBotDescription: "Enable this option if this account is controlled by a program.
If enabled, it will act as a flag for other developers to prevent endless interaction
chains with other bots and adjust Firefish's internal systems to treat this account
as a bot."
chains with other automated accounts and adjust Firefish's internal systems to treat this
account as an automated account."
flagAsCat: "Are you a cat? 😺"
flagAsCatDescription: "You'll get cat ears and speak like a cat!"
flagSpeakAsCat: "Speak as a cat"
@ -312,7 +312,7 @@ agreeTo: "I agree to {0}"
tos: "Terms of Service"
start: "Begin"
home: "Home"
remoteUserCaution: "Information from remote users may be incomplete."
remoteUserCaution: "Information from remote users are incomplete."
activity: "Activity"
images: "Images"
birthday: "Birthday"
@ -429,6 +429,8 @@ withReplies: "Include replies"
connectedTo: "Following account(s) are connected"
notesAndReplies: "Posts and replies"
withFiles: "Including files"
attachedToNotes: "Posts with this file"
showAttachedNotes: "Show posts with this file"
silence: "Silence"
silenceConfirm: "Are you sure that you want to silence this user?"
unsilence: "Undo silencing"
@ -1115,7 +1117,7 @@ noGraze: "Please disable the \"Graze for Mastodon\" browser extension, as it int
with Firefish."
silencedWarning: "This page is showing because these users are from servers your admin
silenced, so they may potentially be spam."
isBot: "This account is a bot"
isBot: "This account is automated"
isLocked: "This account has follow approvals"
isModerator: "Moderator"
isAdmin: "Administrator"
@ -1154,6 +1156,8 @@ detectPostLanguage: "Automatically detect the language and show a translate butt
vibrate: "Play vibrations"
openServerInfo: "Show server information by clicking the server ticker on a post"
iconSet: "Icon set"
useEmojiCdn: "Get Twemoji from CDN"
useEmojiCdnDescription: "Use Twemoji from the JSDelivr CDN instead of the server's assets."
_sensitiveMediaDetection:
description: "Reduces the effort of server moderation through automatically recognizing
@ -2159,3 +2163,5 @@ _iconSets:
regular: "Regular"
fill: "Filled"
duotone: "Duotone"
moreUrls: "Pinned pages"
moreUrlsDescription: "Enter the pages you want to pin to the help menu in the lower left corner using this notation:\n\"Display name\": https://example.com/"

View file

@ -1219,6 +1219,13 @@ _wordMute:
soft: "Suave"
hard: "Duro"
mutedNotes: "Publicaciones silenciadas"
muteLangsDescription2: 'Utilizar códigos de idioma, por ejemplo: en, fr, ja, zh.'
lang: Idioma
langDescription: Ocultar publicaciones de linea de tiempo que coincidan con el idioma
seleccionado.
muteLangs: Idiomas silenciados
muteLangsDescription: Separar con espacios o saltos de lineas para una condición
OR
_instanceMute:
instanceMuteDescription: "Silencia todas las publicaciones e impusos de los servidores
seleccionados, incluyendo respuestas a los usuarios de las mismas."
@ -2162,3 +2169,15 @@ silencedWarning: Esta página se muestra debido a que estos usuarios son de serv
que tu administrador ha silenciado, ya que son presumiblemente fuente de spam.
isBot: Esta cuenta es un bot
clickToShowPatterns: Haz clic para mostrar patrones de módulos
detectPostLanguage: Detectar automáticamente el idioma y mostrar el botón de traducción
para publicaciones en otros idiomas
indexableDescription: Permitir que el buscador integrado muestre tus publicaciones
reactions: Reacciones
exportZip: Exportar ZIP
emojiPackCreator: Creador de pack de Emoji
importZip: Importar ZIP
vibrate: Reproducir vibraciones
openServerInfo: Mostrar información del servidor al presionar el simbolo del servidor
en una publicación
languageForTranslation: Traducción de publicaciones
confirm: Confirmar

View file

@ -114,7 +114,7 @@ you: "Vous"
clickToShow: "Cliquer pour afficher"
sensitive: "Contenu sensible"
add: "Ajouter"
reaction: "Réactions"
reaction: "Réaction"
reactionSetting: "Réactions à afficher dans le sélecteur de réactions"
reactionSettingDescription2: "Déplacer pour réorganiser, cliquer pour effacer, utiliser
« + » pour ajouter."
@ -1148,7 +1148,7 @@ _wordMute:
langDescription: Cacher du fil de publication les publications qui correspondent
à ces langues.
muteLangs: Langages filtrés
muteLangsDescription: Séparer avec des espaces or des retours à la ligne pour une
muteLangsDescription: Séparer avec des espaces ou des retours à la ligne pour une
condition OU (OR).
_instanceMute:
instanceMuteDescription2: "Séparer avec des sauts de lignes"
@ -2225,3 +2225,14 @@ indexable: Indexable
languageForTranslation: Langage post-traduction
vibrate: Jouer les vibrations
clickToShowPatterns: Cliquer pour montrer les patrons de modules
iconSet: Jeu d'icônes
_iconSets:
fill: Rempli
regular: Normal
bold: Gras
duotone: Deux tons
light: Fin
reactions: Réactions
replies: Réponses
quotes: Citations
renotes: Boosts

View file

@ -1,4 +1,4 @@
_lang_: Inglés
_lang_: Galego
introFirefish: Benvida! Firefish é unha plataforma de medios sociais de código aberto,
descentralizada e gratuíta para sempre!🚀
monthAndDay: '{day}/{month}'

View file

@ -148,11 +148,11 @@ cacheRemoteFiles: "Tembolokkan berkas remote"
cacheRemoteFilesDescription: "Ketika pengaturan ini dinonaktifkan, berkas luar akan
dimuat langsung dari server luar. Menonaktifkan ini akan mengurangi penggunaan penyimpanan,
tapi dapat menyebabkan meningkatkan lalu lintas, mengingat keluku tidak akan dihasilkan."
flagAsBot: "Atur akun ini sebagai Bot"
flagAsBotDescription: "Jika akun ini dikendalikan oleh program, tetapkanlah opsi ini.
flagAsBot: "Tandai akun ini sebagai akun otomatis"
flagAsBotDescription: "Jika akun ini dikendalikan oleh program, aktifkan opsi ini.
Jika diaktifkan, ini akan berfungsi sebagai tanda bagi pengembang lain untuk mencegah
interaksi berantai dengan bot lain dan menyesuaikan sistem internal Firefish untuk
memperlakukan akun ini sebagai bot."
interaksi berantai dengan akun otomatis lain dan menyesuaikan sistem internal Firefish
untuk memperlakukan akun ini sebagai akun otomatis."
flagAsCat: "Atur akun ini sebagai kucing"
flagAsCatDescription: "Kamu akan mendapatkan telinga kucing dan berbicara seperti
seekor kucing!"
@ -278,8 +278,7 @@ agreeTo: "Saya setuju kepada {0}"
tos: "Syarat dan ketentuan"
start: "Mulai"
home: "Beranda"
remoteUserCaution: "Informasi ini mungkin tidak mutakhir, karena pengguna ini berasal
dari instansi luar."
remoteUserCaution: "Informasi dari pengguna luar tidak lengkap."
activity: "Aktivitas"
images: "Gambar"
birthday: "Tanggal lahir"
@ -2005,7 +2004,7 @@ signupsDisabled: Pendaftaran ke server ini nonaktif, tapi kamu dapat selalu mend
ke server lain! Jika kamu memiliki kode undangan server ini, harap masukkan di bawah
ini.
enableCustomKaTeXMacro: Aktifkan makro KaTeX khusus
isBot: Akun ini bot
isBot: Akun ini akun otomatis
customMOTD: MOTD khusus (pesan layar percik)
recommendedInstancesDescription: Server yang direkomendasikan dipisahkan dengan garis
baru untuk tampil di linimasa rekomendasi.
@ -2181,3 +2180,21 @@ openServerInfo: Tampilkan informasi server dengan mengeklik ticker server di seb
kiriman
vibrate: Putar getaran
clickToShowPatterns: Klik untuk menampilkan pola modul
iconSet: Set ikon
_iconSets:
fill: Penuh
regular: Reguler
bold: Tebal
duotone: Duotone
light: Tipis
reactions: Reaksi
replies: Balasan
quotes: Kutipan
renotes: Postingan ulang
showAttachedNotes: Tampilkan postingan dengan berkas ini
attachedToNotes: Posting dengan berkas ini
moreUrls: Halaman tersemat
moreUrlsDescription: "Masukkan halaman yang ingin kamu sematkan ke menu bantuan di
pojok kiri bawah dengan notasi ini:\n\"Nama tampilan\": https://contoh.com/"
useEmojiCdn: Dapatkan Twemoji dari CDN
useEmojiCdnDescription: Gunakan Twemoji dari JSDelv CDN bukan dari aset server.

View file

@ -147,11 +147,11 @@ cacheRemoteFiles: "Mantieni i file remoti nella cache"
cacheRemoteFilesDescription: "Disabilitando questa opzione, i file remoti verranno
scaricati direttamente dal loro server. L'opzione permette di risparmiare spazio
ma aumenta il traffico di rete e non verranno generate anteprime."
flagAsBot: "Questo account è un bot"
flagAsBot: "Questo account è automatizzato"
flagAsBotDescription: "Se l'account esegue principalmente operazioni automatiche,
attiva quest'opzione. Quando attivata, opera come un segnalatore per gli altri sviluppatori
allo scopo di prevenire catene dinterazione senza fine con altri bot, e di adeguare
i sistemi interni di Firefish perché trattino questo account come un bot."
attiva quest'opzione. Quando attivata, permette agli sviluppatori di prevenire catene
dinterazione senza fine con altri account automatizzati. Inoltre imposta Firefish
perché tratti questo account come automatizzato."
flagAsCat: "Sei un gatto? 😺"
flagAsCatDescription: "Ti compariranno le orecchie e parlerai come un gatto!"
autoAcceptFollowed: "Accetta in automatico i follow dagli account che segui"
@ -268,7 +268,7 @@ agreeTo: "Sono d'accordo con {0}"
tos: "Termini d'uso"
start: "Inizia"
home: "Home"
remoteUserCaution: "Le informazioni degli utenti remoti possono essere incomplete."
remoteUserCaution: "Le informazioni degli utenti remoti sono incomplete."
activity: "Attività"
images: "Immagini"
birthday: "Compleanno"
@ -2079,7 +2079,7 @@ noGraze: Per favore disattiva l'estenzione del browser "Graze for Mastodon", per
interferisce con Firefish.
silencedWarning: Vedi questa pagina perché gli utenti sono su un server che il tuo
admin ha silenziato, quindi potrebbero essere spam.
isBot: Questo account è un bot
isBot: Questo account è automatizzato
isLocked: Serve una approvazione per seguire questo account
moveFromDescription: Questa operazione crea un alias del vecchio account in modo che
tu possa migrare su questo nuovo account. Fallo PRIMA di migrare il tuo vecchio
@ -2169,3 +2169,21 @@ openServerInfo: Mostra informazioni sul server cliccando sul riquadro del server
un post
vibrate: Abilita la vibrazione
clickToShowPatterns: Clicca per vedere i pattern del modulo
iconSet: Set di icone
_iconSets:
fill: Con riempimento
regular: Regolare
bold: Grassetto
duotone: Con due toni
light: Sottile
reactions: Reazioni
replies: Risposte
quotes: Citazioni
renotes: Boost
showAttachedNotes: Mostra i post con questo allegato
attachedToNotes: Post con questo allegato
moreUrls: Pagine del menu di aiuto
moreUrlsDescription: "Inserisci con questo formato le pagine che vuoi aggiungere al
menu di aiuto nell'angolo in basso a sinistra:\n\"Nome link\": https://example.com/"
useEmojiCdn: Scarica Twemoji da CDN
useEmojiCdnDescription: Usa Twemoji dalla CDN di JSDelivr invece che dal server locale.

View file

@ -149,8 +149,8 @@ addEmoji: "絵文字を追加"
settingGuide: "おすすめ設定"
cacheRemoteFiles: "リモートのファイルをキャッシュする"
cacheRemoteFilesDescription: "この設定を無効にすると、リモートファイルをキャッシュせず直リンクします。サーバーのストレージを節約できますが、サムネイルが生成されないので通信量が増加します。"
flagAsBot: "Botとして設定"
flagAsBotDescription: "このアカウントがBotである場合は、この設定をオンにします。オンにすると、反応の連鎖を防ぐためのフラグとして他の開発者に役立ったり、Firefishのシステム上での扱いがBotに合ったものになります。"
flagAsBot: "自動化されたアカウントとして設定"
flagAsBotDescription: "このアカウントが自動で投稿する場合は、この設定をオンにします。オンにすると、反応の連鎖を防ぐためのフラグとして他の開発者に役立ったり、Firefishのシステム上での扱いが自動で投稿するアカウントに合ったものになります。"
flagAsCat: "あなたは…猫?😺"
flagAsCatDescription: "このアカウントが猫であることを示す猫モードを有効にするには、このフラグをオンにします。"
flagSpeakAsCat: "猫語で話す"
@ -384,6 +384,8 @@ withReplies: "返信を含む"
connectedTo: "次のアカウントに接続されています"
notesAndReplies: "投稿と返信"
withFiles: "ファイル付き"
attachedToNotes: "このファイルが添付された投稿"
showAttachedNotes: "このファイルが添付された投稿を見る"
silence: "サイレンス"
silenceConfirm: "サイレンスしますか?"
unsilence: "サイレンス解除"
@ -979,7 +981,7 @@ customKaTeXMacroDescription: "数式入力を楽にするためのマクロを
が 3 + foo に展開されます。また、マクロの名前を囲む波括弧を丸括弧 () および角括弧 [] に変更した場合、マクロの引数に使用する括弧が変更されます。マクロの定義は一行に一つのみで、途中で改行はできません。マクロの定義が無効な行は無視されます。文字列を単純に置換する機能のみに対応していて、条件分岐などの高度な構文は使用できません。"
enableCustomKaTeXMacro: "カスタムKaTeXマクロを有効にする"
preventAiLearning: "AIによる学習を防止"
preventAiLearningDescription: "投稿したノート、添付した画像などのコンテンツを学習の対象にしないようAIに要求します。これはnoaiフラグをHTMLレスポンスに含めることによって実現されます。"
preventAiLearningDescription: "投稿した文章や、添付した画像などのコンテンツを学習の対象にしないようAIに要求します。これはnoaiフラグをHTMLレスポンスに含めることによって実現されます。"
noGraze: "ブラウザの拡張機能「Graze for Mastodon」は、Firefishの動作を妨げるため、無効にしてください。"
enableServerMachineStats: "サーバーのマシン情報を公開する"
enableIdenticonGeneration: "ユーザーごとのIdenticon生成を有効にする"
@ -992,6 +994,8 @@ addRe: "閲覧注意の投稿への返信で、注釈の先頭に\"re:\"を追
languageForTranslation: "投稿翻訳に使用する言語"
detectPostLanguage: "投稿の言語を自動検出し、外国語の投稿に翻訳ボタンを表示する"
iconSet: "アイコンのスタイル"
useEmojiCdn: "CDNのTwemojiを利用する"
useEmojiCdnDescription: "サーバー上に保存されているTwemojiのアセットの代わりに、JSDelivr CDNから配信されたものを用います。"
_sensitiveMediaDetection:
description: "機械学習を使って自動でセンシティブなメディアを検出し、モデレーションに役立てられます。サーバーの負荷が少し増えます。"
@ -1029,7 +1033,7 @@ _ad:
_forgotPassword:
enterEmail: "アカウントに登録したメールアドレスを入力してください。そのアドレス宛てに、パスワードリセット用のリンクが送信されます。"
ifNoEmail: "メールアドレスを登録していない場合は、管理者までお問い合わせください。"
contactAdmin: "このインスタンスではメールアドレスの登録がサポートされていないため、パスワードリセットを行う場合は管理者までお問い合わせください。"
contactAdmin: "このサーバーではメールアドレスの登録がサポートされていないため、パスワードリセットを行う場合は管理者までお問い合わせください。"
_gallery:
my: "自分の投稿"
liked: "いいねした投稿"
@ -1954,7 +1958,7 @@ isModerator: モデレーター
audio: 音声
image: 画像
video: 動画
isBot: このアカウントはBotで
isBot: このアカウントは自動で投稿しま
isLocked: このアカウントのフォローは承認制です
isAdmin: 管理者
isPatron: Firefish 後援者
@ -2001,3 +2005,5 @@ _iconSets:
regular: "標準"
fill: "塗りつぶし"
duotone: "2色"
moreUrls: "固定するページ"
moreUrlsDescription: "左下のヘルプメニューに固定したいページを以下の形式で、改行区切りで入力してください:\n\"表示名\": https://example.com/"

View file

@ -714,7 +714,7 @@ registry: "レジストリ"
closeAccount: "このアカウントにさいならする"
currentVersion: "現在のバージョン"
latestVersion: "最新のバージョン"
youAreRunningUpToDateClient: "今使てるクライアントが最新やで!"
youAreRunningUpToDateClient: "今使てるクライアントが最新やで!"
newVersionOfClientAvailable: "新しいバージョンのクライアントが使えるで。"
usageAmount: "使用量"
capacity: "容量"
@ -778,7 +778,7 @@ emailNotConfiguredWarning: "メアドの設定がされてへんで。"
ratio: "比率"
previewNoteText: "本文を下見するで"
customCss: "カスタムCSS"
customCssWarn: "この設定は必ず知識のある人がやらなあかんで。あんま良くない設定をしたるとクライアントがちゃんと使えへんくなってくで。"
customCssWarn: "この設定は必ず知識のある人がやらなあかん。下手にいじるとわやなって使えんくなってまうで。"
global: "グローバル"
squareAvatars: "アイコンを四角形で表示するで"
sent: "送信"
@ -793,7 +793,7 @@ whatIsNew: "更新情報を見るで"
translate: "翻訳"
translatedFrom: "{x}から翻訳するで"
accountDeletionInProgress: "アカウント削除しとるで待っとってなー"
usernameInfo: "サーバー上であんたのアカウントをあんたや分かるようにするための名前や。アルファベット(a~z, A~Z)、数字(0~9)、それとアンダーバー(_)が使って考えてな。この名前は後から変更することはできへんからちゃんと考えるんやで。"
usernameInfo: "サーバー上であんたのアカウントをあんたや分かるようにするための名前や。アルファベット(a~z, A~Z)、数字(0~9)、それとアンダーバー(_)が使えるで。この名前は後から変更することはでけへんから、ちゃんと考えや。"
aiChanMode: "藍モードやで"
keepCw: "CWを維持するで"
pubSub: "Pub/Subのアカウント"
@ -812,7 +812,7 @@ typeToConfirm: "この操作をやるんなら {x} と入力してなー"
deleteAccount: "アカウント削除するで"
document: "ドキュメント"
numberOfPageCache: "ページキャッシュ数やで"
numberOfPageCacheDescription: "増やすと使いやすくなる、負荷とメモリ使用量が増えてくで。一長一短やな。"
numberOfPageCacheDescription: "増やすと使いやすくなるけど、サーバーの負荷とメモリ使用量が増えてまう。一長一短やな。"
logoutConfirm: "ログアウトしまっか?"
lastActiveDate: "最後に使った日時"
statusbar: "ステータスバー"
@ -1444,7 +1444,17 @@ _tutorial:
step4_1: 投稿しとーみ
step5_1: タイムライン! 文字と写真の宝石箱や~
step5_2: うちのサーバーでは{timelines}種類のタイムラインをご用意しとります。
step4_2: 最初は{introduction}に投稿したり、シンプルに「ここは賑やかどすなぁ。うちはそこまで喋れまへんが、どうぞよろしゅうに」などと投稿しはる方もいてます。
step4_2: 最初は{introduction}に投稿したり、シンプルに「ここは賑やかどすなぁ」などと投稿しはる方もいてます。
step5_7: グローバル{icon}タイムラインでは、接続しとるそこいらのサーバーからアレがガーッ流れてきてえらいこっちゃで。
step5_6: おすすめ{icon}タイムラインでは、うちの管理人がばりおすすめしとるサーバーの投稿を表示させとうよ。
step5_5: ソーシャル{icon}タイムラインでは、ホームタイムラインとローカルタイムラインの投稿が両方見られてホンマお得やわぁ。
step5_3: ホーム{icon}タイムラインでは、あんたはんがフォローしてはる兄ちゃんらの投稿が見られまんねん。
step5_4: ローカル{icon}タイムラインでは、このサーバーにおる人らの投稿を見られますわ。
step6_1: ほんなら、ここはどないな場所なん?
step6_2: 実は、あんたはんはFirefishに参加しただけやあらしまへん。ここは、何千もの相互接続されたサーバーが構成しとる Fediverse への入口どす。
step6_3:
それぞれのサーバーでは必ずしもFirefishが使われとるわけやなく、異なる動作をしはるサーバーもあります。せやけど、あんたはんは他のサーバーのアカウントもフォローしたり、返信・ブーストしたりできます。一見難儀そやけど、だんないあんじょうやれますえ。
step6_4: これで完了どす。楽しんどくれやす!
_postForm:
_placeholders:
b: なんかおましたか?
@ -1454,6 +1464,7 @@ _postForm:
f: あんさん書くんを待っとるんどす...
a: いまなにしとん?
flagSpeakAsCat: 猫弁で喋る
flagSpeakAsCatDescription: オンにすると、ワレの投稿の「な」を「にゃ」に変換したるで。
flagSpeakAsCatDescription: オンにすると、ワレの投稿の「な」「na」を「にゃ」「nya」に変換したるで。
welcomeBackWithName: おおきに、{name}はん
migration: アカウントの引っ越し
makeReactionsPublicDescription: あんたが付けたリアクションの一覧をみんなにも見せたるで。

View file

@ -1,6 +1,6 @@
_lang_: "Polski"
headlineFirefish: "Otwartoźródłowa, zdecentralizowana sieć społecznościowa, która zawsze
będzie darmowa! 🚀"
headlineFirefish: "Otwartoźródłowa, zdecentralizowana sieć społecznościowa, która
zawsze będzie darmowa! 🚀"
introFirefish: "Hej! Firefish to otwartoźródłowa oraz zdecentralizowana sieć społecznościowa,
która zawsze będzie darmowa! 🚀"
monthAndDay: "{month}-{day}"
@ -94,7 +94,7 @@ privacy: "Prywatność"
makeFollowManuallyApprove: "Prośby o możliwość obserwacji wymagają zatwierdzenia"
defaultNoteVisibility: "Domyślna widoczność"
follow: "Obserwuj"
followRequest: "Poproś o możliwość obserwacji"
followRequest: "Poproś o możliwość obserwowania"
followRequests: "Prośby o możliwość obserwacji"
unfollow: "Przestań obserwować"
followRequestPending: "Oczekująca prośba o możliwość obserwacji"
@ -147,9 +147,9 @@ cacheRemoteFilesDescription: "Gdy ta opcja jest wyłączona, zdalne pliki są ł
bezpośrednio ze zdalnego serwera. Wyłączenie tej opcji zmniejszy użycie powierzchni
dyskowej, ale zwiększy transfer, ponieważ miniaturki nie będą generowane."
flagAsBot: "To konto jest botem"
flagAsBotDescription: "Jeżeli ten kanał jest kontrolowany przez jakiś program, ustaw
tę opcję. Jeżeli włączona, będzie działać jako flaga informująca innych programistów,
aby zapobiegać nieskończonej interakcji z różnymi botami i dostosowywać wewnętrzne
flagAsBotDescription: "Ustaw tę opcję ten jeśli kanał kontrolowany jest przez jakiś
program. Po włączeniu, będzie działać jako flaga informująca innych programistów,
aby zapobiegać nieskończonej interakcji pomiędzy innymi botami i dostosowywać wewnętrzne
systemy Firefish, traktując konto jako bota."
flagAsCat: "Czy jesteś kotem? 😺"
flagAsCatDescription: "Dostaniesz kocie uszka, oraz będziesz mówić jak kot!"
@ -158,7 +158,7 @@ autoAcceptFollowed: "Automatycznie przyjmuj prośby o możliwość obserwacji od
których obserwujesz"
addAccount: "Dodaj konto"
loginFailed: "Nie udało się zalogować"
showOnRemote: "Zobacz na zdalnym serwerze"
showOnRemote: "Zobacz oryginalną treść"
general: "Ogólne"
wallpaper: "Tapeta"
setWallpaper: "Ustaw tapetę"
@ -211,7 +211,7 @@ noUsers: "Brak użytkowników"
editProfile: "Edytuj profil"
noteDeleteConfirm: "Czy na pewno chcesz usunąć ten wpis?"
pinLimitExceeded: "Nie możesz przypiąć więcej wpisów"
intro: "Zakończono instalację Firefish! Utwórz konto administratora."
intro: "Zakończono instalację Firefish! Utwórz teraz konto administratora."
done: "Gotowe"
processing: "Przetwarzanie"
preview: "Podgląd"
@ -299,11 +299,11 @@ emptyDrive: "Dysk jest pusty"
emptyFolder: "Ten katalog jest pusty"
unableToDelete: "Nie można usunąć"
inputNewFileName: "Wprowadź nową nazwę pliku"
inputNewDescription: "Proszę wpisać nowy napis"
inputNewDescription: "Podaj nowy napis"
inputNewFolderName: "Wprowadź nową nazwę katalogu"
circularReferenceFolder: "Katalog docelowy jest podkatalogiem katalogu, który chcesz
przenieść."
hasChildFilesOrFolders: "Ponieważ ten katalog nie jest pusty, nie może być usunięty."
hasChildFilesOrFolders: "Katalog nie może być usunięty ponieważ nie jest pusty."
copyUrl: "Skopiuj adres URL"
rename: "Zmień nazwę"
avatar: "Awatar"
@ -578,8 +578,8 @@ disablePlayer: "Zamknij odtwarzacz wideo"
expandTweet: "Rozwiń tweet"
themeEditor: "Edytor motywu"
description: "Opis"
describeFile: "Dodaj podpis"
enterFileDescription: "Wprowadź napis"
describeFile: "Dodaj opis"
enterFileDescription: "Wprowadź opis"
author: "Autor"
leaveConfirm: "Są niezapisane zmiany. Czy chcesz je odrzucić?"
manage: "Zarządzanie"
@ -616,7 +616,7 @@ emptyToDisableSmtpAuth: "Pozostaw adres e-mail i hasło puste, aby wyłączyć w
SMTP"
smtpSecureInfo: "Wyłącz, jeżeli używasz STARTTLS"
testEmail: "Przetestuj dostarczanie wiadomości e-mail"
wordMute: "Wyciszenie słowa"
wordMute: "Wyciszenie słów i języków"
instanceMute: "Wyciszenie serwera"
userSaysSomething: "{name} powiedział* coś"
makeActive: "Aktywuj"
@ -691,8 +691,7 @@ no: "Nie"
driveFilesCount: "Liczba plików na dysku"
driveUsage: "Użycie przestrzeni dyskowej"
noCrawle: "Odrzuć indeksowanie przez crawlery"
noCrawleDescription: "Proś wyszukiwarki internetowe, aby nie indeksowały Twojego profilu,
wpisów, stron itd."
noCrawleDescription: "Proś wyszukiwarki internetowe, aby nie indeksowały Twoich treści."
lockedAccountInfo: "Dopóki nie ustawisz widoczności wpisu na \"Obserwujący\", twoje
wpisy będą mogli widzieć wszyscy, nawet jeśli ustawisz manualne zatwierdzanie obserwujących."
alwaysMarkSensitive: "Oznacz domyślnie jako NSFW"
@ -759,7 +758,7 @@ 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: "Wć"
clear: "Wyczyść"
markAllAsRead: "Oznacz wszystkie jako przeczytane"
goBack: "Wróć"
unlikeConfirm: "Na pewno chcesz usunąć polubienie?"
@ -800,7 +799,7 @@ gallery: "Galeria"
recentPosts: "Ostatnie wpisy"
popularPosts: "Popularne wpisy"
shareWithNote: "Udostępnij z wpisem"
ads: "Reklamy"
ads: "Banery"
expiration: "Ankieta kończy się"
memo: "Notatki"
priority: "Priorytet"
@ -852,7 +851,7 @@ ffVisibility: "Widoczność obserwowanych/obserwujących"
ffVisibilityDescription: "Pozwala skonfigurować, kto może zobaczyć, kogo obserwujesz
i kto Cię obserwuje."
continueThread: "Kontynuuj wątek"
deleteAccountConfirm: "Spowoduje to nieodwracalne usunięcie Twojego konta. Kontynuować?"
deleteAccountConfirm: "Spowoduje to nieodwracalne usunięcie tego konta. Kontynuować?"
incorrectPassword: "Nieprawidłowe hasło."
voteConfirm: "Potwierdzić swój głos na \"{choice}\"?"
hide: "Ukryj"
@ -1001,8 +1000,8 @@ _nsfw:
force: "Ukrywaj wszystkie media"
_mfm:
cheatSheet: "Ściąga MFM"
intro: "MFM jest językiem składniowym używanym przez m.in. Firefish, forki *key (w
tym Firefish), oraz Akkomę, który może być użyty w wielu miejscach. Tu znajdziesz
intro: "MFM jest językiem składniowym używanym przez m.in. Firefish, forki *key
(w tym Firefish), oraz Akkomę, który może być użyty w wielu miejscach. Tu znajdziesz
listę wszystkich możliwych elementów składni MFM."
dummy: "Firefish rozszerza świat Fediwersum"
mention: "Wspomnij"
@ -1250,7 +1249,7 @@ _tutorial:
innej połączonej instancji."
step6_1: "Więc, czym to jest to miejsce?"
step6_2: "Cóż, nie dołączył*ś po prostu do Firefish. Dołączył*ś do portalu do Fediverse,
połączonej sieci tysięcy serwerów, zwanych instancjami."
sieci tysięcy połączonych ze sobą serwerów, zwanych instancjami."
step6_3: "Każdy serwer działa w inny sposób, i nie wszystkie serwery używają Firefish.
Ten jednak używa! Jest to trochę skomplikowane, ale w krótkim czasie załapiesz
o co chodzi."
@ -1816,8 +1815,8 @@ instanceSecurity: Bezpieczeństwo serwera
privateMode: Tryb prywatny
allowedInstances: Dopuszczone serwery
recommended: Polecane
allowedInstancesDescription: Hosty serwerów, które mają być dopuszczone do federacji,
każdy oddzielony nowym wierszem (dotyczy tylko trybu prywatnego).
allowedInstancesDescription: Hosty serwerów, które mają być dopuszczone do federacji.
Każdy oddzielony nowym wierszem (dotyczy tylko trybu prywatnego).
seperateRenoteQuote: Oddziel przyciski podbicia i cytowania
refreshInterval: 'Częstotliwość aktualizacji '
slow: Wolna
@ -1916,14 +1915,14 @@ sendErrorReportsDescription: "Gdy ta opcja jest włączona, szczegółowe inform
błędach będą udostępnianie z Firefish gdy wystąpi problem, pomagając w ulepszaniu
Firefish.\nZawrze to informacje takie jak wersja twojego systemu operacyjnego, przeglądarki,
Twoja aktywność na Firefish itd."
privateModeInfo: Gdy ta opcja jest włączona, tylko serwery z białej listy mogą federować
się z twoim serwerem. Wszystkie posty będą ukryte publicznie.
privateModeInfo: Gdy ta opcja jest włączona, tylko serwery z listy serwerów dozwolonych
mogą federować się z twoim serwerem. Żadne posty nie będą publicznie dostępne.
oneHour: Godzina
oneDay: Dzień
oneWeek: Tydzień
recommendedInstances: Polecane serwery
recommendedInstancesDescription: Polecane serwery, mające pojawić się w odpowiedniej
osi czasu, oddzielane nowymi liniami. NIE dodawaj “https://”, TYLKO samą domenę.
osi czasu, oddzielane nowymi liniami.
rateLimitExceeded: Przekroczono ratelimit
cropImage: Kadruj zdjęcie
cropImageAsk: Czy chcesz skadrować to zdjęcie?
@ -1948,7 +1947,7 @@ activeEmailValidationDescription: Włącza ściślejszą walidację adresów e-m
obejmuje sprawdzanie adresów jednorazowych oraz tego, czy rzeczywiście można się
z nim komunikować. Jeśli wyłączone, walidowany jest tylko format wiadomości e-mail.
shuffle: Losuj
showAds: Pokazuj reklamy
showAds: Pokazuj banery
enterSendsMessage: Wciśnij Enter w komunikatorze, by wysłać wiadomość (domyślnie
Ctrl + Enter)
adminCustomCssWarn: To ustawienie powinno być używane tylko pod warunkiem, że wiesz
@ -2012,7 +2011,7 @@ silencedInstancesDescription: Wypisz nazwy hostów serwerów, które chcesz wyci
cannotUploadBecauseExceedsFileSizeLimit: Ten plik nie mógł być przesłany, ponieważ
jego wielkość przekracza dozwolony limit.
sendModMail: Wyślij Powiadomienie Moderacyjne
searchPlaceholder: Szukaj Firefish
searchPlaceholder: Szukaj w Firefish
jumpToPrevious: Przejdź do poprzedniej sekcji
listsDesc: Listy umożliwiają tworzenie osi czasu z określonymi użytkownikami. Dostęp
do nich można uzyskać na stronie osi czasu.
@ -2028,3 +2027,15 @@ newer: nowsze
older: starsze
cw: Ostrzeżenie zawartości
removeReaction: Usuń reakcję
reactions: Reakcje
clipsDesc: Spinki to skategoryzowane zakładki, które można udostępniać. Możesz utworzyć
spinkę dla każdego wpisu w menu wpisu.
swipeOnMobile: Pozwalaj na przeciąganie pomiędzy stronami
image: Obrazek
xl: XL
replies: Odpowiedzi
video: Film
quotes: Cytaty
clickToShowPatterns: Kliknij aby pokazać wzory modułów
renotes: Boosty
audio: Dźwięk

View file

@ -1,4 +1,4 @@
_lang_: "Português"
_lang_: "Português (Portugal)"
headlineFirefish: "Uma rede ligada por notas"
introFirefish: "Bem-vindo! Firefish é um serviço de microblogue descentralizado de
código aberto, gratuito para sempre! 🚀"

View file

@ -1,6 +1,6 @@
_lang_: Português (Brasil)
username: Nome de usuário
ok: OK
_lang_: Inglês
headlineFirefish: Uma plataforma de mídia social descentralizada e de código aberto
que é gratuita para sempre! 🚀
search: Pesquisar

View file

@ -111,7 +111,7 @@ you: "Вы"
clickToShow: "Нажмите для просмотра"
sensitive: "Содержимое не для всех"
add: "Добавить"
reaction: "Реакции"
reaction: "Реакция"
reactionSetting: "Реакции, отображаемые в палитре"
reactionSettingDescription2: "Расставляйте перетаскиванием, удаляйте нажатием, добавляйте
кнопкой «+»."
@ -491,7 +491,7 @@ noFollowRequests: "Нерассмотренные запросы на подпи
openImageInNewTab: "Открыть изображение в новой вкладке"
dashboard: "Панель управления"
local: "С этого сайта"
remote: "С других сайтов"
remote: "Исходный сайт"
total: "Всего"
weekOverWeekChanges: "За неделю"
dayOverDayChanges: "За день"
@ -2151,3 +2151,14 @@ deletePasskeysConfirm: Это действие безвозвратно удал
на этом аккаунте. Продолжить?
inputNotMatch: Введённые данные не совпадают
addRe: Добавить "re:" в начале комментария в ответ на запись с предупреждением о содержимом
detectPostLanguage: Автоматическое определение языка и отображение кнопки перевода
для сообщений на иностранных языках
indexableDescription: Разрешить встроенной поисковой системе искать ваши публичные
записи
reactions: Реакции
indexable: Индексируемый(-ая)
languageForTranslation: Язык перевода поста
replies: Ответы
quotes: Цитаты
clickToShowPatterns: Нажмите, чтобы показать модуль шаблонов
renotes: Репосты

View file

@ -1,25 +1,26 @@
_lang_: "ภาษาไทย"
headlineFirefish: "เชื่อมต่อเครือข่ายโดยโน้ต"
introFirefish: "ยินดีต้อนรับค่ะ/ครับ! Firefish เป็นแพลตฟอร์มโซเชียลมีเดียแบบโอเพ่นซอร์สที่มีการกระจายอำนาจซึ่งให้บริการฟรีตลอดไป!
headlineFirefish: "แพลตฟอร์มโซเชียลมีเดียแบบโอเพนซอร์สที่มีการกระจายอำนาจซึ่งให้บริการฟรีตลอดไป!
🚀"
introFirefish: "ยินดีต้อนรับค่ะ/ครับ! Firefish เป็นแพลตฟอร์มโซเชียลมีเดียแบบโอเพนซอร์สที่มีการกระจายอำนาจซึ่งให้บริการฟรีตลอดไป!
🚀"
monthAndDay: "{เดือน}/{วัน}"
search: "ค้นหา"
notifications: "การเเจ้งเตือน"
username: "ชื่อผู้ใช้"
password: "รหัสผ่าน"
forgotPassword: "ลืมรหัสผ่านอ่ะ"
fetchingAsApObject: "กำลังดึงข้อมูล จาก เฟดิเวิร์ส"
forgotPassword: "ลืมรหัสผ่าน"
fetchingAsApObject: "กำลังดึงข้อมูลจากเฟดิเวิร์ส"
ok: "ตกลง"
gotIt: "เข้าใจแล้ว !"
cancel: "ยกเลิก"
enterUsername: "ใส่ชื่อผู้ใช้"
renotedBy: "บูตเตอร์โดย {user}"
renotedBy: "บูสต์โดย {user}"
noNotes: "ไม่มีโพสต์"
noNotifications: "ไม่มีการแจ้งเตือน"
instance: "เซิฟเวอร์"
instance: "เซิร์ฟเวอร์"
settings: "การตั้งค่า"
basicSettings: "การตั้งค่าพื้นฐาน"
otherSettings: "การตั้งค่าอื่นๆ"
otherSettings: "การตั้งค่าอื่น ๆ"
openInWindow: "เปิดในหน้าต่าง"
profile: "โปรไฟล์"
timeline: "ไทม์ไลน์"
@ -28,14 +29,14 @@ login: "เข้าสู่ระบบ"
loggingIn: "กำลังเข้าสู่ระบบ"
logout: "ออกจากระบบ"
signup: "สร้างบัญชีผู้ใช้"
uploading: "กำลังอัโหลด..."
uploading: "กำลังอัโหลด..."
save: "บันทึก"
users: "ผู้ใช้งาน"
addUser: "เพิ่มผู้ใช้"
favorite: "รายการโปรด"
favorite: "เพิ่มลงในรายการโปรด"
favorites: "รายการโปรด"
unfavorite: "ลบออกจากรายการโปรด"
favorited: "เพิ่มแล้วในรายการโปรด"
favorited: "เพิ่มในรายการโปรดแล้ว"
alreadyFavorited: "เพิ่มในรายการโปรดอยู่แล้ว"
cantFavorite: "ไม่สามารถเพิ่มในรายการโปรดได้"
pin: "ปักหมุดไปยังโปรไฟล์"
@ -55,22 +56,22 @@ loadMore: "โหลดเพิ่มเติม"
showMore: "แสดงเพิ่มเติม"
showLess: "ปิด"
youGotNewFollower: "ได้ติดตามคุณ"
receiveFollowRequest: "คำขอผู้ติดตามที่ได้รับ"
followRequestAccepted: "ผู้ติดตามได้ตอบรับคำขอร้องของคุณแล้ว"
receiveFollowRequest: "ได้รับคำขอติดตาม"
followRequestAccepted: "ผู้ติดตามได้ตอบรับคำขอของคุณแล้ว"
mention: "กล่าวถึง"
mentions: "พูดถึง"
mentions: "กล่าวถึง"
directNotes: "ไดเร็คข้อความ"
importAndExport: "นำเข้า / ส่งออก"
import: "การนำเข้า"
export: "การนำออก"
importAndExport: "นำเข้า / ส่งออกข้อมูล"
import: "นำเข้า"
export: "ส่งออก"
files: "ไฟล์"
download: "ดาวน์โหลด"
driveFileDeleteConfirm: "คุณแน่ใจแล้วหรอว่าต้องการลบไฟล์ \"{name}\"? โพสต์ย่อที่แนบมากับไฟล์นี้ก็จะถูกลบด้วยนะ"
unfollowConfirm: "คุณแน่ใจแล้วหรอว่าต้องการเลิกติดตาม {name}?"
exportRequested: "เมื่อคุณได้ร้องขอการส่งออก อาจจะต้องใช้เวลาสักครู่ และจะถูกเพิ่มในไดรฟ์ของคุณเมื่อเสร็จสิ้นแล้ว"
importRequested: "เมื่อคุณได้ร้องขอการนำเข้า อาจจะต้องใช้เวลาสักครู่นะ"
lists: "รายการ"
noLists: "คุณไม่มีลิสต์ใดนะ"
importRequested: "คุณได้ร้องขอการนำเข้า อาจจะต้องใช้เวลาสักครู่นะ"
lists: "ลิสต์"
noLists: "คุณไม่มีลิสต์ใด ๆ"
note: "โพสต์"
notes: "โพสต์"
following: "กำลังติดตาม"
@ -79,76 +80,76 @@ followsYou: "ติดตามคุณ"
createList: "สร้างลิสต์"
manageLists: "จัดการลิสต์"
error: "ผิดพลาด"
somethingHappened: "อุ๊ย ! มีอะไรบางอย่างผิดพลาด"
somethingHappened: "เกิดข้อผิดพลาด"
retry: "ลองใหม่อีกครั้ง"
pageLoadError: "เกิดข้อผิดพลาดในการโหลดหน้านี้"
pageLoadErrorDescription: "โดยปกติแล้วมักจะเกิดจากข้อผิดพลาดของเครือข่ายหรือแคชของเบราว์เซอร์
ลองล้างแคชแล้วลองใหม่อีกครั้งหลังจากรอสักครู่นะ"
serverIsDead: "เซิร์ฟเวอร์นี้ไม่มีการตอบสนอง ได้โปรดกรุณารอสักครู่แล้วลองใหม่อีกครั้งนะ"
youShouldUpgradeClient: "หากต้องการดูหน้านี้ได้โปรดกรุณา รีเซ็ตเพื่ออัปเดตไคลเอ็นต์ของคุณนะ"
enterListName: "ใส่ชื่อสำหรับรายการลิสต์"
serverIsDead: "เซิร์ฟเวอร์นี้ไม่มีการตอบสนอง กรุณารอสักครู่แล้วลองใหม่อีกครั้งนะ"
youShouldUpgradeClient: "หากต้องการดูหน้านี้ กรุณารีเฟรชเพื่ออัปเดตไคลเอ็นต์ของคุณ"
enterListName: "ใส่ชื่อสำหรับลิสต์"
privacy: "ความเป็นส่วนตัว"
makeFollowManuallyApprove: "ติดตามคำขอที่ต้องได้รับการอนุมัติ"
makeFollowManuallyApprove: "คำขอติดตามต้องได้รับการอนุมัติ"
defaultNoteVisibility: "การมองเห็นที่เป็นค่าเริ่มต้น"
follow: "กำลังติดตาม"
follow: "ติดตาม"
followRequest: "คำขอติดตาม"
followRequests: "ติดตามการร้องขอ"
followRequests: "การติดตามที่ร้องขอ"
unfollow: "เลิกติดตาม"
followRequestPending: "กำลังรอดำเนินการร้องขอติดตาม"
enterEmoji: "ใส่อีโมจิ"
renote: "บูสต์"
unrenote: "เลิกบูสต์"
renoted: "บูสต์แล้ว"
cantRenote: "โพสต์นี้ไม่สามารถบูสต์ใหม่ได้"
cantReRenote: "ไม่สามารถบูสต์ไว้ใหม่ได้"
quote: "อ้างคำพูด"
cantRenote: "โพสต์นี้ไม่สามารถบูสต์ได้"
cantReRenote: "ไม่สามารถบูสต์การบูสต์ได้"
quote: "โควต"
pinnedNote: "โพสต์ที่ปักหมุดแล้ว"
pinned: "ปักหมุดไปยังโปรไฟล์"
you: "ตัวเอง"
you: "คุณ"
clickToShow: "คลิกเพื่อแสดง"
sensitive: "เนื้อหาที่ละเอียดอ่อน NSFW"
sensitive: "เนื้อหาที่ละเอียดอ่อน"
add: "เพิ่ม"
reaction: "รีแอคชัน"
reactionSetting: "รีแอคชั่นไปยังแสดงผลในตัวเลือกการรีแอคชัน"
reactionSettingDescription2: "กดลากเพื่อจัดลำดับใหม่ กดคลิกเพื่อลบ กด \"+\" เพื่อเพิ่ม"
reaction: "รีแอคชัน"
reactionSetting: "รีแอคชันที่จะแสดงผลในตัวเลือกการรีแอคชัน"
reactionSettingDescription2: "ลากเพื่อจัดลำดับใหม่ คลิกเพื่อลบ กด \"+\" เพื่อเพิ่ม"
rememberNoteVisibility: "จดจำการตั้งค่าการมองเห็นโพสต์"
attachCancel: "ลบไฟล์ออกที่แนบมา"
attachCancel: "ลบไฟล์ที่แนบมา"
markAsSensitive: "ทำเครื่องหมายว่าละเอียดอ่อน"
unmarkAsSensitive: "ยกเลิกทำเครื่องหมายเป็น NSFW"
unmarkAsSensitive: "ยกเลิกทำเครื่องหมายว่าละเอียดอ่อน"
enterFileName: "พิมพ์ชื่อไฟล์"
mute: "ปิดเสียง"
unmute: "ไม่ปิดเสียง"
unmute: "ยกเลิกการปิดเสียง"
block: "บล็อค"
unblock: "เลิกปิดกั้น"
unblock: "เลิกบล็อค"
suspend: "ถูกระงับ"
unsuspend: "ยกเลิกระงับ"
blockConfirm: "คุณแน่ใจแล้วเหรอ ว่าต้องการบล็อบัญชีนี้?"
blockConfirm: "คุณแน่ใจแล้วเหรอ ว่าต้องการบล็อบัญชีนี้?"
unblockConfirm: "คุณแน่ใจแล้วเหรอ ว่าต้องการปลดบล็อคบัญชีนี้?"
suspendConfirm: "คุณแน่ใจแล้วเหรอว่าต้องการระงับบัญชีนี้อ่ะ?"
suspendConfirm: "คุณแน่ใจแล้วเหรอว่าต้องการระงับบัญชีนี้?"
unsuspendConfirm: "คุณแน่ใจแล้วหรอว่าต้องการยกเลิกการระงับบัญชีนี้?"
selectList: "เลือกรายการ"
selectList: "เลือกลิสต์"
selectAntenna: "เลือกเสาอากาศ"
selectWidget: "เลือกวิดเจ็ต"
editWidgets: "แก้ไขวิดเจ็ต"
editWidgetsExit: "เรียบร้อย"
customEmojis: "กำหนดอีโมจิเอง"
customEmojis: "อีโมจิที่กำหนดเอง"
emoji: "อีโมจิ"
emojis: "อีโมจิ"
emojiName: "ชื่ออิโมจิ"
emojiUrl: "อิโมจิ URL"
addEmoji: "แทรกอีโมจิ"
emojiUrl: "URL ของอิโมจิ"
addEmoji: "เพิ่มอีโมจิ"
settingGuide: "การตั้งค่าที่แนะนำ"
cacheRemoteFiles: "แคชไฟล์ระยะไกล"
cacheRemoteFilesDescription: "เมื่อปิดใช้งานการตั้งค่านี้ ไฟล์ระยะไกลนั้นจะถูกโหลดโดยตรงจากระยะไกลเซิฟเวอร์
แต่กรณีการปิดใช้งานนี้จะช่วยลดปริมาณการใช้พื้นที่จัดเก็บข้อมูล แต่เพิ่มปริมาณการใช้งาน
cacheRemoteFilesDescription: "เมื่อปิดใช้งานการตั้งค่านี้ ไฟล์ระยะไกลนั้นจะถูกโหลดจากเซิร์ฟเวอร์ระยะไกลโดยตรง
แต่กรณีการปิดใช้งานนี้จะช่วยลดปริมาณการใช้พื้นที่จัดเก็บข้อมูล แต่เพิ่มปริมาณทราฟฟิค
เพราะเนื่องจากจะไม่มีการสร้างภาพขนาดย่อ"
flagAsBot: "ทำเครื่องหมายบอกว่าบัญชีนี้เป็นบอท"
flagAsBotDescription: "การเปิดใช้งานตัวเลือกนี้หากบัญชีนี้ถูกควบคุมโดยนักเขียนโปรแกรม
หรือ ถ้าหากเปิดใช้งาน มันจะทำหน้าที่เป็นแฟล็กสำหรับนักพัฒนารายอื่นๆ และเพื่อป้องกันการโต้ตอบแบบไม่มีที่สิ้นสุดกับบอทตัวอื่นๆ
และยังสามารถปรับเปลี่ยนระบบภายในของ Firefish เพื่อปฏิบัติต่อบัญชีนี้เป็นบอท"
flagAsBot: "ทำเครื่องหมายบอกว่าบัญชีนี้เป็นบัญชีอัตโนมัติ"
flagAsBotDescription: "เปิดใช้งานตัวเลือกนี้หากบัญชีนี้ถูกควบคุมโดยโปรแกรม หากเปิดใช้งาน
มันจะทำหน้าที่เป็นแฟล็กสำหรับนักพัฒนารายอื่น ๆ และเพื่อป้องกันการโต้ตอบแบบไม่มีที่สิ้นสุดกับบัญชีอัตโนมัติอื่นๆ
และปรับเปลี่ยนระบบภายในของ Firefish เพื่อปฏิบัติต่อบัญชีนี้เป็นบัญชีอัตโนมัติ"
flagAsCat: "ทำเครื่องหมายบอกว่าบัญชีนี้เป็นแมว"
flagAsCatDescription: "คุณจะได้รับหูแมวและพูดเหมือนแมวนะ!"
flagShowTimelineReplies: "แสดงตอบกลับ ในไทม์ไลน์"
flagShowTimelineReplies: "แสดงการตอบกลับ ในไทม์ไลน์"
flagShowTimelineRepliesDescription: "แสดงการตอบกลับของผู้ใช้งานไปยังโพสต์ของผู้ใช้งานรายอื่นๆในไทม์ไลน์หากได้เปิดเอาไว้"
autoAcceptFollowed: "อนุมัติคำขอติดตามโดยอัตโนมัติทันที จากผู้ใช้งานที่คุณกำลังติดตาม"
addAccount: "เพิ่มบัญชี"
@ -1245,22 +1246,28 @@ _deck:
list: "รายการ"
mentions: "พูดถึง"
noThankYou: ไม่ล่ะขอบคุณ
removeReaction: ลบรีเเอดชั่นของคุณ
renoteMute: ปิดเสียงบูส
renoteUnmute: เลิกปิดเสียงบูส
removeReaction: ลบรีเเอคชันของคุณ
renoteMute: ปิดเสียงบูสต์
renoteUnmute: เลิกปิดเสียงบูสต์
manageGroups: จัดการกลุ่ม
addInstance: เพิ่มเซิฟเวอร์
searchPlaceholder: ค้นหา Firefish
addInstance: เพิ่มเซิร์ฟเวอร์
searchPlaceholder: ค้นหาใน Firefish
deleted: ลบแล้ว
editNote: แก้ไขโพสต์
edited: แก้ไขแล้วเมื่อ {date} {time}
jumpToPrevious: ข้ามไปที่ก่อนหน้านี้
listsDesc: ลิสต์รายการนั้นช่วยให้คุณได้สร้างไทม์ไลน์กับผู้ใช้ที่ระบุได้นะ ยังสามารถเข้าถึงได้จากหน้าไทม์ไลน์ได้อีกด้วย
enableEmojiReactions: เปิดใช้งานรีแอดชั่นอีโมจิ
listsDesc: ลิสต์นั้นช่วยให้คุณได้สร้างไทม์ไลน์กับผู้ใช้ที่ระบุได้ คุณสามารถเข้าถึงได้จากหน้าไทม์ไลน์
enableEmojiReactions: เปิดใช้งานรีแอคชันอีโมจิ
selectChannel: เลือกช่อง
older: เก่ากว่านี้
newer: ใหม่กว่านี้
older: เก่ากว่า
newer: ใหม่กว่า
selectInstance: เลือกเซิฟเวอร์
showEmojisInReactionNotifications: แสดงอิโมจิในการแจ้งเตือนรีแอคชั
showEmojisInReactionNotifications: แสดงอิโมจิในการแจ้งเตือนรีแอคชั
flagSpeakAsCat: พูดเหมือนแมว
cw: คำเตือนเนื้อหา
reactions: รีแอคชัน
replies: การตอบกลับ
quotes: โควต
clickToShowPatterns: คลิกเพื่อแสดงรูปแบบโมดูล
renotes: บูสต์
flagSpeakAsCatDescription: ในโหมดแมว โพสต์ของคุณจะถูกทำให้เป็นแมว

View file

@ -900,8 +900,8 @@ customKaTeXMacro: "自訂KaTeX巨集"
customKaTeXMacroDescription: "使用巨集來輕鬆輸入數學表達式吧!巨集的用法與 LaTeX 中的命令定義相同。你可以使用 \\newcommand{\\
name}{content} 或 \\newcommand{\\name}[number of arguments]{content} 來輸入數學表達式。舉例來說,\\
newcommand{\\add}[2]{#1 + #2} 會將 \\add{3}{foo} 展開為 3 + foo。巨集名稱除了可用大括號 {} 括起來之外,也可使用小括號
() 和中括號 [],但使用於巨集參數的括號會有所變更。每行只能夠定義一個巨集,巨集中間無法間換。無效的行將被忽略。只支援簡單字串的替換功能,不支援條件分歧的進階語法。"
enableCustomKaTeXMacro: "啟用自定義 KaTeX 宏"
() 和中括號 [],但使用於巨集參數的括號會有所變更。每行只能定義一個巨集,巨集中間無法換行,無效的行將被忽略。只支援簡單字串的替換功能,不支援條件分歧的進階語法。"
enableCustomKaTeXMacro: "啟用自訂 KaTeX 巨集"
_sensitiveMediaDetection:
description: "您可以使用機器學習自動檢測敏感媒體並將其用於審核。 伺服器的負荷會稍微增加。"
sensitivity: "檢測敏感度"
@ -1994,3 +1994,12 @@ indexable: 登錄至貼文搜尋引擎
origin: 來源
objectStorageS3ForcePathStyle: 使用基於路徑的端點EndpointURL
clickToShowPatterns: 點擊顯示模組模式Module Pattern
iconSet: 圖示的樣式
_iconSets:
fill: 填滿
regular: 標準
bold: 粗線
duotone: 雙色
light: 細線
showAttachedNotes: 顯示有此附件的貼文
attachedToNotes: 帶有此附件的貼文

View file

@ -1,12 +1,12 @@
{
"name": "firefish",
"version": "1.0.5-dev18",
"version": "1.0.5-rc",
"codename": "aqua",
"repository": {
"type": "git",
"url": "https://git.joinfirefish.org/firefish/firefish.git"
},
"packageManager": "pnpm@8.8.0",
"packageManager": "pnpm@8.11.0",
"private": true,
"scripts": {
"rebuild": "pnpm run clean && pnpm run build",
@ -19,7 +19,7 @@
"migrateandstart": "pnpm run migrate && pnpm run start",
"gulp": "gulp build",
"watch": "pnpm run dev",
"dev": "pnpm node ./scripts/dev.js",
"dev": "pnpm node ./scripts/dev.mjs",
"dev:staging": "NODE_OPTIONS=--max_old_space_size=3072 NODE_ENV=development pnpm run build && pnpm run start",
"lint": "pnpm -r --parallel run lint",
"debug": "pnpm run build:debug && pnpm run start",
@ -30,42 +30,42 @@
"mocha": "pnpm --filter backend run mocha",
"test": "pnpm run mocha",
"format": "pnpm -r --parallel run format",
"clean": "pnpm node ./scripts/clean.js",
"clean-all": "pnpm node ./scripts/clean-all.js",
"clean": "pnpm node ./scripts/clean.mjs",
"clean-all": "pnpm node ./scripts/clean-all.mjs",
"cleanall": "pnpm run clean-all"
},
"resolutions": {
"chokidar": "^3.3.1"
},
"dependencies": {
"@bull-board/api": "5.8.0",
"@bull-board/ui": "5.8.0",
"@napi-rs/cli": "^2.16.2",
"@tensorflow/tfjs": "^4.10.0",
"@bull-board/api": "5.9.1",
"@bull-board/ui": "5.9.1",
"@napi-rs/cli": "^2.16.5",
"@tensorflow/tfjs": "^4.13.0",
"js-yaml": "4.1.0",
"seedrandom": "^3.0.5"
},
"devDependencies": {
"@biomejs/biome": "1.0.0",
"@biomejs/cli-darwin-arm64": "^1.0.0",
"@biomejs/cli-darwin-x64": "^1.0.0",
"@biomejs/cli-linux-arm64": "^1.0.0",
"@biomejs/cli-linux-x64": "^1.0.0",
"@types/gulp": "4.0.13",
"@types/gulp-rename": "2.0.2",
"@types/node": "20.5.8",
"@biomejs/biome": "1.3.3",
"@biomejs/cli-darwin-arm64": "^1.3.3",
"@biomejs/cli-darwin-x64": "^1.3.3",
"@biomejs/cli-linux-arm64": "^1.3.3",
"@biomejs/cli-linux-x64": "^1.3.3",
"@types/gulp": "4.0.17",
"@types/gulp-rename": "2.0.5",
"@types/node": "20.9.0",
"add": "2.0.6",
"cross-env": "7.0.3",
"cypress": "10.11.0",
"execa": "5.1.1",
"cypress": "13.5.1",
"execa": "8.0.1",
"gulp": "4.0.2",
"gulp-cssnano": "2.1.3",
"gulp-rename": "2.0.0",
"gulp-replace": "1.1.4",
"gulp-terser": "2.1.0",
"install-peers": "^1.0.4",
"pnpm": "8.8.0",
"start-server-and-test": "1.15.2",
"pnpm": "8.11.0",
"start-server-and-test": "2.0.3",
"typescript": "5.2.2"
}
}

View file

@ -7,3 +7,4 @@ This directory contains all of the packages Firefish uses.
- `client`: Web interface written in Vue3 and TypeScript
- `sw`: Web [Service Worker](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API) written in TypeScript
- `firefish-js`: TypeScript SDK for both backend and client, also published on [NPM](https://www.npmjs.com/package/firefish-js) for public use
- `megalodon`: TypeScript library used for partial Mastodon API compatibility

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

View file

Before

Width:  |  Height:  |  Size: 1 KiB

After

Width:  |  Height:  |  Size: 1 KiB

View file

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

View file

Before

Width:  |  Height:  |  Size: 889 B

After

Width:  |  Height:  |  Size: 889 B

View file

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 798 B

View file

@ -0,0 +1,13 @@
export class MoreUrls1699305365258 {
name = "MoreUrls1699305365258";
async up(queryRunner) {
queryRunner.query(
`ALTER TABLE "meta" ADD "moreUrls" jsonb NOT NULL DEFAULT '[]'`,
);
}
async down(queryRunner) {
queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "moreUrls"`);
}
}

View file

@ -75,6 +75,8 @@ pub struct Model {
pub pinned_users: StringVec,
#[sea_orm(column_name = "ToSUrl")]
pub to_s_url: Option<String>,
#[sea_orm(column_name = "moreUrls", column_type = "JsonBinary")]
pub more_urls: Json,
#[sea_orm(column_name = "repositoryUrl")]
pub repository_url: String,
#[sea_orm(column_name = "feedbackUrl")]

View file

@ -23,36 +23,36 @@
},
"optionalDependencies": {
"@swc/core-android-arm64": "1.3.11",
"@tensorflow/tfjs-node": "3.21.1"
"@tensorflow/tfjs-node": "4.13.0"
},
"dependencies": {
"@bull-board/api": "5.8.0",
"@bull-board/koa": "5.8.0",
"@bull-board/ui": "5.8.0",
"@discordapp/twemoji": "14.1.2",
"@elastic/elasticsearch": "7.17.0",
"@koa/cors": "3.4.3",
"@bull-board/api": "5.9.1",
"@bull-board/koa": "5.9.1",
"@bull-board/ui": "5.9.1",
"@discordapp/twemoji": "^15.0.2",
"@elastic/elasticsearch": "8.10.0",
"@koa/cors": "4.0.0",
"@koa/multer": "3.0.2",
"@koa/router": "9.0.1",
"@koa/router": "12.0.1",
"@ladjs/koa-views": "9.0.0",
"@peertube/http-signature": "1.7.0",
"@redocly/openapi-core": "1.0.2",
"@sinonjs/fake-timers": "9.1.2",
"@syuilo/aiscript": "0.11.1",
"@tensorflow/tfjs": "^4.2.0",
"@redocly/openapi-core": "1.4.1",
"@sinonjs/fake-timers": "11.2.2",
"@tensorflow/tfjs": "^4.13.0",
"@twemoji/parser": "^15.0.0",
"adm-zip": "^0.5.10",
"ajv": "8.12.0",
"archiver": "6.0.0",
"argon2": "^0.31.1",
"autolinker": "4.0.0",
"autwh": "0.1.0",
"aws-sdk": "2.1413.0",
"axios": "^1.4.0",
"archiver": "6.0.1",
"argon2": "^0.31.2",
"aws-sdk": "2.1498.0",
"axios": "^1.6.2",
"bcryptjs": "2.4.3",
"blurhash": "2.0.5",
"bull": "4.11.3",
"bull": "4.11.5",
"cacheable-lookup": "TheEssem/cacheable-lookup",
"cbor-x": "^1.5.4",
"chalk": "5.3.0",
"chalk-template": "0.4.0",
"chalk-template": "1.1.0",
"chokidar": "^3.5.3",
"cli-highlight": "2.1.11",
"color-convert": "2.0.1",
@ -62,19 +62,18 @@
"deep-email-validator": "0.1.21",
"escape-regexp": "0.0.1",
"feed": "4.2.2",
"file-type": "18.5.0",
"firefish-js": "workspace:*",
"file-type": "18.7.0",
"fluent-ffmpeg": "2.1.2",
"got": "13.0.0",
"gunzip-maybe": "^1.4.2",
"happy-dom": "^11.0.2",
"happy-dom": "^12.10.3",
"hpagent": "1.2.0",
"ioredis": "5.3.2",
"ip-cidr": "3.1.0",
"is-svg": "5.0.0",
"js-yaml": "4.1.0",
"json5": "2.2.3",
"jsonld": "8.2.1",
"jsonld": "8.3.1",
"jsrsasign": "10.8.6",
"koa": "2.14.2",
"koa-body": "^6.0.1",
@ -86,117 +85,117 @@
"koa-remove-trailing-slashes": "2.0.3",
"koa-send": "5.0.1",
"koa-slow": "2.1.0",
"koa-views": "7.0.2",
"megalodon": "8.1.1",
"meilisearch": "0.34.1",
"megalodon": "workspace:*",
"meilisearch": "0.35.0",
"mfm-js": "0.23.3",
"mime-types": "2.1.35",
"msgpackr": "1.9.7",
"multer": "1.4.4-lts.1",
"msgpackr": "^1.9.9",
"multer": "1.4.5-lts.1",
"native-utils": "link:native-utils",
"nested-property": "4.0.0",
"node-fetch": "3.3.2",
"nodemailer": "6.9.4",
"nodemailer": "6.9.7",
"nsfwjs": "2.4.2",
"opencc-js": "^1.0.5",
"os-utils": "0.0.14",
"otpauth": "^9.1.4",
"otpauth": "^9.2.0",
"parse5": "7.1.2",
"pg": "8.11.3",
"private-ip": "3.0.1",
"probe-image-size": "7.2.3",
"promise-limit": "2.7.0",
"punycode": "2.3.0",
"pureimage": "0.4.8",
"punycode": "2.3.1",
"pureimage": "0.4.13",
"qrcode": "1.5.3",
"qs": "6.11.2",
"random-seed": "0.3.0",
"ratelimiter": "3.4.1",
"re2": "1.20.3",
"re2": "1.20.8",
"redis-semaphore": "5.5.0",
"reflect-metadata": "0.1.13",
"rename": "1.0.4",
"rndstr": "1.0.0",
"rss-parser": "3.13.0",
"sanitize-html": "2.11.0",
"seedrandom": "^3.0.5",
"semver": "7.5.4",
"sharp": "0.32.5",
"sharp": "0.32.6",
"sonic-channel": "^1.3.1",
"stringz": "2.1.0",
"summaly": "2.7.0",
"syslog-pro": "1.0.0",
"systeminformation": "5.21.3",
"systeminformation": "5.21.17",
"tar-stream": "^3.1.6",
"tesseract.js": "^4.1.1",
"tesseract.js": "^5.0.3",
"tinycolor2": "1.6.0",
"tinyld": "^1.3.4",
"tmp": "0.2.1",
"twemoji-parser": "14.0.0",
"typeorm": "0.3.17",
"ulid": "2.3.0",
"uuid": "9.0.0",
"web-push": "3.6.5",
"uuid": "9.0.1",
"web-push": "3.6.6",
"websocket": "1.0.34",
"xev": "3.0.2"
},
"devDependencies": {
"@swc/cli": "^0.1.62",
"@swc/cli": "^0.1.63",
"@swc/core": "1.3.78",
"@types/adm-zip": "^0.5.0",
"@types/bcryptjs": "2.4.2",
"@types/escape-regexp": "0.0.1",
"@types/fluent-ffmpeg": "2.1.21",
"@types/js-yaml": "4.0.5",
"@types/jsonld": "1.5.9",
"@types/jsrsasign": "10.5.8",
"@types/koa": "2.13.8",
"@types/koa-bodyparser": "4.3.10",
"@types/koa-cors": "0.0.2",
"@types/koa-favicon": "2.0.21",
"@types/koa-logger": "3.1.2",
"@types/koa-mount": "4.0.2",
"@types/koa-send": "4.1.3",
"@types/koa-views": "7.0.0",
"@types/koa__cors": "3.3.0",
"@types/koa__multer": "2.0.4",
"@types/koa__router": "8.0.11",
"@types/mocha": "9.1.1",
"@types/node": "18.11.18",
"@types/node-fetch": "3.0.3",
"@types/nodemailer": "6.4.9",
"@types/oauth": "0.9.1",
"@types/probe-image-size": "^7.2.0",
"@types/pug": "2.0.6",
"@types/punycode": "2.1.0",
"@types/qrcode": "1.5.1",
"@types/qs": "6.9.7",
"@types/random-seed": "0.3.3",
"@types/ratelimiter": "3.4.4",
"@types/redis": "4.0.11",
"@types/rename": "1.0.4",
"@types/sanitize-html": "2.9.0",
"@types/semver": "7.5.0",
"@types/sinonjs__fake-timers": "8.1.2",
"@types/tinycolor2": "1.4.3",
"@types/tmp": "0.2.3",
"@types/uuid": "9.0.2",
"@types/web-push": "3.3.2",
"@types/websocket": "1.0.5",
"@types/ws": "8.5.5",
"@types/adm-zip": "^0.5.4",
"@types/bcryptjs": "2.4.6",
"@types/color-convert": "^2.0.3",
"@types/content-disposition": "^0.5.8",
"@types/escape-regexp": "0.0.3",
"@types/fluent-ffmpeg": "2.1.24",
"@types/js-yaml": "4.0.9",
"@types/jsonld": "1.5.12",
"@types/jsrsasign": "10.5.12",
"@types/koa": "2.13.11",
"@types/koa-bodyparser": "4.3.12",
"@types/koa-cors": "0.0.5",
"@types/koa-favicon": "2.1.3",
"@types/koa-logger": "3.1.5",
"@types/koa-mount": "4.0.5",
"@types/koa-send": "4.1.6",
"@types/koa__cors": "4.0.3",
"@types/koa__multer": "2.0.7",
"@types/koa__router": "12.0.4",
"@types/mocha": "10.0.4",
"@types/node": "20.9.0",
"@types/node-fetch": "2.6.9",
"@types/nodemailer": "6.4.14",
"@types/oauth": "0.9.4",
"@types/opencc-js": "^1.0.3",
"@types/pg": "^8.10.9",
"@types/probe-image-size": "^7.2.3",
"@types/pug": "2.0.9",
"@types/punycode": "2.1.2",
"@types/qrcode": "1.5.5",
"@types/qs": "6.9.10",
"@types/random-seed": "0.3.5",
"@types/ratelimiter": "3.4.6",
"@types/rename": "1.0.7",
"@types/sanitize-html": "2.9.4",
"@types/semver": "7.5.5",
"@types/sinonjs__fake-timers": "8.1.5",
"@types/syslog-pro": "^1.0.3",
"@types/tinycolor2": "1.4.6",
"@types/tmp": "0.2.6",
"@types/uuid": "9.0.7",
"@types/web-push": "3.6.3",
"@types/websocket": "1.0.9",
"@types/ws": "8.5.9",
"cross-env": "7.0.3",
"eslint": "^8.46.0",
"execa": "6.1.0",
"eslint": "^8.53.0",
"execa": "8.0.1",
"json5-loader": "4.0.1",
"mocha": "10.2.0",
"pug": "3.0.2",
"strict-event-emitter-types": "2.0.0",
"swc-loader": "^0.2.3",
"ts-loader": "9.4.4",
"ts-loader": "9.5.1",
"ts-node": "10.9.1",
"tsconfig-paths": "4.2.0",
"typescript": "5.1.6",
"webpack": "^5.88.2",
"ws": "8.13.0"
"typescript": "5.2.2",
"webpack": "^5.89.0",
"ws": "8.14.2"
}
}

View file

@ -10,10 +10,9 @@ import semver from "semver";
import Logger from "@/services/logger.js";
import loadConfig from "@/config/load.js";
import type { Config } from "@/config/types.js";
import { lessThan } from "@/prelude/array.js";
import { envOption } from "../env.js";
import { envOption } from "@/env.js";
import { showMachineInfo } from "@/misc/show-machine-info.js";
import { db, initDb } from "../db/postgre.js";
import { db, initDb } from "@/db/postgre.js";
const _filename = fileURLToPath(import.meta.url);
const _dirname = dirname(_filename);
@ -149,9 +148,7 @@ function showNodejsVersion(): void {
nodejsLogger.info(`Version ${process.version} detected.`);
const minVersion = fs
.readFileSync(`${_dirname}/../../../../.node-version`, "utf-8")
.trim();
const minVersion = "v18.16.0";
if (semver.lt(process.version, minVersion)) {
nodejsLogger.error(`At least Node.js ${minVersion} required!`);
process.exit(1);

View file

@ -1,6 +1,5 @@
import cluster from "node:cluster";
import { initDb } from "../db/postgre.js";
import config from "@/config/index.js";
import { initDb } from "@/db/postgre.js";
import os from "node:os";
/**

View file

@ -1,7 +1,7 @@
import config from "@/config/index.js";
import {
DB_MAX_NOTE_TEXT_LENGTH,
DB_MAX_IMAGE_COMMENT_LENGTH,
DB_MAX_NOTE_TEXT_LENGTH,
} from "@/misc/hard-limits.js";
export const MAX_NOTE_TEXT_LENGTH = Math.min(
@ -14,10 +14,8 @@ export const MAX_CAPTION_TEXT_LENGTH = Math.min(
);
export const SECOND = 1000;
export const SEC = 1000; // why do we need this duplicate here?
export const MINUTE = 60 * SEC;
export const MIN = 60 * SEC; // why do we need this duplicate here?
export const HOUR = 60 * MIN;
export const MINUTE = 60 * SECOND;
export const HOUR = 60 * MINUTE;
export const DAY = 24 * HOUR;
export const USER_ONLINE_THRESHOLD = 10 * MINUTE;

View file

@ -1,5 +1,5 @@
import Xev from "xev";
import { deliverQueue, inboxQueue } from "../queue/queues.js";
import { deliverQueue, inboxQueue } from "@/queue/queues.js";
const ev = new Xev();

View file

@ -2,7 +2,7 @@ import si from "systeminformation";
import Xev from "xev";
import * as osUtils from "os-utils";
import { fetchMeta } from "@/misc/fetch-meta.js";
import meilisearch from "../db/meilisearch.js";
import meilisearch from "@/db/meilisearch.js";
const ev = new Xev();

View file

@ -154,7 +154,7 @@ function timestampToUnix(timestamp: string) {
if (unix === 0) {
// Try to parse the timestamp as JavaScript Date
const date = Date.parse(timestamp);
if (isNaN(date)) return 0;
if (Number.isNaN(date)) return 0;
unix = date / 1000;
}

View file

@ -74,9 +74,10 @@ import { UserIp } from "@/models/entities/user-ip.js";
import { NoteEdit } from "@/models/entities/note-edit.js";
import { entities as charts } from "@/services/chart/entities.js";
import { envOption } from "../env.js";
import { dbLogger } from "./logger.js";
import { redisClient } from "./redis.js";
// TODO?: should we avoid importing things from built directory?
import { nativeInitDatabase } from "native-utils/built/index.js";
const sqlLogger = dbLogger.createSubLogger("sql", "gray", false);

View file

@ -1,2 +1,2 @@
// rome-ignore lint/suspicious/noExplicitAny: i have no idea
// biome-ignore lint/suspicious/noExplicitAny: i have no idea
type FIXME = any;

View file

@ -1,5 +1,5 @@
/**
* Misskey Entry Point!
* Firefish Entry Point
*/
import { EventEmitter } from "node:events";

View file

@ -1,4 +1,4 @@
import { redisClient } from "../db/redis.js";
import { redisClient } from "@/db/redis.js";
import { Mutex } from "redis-semaphore";
/**

View file

@ -1,6 +1,6 @@
import fetch from "node-fetch";
import { URLSearchParams } from "node:url";
import { getAgentByUrl } from "./fetch.js";
import { getAgentByUrl } from "@/misc/fetch.js";
import config from "@/config/index.js";
export async function verifyRecaptcha(secret: string, response: string) {

View file

@ -2,11 +2,11 @@ import type { Antenna } from "@/models/entities/antenna.js";
import type { Note } from "@/models/entities/note.js";
import type { User } from "@/models/entities/user.js";
import { Blockings, UserProfiles } from "@/models/index.js";
import { getFullApAccount } from "./convert-host.js";
import { getFullApAccount } from "@/misc/convert-host.js";
import * as Acct from "@/misc/acct.js";
import type { Packed } from "./schema.js";
import { Cache } from "./cache.js";
import { getWordHardMute } from "./check-word-mute.js";
import type { Packed } from "@/misc/schema.js";
import { Cache } from "@/misc/cache.js";
import { getWordHardMute } from "@/misc/check-word-mute.js";
const blockingCache = new Cache<User["id"][]>("blocking", 60 * 5);
const mutedWordsCache = new Cache<string[][] | undefined>("mutedWords", 60 * 5);

View file

@ -1,4 +1,4 @@
import twemoji from "twemoji-parser/dist/lib/regex.js";
import twemoji from "@twemoji/parser/dist/lib/regex.js";
const twemojiRegex = twemoji.default;
export const emojiRegex = new RegExp(`(${twemojiRegex.source})`);

View file

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

View file

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

View file

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

View file

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

View file

@ -383,6 +383,12 @@ export class Meta {
})
public ToSUrl: string | null;
@Column("jsonb", {
default: [],
nullable: false,
})
public moreUrls: [string, string][];
@Column("varchar", {
length: 512,
default: "https://git.joinfirefish.org/firefish/firefish",

View file

@ -7,7 +7,6 @@ import {
ManyToOne,
} from "typeorm";
import { User } from "./user.js";
import { Note } from "./note.js";
import { id } from "../id.js";
@Entity()

View file

@ -1,14 +1,5 @@
import {
PrimaryColumn,
Entity,
Index,
JoinColumn,
Column,
ManyToOne,
PrimaryGeneratedColumn,
} from "typeorm";
import { Entity, Index, Column, PrimaryGeneratedColumn } from "typeorm";
import { id } from "../id.js";
import { Note } from "./note.js";
import type { User } from "./user.js";
@Entity()

View file

@ -1,9 +1,7 @@
import {} from "typeorm";
import { db } from "@/db/postgre.js";
import { Announcement } from "./entities/announcement.js";
import { AnnouncementRead } from "./entities/announcement-read.js";
import { Instance } from "./entities/instance.js";
import { Poll } from "./entities/poll.js";
import { PollVote } from "./entities/poll-vote.js";
import { Meta } from "./entities/meta.js";

View file

@ -2,12 +2,10 @@ import { db } from "@/db/postgre.js";
import { DriveFile } from "@/models/entities/drive-file.js";
import type { User } from "@/models/entities/user.js";
import { toPuny } from "@/misc/convert-host.js";
import { awaitAll, Promiseable } from "@/prelude/await-all.js";
import { awaitAll } from "@/prelude/await-all.js";
import type { Packed } from "@/misc/schema.js";
import config from "@/config/index.js";
import { query, appendQuery } from "@/prelude/url.js";
import { Meta } from "@/models/entities/meta.js";
import { fetchMeta } from "@/misc/fetch-meta.js";
import { Users, DriveFolders } from "../index.js";
import { deepClone } from "@/misc/clone.js";

View file

@ -1,4 +1,4 @@
import { In, Repository } from "typeorm";
import { In } from "typeorm";
import { Notification } from "@/models/entities/notification.js";
import { awaitAll } from "@/prelude/await-all.js";
import type { Packed } from "@/misc/schema.js";
@ -6,7 +6,6 @@ import type { Note } from "@/models/entities/note.js";
import type { NoteReaction } from "@/models/entities/note-reaction.js";
import type { User } from "@/models/entities/user.js";
import { aggregateNoteEmojis, prefetchEmojis } from "@/misc/populate-emojis.js";
import { notificationTypes } from "@/types.js";
import { db } from "@/db/postgre.js";
import {
Users,

View file

@ -7,7 +7,6 @@ import type { Packed } from "@/misc/schema.js";
import type { Promiseable } from "@/prelude/await-all.js";
import { awaitAll } from "@/prelude/await-all.js";
import { populateEmojis } from "@/misc/populate-emojis.js";
import { getAntennas } from "@/misc/antenna-cache.js";
import { USER_ACTIVE_THRESHOLD, USER_ONLINE_THRESHOLD } from "@/const.js";
import { Cache } from "@/misc/cache.js";
import { db } from "@/db/postgre.js";

View file

@ -6,7 +6,7 @@ import { Notes } from "@/models/index.js";
import { MoreThan } from "typeorm";
import { index } from "@/services/note/create.js";
import { Note } from "@/models/entities/note.js";
import meilisearch from "../../../db/meilisearch.js";
import meilisearch from "@/db/meilisearch.js";
const logger = queueLogger.createSubLogger("index-all-notes");

View file

@ -1,14 +1,12 @@
import type Bull from "bull";
import * as fs from "node:fs";
import { ulid } from "ulid";
import mime from "mime-types";
import archiver from "archiver";
import { queueLogger } from "../../logger.js";
import { addFile } from "@/services/drive/add-file.js";
import { format as dateFormat } from "date-fns";
import { Users, Emojis } from "@/models/index.js";
import {} from "@/queue/types.js";
import { createTemp, createTempDir } from "@/misc/create-temp.js";
import { downloadUrl } from "@/misc/download-url.js";
import config from "@/config/index.js";

View file

@ -5,7 +5,7 @@ import * as Acct from "@/misc/acct.js";
import { resolveUser } from "@/remote/resolve-user.js";
import { downloadTextFile } from "@/misc/download-text-file.js";
import { isSelfHost, toPuny } from "@/misc/convert-host.js";
import { Users, DriveFiles, Blockings } from "@/models/index.js";
import { Users, DriveFiles } from "@/models/index.js";
import type { DbUserImportJobData } from "@/queue/types.js";
import block from "@/services/blocking/create.js";
import { IsNull } from "typeorm";

View file

@ -5,7 +5,6 @@ import { queueLogger } from "../../logger.js";
import type Bull from "bull";
import { htmlToMfm } from "@/remote/activitypub/misc/html-to-mfm.js";
import { resolveNote } from "@/remote/activitypub/models/note.js";
import { Note } from "@/models/entities/note.js";
import { uploadFromUrl } from "@/services/drive/upload-from-url.js";
import type { DriveFile } from "@/models/entities/drive-file.js";
import { Notes, NoteEdits } from "@/models/index.js";

View file

@ -1,11 +1,12 @@
import type Bull from "bull";
import { Notes, PollVotes } from "@/models/index.js";
import { queueLogger } from "../logger.js";
// import { queueLogger } from "../logger.js";
import type { EndedPollNotificationJobData } from "@/queue/types.js";
import { createNotification } from "@/services/create-notification.js";
import { deliverQuestionUpdate } from "@/services/note/polls/update.js";
const logger = queueLogger.createSubLogger("ended-poll-notification");
// unused
// const logger = queueLogger.createSubLogger("ended-poll-notification");
export async function endedPollNotification(
job: Bull.Job<EndedPollNotificationJobData>,

View file

@ -22,6 +22,7 @@ import { StatusError } from "@/misc/fetch.js";
import type { CacheableRemoteUser } from "@/models/entities/user.js";
import type { UserPublickey } from "@/models/entities/user-publickey.js";
import { shouldBlockInstance } from "@/misc/should-block-instance.js";
import { verifySignature } from "@/remote/activitypub/check-fetch.js";
const logger = new Logger("inbox");
@ -114,6 +115,12 @@ export default async (job: Bull.Job<InboxJobData>): Promise<string> => {
);
}
if (httpSignatureValidated) {
if (!verifySignature(signature, authUser.key)) {
return "skip: Invalid HTTP signature";
}
}
// また、signatureのsignerは、activity.actorと一致する必要がある
if (!httpSignatureValidated || authUser.user.uri !== activity.actor) {
// 一致しなくても、でもLD-Signatureがありそうならそっちも見る

View file

@ -4,7 +4,6 @@ import { UserProfiles } from "@/models/index.js";
import { Not } from "typeorm";
import { queueLogger } from "../../logger.js";
import { verifyLink } from "@/services/fetch-rel-me.js";
import config from "@/config/index.js";
const logger = queueLogger.createSubLogger("verify-links");

View file

@ -1,4 +1,3 @@
import { URL } from "node:url";
import type Bull from "bull";
import Logger from "@/services/logger.js";
import type { WebhookDeliverJobData } from "../types.js";

View file

@ -8,7 +8,6 @@ import type {
CacheableRemoteUser,
CacheableUser,
} from "@/models/entities/user.js";
import { User } from "@/models/entities/user.js";
type Visibility = "public" | "home" | "followers" | "specified";

View file

@ -1,5 +1,5 @@
import { URL } from "url";
import httpSignature from "@peertube/http-signature";
import httpSignature, { IParsedSignature } from "@peertube/http-signature";
import config from "@/config/index.js";
import { fetchMeta } from "@/misc/fetch-meta.js";
import { toPuny } from "@/misc/convert-host.js";
@ -9,6 +9,9 @@ import { shouldBlockInstance } from "@/misc/should-block-instance.js";
import type { IncomingMessage } from "http";
import type { CacheableRemoteUser } from "@/models/entities/user.js";
import type { UserPublickey } from "@/models/entities/user-publickey.js";
import { verify } from "node:crypto";
import { toSingle } from "@/prelude/array.js";
import { createHash } from "node:crypto";
export async function hasSignature(req: IncomingMessage): Promise<string> {
const meta = await fetchMeta();
@ -28,10 +31,14 @@ export async function hasSignature(req: IncomingMessage): Promise<string> {
export async function checkFetch(req: IncomingMessage): Promise<number> {
const meta = await fetchMeta();
if (meta.secureMode || meta.privateMode) {
if (req.headers.host !== config.host) return 400;
let signature;
try {
signature = httpSignature.parseRequest(req, { headers: [] });
signature = httpSignature.parseRequest(req, {
headers: ["(request-target)", "host", "date"],
});
} catch (e) {
return 401;
}
@ -108,6 +115,8 @@ export async function checkFetch(req: IncomingMessage): Promise<number> {
if (!httpSignatureValidated) {
return 403;
}
return verifySignature(signature, authUser.key) ? 200 : 401;
}
return 200;
}
@ -130,3 +139,39 @@ export async function getSignatureUser(req: IncomingMessage): Promise<{
keyId.hash = "";
return await dbResolver.getAuthUserFromApId(getApId(keyId.toString()));
}
export function verifySignature(
sig: IParsedSignature,
key: UserPublickey,
): boolean {
if (!["hs2019", "rsa-sha256"].includes(sig.algorithm.toLowerCase()))
return false;
try {
return verify(
"rsa-sha256",
Buffer.from(sig.signingString, "utf8"),
key.keyPem,
Buffer.from(sig.params.signature, "base64"),
);
} catch {
// Algo not supported
return false;
}
}
export function verifyDigest(
body: string,
digest: string | string[] | undefined,
): boolean {
digest = toSingle(digest);
if (
body == null ||
digest == null ||
!digest.toLowerCase().startsWith("sha-256=")
)
return false;
return (
createHash("sha256").update(body).digest("base64") === digest.substring(8)
);
}

View file

@ -70,7 +70,7 @@ export function parseUri(value: string | IObject): UriParseResult {
export default class DbResolver {
/**
* AP Note => Misskey Note in DB
* AP Note => Firefish Note in DB
*/
public async getNoteFromApId(value: string | IObject): Promise<Note | null> {
const parsed = parseUri(value);
@ -114,7 +114,7 @@ export default class DbResolver {
}
/**
* AP Person => Misskey User in DB
* AP Person => Firefish User in DB
*/
public async getUserFromApId(
value: string | IObject,
@ -147,7 +147,7 @@ export default class DbResolver {
}
/**
* AP KeyId => Misskey User and Key
* AP KeyId => Firefish User and Key
*/
public async getAuthUserFromKeyId(keyId: string): Promise<{
user: CacheableRemoteUser;
@ -181,7 +181,7 @@ export default class DbResolver {
}
/**
* AP Actor id => Misskey User and Key
* AP Actor id => Firefish User and Key
*/
public async getAuthUserFromApId(uri: string): Promise<{
user: CacheableRemoteUser;

View file

@ -3,7 +3,7 @@ import type { IRead } from "../type.js";
import { getApId } from "../type.js";
import { isSelfHost, extractDbHost } from "@/misc/convert-host.js";
import { MessagingMessages } from "@/models/index.js";
import { readUserMessagingMessage } from "../../../server/api/common/read-messaging-message.js";
import { readUserMessagingMessage } from "@/server/api/common/read-messaging-message.js";
export const performReadActivity = async (
actor: CacheableRemoteUser,

View file

@ -1,5 +1,4 @@
import unfollow from "@/services/following/delete.js";
import cancelRequest from "@/services/following/requests/cancel.js";
import type { IAccept } from "../../type.js";
import type { CacheableRemoteUser } from "@/models/entities/user.js";
import { Followings } from "@/models/index.js";

View file

@ -1,6 +1,6 @@
import * as mfm from "mfm-js";
import type { Note } from "@/models/entities/note.js";
import { toHtml } from "../../../mfm/to-html.js";
import { toHtml } from "@/mfm/to-html.js";
export default function (note: Note) {
if (!note.text) return "";

View file

@ -1,6 +1,6 @@
import type { IObject } from "../type.js";
import { extractApHashtagObjects } from "../models/tag.js";
import { fromHtml } from "../../../mfm/from-html.js";
import { fromHtml } from "@/mfm/from-html.js";
export function htmlToMfm(html: string, tag?: IObject | IObject[]) {
const hashtagNames = extractApHashtagObjects(tag)

View file

@ -1,11 +1,10 @@
import { uploadFromUrl } from "@/services/drive/upload-from-url.js";
import type { CacheableRemoteUser } from "@/models/entities/user.js";
import { IRemoteUser } from "@/models/entities/user.js";
import Resolver from "../resolver.js";
import { fetchMeta } from "@/misc/fetch-meta.js";
import { apLogger } from "../logger.js";
import type { DriveFile } from "@/models/entities/drive-file.js";
import { DriveFiles, Users } from "@/models/index.js";
import { DriveFiles } from "@/models/index.js";
import { truncate } from "@/misc/truncate.js";
import { DB_MAX_IMAGE_COMMENT_LENGTH } from "@/misc/hard-limits.js";

View file

@ -1,7 +1,6 @@
import promiseLimit from "promise-limit";
import { toArray, unique } from "@/prelude/array.js";
import type { CacheableUser } from "@/models/entities/user.js";
import { User } from "@/models/entities/user.js";
import type { IObject, IApMention } from "../type.js";
import { isMention } from "../type.js";
import Resolver from "../resolver.js";

View file

@ -6,10 +6,7 @@ import post from "@/services/note/create.js";
import { extractMentionedUsers } from "@/services/note/create.js";
import { resolvePerson } from "./person.js";
import { resolveImage } from "./image.js";
import type {
ILocalUser,
CacheableRemoteUser,
} from "@/models/entities/user.js";
import type { CacheableRemoteUser } from "@/models/entities/user.js";
import { htmlToMfm } from "../misc/html-to-mfm.js";
import { extractApHashtags } from "./tag.js";
import { unique, toArray, toSingle } from "@/prelude/array.js";
@ -52,7 +49,6 @@ import { In } from "typeorm";
import { DB_MAX_IMAGE_COMMENT_LENGTH } from "@/misc/hard-limits.js";
import { truncate } from "@/misc/truncate.js";
import { type Size, getEmojiSize } from "@/misc/emoji-meta.js";
import { fetchMeta } from "@/misc/fetch-meta.js";
import { langmap } from "@/misc/langmap.js";
const logger = apLogger;

View file

@ -8,7 +8,6 @@ import { updateUsertags } from "@/services/update-hashtag.js";
import {
Users,
Instances,
DriveFiles,
Followings,
UserProfiles,
UserPublickeys,
@ -33,8 +32,8 @@ import { publishInternalEvent } from "@/services/stream.js";
import { db } from "@/db/postgre.js";
import { apLogger } from "../logger.js";
import { htmlToMfm } from "../misc/html-to-mfm.js";
import { fromHtml } from "../../../mfm/from-html.js";
import type { IActor, IObject, IApPropertyValue } from "../type.js";
import { fromHtml } from "@/mfm/from-html.js";
import type { IActor, IObject } from "../type.js";
import {
isCollectionOrOrderedCollection,
isCollection,
@ -313,7 +312,9 @@ export async function createPerson(
await transactionalEntityManager.save(
new UserProfile({
userId: user.id,
description: person.summary
description: person._misskey_summary
? truncate(person._misskey_summary, summaryLength)
: person.summary
? htmlToMfm(truncate(person.summary, summaryLength), person.tag)
: null,
url: url,
@ -456,7 +457,7 @@ export async function updatePerson(
const emojiNames = emojis.map((emoji) => emoji.name);
const { fields } = analyzeAttachments(person.attachment || []);
const fields = analyzeAttachments(person.attachment || []);
const tags = extractApHashtags(person.tag)
.map((tag) => normalizeForSearch(tag))
@ -589,7 +590,9 @@ export async function updatePerson(
{
url: url,
fields,
description: person.summary
description: person._misskey_summary
? truncate(person._misskey_summary, summaryLength)
: person.summary
? htmlToMfm(truncate(person.summary, summaryLength), person.tag)
: null,
birthday: bday ? bday[0] : null,

View file

@ -1,8 +1,5 @@
import config from "@/config/index.js";
import { IObject, IActivity } from "@/remote/activitypub/type.js";
import type { ILocalUser } from "@/models/entities/user.js";
import { IRemoteUser } from "@/models/entities/user.js";
import { getInstanceActor } from "@/services/instance-actor.js";
// to anonymise reporters, the reporting actor must be a system user
// object has to be a uri or array of uris

View file

@ -43,6 +43,7 @@ export const renderActivity = (x: any): IActivity | null => {
_misskey_talk: "misskey:_misskey_talk",
_misskey_reaction: "misskey:_misskey_reaction",
_misskey_votes: "misskey:_misskey_votes",
_misskey_summary: "misskey:_misskey_summary",
isCat: "misskey:isCat",
// Fedibird
fedibird: "http://fedibird.com/ns#",

View file

@ -74,6 +74,7 @@ export async function renderPerson(user: ILocalUser) {
summary: profile.description
? toHtml(mfm.parse(profile.description))
: null,
_misskey_summary: profile.description,
icon: avatar ? renderImage(avatar) : null,
image: banner ? renderImage(banner) : null,
tag,

View file

@ -1,6 +1,5 @@
import config from "@/config/index.js";
import type { User } from "@/models/entities/user.js";
import { ILocalUser } from "@/models/entities/user.js";
export default (object: any, user: { id: User["id"] }) => {
if (object == null) return null;

View file

@ -7,13 +7,7 @@ import { extractDbHost, isSelfHost } from "@/misc/convert-host.js";
import { signedGet } from "./request.js";
import type { IObject, ICollection, IOrderedCollection } from "./type.js";
import { isCollectionOrOrderedCollection, getApId } from "./type.js";
import {
FollowRequests,
Notes,
NoteReactions,
Polls,
Users,
} from "@/models/index.js";
import { Notes, NoteReactions, Polls, Users } from "@/models/index.js";
import { parseUri } from "./db-resolver.js";
import renderNote from "@/remote/activitypub/renderer/note.js";
import { renderLike } from "@/remote/activitypub/renderer/like.js";

View file

@ -205,6 +205,7 @@ export interface IActor extends IObject {
};
"vcard:bday"?: string;
"vcard:Address"?: string;
_misskey_summary?: string;
}
export const isCollection = (object: IObject): object is ICollection =>

View file

@ -1,5 +1,5 @@
import Router from "@koa/router";
import json from "koa-json-body";
import bodyParser from "koa-bodyparser";
import httpSignature from "@peertube/http-signature";
import { In, IsNull, Not } from "typeorm";
@ -9,7 +9,7 @@ import renderKey from "@/remote/activitypub/renderer/key.js";
import { renderPerson } from "@/remote/activitypub/renderer/person.js";
import renderEmoji from "@/remote/activitypub/renderer/emoji.js";
import { inbox as processInbox } from "@/queue/index.js";
import { isSelfHost, toPuny } from "@/misc/convert-host.js";
import { isSelfHost } from "@/misc/convert-host.js";
import {
Notes,
Users,
@ -22,8 +22,8 @@ import { renderLike } from "@/remote/activitypub/renderer/like.js";
import { getUserKeypair } from "@/misc/keypair-store.js";
import {
checkFetch,
hasSignature,
getSignatureUser,
verifyDigest,
} from "@/remote/activitypub/check-fetch.js";
import { getInstanceActor } from "@/services/instance-actor.js";
import { fetchMeta } from "@/misc/fetch-meta.js";
@ -33,6 +33,8 @@ import Following from "./activitypub/following.js";
import Followers from "./activitypub/followers.js";
import Outbox, { packActivity } from "./activitypub/outbox.js";
import { serverLogger } from "./index.js";
import config from "@/config/index.js";
import Koa from "koa";
// Init router
const router = new Router();
@ -40,15 +42,27 @@ const router = new Router();
//#region Routing
function inbox(ctx: Router.RouterContext) {
if (ctx.req.headers.host !== config.host) {
ctx.status = 400;
return;
}
let signature;
try {
signature = httpSignature.parseRequest(ctx.req, { headers: [] });
signature = httpSignature.parseRequest(ctx.req, {
headers: ["(request-target)", "digest", "host", "date"],
});
} catch (e) {
ctx.status = 401;
return;
}
if (!verifyDigest(ctx.request.rawBody, ctx.headers.digest)) {
ctx.status = 401;
return;
}
processInbox(ctx.request.body, signature);
ctx.status = 202;
@ -73,9 +87,23 @@ export function setResponseType(ctx: Router.RouterContext) {
}
}
async function parseJsonBodyOrFail(ctx: Router.RouterContext, next: Koa.Next) {
const koaBodyParser = bodyParser({
enableTypes: ["json"],
detectJSON: () => true,
});
try {
await koaBodyParser(ctx, next);
} catch {
ctx.status = 400;
return;
}
}
// inbox
router.post("/inbox", json(), inbox);
router.post("/users/:user/inbox", json(), inbox);
router.post("/inbox", parseJsonBodyOrFail, inbox);
router.post("/users/:user/inbox", parseJsonBodyOrFail, inbox);
// note
router.get("/notes/:note", async (ctx, next) => {

View file

@ -28,13 +28,19 @@ export default async (ctx: Router.RouterContext) => {
return;
}
const pinings = await UserNotePinings.find({
const pinning = await UserNotePinings.find({
where: { userId: user.id },
order: { id: "DESC" },
});
const pinnedNotes = await Promise.all(
pinings.map((pining) => Notes.findOneByOrFail({ id: pining.noteId })),
const pinnedNotes = (
await Promise.all(
pinning.map((pinnedNote) =>
this.notesRepository.findOneByOrFail({ id: pinnedNote.noteId }),
),
)
).filter(
(note) => !note.localOnly && ["public", "home"].includes(note.visibility),
);
const renderedNotes = await Promise.all(

View file

@ -1,7 +1,6 @@
import { performance } from "perf_hooks";
import type Koa from "koa";
import type { CacheableLocalUser } from "@/models/entities/user.js";
import { User } from "@/models/entities/user.js";
import type { AccessToken } from "@/models/entities/access-token.js";
import { getIpHash } from "@/misc/get-ip-hash.js";
import { limiter } from "./limiter.js";
@ -10,7 +9,6 @@ import endpoints from "./endpoints.js";
import compatibility from "./compatibility.js";
import { ApiError } from "./error.js";
import { apiLogger } from "./logger.js";
import type { AccessToken } from "@/models/entities/access-token.js";
import { fetchMeta } from "@/misc/fetch-meta.js";
const accessDenied = {

View file

@ -1,7 +1,6 @@
import * as fs from "node:fs";
import Ajv from "ajv";
import type { CacheableLocalUser } from "@/models/entities/user.js";
import { ILocalUser } from "@/models/entities/user.js";
import type { Schema, SchemaType } from "@/misc/schema.js";
import type { AccessToken } from "@/models/entities/access-token.js";
import type { IEndpointMeta } from "./endpoints.js";

View file

@ -1,6 +1,6 @@
import define from "../../define.js";
import define from "@/server/api/define.js";
import { AbuseUserReports } from "@/models/index.js";
import { makePaginationQuery } from "../../common/make-pagination-query.js";
import { makePaginationQuery } from "@/server/api/common/make-pagination-query.js";
export const meta = {
tags: ["admin"],

View file

@ -1,6 +1,6 @@
import define from "../../../define.js";
import define from "@/server/api/define.js";
import { Users } from "@/models/index.js";
import { signup } from "../../../common/signup.js";
import { signup } from "@/server/api/common/signup.js";
import { IsNull } from "typeorm";
export const meta = {

View file

@ -1,4 +1,4 @@
import define from "../../../define.js";
import define from "@/server/api/define.js";
import { Users } from "@/models/index.js";
import { doPostSuspend } from "@/services/suspend-user.js";
import { publishUserEvent } from "@/services/stream.js";

View file

@ -2,7 +2,7 @@ import config from "@/config/index.js";
import { Meta } from "@/models/entities/meta.js";
import { insertModerationLog } from "@/services/insert-moderation-log.js";
import { db } from "@/db/postgre.js";
import define from "../../../define.js";
import define from "@/server/api/define.js";
export const meta = {
tags: ["admin"],

View file

@ -1,4 +1,4 @@
import define from "../../../define.js";
import define from "@/server/api/define.js";
import { Ads } from "@/models/index.js";
import { genId } from "@/misc/gen-id.js";

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