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) # Proxy remote files (default: false)
#proxyRemoteFiles: true #proxyRemoteFiles: true
# Use authorized fetch for outgoing requests
signToActivityPubGet: true
#allowedPrivateNetworks: [ #allowedPrivateNetworks: [
# '127.0.0.1/32' # '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/db
!/packages/backend/src/server/api/endpoints/drive/files !/packages/backend/src/server/api/endpoints/drive/files
packages/megalodon/lib
packages/megalodon/.idea
# blender backups # blender backups
*.blend1 *.blend1
*.blend2 *.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 - The tag name must be the version
## Development ## 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. 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 ## Testing
- Test codes are located in [`/test`](/test). - Test codes are located in [`/test`](/test).
@ -119,20 +119,13 @@ yarn test
#### Run specify 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 ## Vue
Misskey uses Vue(v3) as its front-end framework. Firefish uses Vue(v3) as its front-end framework.
- Use TypeScript. - 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. - 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 ## nirax

View file

@ -1,5 +1,5 @@
## Install dev and compilation dependencies, build files ## Install dev and compilation dependencies, build files
FROM node:latest as build FROM node:20-slim as build
WORKDIR /firefish WORKDIR /firefish
# Install compilation dependencies # 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/client/package.json packages/client/package.json
COPY packages/sw/package.json packages/sw/package.json COPY packages/sw/package.json packages/sw/package.json
COPY packages/firefish-js/package.json packages/firefish-js/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/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-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 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 RUN pnpm i --prod --frozen-lockfile
## Runtime container ## Runtime container
FROM node:latest FROM node:20-slim
WORKDIR /firefish WORKDIR /firefish
# Install runtime dependencies # 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 . ./
COPY --from=build /firefish/packages/megalodon /firefish/packages/megalodon
# Copy node modules # Copy node modules
COPY --from=build /firefish/node_modules /firefish/node_modules COPY --from=build /firefish/node_modules /firefish/node_modules
COPY --from=build /firefish/packages/backend/node_modules /firefish/packages/backend/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 └─────────────────────────────────── #───┘ 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/ ------------+ # +----- 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 # An encrypted connection with HTTPS is highly recommended
# because tokens may be transferred in GET requests. # 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 port: 3000
# ┌──────────────────────────┐ # ┌──────────────────────────┐

View file

@ -139,10 +139,10 @@ describe("After user singed in", () => {
it("note", () => { it("note", () => {
cy.get("[data-cy-open-post-form]").click(); 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.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: services:
web: 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 container_name: firefish_web
restart: unless-stopped restart: unless-stopped
depends_on: depends_on:

View file

@ -4,6 +4,11 @@ Breaking changes are indicated by the :warning: icon.
## v1.0.5 (unreleased) ## 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 ### dev18
- :warning: response of `meta` no longer includes the following: - :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/bios.js",
"./packages/backend/src/server/web/cli.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( .pipe(
terser({ terser({
toplevel: true, toplevel: true,

View file

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

View file

@ -112,7 +112,7 @@ you: "Tu"
clickToShow: "Fes clic per a mostrar" clickToShow: "Fes clic per a mostrar"
sensitive: "NSFW" sensitive: "NSFW"
add: "Afegeix" add: "Afegeix"
reaction: "Reaccions" reaction: "Reacció"
reactionSetting: "Reaccions a mostrar al selector de reaccions" reactionSetting: "Reaccions a mostrar al selector de reaccions"
reactionSettingDescription2: "Arrossega per reordenar, fes clic per suprimir, prem reactionSettingDescription2: "Arrossega per reordenar, fes clic per suprimir, prem
\"+\" per afegir." \"+\" per afegir."
@ -792,11 +792,11 @@ customEmojis: Emojis personalitzats
cacheRemoteFilesDescription: Quan aquesta opció està desactivada, els fitxers remots cacheRemoteFilesDescription: Quan aquesta opció està desactivada, els fitxers remots
es carreguen directament del servidor remot. Desactivar-la farà que baixi l'ús d'emmagatzematge, 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. 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. 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 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 prevenir cadenes de interaccions infinites amb altres comptes automatitzats a més
interns de Firefish per tractar aquest compte com un bot. d'ajustar els sistemes interns de Firefish per tractar aquest compte com automatitzat.
flagAsCat: Ets un gat? 🐱 flagAsCat: Ets un gat? 🐱
flagShowTimelineReplies: Mostra respostes a la línia de temps flagShowTimelineReplies: Mostra respostes a la línia de temps
flagAsCatDescription: Guanyaràs unes orelles de gat i parlares com un gat! 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} agreeTo: Estic d'acord amb {0}
activity: Activitat activity: Activitat
home: Inici 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 themeForDarkMode: Tema a fer servir en mode fosc
light: Clar light: Clar
registeredDate: Data de registre registeredDate: Data de registre
@ -1051,7 +1051,7 @@ popularTags: Etiquetes populars
about: Sobre about: Sobre
recentlyUpdatedUsers: Usuaris actius fa poc recentlyUpdatedUsers: Usuaris actius fa poc
recentlyRegisteredUsers: Usuaris registrats fa poc recentlyRegisteredUsers: Usuaris registrats fa poc
recentlyDiscoveredUsers: Nous suaris descoberts recentlyDiscoveredUsers: Nous usuaris descoberts
administrator: Administrador administrator: Administrador
token: Token token: Token
registerSecurityKey: Registreu una clau de seguretat registerSecurityKey: Registreu una clau de seguretat
@ -1550,7 +1550,7 @@ troubleshooting: Resolució de problemes
learnMore: Més informació learnMore: Més informació
misskeyUpdated: Firefish s'ha actualitzat! misskeyUpdated: Firefish s'ha actualitzat!
translate: Tradueix translate: Tradueix
translatedFrom: Traduït desde {x} translatedFrom: Traduït del {x}
aiChanMode: Ai-chan a la interfície d'usuari clàssica aiChanMode: Ai-chan a la interfície d'usuari clàssica
keepCw: Mantenir els avisos de contingut keepCw: Mantenir els avisos de contingut
pubSub: Comptes Pub/Sub 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 selectChannel: Selecciona un canal
isLocked: Aquest compte té les següents aprovacions isLocked: Aquest compte té les següents aprovacions
isPatron: Mecenes de Firefish isPatron: Mecenes de Firefish
isBot: Aquest compte és un bot isBot: Aquest es un compte automatitzat
isModerator: Moderador isModerator: Moderador
isAdmin: Administrador isAdmin: Administrador
_filters: _filters:
@ -2199,3 +2199,22 @@ openServerInfo: Mostra la informació del servidor fent clic al símbol del serv
en un missatge en un missatge
vibrate: Activar vibracions vibrate: Activar vibracions
clickToShowPatterns: Fes clic per veure patrons de mòduls 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" connectedTo: "Mit folgenden Nutzerkonten verknüpft"
notesAndReplies: "Beiträge und Antworten" notesAndReplies: "Beiträge und Antworten"
withFiles: "Beiträge mit Dateien" withFiles: "Beiträge mit Dateien"
attachedToNotes: "Beiträge mit dieser Datei"
showAttachedNotes: "Zeige Beiträge mit dieser Datei"
silence: "stummschalten" silence: "stummschalten"
silenceConfirm: "Sind Sie sicher, dass Sie diesen Benutzer Stummschalten möchten?" silenceConfirm: "Sind Sie sicher, dass Sie diesen Benutzer Stummschalten möchten?"
unsilence: "Stummschaltung aufheben" unsilence: "Stummschaltung aufheben"
@ -2216,3 +2218,4 @@ openServerInfo: Anzeigen von Serverinformationen durch Anklicken des Server-Tick
in einem Beitrag in einem Beitrag
vibrate: Vibrationen abspielen vibrate: Vibrationen abspielen
clickToShowPatterns: Klicken um Modul-Muster anzuzeigen 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 cacheRemoteFilesDescription: "When this setting is disabled, remote files are loaded
directly from the remote server. Disabling this will decrease storage usage, but directly from the remote server. Disabling this will decrease storage usage, but
increase traffic, as thumbnails will not be generated." 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. 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 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 chains with other automated accounts and adjust Firefish's internal systems to treat this
as a bot." account as an automated account."
flagAsCat: "Are you a cat? 😺" flagAsCat: "Are you a cat? 😺"
flagAsCatDescription: "You'll get cat ears and speak like a cat!" flagAsCatDescription: "You'll get cat ears and speak like a cat!"
flagSpeakAsCat: "Speak as a cat" flagSpeakAsCat: "Speak as a cat"
@ -312,7 +312,7 @@ agreeTo: "I agree to {0}"
tos: "Terms of Service" tos: "Terms of Service"
start: "Begin" start: "Begin"
home: "Home" home: "Home"
remoteUserCaution: "Information from remote users may be incomplete." remoteUserCaution: "Information from remote users are incomplete."
activity: "Activity" activity: "Activity"
images: "Images" images: "Images"
birthday: "Birthday" birthday: "Birthday"
@ -429,6 +429,8 @@ withReplies: "Include replies"
connectedTo: "Following account(s) are connected" connectedTo: "Following account(s) are connected"
notesAndReplies: "Posts and replies" notesAndReplies: "Posts and replies"
withFiles: "Including files" withFiles: "Including files"
attachedToNotes: "Posts with this file"
showAttachedNotes: "Show posts with this file"
silence: "Silence" silence: "Silence"
silenceConfirm: "Are you sure that you want to silence this user?" silenceConfirm: "Are you sure that you want to silence this user?"
unsilence: "Undo silencing" unsilence: "Undo silencing"
@ -1115,7 +1117,7 @@ noGraze: "Please disable the \"Graze for Mastodon\" browser extension, as it int
with Firefish." with Firefish."
silencedWarning: "This page is showing because these users are from servers your admin silencedWarning: "This page is showing because these users are from servers your admin
silenced, so they may potentially be spam." silenced, so they may potentially be spam."
isBot: "This account is a bot" isBot: "This account is automated"
isLocked: "This account has follow approvals" isLocked: "This account has follow approvals"
isModerator: "Moderator" isModerator: "Moderator"
isAdmin: "Administrator" isAdmin: "Administrator"
@ -1154,6 +1156,8 @@ detectPostLanguage: "Automatically detect the language and show a translate butt
vibrate: "Play vibrations" vibrate: "Play vibrations"
openServerInfo: "Show server information by clicking the server ticker on a post" openServerInfo: "Show server information by clicking the server ticker on a post"
iconSet: "Icon set" iconSet: "Icon set"
useEmojiCdn: "Get Twemoji from CDN"
useEmojiCdnDescription: "Use Twemoji from the JSDelivr CDN instead of the server's assets."
_sensitiveMediaDetection: _sensitiveMediaDetection:
description: "Reduces the effort of server moderation through automatically recognizing description: "Reduces the effort of server moderation through automatically recognizing
@ -2159,3 +2163,5 @@ _iconSets:
regular: "Regular" regular: "Regular"
fill: "Filled" fill: "Filled"
duotone: "Duotone" 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" soft: "Suave"
hard: "Duro" hard: "Duro"
mutedNotes: "Publicaciones silenciadas" 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: _instanceMute:
instanceMuteDescription: "Silencia todas las publicaciones e impusos de los servidores instanceMuteDescription: "Silencia todas las publicaciones e impusos de los servidores
seleccionados, incluyendo respuestas a los usuarios de las mismas." 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. que tu administrador ha silenciado, ya que son presumiblemente fuente de spam.
isBot: Esta cuenta es un bot isBot: Esta cuenta es un bot
clickToShowPatterns: Haz clic para mostrar patrones de módulos 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" clickToShow: "Cliquer pour afficher"
sensitive: "Contenu sensible" sensitive: "Contenu sensible"
add: "Ajouter" add: "Ajouter"
reaction: "Réactions" reaction: "Réaction"
reactionSetting: "Réactions à afficher dans le sélecteur de réactions" reactionSetting: "Réactions à afficher dans le sélecteur de réactions"
reactionSettingDescription2: "Déplacer pour réorganiser, cliquer pour effacer, utiliser reactionSettingDescription2: "Déplacer pour réorganiser, cliquer pour effacer, utiliser
« + » pour ajouter." « + » pour ajouter."
@ -1148,7 +1148,7 @@ _wordMute:
langDescription: Cacher du fil de publication les publications qui correspondent langDescription: Cacher du fil de publication les publications qui correspondent
à ces langues. à ces langues.
muteLangs: Langages filtrés 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). condition OU (OR).
_instanceMute: _instanceMute:
instanceMuteDescription2: "Séparer avec des sauts de lignes" instanceMuteDescription2: "Séparer avec des sauts de lignes"
@ -2225,3 +2225,14 @@ indexable: Indexable
languageForTranslation: Langage post-traduction languageForTranslation: Langage post-traduction
vibrate: Jouer les vibrations vibrate: Jouer les vibrations
clickToShowPatterns: Cliquer pour montrer les patrons de modules 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, introFirefish: Benvida! Firefish é unha plataforma de medios sociais de código aberto,
descentralizada e gratuíta para sempre!🚀 descentralizada e gratuíta para sempre!🚀
monthAndDay: '{day}/{month}' monthAndDay: '{day}/{month}'

View file

@ -148,11 +148,11 @@ cacheRemoteFiles: "Tembolokkan berkas remote"
cacheRemoteFilesDescription: "Ketika pengaturan ini dinonaktifkan, berkas luar akan cacheRemoteFilesDescription: "Ketika pengaturan ini dinonaktifkan, berkas luar akan
dimuat langsung dari server luar. Menonaktifkan ini akan mengurangi penggunaan penyimpanan, dimuat langsung dari server luar. Menonaktifkan ini akan mengurangi penggunaan penyimpanan,
tapi dapat menyebabkan meningkatkan lalu lintas, mengingat keluku tidak akan dihasilkan." tapi dapat menyebabkan meningkatkan lalu lintas, mengingat keluku tidak akan dihasilkan."
flagAsBot: "Atur akun ini sebagai Bot" flagAsBot: "Tandai akun ini sebagai akun otomatis"
flagAsBotDescription: "Jika akun ini dikendalikan oleh program, tetapkanlah opsi ini. flagAsBotDescription: "Jika akun ini dikendalikan oleh program, aktifkan opsi ini.
Jika diaktifkan, ini akan berfungsi sebagai tanda bagi pengembang lain untuk mencegah Jika diaktifkan, ini akan berfungsi sebagai tanda bagi pengembang lain untuk mencegah
interaksi berantai dengan bot lain dan menyesuaikan sistem internal Firefish untuk interaksi berantai dengan akun otomatis lain dan menyesuaikan sistem internal Firefish
memperlakukan akun ini sebagai bot." untuk memperlakukan akun ini sebagai akun otomatis."
flagAsCat: "Atur akun ini sebagai kucing" flagAsCat: "Atur akun ini sebagai kucing"
flagAsCatDescription: "Kamu akan mendapatkan telinga kucing dan berbicara seperti flagAsCatDescription: "Kamu akan mendapatkan telinga kucing dan berbicara seperti
seekor kucing!" seekor kucing!"
@ -278,8 +278,7 @@ agreeTo: "Saya setuju kepada {0}"
tos: "Syarat dan ketentuan" tos: "Syarat dan ketentuan"
start: "Mulai" start: "Mulai"
home: "Beranda" home: "Beranda"
remoteUserCaution: "Informasi ini mungkin tidak mutakhir, karena pengguna ini berasal remoteUserCaution: "Informasi dari pengguna luar tidak lengkap."
dari instansi luar."
activity: "Aktivitas" activity: "Aktivitas"
images: "Gambar" images: "Gambar"
birthday: "Tanggal lahir" 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 ke server lain! Jika kamu memiliki kode undangan server ini, harap masukkan di bawah
ini. ini.
enableCustomKaTeXMacro: Aktifkan makro KaTeX khusus enableCustomKaTeXMacro: Aktifkan makro KaTeX khusus
isBot: Akun ini bot isBot: Akun ini akun otomatis
customMOTD: MOTD khusus (pesan layar percik) customMOTD: MOTD khusus (pesan layar percik)
recommendedInstancesDescription: Server yang direkomendasikan dipisahkan dengan garis recommendedInstancesDescription: Server yang direkomendasikan dipisahkan dengan garis
baru untuk tampil di linimasa rekomendasi. baru untuk tampil di linimasa rekomendasi.
@ -2181,3 +2180,21 @@ openServerInfo: Tampilkan informasi server dengan mengeklik ticker server di seb
kiriman kiriman
vibrate: Putar getaran vibrate: Putar getaran
clickToShowPatterns: Klik untuk menampilkan pola modul 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 cacheRemoteFilesDescription: "Disabilitando questa opzione, i file remoti verranno
scaricati direttamente dal loro server. L'opzione permette di risparmiare spazio scaricati direttamente dal loro server. L'opzione permette di risparmiare spazio
ma aumenta il traffico di rete e non verranno generate anteprime." 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, flagAsBotDescription: "Se l'account esegue principalmente operazioni automatiche,
attiva quest'opzione. Quando attivata, opera come un segnalatore per gli altri sviluppatori attiva quest'opzione. Quando attivata, permette agli sviluppatori di prevenire catene
allo scopo di prevenire catene dinterazione senza fine con altri bot, e di adeguare dinterazione senza fine con altri account automatizzati. Inoltre imposta Firefish
i sistemi interni di Firefish perché trattino questo account come un bot." perché tratti questo account come automatizzato."
flagAsCat: "Sei un gatto? 😺" flagAsCat: "Sei un gatto? 😺"
flagAsCatDescription: "Ti compariranno le orecchie e parlerai come un gatto!" flagAsCatDescription: "Ti compariranno le orecchie e parlerai come un gatto!"
autoAcceptFollowed: "Accetta in automatico i follow dagli account che segui" autoAcceptFollowed: "Accetta in automatico i follow dagli account che segui"
@ -268,7 +268,7 @@ agreeTo: "Sono d'accordo con {0}"
tos: "Termini d'uso" tos: "Termini d'uso"
start: "Inizia" start: "Inizia"
home: "Home" home: "Home"
remoteUserCaution: "Le informazioni degli utenti remoti possono essere incomplete." remoteUserCaution: "Le informazioni degli utenti remoti sono incomplete."
activity: "Attività" activity: "Attività"
images: "Immagini" images: "Immagini"
birthday: "Compleanno" birthday: "Compleanno"
@ -2079,7 +2079,7 @@ noGraze: Per favore disattiva l'estenzione del browser "Graze for Mastodon", per
interferisce con Firefish. interferisce con Firefish.
silencedWarning: Vedi questa pagina perché gli utenti sono su un server che il tuo silencedWarning: Vedi questa pagina perché gli utenti sono su un server che il tuo
admin ha silenziato, quindi potrebbero essere spam. admin ha silenziato, quindi potrebbero essere spam.
isBot: Questo account è un bot isBot: Questo account è automatizzato
isLocked: Serve una approvazione per seguire questo account isLocked: Serve una approvazione per seguire questo account
moveFromDescription: Questa operazione crea un alias del vecchio account in modo che 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 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 un post
vibrate: Abilita la vibrazione vibrate: Abilita la vibrazione
clickToShowPatterns: Clicca per vedere i pattern del modulo 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: "おすすめ設定" settingGuide: "おすすめ設定"
cacheRemoteFiles: "リモートのファイルをキャッシュする" cacheRemoteFiles: "リモートのファイルをキャッシュする"
cacheRemoteFilesDescription: "この設定を無効にすると、リモートファイルをキャッシュせず直リンクします。サーバーのストレージを節約できますが、サムネイルが生成されないので通信量が増加します。" cacheRemoteFilesDescription: "この設定を無効にすると、リモートファイルをキャッシュせず直リンクします。サーバーのストレージを節約できますが、サムネイルが生成されないので通信量が増加します。"
flagAsBot: "Botとして設定" flagAsBot: "自動化されたアカウントとして設定"
flagAsBotDescription: "このアカウントがBotである場合は、この設定をオンにします。オンにすると、反応の連鎖を防ぐためのフラグとして他の開発者に役立ったり、Firefishのシステム上での扱いがBotに合ったものになります。" flagAsBotDescription: "このアカウントが自動で投稿する場合は、この設定をオンにします。オンにすると、反応の連鎖を防ぐためのフラグとして他の開発者に役立ったり、Firefishのシステム上での扱いが自動で投稿するアカウントに合ったものになります。"
flagAsCat: "あなたは…猫?😺" flagAsCat: "あなたは…猫?😺"
flagAsCatDescription: "このアカウントが猫であることを示す猫モードを有効にするには、このフラグをオンにします。" flagAsCatDescription: "このアカウントが猫であることを示す猫モードを有効にするには、このフラグをオンにします。"
flagSpeakAsCat: "猫語で話す" flagSpeakAsCat: "猫語で話す"
@ -384,6 +384,8 @@ withReplies: "返信を含む"
connectedTo: "次のアカウントに接続されています" connectedTo: "次のアカウントに接続されています"
notesAndReplies: "投稿と返信" notesAndReplies: "投稿と返信"
withFiles: "ファイル付き" withFiles: "ファイル付き"
attachedToNotes: "このファイルが添付された投稿"
showAttachedNotes: "このファイルが添付された投稿を見る"
silence: "サイレンス" silence: "サイレンス"
silenceConfirm: "サイレンスしますか?" silenceConfirm: "サイレンスしますか?"
unsilence: "サイレンス解除" unsilence: "サイレンス解除"
@ -979,7 +981,7 @@ customKaTeXMacroDescription: "数式入力を楽にするためのマクロを
が 3 + foo に展開されます。また、マクロの名前を囲む波括弧を丸括弧 () および角括弧 [] に変更した場合、マクロの引数に使用する括弧が変更されます。マクロの定義は一行に一つのみで、途中で改行はできません。マクロの定義が無効な行は無視されます。文字列を単純に置換する機能のみに対応していて、条件分岐などの高度な構文は使用できません。" が 3 + foo に展開されます。また、マクロの名前を囲む波括弧を丸括弧 () および角括弧 [] に変更した場合、マクロの引数に使用する括弧が変更されます。マクロの定義は一行に一つのみで、途中で改行はできません。マクロの定義が無効な行は無視されます。文字列を単純に置換する機能のみに対応していて、条件分岐などの高度な構文は使用できません。"
enableCustomKaTeXMacro: "カスタムKaTeXマクロを有効にする" enableCustomKaTeXMacro: "カスタムKaTeXマクロを有効にする"
preventAiLearning: "AIによる学習を防止" preventAiLearning: "AIによる学習を防止"
preventAiLearningDescription: "投稿したノート、添付した画像などのコンテンツを学習の対象にしないようAIに要求します。これはnoaiフラグをHTMLレスポンスに含めることによって実現されます。" preventAiLearningDescription: "投稿した文章や、添付した画像などのコンテンツを学習の対象にしないようAIに要求します。これはnoaiフラグをHTMLレスポンスに含めることによって実現されます。"
noGraze: "ブラウザの拡張機能「Graze for Mastodon」は、Firefishの動作を妨げるため、無効にしてください。" noGraze: "ブラウザの拡張機能「Graze for Mastodon」は、Firefishの動作を妨げるため、無効にしてください。"
enableServerMachineStats: "サーバーのマシン情報を公開する" enableServerMachineStats: "サーバーのマシン情報を公開する"
enableIdenticonGeneration: "ユーザーごとのIdenticon生成を有効にする" enableIdenticonGeneration: "ユーザーごとのIdenticon生成を有効にする"
@ -992,6 +994,8 @@ addRe: "閲覧注意の投稿への返信で、注釈の先頭に\"re:\"を追
languageForTranslation: "投稿翻訳に使用する言語" languageForTranslation: "投稿翻訳に使用する言語"
detectPostLanguage: "投稿の言語を自動検出し、外国語の投稿に翻訳ボタンを表示する" detectPostLanguage: "投稿の言語を自動検出し、外国語の投稿に翻訳ボタンを表示する"
iconSet: "アイコンのスタイル" iconSet: "アイコンのスタイル"
useEmojiCdn: "CDNのTwemojiを利用する"
useEmojiCdnDescription: "サーバー上に保存されているTwemojiのアセットの代わりに、JSDelivr CDNから配信されたものを用います。"
_sensitiveMediaDetection: _sensitiveMediaDetection:
description: "機械学習を使って自動でセンシティブなメディアを検出し、モデレーションに役立てられます。サーバーの負荷が少し増えます。" description: "機械学習を使って自動でセンシティブなメディアを検出し、モデレーションに役立てられます。サーバーの負荷が少し増えます。"
@ -1029,7 +1033,7 @@ _ad:
_forgotPassword: _forgotPassword:
enterEmail: "アカウントに登録したメールアドレスを入力してください。そのアドレス宛てに、パスワードリセット用のリンクが送信されます。" enterEmail: "アカウントに登録したメールアドレスを入力してください。そのアドレス宛てに、パスワードリセット用のリンクが送信されます。"
ifNoEmail: "メールアドレスを登録していない場合は、管理者までお問い合わせください。" ifNoEmail: "メールアドレスを登録していない場合は、管理者までお問い合わせください。"
contactAdmin: "このインスタンスではメールアドレスの登録がサポートされていないため、パスワードリセットを行う場合は管理者までお問い合わせください。" contactAdmin: "このサーバーではメールアドレスの登録がサポートされていないため、パスワードリセットを行う場合は管理者までお問い合わせください。"
_gallery: _gallery:
my: "自分の投稿" my: "自分の投稿"
liked: "いいねした投稿" liked: "いいねした投稿"
@ -1954,7 +1958,7 @@ isModerator: モデレーター
audio: 音声 audio: 音声
image: 画像 image: 画像
video: 動画 video: 動画
isBot: このアカウントはBotで isBot: このアカウントは自動で投稿しま
isLocked: このアカウントのフォローは承認制です isLocked: このアカウントのフォローは承認制です
isAdmin: 管理者 isAdmin: 管理者
isPatron: Firefish 後援者 isPatron: Firefish 後援者
@ -2001,3 +2005,5 @@ _iconSets:
regular: "標準" regular: "標準"
fill: "塗りつぶし" fill: "塗りつぶし"
duotone: "2色" duotone: "2色"
moreUrls: "固定するページ"
moreUrlsDescription: "左下のヘルプメニューに固定したいページを以下の形式で、改行区切りで入力してください:\n\"表示名\": https://example.com/"

View file

@ -714,7 +714,7 @@ registry: "レジストリ"
closeAccount: "このアカウントにさいならする" closeAccount: "このアカウントにさいならする"
currentVersion: "現在のバージョン" currentVersion: "現在のバージョン"
latestVersion: "最新のバージョン" latestVersion: "最新のバージョン"
youAreRunningUpToDateClient: "今使てるクライアントが最新やで!" youAreRunningUpToDateClient: "今使てるクライアントが最新やで!"
newVersionOfClientAvailable: "新しいバージョンのクライアントが使えるで。" newVersionOfClientAvailable: "新しいバージョンのクライアントが使えるで。"
usageAmount: "使用量" usageAmount: "使用量"
capacity: "容量" capacity: "容量"
@ -778,7 +778,7 @@ emailNotConfiguredWarning: "メアドの設定がされてへんで。"
ratio: "比率" ratio: "比率"
previewNoteText: "本文を下見するで" previewNoteText: "本文を下見するで"
customCss: "カスタムCSS" customCss: "カスタムCSS"
customCssWarn: "この設定は必ず知識のある人がやらなあかんで。あんま良くない設定をしたるとクライアントがちゃんと使えへんくなってくで。" customCssWarn: "この設定は必ず知識のある人がやらなあかん。下手にいじるとわやなって使えんくなってまうで。"
global: "グローバル" global: "グローバル"
squareAvatars: "アイコンを四角形で表示するで" squareAvatars: "アイコンを四角形で表示するで"
sent: "送信" sent: "送信"
@ -793,7 +793,7 @@ whatIsNew: "更新情報を見るで"
translate: "翻訳" translate: "翻訳"
translatedFrom: "{x}から翻訳するで" translatedFrom: "{x}から翻訳するで"
accountDeletionInProgress: "アカウント削除しとるで待っとってなー" accountDeletionInProgress: "アカウント削除しとるで待っとってなー"
usernameInfo: "サーバー上であんたのアカウントをあんたや分かるようにするための名前や。アルファベット(a~z, A~Z)、数字(0~9)、それとアンダーバー(_)が使って考えてな。この名前は後から変更することはできへんからちゃんと考えるんやで。" usernameInfo: "サーバー上であんたのアカウントをあんたや分かるようにするための名前や。アルファベット(a~z, A~Z)、数字(0~9)、それとアンダーバー(_)が使えるで。この名前は後から変更することはでけへんから、ちゃんと考えや。"
aiChanMode: "藍モードやで" aiChanMode: "藍モードやで"
keepCw: "CWを維持するで" keepCw: "CWを維持するで"
pubSub: "Pub/Subのアカウント" pubSub: "Pub/Subのアカウント"
@ -812,7 +812,7 @@ typeToConfirm: "この操作をやるんなら {x} と入力してなー"
deleteAccount: "アカウント削除するで" deleteAccount: "アカウント削除するで"
document: "ドキュメント" document: "ドキュメント"
numberOfPageCache: "ページキャッシュ数やで" numberOfPageCache: "ページキャッシュ数やで"
numberOfPageCacheDescription: "増やすと使いやすくなる、負荷とメモリ使用量が増えてくで。一長一短やな。" numberOfPageCacheDescription: "増やすと使いやすくなるけど、サーバーの負荷とメモリ使用量が増えてまう。一長一短やな。"
logoutConfirm: "ログアウトしまっか?" logoutConfirm: "ログアウトしまっか?"
lastActiveDate: "最後に使った日時" lastActiveDate: "最後に使った日時"
statusbar: "ステータスバー" statusbar: "ステータスバー"
@ -1444,7 +1444,17 @@ _tutorial:
step4_1: 投稿しとーみ step4_1: 投稿しとーみ
step5_1: タイムライン! 文字と写真の宝石箱や~ step5_1: タイムライン! 文字と写真の宝石箱や~
step5_2: うちのサーバーでは{timelines}種類のタイムラインをご用意しとります。 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: _postForm:
_placeholders: _placeholders:
b: なんかおましたか? b: なんかおましたか?
@ -1454,6 +1464,7 @@ _postForm:
f: あんさん書くんを待っとるんどす... f: あんさん書くんを待っとるんどす...
a: いまなにしとん? a: いまなにしとん?
flagSpeakAsCat: 猫弁で喋る flagSpeakAsCat: 猫弁で喋る
flagSpeakAsCatDescription: オンにすると、ワレの投稿の「な」を「にゃ」に変換したるで。 flagSpeakAsCatDescription: オンにすると、ワレの投稿の「な」「na」を「にゃ」「nya」に変換したるで。
welcomeBackWithName: おおきに、{name}はん welcomeBackWithName: おおきに、{name}はん
migration: アカウントの引っ越し migration: アカウントの引っ越し
makeReactionsPublicDescription: あんたが付けたリアクションの一覧をみんなにも見せたるで。

View file

@ -1,6 +1,6 @@
_lang_: "Polski" _lang_: "Polski"
headlineFirefish: "Otwartoźródłowa, zdecentralizowana sieć społecznościowa, która zawsze headlineFirefish: "Otwartoźródłowa, zdecentralizowana sieć społecznościowa, która
będzie darmowa! 🚀" zawsze będzie darmowa! 🚀"
introFirefish: "Hej! Firefish to otwartoźródłowa oraz zdecentralizowana sieć społecznościowa, introFirefish: "Hej! Firefish to otwartoźródłowa oraz zdecentralizowana sieć społecznościowa,
która zawsze będzie darmowa! 🚀" która zawsze będzie darmowa! 🚀"
monthAndDay: "{month}-{day}" monthAndDay: "{month}-{day}"
@ -94,7 +94,7 @@ privacy: "Prywatność"
makeFollowManuallyApprove: "Prośby o możliwość obserwacji wymagają zatwierdzenia" makeFollowManuallyApprove: "Prośby o możliwość obserwacji wymagają zatwierdzenia"
defaultNoteVisibility: "Domyślna widoczność" defaultNoteVisibility: "Domyślna widoczność"
follow: "Obserwuj" follow: "Obserwuj"
followRequest: "Poproś o możliwość obserwacji" followRequest: "Poproś o możliwość obserwowania"
followRequests: "Prośby o możliwość obserwacji" followRequests: "Prośby o możliwość obserwacji"
unfollow: "Przestań obserwować" unfollow: "Przestań obserwować"
followRequestPending: "Oczekująca prośba o możliwość obserwacji" 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 bezpośrednio ze zdalnego serwera. Wyłączenie tej opcji zmniejszy użycie powierzchni
dyskowej, ale zwiększy transfer, ponieważ miniaturki nie będą generowane." dyskowej, ale zwiększy transfer, ponieważ miniaturki nie będą generowane."
flagAsBot: "To konto jest botem" flagAsBot: "To konto jest botem"
flagAsBotDescription: "Jeżeli ten kanał jest kontrolowany przez jakiś program, ustaw flagAsBotDescription: "Ustaw tę opcję ten jeśli kanał kontrolowany jest przez jakiś
tę opcję. Jeżeli włączona, będzie działać jako flaga informująca innych programistów, program. Po włączeniu, będzie działać jako flaga informująca innych programistów,
aby zapobiegać nieskończonej interakcji z różnymi botami i dostosowywać wewnętrzne aby zapobiegać nieskończonej interakcji pomiędzy innymi botami i dostosowywać wewnętrzne
systemy Firefish, traktując konto jako bota." systemy Firefish, traktując konto jako bota."
flagAsCat: "Czy jesteś kotem? 😺" flagAsCat: "Czy jesteś kotem? 😺"
flagAsCatDescription: "Dostaniesz kocie uszka, oraz będziesz mówić jak kot!" 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" których obserwujesz"
addAccount: "Dodaj konto" addAccount: "Dodaj konto"
loginFailed: "Nie udało się zalogować" loginFailed: "Nie udało się zalogować"
showOnRemote: "Zobacz na zdalnym serwerze" showOnRemote: "Zobacz oryginalną treść"
general: "Ogólne" general: "Ogólne"
wallpaper: "Tapeta" wallpaper: "Tapeta"
setWallpaper: "Ustaw tapetę" setWallpaper: "Ustaw tapetę"
@ -211,7 +211,7 @@ noUsers: "Brak użytkowników"
editProfile: "Edytuj profil" editProfile: "Edytuj profil"
noteDeleteConfirm: "Czy na pewno chcesz usunąć ten wpis?" noteDeleteConfirm: "Czy na pewno chcesz usunąć ten wpis?"
pinLimitExceeded: "Nie możesz przypiąć więcej wpisów" 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" done: "Gotowe"
processing: "Przetwarzanie" processing: "Przetwarzanie"
preview: "Podgląd" preview: "Podgląd"
@ -299,11 +299,11 @@ emptyDrive: "Dysk jest pusty"
emptyFolder: "Ten katalog jest pusty" emptyFolder: "Ten katalog jest pusty"
unableToDelete: "Nie można usunąć" unableToDelete: "Nie można usunąć"
inputNewFileName: "Wprowadź nową nazwę pliku" inputNewFileName: "Wprowadź nową nazwę pliku"
inputNewDescription: "Proszę wpisać nowy napis" inputNewDescription: "Podaj nowy napis"
inputNewFolderName: "Wprowadź nową nazwę katalogu" inputNewFolderName: "Wprowadź nową nazwę katalogu"
circularReferenceFolder: "Katalog docelowy jest podkatalogiem katalogu, który chcesz circularReferenceFolder: "Katalog docelowy jest podkatalogiem katalogu, który chcesz
przenieść." 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" copyUrl: "Skopiuj adres URL"
rename: "Zmień nazwę" rename: "Zmień nazwę"
avatar: "Awatar" avatar: "Awatar"
@ -578,8 +578,8 @@ disablePlayer: "Zamknij odtwarzacz wideo"
expandTweet: "Rozwiń tweet" expandTweet: "Rozwiń tweet"
themeEditor: "Edytor motywu" themeEditor: "Edytor motywu"
description: "Opis" description: "Opis"
describeFile: "Dodaj podpis" describeFile: "Dodaj opis"
enterFileDescription: "Wprowadź napis" enterFileDescription: "Wprowadź opis"
author: "Autor" author: "Autor"
leaveConfirm: "Są niezapisane zmiany. Czy chcesz je odrzucić?" leaveConfirm: "Są niezapisane zmiany. Czy chcesz je odrzucić?"
manage: "Zarządzanie" manage: "Zarządzanie"
@ -616,7 +616,7 @@ emptyToDisableSmtpAuth: "Pozostaw adres e-mail i hasło puste, aby wyłączyć w
SMTP" 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" wordMute: "Wyciszenie słów i języków"
instanceMute: "Wyciszenie serwera" instanceMute: "Wyciszenie serwera"
userSaysSomething: "{name} powiedział* coś" userSaysSomething: "{name} powiedział* coś"
makeActive: "Aktywuj" makeActive: "Aktywuj"
@ -691,8 +691,7 @@ no: "Nie"
driveFilesCount: "Liczba plików na dysku" driveFilesCount: "Liczba plików na dysku"
driveUsage: "Użycie przestrzeni dyskowej" driveUsage: "Użycie przestrzeni dyskowej"
noCrawle: "Odrzuć indeksowanie przez crawlery" noCrawle: "Odrzuć indeksowanie przez crawlery"
noCrawleDescription: "Proś wyszukiwarki internetowe, aby nie indeksowały Twojego profilu, noCrawleDescription: "Proś wyszukiwarki internetowe, aby nie indeksowały Twoich treści."
wpisów, stron itd."
lockedAccountInfo: "Dopóki nie ustawisz widoczności wpisu na \"Obserwujący\", twoje 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." wpisy będą mogli widzieć wszyscy, nawet jeśli ustawisz manualne zatwierdzanie obserwujących."
alwaysMarkSensitive: "Oznacz domyślnie jako NSFW" alwaysMarkSensitive: "Oznacz domyślnie jako NSFW"
@ -759,7 +758,7 @@ useReactionPickerForContextMenu: "Otwórz wybornik reakcji prawym kliknięciem"
typingUsers: "{users} pisze/ą" typingUsers: "{users} pisze/ą"
jumpToSpecifiedDate: "Przejdź do określonej daty" jumpToSpecifiedDate: "Przejdź do określonej daty"
showingPastTimeline: "Obecnie wyświetla starą oś czasu" showingPastTimeline: "Obecnie wyświetla starą oś czasu"
clear: "Wć" clear: "Wyczyść"
markAllAsRead: "Oznacz wszystkie jako przeczytane" markAllAsRead: "Oznacz wszystkie jako przeczytane"
goBack: "Wróć" goBack: "Wróć"
unlikeConfirm: "Na pewno chcesz usunąć polubienie?" unlikeConfirm: "Na pewno chcesz usunąć polubienie?"
@ -800,7 +799,7 @@ gallery: "Galeria"
recentPosts: "Ostatnie wpisy" recentPosts: "Ostatnie wpisy"
popularPosts: "Popularne wpisy" popularPosts: "Popularne wpisy"
shareWithNote: "Udostępnij z wpisem" shareWithNote: "Udostępnij z wpisem"
ads: "Reklamy" ads: "Banery"
expiration: "Ankieta kończy się" expiration: "Ankieta kończy się"
memo: "Notatki" memo: "Notatki"
priority: "Priorytet" priority: "Priorytet"
@ -852,7 +851,7 @@ ffVisibility: "Widoczność obserwowanych/obserwujących"
ffVisibilityDescription: "Pozwala skonfigurować, kto może zobaczyć, kogo obserwujesz ffVisibilityDescription: "Pozwala skonfigurować, kto może zobaczyć, kogo obserwujesz
i kto Cię obserwuje." i kto Cię obserwuje."
continueThread: "Kontynuuj wątek" 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." incorrectPassword: "Nieprawidłowe hasło."
voteConfirm: "Potwierdzić swój głos na \"{choice}\"?" voteConfirm: "Potwierdzić swój głos na \"{choice}\"?"
hide: "Ukryj" hide: "Ukryj"
@ -1001,8 +1000,8 @@ _nsfw:
force: "Ukrywaj wszystkie media" force: "Ukrywaj wszystkie media"
_mfm: _mfm:
cheatSheet: "Ściąga MFM" cheatSheet: "Ściąga MFM"
intro: "MFM jest językiem składniowym używanym przez m.in. Firefish, forki *key (w intro: "MFM jest językiem składniowym używanym przez m.in. Firefish, forki *key
tym Firefish), oraz Akkomę, który może być użyty w wielu miejscach. Tu znajdziesz (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." listę wszystkich możliwych elementów składni MFM."
dummy: "Firefish rozszerza świat Fediwersum" dummy: "Firefish rozszerza świat Fediwersum"
mention: "Wspomnij" mention: "Wspomnij"
@ -1250,7 +1249,7 @@ _tutorial:
innej połączonej instancji." innej połączonej instancji."
step6_1: "Więc, czym to jest to miejsce?" 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, 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. 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 Ten jednak używa! Jest to trochę skomplikowane, ale w krótkim czasie załapiesz
o co chodzi." o co chodzi."
@ -1816,8 +1815,8 @@ instanceSecurity: Bezpieczeństwo serwera
privateMode: Tryb prywatny privateMode: Tryb prywatny
allowedInstances: Dopuszczone serwery allowedInstances: Dopuszczone serwery
recommended: Polecane recommended: Polecane
allowedInstancesDescription: Hosty serwerów, które mają być dopuszczone do federacji, allowedInstancesDescription: Hosty serwerów, które mają być dopuszczone do federacji.
każdy oddzielony nowym wierszem (dotyczy tylko trybu prywatnego). Każdy oddzielony nowym wierszem (dotyczy tylko trybu prywatnego).
seperateRenoteQuote: Oddziel przyciski podbicia i cytowania seperateRenoteQuote: Oddziel przyciski podbicia i cytowania
refreshInterval: 'Częstotliwość aktualizacji ' refreshInterval: 'Częstotliwość aktualizacji '
slow: Wolna 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 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, Firefish.\nZawrze to informacje takie jak wersja twojego systemu operacyjnego, przeglądarki,
Twoja aktywność na Firefish itd." Twoja aktywność na Firefish itd."
privateModeInfo: Gdy ta opcja jest włączona, tylko serwery z białej listy mogą federować privateModeInfo: Gdy ta opcja jest włączona, tylko serwery z listy serwerów dozwolonych
się z twoim serwerem. Wszystkie posty będą ukryte publicznie. mogą federować się z twoim serwerem. Żadne posty nie będą publicznie dostępne.
oneHour: Godzina oneHour: Godzina
oneDay: Dzień oneDay: Dzień
oneWeek: Tydzień oneWeek: Tydzień
recommendedInstances: Polecane serwery recommendedInstances: Polecane serwery
recommendedInstancesDescription: Polecane serwery, mające pojawić się w odpowiedniej 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 rateLimitExceeded: Przekroczono ratelimit
cropImage: Kadruj zdjęcie cropImage: Kadruj zdjęcie
cropImageAsk: Czy chcesz skadrować to 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ę 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. z nim komunikować. Jeśli wyłączone, walidowany jest tylko format wiadomości e-mail.
shuffle: Losuj shuffle: Losuj
showAds: Pokazuj reklamy showAds: Pokazuj banery
enterSendsMessage: Wciśnij Enter w komunikatorze, by wysłać wiadomość (domyślnie enterSendsMessage: Wciśnij Enter w komunikatorze, by wysłać wiadomość (domyślnie
Ctrl + Enter) Ctrl + Enter)
adminCustomCssWarn: To ustawienie powinno być używane tylko pod warunkiem, że wiesz 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ż cannotUploadBecauseExceedsFileSizeLimit: Ten plik nie mógł być przesłany, ponieważ
jego wielkość przekracza dozwolony limit. jego wielkość przekracza dozwolony limit.
sendModMail: Wyślij Powiadomienie Moderacyjne sendModMail: Wyślij Powiadomienie Moderacyjne
searchPlaceholder: Szukaj Firefish searchPlaceholder: Szukaj w Firefish
jumpToPrevious: Przejdź do poprzedniej sekcji jumpToPrevious: Przejdź do poprzedniej sekcji
listsDesc: Listy umożliwiają tworzenie osi czasu z określonymi użytkownikami. Dostęp listsDesc: Listy umożliwiają tworzenie osi czasu z określonymi użytkownikami. Dostęp
do nich można uzyskać na stronie osi czasu. do nich można uzyskać na stronie osi czasu.
@ -2028,3 +2027,15 @@ newer: nowsze
older: starsze older: starsze
cw: Ostrzeżenie zawartości cw: Ostrzeżenie zawartości
removeReaction: Usuń reakcję 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" headlineFirefish: "Uma rede ligada por notas"
introFirefish: "Bem-vindo! Firefish é um serviço de microblogue descentralizado de introFirefish: "Bem-vindo! Firefish é um serviço de microblogue descentralizado de
código aberto, gratuito para sempre! 🚀" código aberto, gratuito para sempre! 🚀"

View file

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

View file

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

View file

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

View file

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

View file

@ -1,12 +1,12 @@
{ {
"name": "firefish", "name": "firefish",
"version": "1.0.5-dev18", "version": "1.0.5-rc",
"codename": "aqua", "codename": "aqua",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://git.joinfirefish.org/firefish/firefish.git" "url": "https://git.joinfirefish.org/firefish/firefish.git"
}, },
"packageManager": "pnpm@8.8.0", "packageManager": "pnpm@8.11.0",
"private": true, "private": true,
"scripts": { "scripts": {
"rebuild": "pnpm run clean && pnpm run build", "rebuild": "pnpm run clean && pnpm run build",
@ -19,7 +19,7 @@
"migrateandstart": "pnpm run migrate && pnpm run start", "migrateandstart": "pnpm run migrate && pnpm run start",
"gulp": "gulp build", "gulp": "gulp build",
"watch": "pnpm run dev", "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", "dev:staging": "NODE_OPTIONS=--max_old_space_size=3072 NODE_ENV=development pnpm run build && pnpm run start",
"lint": "pnpm -r --parallel run lint", "lint": "pnpm -r --parallel run lint",
"debug": "pnpm run build:debug && pnpm run start", "debug": "pnpm run build:debug && pnpm run start",
@ -30,42 +30,42 @@
"mocha": "pnpm --filter backend run mocha", "mocha": "pnpm --filter backend run mocha",
"test": "pnpm run mocha", "test": "pnpm run mocha",
"format": "pnpm -r --parallel run format", "format": "pnpm -r --parallel run format",
"clean": "pnpm node ./scripts/clean.js", "clean": "pnpm node ./scripts/clean.mjs",
"clean-all": "pnpm node ./scripts/clean-all.js", "clean-all": "pnpm node ./scripts/clean-all.mjs",
"cleanall": "pnpm run clean-all" "cleanall": "pnpm run clean-all"
}, },
"resolutions": { "resolutions": {
"chokidar": "^3.3.1" "chokidar": "^3.3.1"
}, },
"dependencies": { "dependencies": {
"@bull-board/api": "5.8.0", "@bull-board/api": "5.9.1",
"@bull-board/ui": "5.8.0", "@bull-board/ui": "5.9.1",
"@napi-rs/cli": "^2.16.2", "@napi-rs/cli": "^2.16.5",
"@tensorflow/tfjs": "^4.10.0", "@tensorflow/tfjs": "^4.13.0",
"js-yaml": "4.1.0", "js-yaml": "4.1.0",
"seedrandom": "^3.0.5" "seedrandom": "^3.0.5"
}, },
"devDependencies": { "devDependencies": {
"@biomejs/biome": "1.0.0", "@biomejs/biome": "1.3.3",
"@biomejs/cli-darwin-arm64": "^1.0.0", "@biomejs/cli-darwin-arm64": "^1.3.3",
"@biomejs/cli-darwin-x64": "^1.0.0", "@biomejs/cli-darwin-x64": "^1.3.3",
"@biomejs/cli-linux-arm64": "^1.0.0", "@biomejs/cli-linux-arm64": "^1.3.3",
"@biomejs/cli-linux-x64": "^1.0.0", "@biomejs/cli-linux-x64": "^1.3.3",
"@types/gulp": "4.0.13", "@types/gulp": "4.0.17",
"@types/gulp-rename": "2.0.2", "@types/gulp-rename": "2.0.5",
"@types/node": "20.5.8", "@types/node": "20.9.0",
"add": "2.0.6", "add": "2.0.6",
"cross-env": "7.0.3", "cross-env": "7.0.3",
"cypress": "10.11.0", "cypress": "13.5.1",
"execa": "5.1.1", "execa": "8.0.1",
"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.4", "gulp-replace": "1.1.4",
"gulp-terser": "2.1.0", "gulp-terser": "2.1.0",
"install-peers": "^1.0.4", "install-peers": "^1.0.4",
"pnpm": "8.8.0", "pnpm": "8.11.0",
"start-server-and-test": "1.15.2", "start-server-and-test": "2.0.3",
"typescript": "5.2.2" "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 - `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 - `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 - `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, pub pinned_users: StringVec,
#[sea_orm(column_name = "ToSUrl")] #[sea_orm(column_name = "ToSUrl")]
pub to_s_url: Option<String>, pub to_s_url: Option<String>,
#[sea_orm(column_name = "moreUrls", column_type = "JsonBinary")]
pub more_urls: Json,
#[sea_orm(column_name = "repositoryUrl")] #[sea_orm(column_name = "repositoryUrl")]
pub repository_url: String, pub repository_url: String,
#[sea_orm(column_name = "feedbackUrl")] #[sea_orm(column_name = "feedbackUrl")]

View file

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

View file

@ -10,10 +10,9 @@ import semver from "semver";
import Logger from "@/services/logger.js"; import Logger from "@/services/logger.js";
import loadConfig from "@/config/load.js"; import loadConfig from "@/config/load.js";
import type { Config } from "@/config/types.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 { 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 _filename = fileURLToPath(import.meta.url);
const _dirname = dirname(_filename); const _dirname = dirname(_filename);
@ -149,9 +148,7 @@ function showNodejsVersion(): void {
nodejsLogger.info(`Version ${process.version} detected.`); nodejsLogger.info(`Version ${process.version} detected.`);
const minVersion = fs const minVersion = "v18.16.0";
.readFileSync(`${_dirname}/../../../../.node-version`, "utf-8")
.trim();
if (semver.lt(process.version, minVersion)) { if (semver.lt(process.version, minVersion)) {
nodejsLogger.error(`At least Node.js ${minVersion} required!`); nodejsLogger.error(`At least Node.js ${minVersion} required!`);
process.exit(1); process.exit(1);

View file

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

View file

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

View file

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

View file

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

View file

@ -154,7 +154,7 @@ function timestampToUnix(timestamp: string) {
if (unix === 0) { if (unix === 0) {
// Try to parse the timestamp as JavaScript Date // Try to parse the timestamp as JavaScript Date
const date = Date.parse(timestamp); const date = Date.parse(timestamp);
if (isNaN(date)) return 0; if (Number.isNaN(date)) return 0;
unix = date / 1000; 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 { NoteEdit } from "@/models/entities/note-edit.js";
import { entities as charts } from "@/services/chart/entities.js"; import { entities as charts } from "@/services/chart/entities.js";
import { envOption } from "../env.js";
import { dbLogger } from "./logger.js"; import { dbLogger } from "./logger.js";
import { redisClient } from "./redis.js"; import { redisClient } from "./redis.js";
// TODO?: should we avoid importing things from built directory?
import { nativeInitDatabase } from "native-utils/built/index.js"; import { nativeInitDatabase } from "native-utils/built/index.js";
const sqlLogger = dbLogger.createSubLogger("sql", "gray", false); 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; type FIXME = any;

View file

@ -1,5 +1,5 @@
/** /**
* Misskey Entry Point! * Firefish Entry Point
*/ */
import { EventEmitter } from "node:events"; 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"; import { Mutex } from "redis-semaphore";
/** /**

View file

@ -1,6 +1,6 @@
import fetch from "node-fetch"; import fetch from "node-fetch";
import { URLSearchParams } from "node:url"; import { URLSearchParams } from "node:url";
import { getAgentByUrl } from "./fetch.js"; import { getAgentByUrl } from "@/misc/fetch.js";
import config from "@/config/index.js"; import config from "@/config/index.js";
export async function verifyRecaptcha(secret: string, response: string) { 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 { Note } from "@/models/entities/note.js";
import type { User } from "@/models/entities/user.js"; import type { User } from "@/models/entities/user.js";
import { Blockings, UserProfiles } from "@/models/index.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 * as Acct from "@/misc/acct.js";
import type { Packed } from "./schema.js"; import type { Packed } from "@/misc/schema.js";
import { Cache } from "./cache.js"; import { Cache } from "@/misc/cache.js";
import { getWordHardMute } from "./check-word-mute.js"; import { getWordHardMute } from "@/misc/check-word-mute.js";
const blockingCache = new Cache<User["id"][]>("blocking", 60 * 5); const blockingCache = new Cache<User["id"][]>("blocking", 60 * 5);
const mutedWordsCache = new Cache<string[][] | undefined>("mutedWords", 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; const twemojiRegex = twemoji.default;
export const emojiRegex = new RegExp(`(${twemojiRegex.source})`); 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; public ToSUrl: string | null;
@Column("jsonb", {
default: [],
nullable: false,
})
public moreUrls: [string, string][];
@Column("varchar", { @Column("varchar", {
length: 512, length: 512,
default: "https://git.joinfirefish.org/firefish/firefish", default: "https://git.joinfirefish.org/firefish/firefish",

View file

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

View file

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

View file

@ -1,9 +1,7 @@
import {} from "typeorm";
import { db } from "@/db/postgre.js"; import { db } from "@/db/postgre.js";
import { Announcement } from "./entities/announcement.js"; import { Announcement } from "./entities/announcement.js";
import { AnnouncementRead } from "./entities/announcement-read.js"; import { AnnouncementRead } from "./entities/announcement-read.js";
import { Instance } from "./entities/instance.js";
import { Poll } from "./entities/poll.js"; import { Poll } from "./entities/poll.js";
import { PollVote } from "./entities/poll-vote.js"; import { PollVote } from "./entities/poll-vote.js";
import { Meta } from "./entities/meta.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 { DriveFile } from "@/models/entities/drive-file.js";
import type { User } from "@/models/entities/user.js"; import type { User } from "@/models/entities/user.js";
import { toPuny } from "@/misc/convert-host.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 type { Packed } from "@/misc/schema.js";
import config from "@/config/index.js"; import config from "@/config/index.js";
import { query, appendQuery } from "@/prelude/url.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 { Users, DriveFolders } from "../index.js";
import { deepClone } from "@/misc/clone.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 { Notification } from "@/models/entities/notification.js";
import { awaitAll } from "@/prelude/await-all.js"; import { awaitAll } from "@/prelude/await-all.js";
import type { Packed } from "@/misc/schema.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 { NoteReaction } from "@/models/entities/note-reaction.js";
import type { User } from "@/models/entities/user.js"; import type { User } from "@/models/entities/user.js";
import { aggregateNoteEmojis, prefetchEmojis } from "@/misc/populate-emojis.js"; import { aggregateNoteEmojis, prefetchEmojis } from "@/misc/populate-emojis.js";
import { notificationTypes } from "@/types.js";
import { db } from "@/db/postgre.js"; import { db } from "@/db/postgre.js";
import { import {
Users, Users,

View file

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

View file

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

View file

@ -1,14 +1,12 @@
import type Bull from "bull"; import type Bull from "bull";
import * as fs from "node:fs"; import * as fs from "node:fs";
import { ulid } from "ulid";
import mime from "mime-types"; import mime from "mime-types";
import archiver from "archiver"; import archiver from "archiver";
import { queueLogger } from "../../logger.js"; import { queueLogger } from "../../logger.js";
import { addFile } from "@/services/drive/add-file.js"; import { addFile } from "@/services/drive/add-file.js";
import { format as dateFormat } from "date-fns"; import { format as dateFormat } from "date-fns";
import { Users, Emojis } from "@/models/index.js"; import { Users, Emojis } from "@/models/index.js";
import {} from "@/queue/types.js";
import { createTemp, createTempDir } from "@/misc/create-temp.js"; import { createTemp, createTempDir } from "@/misc/create-temp.js";
import { downloadUrl } from "@/misc/download-url.js"; import { downloadUrl } from "@/misc/download-url.js";
import config from "@/config/index.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 { resolveUser } from "@/remote/resolve-user.js";
import { downloadTextFile } from "@/misc/download-text-file.js"; import { downloadTextFile } from "@/misc/download-text-file.js";
import { isSelfHost, toPuny } from "@/misc/convert-host.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 type { DbUserImportJobData } from "@/queue/types.js";
import block from "@/services/blocking/create.js"; import block from "@/services/blocking/create.js";
import { IsNull } from "typeorm"; import { IsNull } from "typeorm";

View file

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

View file

@ -1,11 +1,12 @@
import type Bull from "bull"; import type Bull from "bull";
import { Notes, PollVotes } from "@/models/index.js"; 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 type { EndedPollNotificationJobData } from "@/queue/types.js";
import { createNotification } from "@/services/create-notification.js"; import { createNotification } from "@/services/create-notification.js";
import { deliverQuestionUpdate } from "@/services/note/polls/update.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( export async function endedPollNotification(
job: Bull.Job<EndedPollNotificationJobData>, 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 { CacheableRemoteUser } from "@/models/entities/user.js";
import type { UserPublickey } from "@/models/entities/user-publickey.js"; import type { UserPublickey } from "@/models/entities/user-publickey.js";
import { shouldBlockInstance } from "@/misc/should-block-instance.js"; import { shouldBlockInstance } from "@/misc/should-block-instance.js";
import { verifySignature } from "@/remote/activitypub/check-fetch.js";
const logger = new Logger("inbox"); 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と一致する必要がある // また、signatureのsignerは、activity.actorと一致する必要がある
if (!httpSignatureValidated || authUser.user.uri !== activity.actor) { if (!httpSignatureValidated || authUser.user.uri !== activity.actor) {
// 一致しなくても、でもLD-Signatureがありそうならそっちも見る // 一致しなくても、でもLD-Signatureがありそうならそっちも見る

View file

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

View file

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

View file

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

View file

@ -1,5 +1,5 @@
import { URL } from "url"; import { URL } from "url";
import httpSignature from "@peertube/http-signature"; import httpSignature, { IParsedSignature } from "@peertube/http-signature";
import config from "@/config/index.js"; import config from "@/config/index.js";
import { fetchMeta } from "@/misc/fetch-meta.js"; import { fetchMeta } from "@/misc/fetch-meta.js";
import { toPuny } from "@/misc/convert-host.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 { IncomingMessage } from "http";
import type { CacheableRemoteUser } from "@/models/entities/user.js"; import type { CacheableRemoteUser } from "@/models/entities/user.js";
import type { UserPublickey } from "@/models/entities/user-publickey.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> { export async function hasSignature(req: IncomingMessage): Promise<string> {
const meta = await fetchMeta(); const meta = await fetchMeta();
@ -28,10 +31,14 @@ export async function hasSignature(req: IncomingMessage): Promise<string> {
export async function checkFetch(req: IncomingMessage): Promise<number> { export async function checkFetch(req: IncomingMessage): Promise<number> {
const meta = await fetchMeta(); const meta = await fetchMeta();
if (meta.secureMode || meta.privateMode) { if (meta.secureMode || meta.privateMode) {
if (req.headers.host !== config.host) return 400;
let signature; let signature;
try { try {
signature = httpSignature.parseRequest(req, { headers: [] }); signature = httpSignature.parseRequest(req, {
headers: ["(request-target)", "host", "date"],
});
} catch (e) { } catch (e) {
return 401; return 401;
} }
@ -108,6 +115,8 @@ export async function checkFetch(req: IncomingMessage): Promise<number> {
if (!httpSignatureValidated) { if (!httpSignatureValidated) {
return 403; return 403;
} }
return verifySignature(signature, authUser.key) ? 200 : 401;
} }
return 200; return 200;
} }
@ -130,3 +139,39 @@ export async function getSignatureUser(req: IncomingMessage): Promise<{
keyId.hash = ""; keyId.hash = "";
return await dbResolver.getAuthUserFromApId(getApId(keyId.toString())); 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 { 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> { public async getNoteFromApId(value: string | IObject): Promise<Note | null> {
const parsed = parseUri(value); 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( public async getUserFromApId(
value: string | IObject, 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<{ public async getAuthUserFromKeyId(keyId: string): Promise<{
user: CacheableRemoteUser; 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<{ public async getAuthUserFromApId(uri: string): Promise<{
user: CacheableRemoteUser; user: CacheableRemoteUser;

View file

@ -3,7 +3,7 @@ import type { IRead } from "../type.js";
import { getApId } from "../type.js"; import { getApId } from "../type.js";
import { isSelfHost, extractDbHost } from "@/misc/convert-host.js"; import { isSelfHost, extractDbHost } from "@/misc/convert-host.js";
import { MessagingMessages } from "@/models/index.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 ( export const performReadActivity = async (
actor: CacheableRemoteUser, actor: CacheableRemoteUser,

View file

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

View file

@ -1,6 +1,6 @@
import * as mfm from "mfm-js"; import * as mfm from "mfm-js";
import type { Note } from "@/models/entities/note.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) { export default function (note: Note) {
if (!note.text) return ""; if (!note.text) return "";

View file

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

View file

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

View file

@ -1,7 +1,6 @@
import promiseLimit from "promise-limit"; import promiseLimit from "promise-limit";
import { toArray, unique } from "@/prelude/array.js"; import { toArray, unique } from "@/prelude/array.js";
import type { CacheableUser } from "@/models/entities/user.js"; import type { CacheableUser } from "@/models/entities/user.js";
import { User } from "@/models/entities/user.js";
import type { IObject, IApMention } from "../type.js"; import type { IObject, IApMention } from "../type.js";
import { isMention } from "../type.js"; import { isMention } from "../type.js";
import Resolver from "../resolver.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 { extractMentionedUsers } from "@/services/note/create.js";
import { resolvePerson } from "./person.js"; import { resolvePerson } from "./person.js";
import { resolveImage } from "./image.js"; import { resolveImage } from "./image.js";
import type { import type { CacheableRemoteUser } from "@/models/entities/user.js";
ILocalUser,
CacheableRemoteUser,
} from "@/models/entities/user.js";
import { htmlToMfm } from "../misc/html-to-mfm.js"; import { htmlToMfm } from "../misc/html-to-mfm.js";
import { extractApHashtags } from "./tag.js"; import { extractApHashtags } from "./tag.js";
import { unique, toArray, toSingle } from "@/prelude/array.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 { DB_MAX_IMAGE_COMMENT_LENGTH } from "@/misc/hard-limits.js";
import { truncate } from "@/misc/truncate.js"; import { truncate } from "@/misc/truncate.js";
import { type Size, getEmojiSize } from "@/misc/emoji-meta.js"; import { type Size, getEmojiSize } from "@/misc/emoji-meta.js";
import { fetchMeta } from "@/misc/fetch-meta.js";
import { langmap } from "@/misc/langmap.js"; import { langmap } from "@/misc/langmap.js";
const logger = apLogger; const logger = apLogger;

View file

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

View file

@ -1,8 +1,5 @@
import config from "@/config/index.js"; import config from "@/config/index.js";
import { IObject, IActivity } from "@/remote/activitypub/type.js";
import type { ILocalUser } from "@/models/entities/user.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 // to anonymise reporters, the reporting actor must be a system user
// object has to be a uri or array of uris // 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_talk: "misskey:_misskey_talk",
_misskey_reaction: "misskey:_misskey_reaction", _misskey_reaction: "misskey:_misskey_reaction",
_misskey_votes: "misskey:_misskey_votes", _misskey_votes: "misskey:_misskey_votes",
_misskey_summary: "misskey:_misskey_summary",
isCat: "misskey:isCat", isCat: "misskey:isCat",
// Fedibird // Fedibird
fedibird: "http://fedibird.com/ns#", fedibird: "http://fedibird.com/ns#",

View file

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

View file

@ -1,6 +1,5 @@
import config from "@/config/index.js"; import config from "@/config/index.js";
import type { User } from "@/models/entities/user.js"; import type { User } from "@/models/entities/user.js";
import { ILocalUser } from "@/models/entities/user.js";
export default (object: any, user: { id: User["id"] }) => { export default (object: any, user: { id: User["id"] }) => {
if (object == null) return null; 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 { signedGet } from "./request.js";
import type { IObject, ICollection, IOrderedCollection } from "./type.js"; import type { IObject, ICollection, IOrderedCollection } from "./type.js";
import { isCollectionOrOrderedCollection, getApId } from "./type.js"; import { isCollectionOrOrderedCollection, getApId } from "./type.js";
import { import { Notes, NoteReactions, Polls, Users } from "@/models/index.js";
FollowRequests,
Notes,
NoteReactions,
Polls,
Users,
} from "@/models/index.js";
import { parseUri } from "./db-resolver.js"; import { parseUri } from "./db-resolver.js";
import renderNote from "@/remote/activitypub/renderer/note.js"; import renderNote from "@/remote/activitypub/renderer/note.js";
import { renderLike } from "@/remote/activitypub/renderer/like.js"; import { renderLike } from "@/remote/activitypub/renderer/like.js";

View file

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

View file

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

View file

@ -28,13 +28,19 @@ export default async (ctx: Router.RouterContext) => {
return; return;
} }
const pinings = await UserNotePinings.find({ const pinning = await UserNotePinings.find({
where: { userId: user.id }, where: { userId: user.id },
order: { id: "DESC" }, order: { id: "DESC" },
}); });
const pinnedNotes = await Promise.all( const pinnedNotes = (
pinings.map((pining) => Notes.findOneByOrFail({ id: pining.noteId })), 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( const renderedNotes = await Promise.all(

View file

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

View file

@ -1,7 +1,6 @@
import * as fs from "node:fs"; import * as fs from "node:fs";
import Ajv from "ajv"; import Ajv from "ajv";
import type { CacheableLocalUser } from "@/models/entities/user.js"; 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 { Schema, SchemaType } from "@/misc/schema.js";
import type { AccessToken } from "@/models/entities/access-token.js"; import type { AccessToken } from "@/models/entities/access-token.js";
import type { IEndpointMeta } from "./endpoints.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 { 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 = { export const meta = {
tags: ["admin"], 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 { Users } from "@/models/index.js";
import { signup } from "../../../common/signup.js"; import { signup } from "@/server/api/common/signup.js";
import { IsNull } from "typeorm"; import { IsNull } from "typeorm";
export const meta = { 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 { Users } from "@/models/index.js";
import { doPostSuspend } from "@/services/suspend-user.js"; import { doPostSuspend } from "@/services/suspend-user.js";
import { publishUserEvent } from "@/services/stream.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 { Meta } from "@/models/entities/meta.js";
import { insertModerationLog } from "@/services/insert-moderation-log.js"; import { insertModerationLog } from "@/services/insert-moderation-log.js";
import { db } from "@/db/postgre.js"; import { db } from "@/db/postgre.js";
import define from "../../../define.js"; import define from "@/server/api/define.js";
export const meta = { export const meta = {
tags: ["admin"], 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 { Ads } from "@/models/index.js";
import { genId } from "@/misc/gen-id.js"; import { genId } from "@/misc/gen-id.js";

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