Compare commits

...

21 commits

Author SHA1 Message Date
43708ead38
git: make users pull main branch only 2024-01-18 00:26:46 +09:00
ce7a95796c
dev: fix typo in clean-all command 2024-01-18 00:16:41 +09:00
09b76b4a13
dev: sleep 2 before CREATE EXTENSION pgroonga in dev db container to make it work 2024-01-18 00:14:38 +09:00
c4c9041222
refactor: move env.ts to native-utils (partially) 2024-01-18 00:11:42 +09:00
e35ae0846d
docs: republish history branch 2024-01-17 21:41:54 +09:00
f8d04eafa4
docs: add cargo to dev requirements 2024-01-17 18:09:48 +09:00
ed6d506bc2
docs: add commit message conventions 2024-01-17 18:05:59 +09:00
df2989bade
chore: remove unneeded derivations 2024-01-17 12:53:41 +09:00
fb8a8614f4
chore: add more fields to Config type (native-utils) 2024-01-17 06:34:33 +09:00
d6dd6ff897
refactor: move misc/post.ts to native-utils
Co-authored-by: naskya <m@naskya.net>
2024-01-16 03:17:43 +09:00
0d8ee4387b
dev: track dev directory 2024-01-15 23:37:30 +09:00
c1e0186e7b
refactor: move misc/convert-host.ts to native-utils 2024-01-15 15:42:55 +09:00
15dabfc878
fix: unable to update profiles in some cases 2024-01-15 02:00:18 +09:00
595fe24471
dev: fix typo in Makefile 2024-01-14 21:57:23 +09:00
825eef74aa
refactor: move misc/{safe-for-sql, sql-like-escape}.ts to native-utils 2024-01-14 21:51:30 +09:00
fcccf2eb29
dev: update Makefile 2024-01-14 20:15:05 +09:00
d8ffcf8c51
docs: refactor README 2024-01-14 19:48:41 +09:00
6bfe6066db
refactor: move misc/convert-milliseconds.ts to native-utils 2024-01-14 12:30:47 +09:00
4e63873fc1
refactor: remove misc/secure-rndstr.ts 2024-01-14 12:24:29 +09:00
26cd2a68da
refactor: use cfg_attr to enable napi features conditionally 2024-01-14 12:19:32 +09:00
69cf9b42e0
chore: regenerate SeaORM entities 2024-01-13 22:52:32 +09:00
99 changed files with 1472 additions and 916 deletions

5
.gitignore vendored
View file

@ -28,10 +28,7 @@ coverage
!/.config/docker_ci.env
!/.config/helm_values_example.yml
!/.config/LICENSE
docker-compose.yml
# docker dev config
/dev/docker-compose.yml
/docker-compose.yml
# ESLint
.eslintcache

View file

@ -1,19 +1,49 @@
.PHONY: all
all: shellcheck build_image
.PHONY: build
build: build-image
.PHONY: pre-commit
pre-commit: shellcheck rust-lint regenerate-entities update-index-js
.PHONY: debug
debug:
pnpm install
pnpm run build:debug
pnpm run migrate
pnpm run start
.PHONY: shellcheck
shellcheck:
shellcheck --external-sources update.sh
shellcheck --external-sources neko/update/main.sh
shellcheck --external-sources neko/update/patch.sh
shellcheck --external-sources neko/update/container.sh
shellcheck --external-sources neko/update/native.sh
shellcheck --external-sources --exclude=SC2148 neko/update/utils
shellcheck --external-sources neko/update/*
.PHONY: build_image
build_image:
.PHONY: rust-lint
rust-lint:
cd packages/backend/native-utils && pnpm run lint
.PHONY: regenerate-entities
regenerate-entities:
cd packages/backend/native-utils && \
sea-orm-cli generate entity \
--output-dir='src/model/entity' \
--database-url='postgres://firefish:password@localhost/firefish_db'
.PHONY: update-index-js
update-index-js:
pnpm --filter='native-utils' run build:debug
[ -f packages/backend/native-utils/built/index.js ]
pnpm run format
rm neko/index.js
cp packages/backend/native-utils/built/index.js neko/index.js
.PHONY: build-image
build-image:
. neko/update/utils && \
buildah build \
--no-cache \

647
README.md
View file

@ -1,641 +1,16 @@
[Misskey](https://misskey-hub.net/) のフォークの [Firefish](https://joinfirefish.org/) のフォークです。
# Firefish
本家 Firefish のリポジトリは[こちら](https://git.joinfirefish.org/firefish/firefish)
[Misskey](https://misskey-hub.net/) のハードフォークの [Firefish](https://joinfirefish.org/) のソフトフォークです。[私](https://post.naskya.net/@dev)が個人的に使うことが主な目的ですから、これを使うことを積極的におすすめすることはありません。しかし労力を掛けて作ったものを一人占めするのももったいないので、使いたかったら使ってもいいよ、という気持ちで公開しています。
`main` ブランチではこのフォークに適用された変更のコミット履歴のみが、`history` ブランチでは本家 Firefish のコミットを含む完全なコミット履歴が閲覧できます。
本家 Firefish のリポジトリは[こちら](https://git.joinfirefish.org/firefish/firefish)です。
このフォークの機能について調べるには `main` ブランチを、コードの著者について調べるには `history` ブランチを参照してください。
`main` ブランチではこのフォークに適用された変更のコミット履歴のみが、`history` ブランチでは Misskey の最初のコミットから始まる完全なコミット履歴を確認できます。このフォークの機能について調べるには `main` ブランチを、コードの著者について調べるには `history` ブランチを参照してください。
# 変更点
## 各種説明
## 主要な変更点
- 非ログインユーザーにもローカルタイムラインとグローバルタイムラインを公開できるように変更
- コントロールパネルから設定すると `https://server.example.com/timeline` で公開されます
- 検索フィルターを強化
- 以下の機能があります
- AND 検索
- OR 検索
- 自分の(未収載・フォロワー限定・ダイレクト・秘密を含む)全ての投稿からの検索
- 特定のユーザーの投稿の検索
- 特定のサーバーの投稿の検索
- 特定の期間の投稿の検索
- 添付ファイル付きの投稿の検索
- 全文検索のエンジンを [PGroonga](https://pgroonga.github.io/) に変更
- PGroonga のインストールが必要になります!詳しくは[この投稿](https://post.naskya.net/notes/9ldi29amfanomef5)をご覧ください
- Meilisearch, Elasticsearch, Sonic は使えません
- 「秘密」という公開範囲を追加
- 宛先無しのダイレクト投稿を言い換えているだけです
- 既存の投稿を削除せずに後から秘密にすることもできます
- パフォーマンス向上のためアクティブユーザー以外のチャート生成を無効化
- サードパーティー製クライアントが動かなくなるのを阻止するため API のエンドポイントは残していますが、叩いても `0` が並んだ配列しか返しません。
- モデレーターでない一般ユーザーにもカスタム絵文字の管理権を与えられるように
- カスタム絵文字の管理が大変なサーバー管理者さんがたくさんいらっしゃったのでこの機能を追加するべきではないか他の開発者に訊いたところロール機能の実装を待つべきだと言われてしまったが、Firefish のロール機能は現状では仕様がまだ固まっておらず実装までに時間が掛かると考えられるため
- 以下の権限を与えられます
- 不許可: 絵文字の管理を許可しない
- 追加: 新しい絵文字の追加のみを許可する
- 追加と変更: 「追加」に加え、既存のカスタム絵文字の名前・カテゴリ・タグ・ライセンスの編集を許可する
- 全て許可:「追加と変更」に加え、既存のカスタム絵文字の削除を許可する
- モバイル表示の下部のウィジェットボタンを再読み込みボタンに変更可能に
- スマートフォンでウィジェットは使わないけど再読み込みはたくさんする人はいそう
- モバイル表示の下部のチャットボタンをアカウント切り替えボタンに変更可能に
- これ無しで PWA で複数アカウントを使おうとすると腱鞘炎になる
- ローカルタイムラインの位置をグローバルタイムラインの直前に移動
- ローカルタイムラインよりもソーシャルタイムラインのほうが使いやすいと考えたため
## 細かい変更点
- 猫語で "nA" を "nYA" に置換しない
- この置き換えはあまり嬉しくないことが多い
- `reset-db` という API を無効化
- エンドポイント自体は残してありますが、叩いても何もしません
- データベースをリセットする API って何?怖すぎる
- ユーザーページでプロフィール画像を選択すると画像を拡大するCatodon から取り込み)
- 署名アルゴリズムとして ECDSA や Ed25519 なども受け入れる([github.com/mei23/misskey-v12](https://github.com/mei23/misskey-v12) から取り込み)
- Pleroma のチャットに対応Catodon から取り込み)
- 翻訳機能にて、投稿言語が指定されていない場合にのみ言語の自動検出を用いるように変更
- アップデート時に更新内容を確認できる機能を追加
- 依存ライブラリを最新版にアップデート
- ちゃんと動くか本家に push する前に実験したいという意図もあります
- 中国語の猫モードでは 0.1 の確率で投稿の末尾に「喵」を追加するように
- 設定のバックアップファイルに `misskeyVersion` の値が含まれていなくても警告しないように変更
- マージされていない本家版へのプルリクエストを独断でマージ
- RTL Layout Support ([!10452](https://git.joinfirefish.org/firefish/firefish/-/merge_requests/10452))
- Add language picker to post form ([!10616](https://git.joinfirefish.org/firefish/firefish/-/merge_requests/10616))
- chore: up swc ([!10649](https://git.joinfirefish.org/firefish/firefish/-/merge_requests/10649))
- `emojis` の API エンドポイントMisskey v13- 互換)を追加([firefish-mkdir](https://git.mkdir.uk/hiira/firefish-mkdir) から取り込み)
- Docker のベースイメージに Node v21 を使用
- HTML のコードに入るコメントアートを削除
- 全ページにこんなの入れなくても……
- デフォルトではバイブレーションを無効に
- ログインしていなければ投稿検索ができないように
- 攻撃対策のため
- もしこのせいでサードパーティークライアントに不具合が出る場合には変更するかもしれません
- 投稿と投稿の間を空けて表示する設定をデフォルトでは無効に
- Enter キーのみでチャットを送信する設定をデフォルトでは無効に
- 管理者アカウントも引っ越しできるように
- デフォルトのアイコンの太さを細めに変更
- スタイルを選択する画面のサンプルのアイコンを星に変更
- リアクションの履歴を公開する設定をデフォルトで有効に
- 私がみんなのリアクションを見たいと思っているため
- もちろん設定から無効にできます
- 標準のフォントを Atkinson Hyperlegible にする変更を取り消し
- フォントを変更したい場合は[このカスタム CSS](https://gitlab.com/LunarEclipse363/lunar-misskey-tweaks#setting-a-different-font) を使ってください
- 簡体中文の翻訳が存在しない項目では繁体中文の翻訳を用いるように(本家では逆)
- [サポミク](https://fedi.sup39.dev/@sup39)さんが独自機能に使われているラベルの繁体中文訳を提供してくださったため
- オンラインステータスが非公開のアカウントにはステータスを表す丸印を表示しないように
- 灰色の丸が表示されていてもそんなに嬉しくないため
- デフォルトで検索エンジンからのクロールを拒否するように変更
- 検索の MFM で使用する検索エンジンを設定から変更可能に
- 以下の選択肢から選べます
- DuckDuckGo (duckduckgo.com)
- SearXNG (searx.be)
- Google Search (google.com)
- Moon Search (search.naskya.net)
- サーバーの投稿検索
- サーバー設定の初期値を変更
- 新規登録を無効化
- 新規登録を受け付けたくないのに無効化する前にアカウントを登録されてしまうことを防ぐため
- プライベートモード(連合しないモード)を有効化
- サーバーの準備が整っていないうちにリモートサーバーに認識されてしまうことを防ぐため
- サーバーメトリクスの表示を有効化
- 有効化しているサーバーが多いため
- ランダムなアイコンの生成を無効化
- ランダムなアイコンはそんなにかわいくないため
- 身バレ防止の設定を追加
- 「おかえりなさい、◯◯さん」が出ないようにできるように
- 自分のアイコンを非表示にできるように
- 自分の名前とIDを非表示にできるように
- 名前とIDの部分が空白になるので慣れるまで時間が掛かります
- ユーザーページのデフォルトのタブを「投稿と返信」に変更
- タイムラインにリプライを表示する設定をデフォルトで有効に
- 未読通知のタブをリアクションの通知を表示するタブに変更
- 未読のタブ、使ってる人いる?
- MFM チートシートのボタンを投稿画面から左下のヘルプメニューに移動
- これがあるために投稿画面下部のボタンが 2 段になってしまうことがあるため
- 「フォローされています」の表示を目立たせられるように
- デフォルトの表示は目立たないため
- 最大 15 件の投稿を固定できるように
- 5 件は少ないと思ったため
- 投稿ボタンを巨大にできるように
- [mstdn.maud.io / mstdn.poyo.me の機能](https://mstdn.poyo.me/@prime/110668364208741253) を真似しました
- アンテナにフォロー中のユーザーのホーム投稿も表示する
- フォロー中のユーザーの投稿は見たいから
- 猫のアカウントはアイコンを常に丸く表示する
- そのほうがかわいいため
- NSFW メディアを隠す設定をブラウザごとの設定からブラウザごとかつアカウントごとの設定に変更
- 「このアカウントでは NSFW の画像を常に表示したい」みたいな需要が私にあったため
- インスタンスティッカーをデフォルトで常に表示する
- そのほうが楽しいと思ったから
- 藍ちゃんウィジェットの復活
- インスタンスティッカーのツールチップにソフトウェアのバージョン番号も表示する
- 気になるから
- いいねボタンリアクションピッカーの左にある、⭐とか👍のリアクションをワンクリックで押せるやつで空のリアクションMastodon がふぁぼで送ってくるものと同じ)ではなく本当にその絵文字リアクション(⭐とか👍とか)を送るようにする
- 最新の Misskey ではデフォルトリアクションが❤️になったため空のリアクションを送ると❤️として表示されてしまうが、❤️は 𝓋... という気持ちを伝えるためのリアクションであってただの「いいね」とは異なるため、このボタンで❤️は送りたくないから
- 通知の表示を簡潔にする
- 「がリアクションしました」とかリプライの上にある白い線とかが邪魔に思えたため
- 一部の表示の色も Misskey の通知の色が個人的に好みだったので戻した
- 支援者リストをファイルから読み込む
- 外部のサーバーが落ちるとユーザーページが開けなくなることを防ぐため
- 閲覧注意の注釈と画像の代替テキストもアンテナで調べる対象にする
- ~~「そぎぎ」でアンテナを作れる~~
- インデックス拒否に `noindex` に加えて `nofollow,noarchive,nocache,noimageindex` も指定
- インスタンスティッカーに表示するサーバーのアイコンとして favicon を優先する
- favicon のほうがよくカスタマイズされているため
- 誤爆しやすい位置にあるフォローボタンを隠す設定を追加
- フォローを誤爆すると悲しいため
- デフォルトの robots.txt の設定を変更し、クローラーを拒否するように
- joinfirefish と FediDB のクローラーは許可しています
- 投稿プレビューをデフォルトでオンにする設定を追加
- バージョン番号に最新のコミットの日付とコミットハッシュの頭文字が含まれるように
- 正確なバージョンが分かるとバグ修正に役立つため
## 検証中の変更点
うまく動いていそうだったら本家に push されます
- 特定のユーザーのリプライをタイムラインから非表示する機能(「ブーストをミュート」のリプライ版)を追加
- Docker/Podman の環境で `custom` ディレクトリの内容が反映されない不具合を修正
- 画面を下に引いてタイムラインなどを更新する機能を追加Misskey から取り込み)
- 本家にもマージリクエストを出しました ([!10644](https://git.joinfirefish.org/firefish/firefish/-/merge_requests/10644))
## このフォークから本家 Firefish に輸出された変更点
このフォークは本家に push する前のテスト環境としても使われるため、有用な機能はよく輸出されます(そしてニッチな機能だけが残る)
- iOS で効果音と音楽の再生が干渉する問題を修正Misskey から取り込み)
- サーバーの管理者が左下のヘルプメニューに利用規約以外のページも固定できるように
- 依存ライブラリのバージョンをアップデート
- AiScript のバージョンも上がりました!
- 絵文字ピッカーに表示されるカスタム絵文字の検索結果の件数を最大 100 件に変更Misskey の変更を取り込み)
- 投稿中に表示されるインスタンスティッカーをクリックするとサーバー情報を開くように
- UI 用の言語とは別に、投稿翻訳に使用する言語を設定可能に
- UI 用の言語は翻訳先の言語の第二候補として使われます(投稿の言語と投稿翻訳先に設定した言語が同じだった場合には UI 用の言語に翻訳されます)
- 投稿言語を自動検出して外国語の投稿に翻訳ボタンを表示する設定を追加
- 繁体中文への投稿翻訳を繁体字で表示する
- DeepL 翻訳や LibreTranslate は簡体中文への翻訳しか提供していない……。
- 「Firefish について」のページに Misskey の主要な貢献者を表示
- このソフトウェアは Misskey のフォークであるため
- 閲覧注意の投稿への返信で注釈の先頭に "re:" をつける設定を追加
- 返信で閲覧注意は維持したいけどそのままの注釈を用いるのには違和感を覚えることがよくあるため
- 猫耳の角を少し丸くするMisskey から取り込み)
- そのほうがかわいいため
- インスタンスティッカーのツールチップに出るソフトウェア名で FoundKey, PeerTube, GNU social, WriteFreely などを正しく表示する
# 使用方法
## インストール
以下の方法でインストールできます。
- systemd 版(手動インストールのみ)
- [Debian package](https://code.naskya.net/repos/Wmaym) 版
- [Docker](https://hub.docker.com/r/naskya/firefish) 版
更新が最も早く反映されるのは systemd 版の手動インストールです。Docker イメージや Debian/Ubuntu 向けの deb ファイルの更新も定期的に行うようにしますが、Vervis にはそのようなことを自動で行う仕組みが無いのでちょっとつらいです。
### 自動でインストールする
[Ubuntu 向けのインストールスクリプト](https://code.naskya.net/repos/Wm6vm) があります。
### 手動でインストールする
基本的に本家版のインストールと同じ手順で行えますが、[PGroonga](https://pgroonga.github.io/ja/) のインストールが必要である点が異なります。
#### Docker 版
このリポジトリをクローンして `docker-compose.example.yml``docker-compose.yml` にコピーし、本家版と同様にサーバーを構築すればよいですが、そのまま起動するとデータベースのマイグレーションに失敗します。最初にデータベースのコンテナのみを起動させて以下のコマンドで PGroonga を有効にしてください(`firefish` と `firefish_db` はそれぞれ `.config/docker.env``.config/default.yml` に書いた PostgreSQL のユーザー名とデータベース名にしてください)。
```bash
# DNS・ファイヤーウォール・リバースプロキシなどを各自で設定する
git clone https://code.naskya.net/naskya/firefish
cd firefish
cp docker-compose.example.yml docker-compose.yml
cp .config/example.yml .config/default.yml
# docker-compose.yml, .config/default.yml, .config/docker.env を編集する
docker-compose up db --detach
docker-compose exec db psql --command='CREATE EXTENSION pgroonga;' --user=firefish --dbname=firefish_db
./update.sh --install
```
#### systemd 版
このリポジトリをクローンして、本家版と同様にサーバーを構築します。ビルド等の作業 (`pnpm install`, `pnpm run build`, `pnpm run migrate`) は添付のアップデートスクリプトによって一括で行えます。
```bash
# DNS・ファイヤーウォール・リバースプロキシ・PostgreSQL・Redis などを各自でインストールして設定する
git clone https://code.naskya.net/naskya/firefish
cd firefish
cp .config/example.yml .config/default.yml
# .config/default.yml を編集する
sudo -u postgres psql --command "CREATE EXTENSION pgroonga;" --dbname firefish_db
./update.sh --install
```
[インストール方法に関する私の記事](https://blog.naskya.net/post/6kic0tebueju/)も参考になるかもしれません。
## アップデート
### Docker 版(手動インストール・自動インストール)
重要なお知らせがある場合にはアップデートスクリプトを通じてお伝えするので、必ず `update.sh` を用いてアップデートしてください。
1. サーバーのバックアップを取る
2. `update.sh` を実行し、表示される指示に従う
```bash
./update.sh
```
3. サーバーを起動して動作を確認する
```bash
docker-compose up --detach
```
### systemd 版(手動インストール)
重要なお知らせがある場合にはアップデートスクリプトを通じてお伝えするので、必ず `update.sh` を用いてアップデートしてください。
1. サーバーのバックアップを取る
2. サーバーを停止する
```bash
sudo systemctl stop yourserver.example.com
```
3. `update.sh` を実行し、表示される指示に従う
```bash
./update.sh
```
4. サーバーを起動して動作を確認する
```bash
sudo systemctl start yourserver.example.com
```
### Debian package 版(自動インストール)
`apt` を利用してアップデートします。
1. サーバーのバックアップを取る
2. パッケージをアップデートして動作を確認する
```bash
sudo apt update
sudo apt upgrade
```
#### 正常にアップデートできなかった場合
私が対処する必要がある問題が発生している可能性もあるため、一人で解決しようとせずに `./update.sh` の[実行ログを私まで送ってください](https://code.naskya.net/naskya/firefish#私にコマンドの実行ログを送る)。
## [本家 Firefish](https://git.joinfirefish.org/firefish/firefish) からの乗り換え
### systemd 版
1. サーバーのバックアップを取る
2. サーバーを停止する
```bash
sudo systemctl stop yourserver.example.com
```
3. Firefish がインストールされているディレクトリ (e.g., `/home/calckey/calckey`) の親ディレクトリ (e.g., `/home/calckey`) に移動する
```bash
cd /home/calckey
```
4. Firefish がインストールされているディレクトリ (e.g., `./calckey`) の名前を変える
```bash
mv calckey calckey.old
```
5. 元々 Firefish がインストールされていたディレクトリ (e.g., `./calckey`) と同じ名前でこのリポジトリをクローンする
```bash
git clone https://code.naskya.net/naskya/firefish calckey
```
6. 必要なファイルを元のディレクトリからコピーする
```bash
rm -rf calckey/files calckey/custom calckey/.config
cp -r calckey.old/files calckey
cp -r calckey.old/custom calckey
cp -r calckey.old/.config calckey
```
7. 全文検索エンジンMeilisearch, Sonic, Elasticsearch のいずれか)を使用している場合には、`.config/default.yml` からその設定を削除またはコメントアウトする
先頭に `#` をつけると設定をコメントアウトできます。
```yaml
#sonic:
# host: localhost
# port: 1491
# auth: SecretPassword
# collection: notes
# bucket: default
```
全文検索エンジンは停止またはアンインストールしてしまってよいです。本家の Firefish に戻るつもりがあるなら停止を、そうでなければアンインストールをおすすめします。
停止コマンドの例
```bash
sudo systemctl disable --now sonic
```
8. PostgreSQL のバージョンを確認する
```bash
psql --version
```
9. PGroonga をインストールする
コマンドの例(詳しくは[この投稿](https://post.naskya.net/notes/9ldi29amfanomef5)を参考にしてください)
```bash
sudo apt install -y software-properties-common
sudo add-apt-repository -y universe
sudo add-apt-repository -y ppa:groonga/ppa
sudo apt install -y wget lsb-release
wget https://packages.groonga.org/ubuntu/groonga-apt-source-latest-$(lsb_release --codename --short).deb
sudo apt install -y -V ./groonga-apt-source-latest-$(lsb_release --codename --short).deb
echo "deb http://apt.postgresql.org/pub/repos/apt/ $(lsb_release --codename --short)-pgdg main" | sudo tee /etc/apt/sources.list.d/pgdg.list
wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add -
sudo apt update
sudo apt install -y -V postgresql-14-pgdg-pgroonga
```
10. `.config/default.yml` に書かれているデータベースの名前を確認する(以下の例では `mk1`
```yaml
db:
host: localhost
port: 5432
db: mk1 # <---
```
11. 以下のコマンドを実行して PGroonga の拡張機能を有効にする(`mk1` の部分は自分のデータベース名に変えて実行)
```bash
sudo -iu postgres psql --command="CREATE EXTENSION pgroonga;" --dbname=mk1
```
12. 新しい Firefish のディレクトリに入ってビルドする
```bash
cd calckey
./update.sh --install
```
13. サーバーを起動して動作を確認する
```bash
sudo systemctl start yourserver.example.com
```
14. 元々 Firefish がインストールされていたディレクトリを削除する
```bash
cd ..
rm -rf calckey.old
```
### Docker 版
1. サーバーのバックアップを取る
2. サーバーを停止する
```bash
docker-compose down
```
3. Firefish がインストールされているディレクトリ (e.g., `/home/calckey/calckey`) の親ディレクトリ (e.g., `/home/calckey`) に移動する
```bash
cd /home/calckey
```
4. Firefish がインストールされているディレクトリ (e.g., `./calckey`) の名前を変える
```bash
mv calckey calckey.old
```
5. 元々 Firefish がインストールされていたディレクトリ (e.g., `./calckey`) と同じ名前でこのリポジトリをクローンする
```bash
git clone https://code.naskya.net/naskya/firefish calckey
```
6. 必要なファイルを元のディレクトリからコピーする
```bash
rm -rf calckey/files calckey/custom calckey/.config
cp -r calckey.old/files calckey
cp -r calckey.old/custom calckey
cp -r calckey.old/.config calckey
```
7. 以下のコマンドを実行して PGroonga を有効にするfirefish と firefish_db はそれぞれ `.config/docker.env``.config/default.yml` に書いた PostgreSQL のユーザー名とデータベース名にしてください)
```bash
docker-compose up db --detach
docker-compose exec db psql --command='CREATE EXTENSION pgroonga;' --user=firefish --dbname=firefish_db
```
8. サーバーを起動して動作を確認する
```bash
docker-compose up --detach
```
9. 元々 Firefish がインストールされていたディレクトリを削除する
```bash
cd ..
rm -rf calckey.old
```
## このフォークから[本家 Firefish](https://git.joinfirefish.org/firefish/firefish) へ戻る
### systemd 版
1. サーバーのバックアップを取る
2. サーバーを停止する
```bash
sudo systemctl stop yourserver.example.com
```
3. Firefish がインストールされているディレクトリ (e.g., `/home/calckey/calckey`) へ移動する
```bash
cd /home/calckey/calckey
```
4. 最新版にアップデートする
```bash
./update.sh
```
5. `.config/default.yml` に書かれているデータベースの名前を確認する(以下の例では `mk1`
```yaml
db:
host: localhost
port: 5432
db: mk1 # <---
```
6. 次のコマンドでデータベースをいじる前に、そのコマンドが正常に動作するか確認する(`mk1` の部分は自分のデータベース名に変更する)
```bash
printf 'BEGIN;\n%s\nROLLBACK;' "$(cat neko/revert.sql)" | sudo -iu postgres psql --set='ON_ERROR_STOP=1' --dbname=mk1
```
最後の行が `ROLLBACK` で終わっていれば問題ありません。そうでない場合には以下の[コマンドの実行結果を私に送ってください](https://code.naskya.net/naskya/firefish#私にコマンドの実行ログを送る)。
```bash
printf 'BEGIN;\n%s\nROLLBACK;' "$(cat neko/revert.sql)" | sudo -iu postgres psql --echo-all --set='ON_ERROR_STOP=1' --dbname=mk1
```
7. このフォークで加えられたデータベースへの変更を実際に取り消す(`mk1` の部分は自分のデータベース名に変更する)
```bash
sudo -iu postgres psql --file=neko/revert.sql --dbname=mk1
```
8. PGroonga をアンインストールする
コマンドの例
```bash
sudo apt purge --remove postgresql-14-pgdg-pgroonga
sudo add-apt-repository --remove ppa:groonga/ppa
sudo apt-key del ACCC4CF8
sudo apt update
```
9. Firefish がインストールされているディレクトリの親ディレクトリ (e.g., `/home/calckey`) に行く
```bash
cd ..
```
10. Firefish がインストールされているディレクトリ (e.g., `./calckey`) の名前を変える
```bash
mv calckey calckey.old
```
11. Firefish がインストールされているディレクトリと同じ名前で本家版の Firefish を clone する
```bash
git clone https://git.joinfirefish.org/firefish/firefish.git calckey
```
12. 必要なファイルをコピーする
```bash
rm -rf calckey/files calckey/custom calckey/.config
cp -r calckey.old/files calckey
cp -r calckey.old/custom calckey
cp -r calckey.old/.config calckey
```
13. 新しい Firefish のディレクトリ (e.g., `./calckey`) に入り、`develop` ブランチに行く(実際には既に `develop` にいるはず)
```bash
cd calckey
git checkout develop
```
14. Firefish をビルドする
```bash
corepack prepare pnpm@latest --activate
pnpm i
NODE_ENV=production pnpm run build
pnpm run migrate
```
15. サーバーを起動して動作を確認する
```bash
sudo systemctl start yourserver.example.com
```
16. 元々 Firefish がインストールされていたディレクトリを削除する
```bash
cd ..
rm -rf calckey.old
```
#### 注意
この手順を踏むとあなたの Firefish サーバーは `develop` 版になります。他のバージョンを動かしたい場合も、**次のアップデートがリリースされるまでは `develop` 版を動かしてください**。
例えば `beta` 版を動かしたい場合、次に `beta` 版がリリースされたらそちらに移れます。
```bash
git checkout beta
git pull --ff
corepack prepare pnpm@latest --activate
pnpm i
NODE_ENV=production pnpm run build
pnpm run migrate
```
# 補足
## 私にコマンドの実行ログを送る
実行したいコマンドの後ろに `|& tee /tmp/fflog` をつけてコマンドを実行します。例えば実行したいコマンドが `./update.sh` ならば
```bash
./update.sh |& tee /tmp/fflog
```
とし、実行したいコマンドが
```bash
printf 'BEGIN;\n%s\nROLLBACK;' "$(cat neko/revert.sql)" | sudo -iu postgres psql --echo-all --set='ON_ERROR_STOP=1' --dbname=mk1
```
ならば
```bash
printf 'BEGIN;\n%s\nROLLBACK;' "$(cat neko/revert.sql)" | sudo -iu postgres psql --echo-all --set='ON_ERROR_STOP=1' --dbname=mk1 |& tee /tmp/fflog
```
とします。
するとコマンドの実行ログが `/tmp/fflog` に保存されるので、保存されたテキスト全体をサーバーの OS などの環境の情報とともに[私](https://post.naskya.net/@dev)に送ってください。
ログのうちの参考になりそうだと思った一部だけを抜粋して送ることは絶対にしないでください。必要な情報が欠落していてもう一度全体を送り直してもらうことになったり、私が対処方法の判断を誤ったりする可能性が高まったりします。
テキストが一つの投稿に収まる長さの場合はコードブロックの記法(``` で囲む)を用いて投稿してもらってよいです。ログが一投稿に収まらない長さの場合は [Pastebin](https://pastebin.com/) などのサービスを使って共有してください。投稿にログファイルを直接添付して送ることはしないでください。
ログを私に送ったら `/tmp/fflog` は削除してよいです。
```bash
rm /tmp/fflog
```
- [本家版と異なる点](https://code.naskya.net/naskya/firefish/source-by/main/docs/changes.md)
- [インストール方法](https://code.naskya.net/naskya/firefish/source-by/main/docs/install.md)
- [アップデート方法](https://code.naskya.net/naskya/firefish/source-by/main/docs/update.md)
- [本家版からの移行方法](https://code.naskya.net/naskya/firefish/source-by/main/docs/migrate.md)
- [本家版への移行方法](https://code.naskya.net/naskya/firefish/source-by/main/docs/migrate_back.md)
- [開発への協力方法](https://code.naskya.net/naskya/firefish/source-by/main/docs/contributing.md)

17
dev/Makefile Normal file
View file

@ -0,0 +1,17 @@
.PHONY: recreate
recreate: down up
.PHONY: down
down:
podman-compose down
.PHONY: up
up:
podman-compose up --detach
sleep 2
podman-compose exec db psql \
--user=firefish \
--dbname=firefish_db \
--command='CREATE EXTENSION pgroonga;'

16
dev/docker-compose.yml Normal file
View file

@ -0,0 +1,16 @@
version: "3"
services:
redis:
image: docker.io/redis:7-alpine
ports:
- "6379:6379"
db:
image: docker.io/groonga/pgroonga:latest-alpine-16
environment:
- "POSTGRES_PASSWORD=password"
- "POSTGRES_USER=firefish"
- "POSTGRES_DB=firefish_db"
ports:
- "5432:5432"

202
docs/changes.md Normal file
View file

@ -0,0 +1,202 @@
# 本家版からの変更点
## Firefish そのものに関する変更点(メタな変更点)
- [ローリングリリース](https://ja.wikipedia.org/wiki/%E3%83%AD%E3%83%BC%E3%83%AA%E3%83%B3%E3%82%B0%E3%83%AA%E3%83%AA%E3%83%BC%E3%82%B9)を採用
- いわゆる「新バージョンのリリース」はありません。細かい更新が頻繁に行われるので、好きなタイミングでアップデートしてください。
- バージョン番号は `{本家のバージョン}+neko:{コミットの日付}.{コミットハッシュの上 2 桁}` です。
- 例えば [`6424b685e9` というコミット](https://code.naskya.net/repos/a3WPw/commits/6424b685e971578427c8db5b14710494c5cdc0fd)の直後のバージョンは `1.0.5-dev20+neko:231107.64` です。
- 元々私は `neko.なんとか` というドメインで登録を一般開放したサーバーを立てようと思っていて、そのサーバーにこの Firefish をインストールしようと思っていたことからバージョン番号に `neko` が含まれています。
- 現在は生活に余裕が無く、管理に時間を割けなそうにないためこのサーバーを立てる計画は中断しています。
- この Firefish はよく Firefish neko flavor と呼ばれていますが、これはこのバージョン番号に由来します。私自身は特にこの呼称を普及させたいわけではなかったのですが、[私のアカウント](https://post.naskya.net/@dev)の数割のフォロワーが英語圏の本家 Firefish の利用者であるため単に「この機能を追加しました!」などと投稿すると本家版の更新と誤解される恐れがありますし、「私の Firefish フォークにこの機能を追加しました!」などと投稿すると私が本家 Firefish の開発を離れて新たなハードフォークを作っているのだと誤解される恐れがあるため、投稿を機械翻訳で英訳されて読まれてもなるべく誤解されないための苦肉の策としてこの呼称を使い始めました。このソフトフォークはあくまで本家 Firefish の私による「味付け」です。
- 依存ライブラリをほとんど常に最新版にアップデート
- 本家版を更新する前にちゃんと動くか実験したいという意図もあります
## クライアントの変更点
### 主要な変更点
- 投稿ごとに言語を指定できるように([!10616](https://git.joinfirefish.org/firefish/firefish/-/merge_requests/10616) を独断でマージ)
- これにより Mastodon の翻訳機能や言語フィルターなどが正常に機能します。今後 Firefish 側でもこの情報をもっと活用できるようにしたいです。
- 検索フィルターを強化
- 以下の機能があります
- AND 検索
- OR 検索
- 自分の(未収載・フォロワー限定・ダイレクト・秘密を含む)全ての投稿からの検索
- 特定のユーザーの投稿の検索
- 特定のサーバーの投稿の検索
- 特定の期間の投稿の検索
- 添付ファイル付きの投稿の検索
- 「秘密」という公開範囲を追加
- 宛先無しのダイレクト投稿を言い換えているだけです
- 既存の投稿を削除せずに後から秘密にすることもできます
- モバイル表示の下部のチャットボタンをアカウント切り替えボタンに変更可能に
- これ無しで PWA で複数アカウントを使おうとすると腱鞘炎になる
- ローカルタイムラインの位置をグローバルタイムラインの直前に移動
- ローカルタイムラインよりもソーシャルタイムラインのほうが使いやすいと考えたため
- TODO: もっとカスタムできるようにして、本家にもその機能を入れたい ([#rw0Dw](https://code.naskya.net/decks/4wJQ3/tickets/rw0Dw))
- 画面を下に引いてタイムラインなどを更新する機能を追加([Misskey から取り込み](https://github.com/misskey-dev/misskey/commit/c239058624dcd880ec1c5f3c436f3a2a06fc22c3)
- 本家にもマージリクエストを出しています ([!10644](https://git.joinfirefish.org/firefish/firefish/-/merge_requests/10644))
### 細かい変更点
- モバイル表示の下部のウィジェットボタンを再読み込みボタンに変更可能に
- スマートフォンでウィジェットは使わないけど再読み込みはたくさんする人はいそう
- 設定のバックアップファイルに `misskeyVersion` の値が含まれていなくても警告しないように変更
- TODO: 本家版にもマージリクエストを出す
- RTL Layout右から左に書く言語の表示の部分的なサポート[!10452](https://git.joinfirefish.org/firefish/firefish/-/merge_requests/10452) を独断でマージ)
- アップデート時に更新内容を確認できる機能を追加
- HTML のコードに入るコメントアートを削除
- 全ページにこんなの入れなくても……
- デフォルトではバイブレーションを無効に
- 投稿と投稿の間を空けて表示する設定をデフォルトでは無効に
- Enter キーのみでチャットを送信する設定をデフォルトでは無効に
- デフォルトのアイコンの太さを細めに変更
- スタイルを選択する画面のサンプルのアイコンを星に変更
- 標準のフォントを Atkinson Hyperlegible にする変更を取り消し
- フォントを変更したい場合は[このカスタム CSS](https://gitlab.com/LunarEclipse363/lunar-misskey-tweaks#setting-a-different-font) を使ってください
- 簡体中文の翻訳が存在しない項目では繁体中文の翻訳を用いるように(本家では逆)
- [サポミク](https://fedi.sup39.dev/@sup39)さんが独自機能に使われているラベルの繁体中文訳を提供してくださったため
- オンラインステータスが非公開のアカウントにはステータスを表す丸印を表示しないように
- 灰色の丸が表示されていてもそんなに嬉しくないため
- デフォルトで検索エンジンからのクロールを拒否するように変更
- 検索の MFM で使用する検索エンジンを設定から変更可能に
- 以下の選択肢から選べます
- DuckDuckGo (duckduckgo.com)
- SearXNG (searx.be)
- Google Search (google.com)
- Moon Search (search.naskya.net)
- まだこの検索エンジンは作っていません……
- サーバーの投稿検索
- 身バレ防止の設定を追加
- 「おかえりなさい、◯◯さん」が出ないようにできるように
- 自分のアイコンを非表示にできるように
- 自分の名前と ID を非表示にできるように
- 名前と ID の部分をただ空白にするだけです
- ユーザーページのデフォルトのタブを「投稿と返信」に変更
- タイムラインにリプライを表示する設定をデフォルトで有効に
- 未読通知のタブをリアクションの通知を表示するタブに変更
- 未読のタブ、使ってる人いる?
- MFM チートシートのボタンを投稿画面から左下のヘルプメニューに移動
- これがあるために投稿画面下部のボタンが 2 段になってしまうことがあるため
- 「フォローされています」の表示を目立たせられるように
- デフォルトの表示は目立たないため
- 投稿ボタンを巨大にできるように
- [mstdn.maud.io / mstdn.poyo.me の機能](https://mstdn.poyo.me/@prime/110668364208741253) を真似しました
- 猫のアカウントはアイコンを常に丸く表示する
- そのほうがかわいいため
- NSFW メディアを隠す設定をブラウザごとの設定からブラウザごとかつアカウントごとの設定に変更
- 「このアカウントでは NSFW の画像を常に表示したい」みたいな需要が私にあったため
- インスタンスティッカーをデフォルトで常に表示する
- そのほうが楽しいと思ったから
- 藍ちゃんウィジェットの復活
- インスタンスティッカーのツールチップにソフトウェアのバージョン番号も表示する
- 気になるから
- いいねボタンリアクションピッカーの左にある、⭐とか👍のリアクションをワンクリックで押せるやつで空のリアクションMastodon がふぁぼで送ってくるものと同じ)ではなく本当にその絵文字リアクション(⭐とか👍とか)を送るようにする
- 最新の Misskey ではデフォルトリアクションが❤️になったため空のリアクションを送ると❤️として表示されてしまうが、❤️は 𝓋... という気持ちを伝えるためのリアクションであってただの「いいね」とは異なるため、このボタンで❤️は送りたくないから
- 通知の表示を簡潔にする
- 「がリアクションしました」とかリプライの上にある白い線とかが邪魔に思えたため
- 一部の表示の色も Misskey の通知の色が個人的に好みだったので戻しました
- インスタンスティッカーに表示するサーバーのアイコンとして favicon を優先する
- favicon のほうがよくカスタマイズされているため
- TODO: サーバーによってはちゃんと動いていない気がするので調べる ([#13EQ3](https://code.naskya.net/decks/4wJQ3/tickets/13EQ3))
- 誤爆しやすい位置にあるフォローボタンを隠す設定を追加
- フォローを誤爆すると悲しいため
- 投稿プレビューをデフォルトでオンにする設定を追加
## サーバーの変更点
### 主要な変更点
- 非ログインユーザーにもローカルタイムラインとグローバルタイムラインを公開できるように変更
- コントロールパネルから設定すると `https://server.example.com/timeline` で公開されます
- 全文検索のエンジンを [PGroonga](https://pgroonga.github.io/) に変更([tamaina さんの PGroonga パッチ](https://gist.github.com/tamaina/29e88e02d0dc5acfa2f50b3347d3d20d)を拡張)
- Meilisearch, Elasticsearch, Sonic は使えません
- モデレーターでない一般ユーザーにもカスタム絵文字の管理権を与えられるように変更
- カスタム絵文字の管理が大変なサーバー管理者さんがたくさんいらっしゃったのでこの機能を追加するべきではないか他の開発者に訊いたところロール機能の実装を待つべきだと言われてしまったが、Firefish のロール機能は現状では仕様がまだ固まっておらず実装までに時間が掛かると考えられるため
- 以下の権限を与えられます
- 不許可: 絵文字の管理を許可しない
- 追加: 新しい絵文字の追加のみを許可する
- 追加と変更: 「追加」に加え、既存のカスタム絵文字の名前・カテゴリ・タグ・ライセンスの編集を許可する
- 全て許可:「追加と変更」に加え、既存のカスタム絵文字の削除を許可する
- 特定のユーザーのリプライをタイムラインから非表示する機能(「ブーストをミュート」のリプライ版)を追加
- 本家版にもマージリクエストを出しています ([!10658](https://git.joinfirefish.org/firefish/firefish/-/merge_requests/10658))
### 細かい変更点
- 設定の初期値を変更
- 新規登録を無効化
- 新規登録を受け付けたくないのに無効化する前にアカウントを登録されてしまうことを防ぐため
- プライベートモード(連合しないモード)を有効化
- サーバーの準備が整っていないうちにリモートサーバーに認識されてしまうことを防ぐため
- サーバーメトリクスの表示を有効化
- 有効化しているサーバーが多いため
- ランダムなアイコンの生成を無効化
- ランダムなアイコンはそんなにかわいくないため
- パフォーマンス向上のためアクティブユーザー以外のチャート生成を無効化
- サードパーティー製クライアントが動かなくなるのを阻止するため API のエンドポイントは残していますが、叩いても `0` が並んだ配列しか返しません。
- 猫語で "nA" を "nYA" に置換しない
- この置き換えはあまり嬉しくないことが多い
- `reset-db` という API を無効化
- エンドポイント自体は残してありますが、叩いても何もしません
- データベースをリセットする API って何?怖すぎる
- ユーザーページでプロフィール画像を選択すると画像を拡大する([Catodon から取り込み](https://codeberg.org/catodon/catodon/commit/a4b74d9b6bd7db56f0a4ce3439c29b9df4b41100)
- 本家版にもマージリクエストを出しています ([!10659](https://git.joinfirefish.org/firefish/firefish/-/merge_requests/10659))
- 署名アルゴリズムとして ECDSA や Ed25519 なども受け入れる([mei23/misskey-v12 から取り込み](https://github.com/mei23/misskey-v12/commit/b008e1e7169ea7cbb4a49fb1856f8cd838333230)
- TODO: 本家版にもマージリクエストを出す
- Pleroma のチャットに対応([Catodon から取り込み](https://codeberg.org/catodon/catodon/commit/0ed49d05a98ecb3df938f5558fdfe7011844732f)
- 本家版にもマージリクエストを出しています ([!10660](https://git.joinfirefish.org/firefish/firefish/-/merge_requests/10660))
- 翻訳機能にて、投稿言語が指定されていない場合にのみ言語の自動検出を用いるように変更
- TODO: 本家版にもマージリクエストを出す
- 中国語の猫モードでは 0.1 の確率で投稿の末尾に「喵」を追加するように
- TODO: 様子を見て、良さそうだったら本家版にもマージリクエストを出す
- `emojis` APIMisskey v13- 互換)を追加([firefish-mkdir から取り込み](https://git.mkdir.uk/hiira/firefish-mkdir/commit/e98af2b2a78463bd64393936dbe223127552bff5)
- ログインしていなければ投稿検索ができないように
- 攻撃対策のため
- もしこのせいでサードパーティークライアントに不具合が出る場合には変更するかもしれません
- 管理者アカウントも引っ越しできるように
- リアクションの履歴を公開する設定をデフォルトで有効に
- 私がみんなのリアクションを見たいと思っているため
- もちろん設定から無効にできます
- 最大 15 件の投稿を固定できるように
- 5 件は少ないと思ったため
- アンテナにフォロー中のユーザーのホーム投稿も表示する
- フォロー中のユーザーの投稿は見たいから
- 支援者リストをファイルから読み込む
- 外部のサーバーが落ちるとユーザーページが開けなくなることを防ぐため
- 閲覧注意の注釈~~と添付ファイルの代替テキスト~~もアンテナで調べる対象にする
- ~~「そぎぎ」でアンテナを作れる~~
- TODO: 代替テキストがちゃんと調べられていない気がするので直す ([#q32v3](https://code.naskya.net/decks/4wJQ3/tickets/q32v3))
- インデックス拒否に `noindex` に加えて `nofollow,noarchive,nocache,noimageindex` も指定
- デフォルトの robots.txt の設定を変更し、クローラーを拒否するように
- joinfirefish と FediDB のクローラーは許可しています
## コンテナイメージの変更点
- ベースイメージに [`docker.io/node:21-slim`](https://hub.docker.com/layers/library/node/21-slim/images/sha256-5a3290b496bb63e1e19d440810a55eea3e004eeda7cc15f75a8b256995e41542) を使用
- イメージのビルドに [buildah](https://github.com/containers/buildah) を使用
- もし Docker との互換性に問題があったりしたら教えてください
- Rust のリンカーに [mold](https://github.com/rui314/mold) を使用
- `custom` ディレクトリの内容が反映されない不具合を修正
- TODO: 本家版も修正する
## このフォークから本家 Firefish に輸出された変更点
このフォークは本家に push する前のテスト環境としても使われるため、有用な機能はよく輸出されます(そしてニッチな機能だけが残る)
- iOS で効果音と音楽の再生が干渉する問題を修正([Misskey から取り込み](https://github.com/misskey-dev/misskey/commit/38d6580a36bb6474153536edb50b59376ce16779)
- サーバーの管理者が左下のヘルプメニューに利用規約以外のページも固定できるように
- 絵文字ピッカーに表示されるカスタム絵文字の検索結果の件数を最大 100 件に変更([Misskey から取り込み](https://github.com/misskey-dev/misskey/commit/e11320dbb70bbae0da78975718cc06b440daa6a4)
- 投稿中に表示されるインスタンスティッカーをクリックするとサーバー情報を開くように
- UI 用の言語とは別に、投稿翻訳に使用する言語を設定可能に
- UI 用の言語は翻訳先の言語の第二候補として使われます(投稿の言語と投稿翻訳先に設定した言語が同じだった場合には UI 用の言語に翻訳されます)
- 投稿言語を自動検出して外国語の投稿に翻訳ボタンを表示する設定を追加
- 繁体中文への投稿翻訳を繁体字で表示する
- DeepL 翻訳や LibreTranslate は簡体中文への翻訳しか提供していない……。
- 「Firefish について」のページに Misskey の主要な貢献者を表示
- このソフトウェアは Misskey のフォークであるため
- 閲覧注意の投稿への返信で注釈の先頭に "re:" をつける設定を追加
- 返信で閲覧注意は維持したいけどそのままの注釈を用いるのには違和感を覚えることがよくあるため
- 猫耳の角を少し丸くする([Misskey から取り込み](https://github.com/misskey-dev/misskey/commit/1a96425768b743cf6a7f93b7813c082152497461)
- そのほうがかわいいため
- インスタンスティッカーをマウスオーバーしたときに出るソフトウェア名で FoundKey, PeerTube, GNU social, WriteFreely などを正しく表示する

98
docs/contributing.md Normal file
View file

@ -0,0 +1,98 @@
# 開発への協力方法
これは個人的なフォークなので、基本的には本家 Firefish の開発に協力してもらえればと思います。ただし、最近は本家 Firefish へのマージリクエストのマージが滞りがちですし、本家版にいきなり貢献するのはハードルが高いと思ったりマージしてもらえるか分からないニッチな機能を作ったりした場合にはこのフォークへ貢献していただいてもよいです。
現状、マージリクエストを直接送る方法が無いので patch ファイルまたは別のサイトにフォークしたリポジトリに追加したコミットへの URL を[私](https://post.naskya.net/@dev)に送ってください。
## 開発の方法
### 環境構築
1. 以下のソフトウェアをインストールする
- podman
- podman-compose
- make
- nodejs
- pnpm
- shellcheck
- sea-orm-cli
- cargo
2. コンテナを起動し、PGroonga を有効化する
```bash
cd dev
make up
```
3. 以下の内容の `.config/default.yml` を作成する
```yaml
url: http://localhost:3000
port: 3000
db:
host: localhost
port: 5432
db: firefish_db
user: firefish
pass: password
redis:
host: localhost
port: 6379
```
コンテナを作り直すと簡単にデータベースを初期化できます。
```bash
cd dev
make
```
参考にした記事: [Firefish 開発環境の準備(バックエンド向け)](https://hackmd.io/@nmkj-io/HJHNbM_8a)
### 実行
```bash
make debug
```
### コミット前に行う確認
```bash
make pre-commit
```
### コミットメッセージ
コミットメッセージの書き方に厳密な規則はありませんが、私は以下のようにコミットメッセージを書いているのである程度それに則ってもらえると助かります。でも適当で大丈夫です(更新を取り込む際にコミットメッセージが書き換えられる場合もありますが、ご了承ください)。
コミットメッセージは英語で書き、場合によっては冠詞などを適度に省いて短く収めてください。長い説明が必要な場合には、以下の規則で 1 行目にざっくりとしたコミットメッセージを書いてから 2 つ改行して(つまり 1 行空けて)詳細説明を続けてください。
コミットメッセージの頭文字は大文字にせず、動詞から始める場合には現在形を用いてください。例えば、`feat: Added xxxxx` とはせずに `feat: add xxxxx` としてください。
1. 重大な不具合の緊急修正: `hotfix: 不具合の内容`
- プログラム自体が起動しなくなったり投稿が一切できなくなるなどの重大な不具合を回避するための更新には `hotfix:` を頭につけます。
2. ドキュメントのみの更新: `docs: 更新の概要`
- 内容がドキュメントの更新のみの場合、`docs:` を頭につけます。たとえ更新内容が誤字の訂正であったとしても `fix: typo` ではなく `docs: fix typo` とします。
3. 翻訳の更新: `locale: 更新の概要`
- `locales/` 以下のファイルをいじった場合のコミットなどがこれに該当します。
4. コンテナイメージに関する更新: `container: 更新の概要`
- `Dockerfile``docker-compose.yml` の更新など、Podman/Docker のユーザー以外には一切影響しない更新がこれに該当します。
5. Firefish のプログラム本体には変更を加えない更新: `dev: 更新の概要`
- アップデートスクリプトや Makefile の更新など、プログラム本体への変更は加えない場合には `dev:` を頭につけます。
- `packages.json`, `packages/`, `locales/` に変更を全く加えていないコミットは大体これに該当します。
- 開発とは全く関係無い場合には `meta:` を用いてもよいです。
6. 不具合の修正: `fix: 不具合の内容`
7. 機能の追加: `feat: 更新の概要`
8. パフォーマンスの向上のための再設計: `perf: 再設計の概要`
9. Web クライアントの見た目の変更: `style: 更新の概要`
10. プログラムの再設計: `refactor: 再設計の概要`
11. その他の雑務: `chore: 更新の概要`
- コードのフォーマットや非常に小規模なプログラムの書き換え(一箇所の `||``??` に書き換えるなど)や依存パッケージの更新がこれに該当します。
更新が本当に小規模な場合には `:` の直前に `(minor)` を入れても良いです(例: `docs (minor): fix typo in README.md`)が、ほとんどの場合にこれは使う必要がありません。
## 注意事項
データベースのマイグレーションを伴う変更を加える場合にはマイグレーションのファイルを [`packages/backend/migration-neko`](https://code.naskya.net/naskya/firefish/source-by/main/packages/backend/migration-neko) の下に作成し、マイグレーションを打ち消す SQL クエリを [`neko/revert.sql`](https://code.naskya.net/naskya/firefish/source-by/main/neko/revert.sql) の**一番上に**追記してください。

224
docs/install.md Normal file
View file

@ -0,0 +1,224 @@
# インストール方法
## サーバーに直接インストールする
1. サーバーに git, Nodejs, npm, PostgreSQL, PGroonga, Redis, Rust をインストールします。Arch Linux にインストールする場合には[私のブログ](https://blog.naskya.net/post/6kic0tebueju/)が参考になるかもしれません。
Ubuntu や Debian にインストールする場合には、例えば以下のようになります。
```bash
# Rust のインストール
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# Nodejs v21 のインストールの準備
NODE_MAJOR=21
sudo apt install --no-install-recommends curl ca-certificates gnupg
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key \
| sudo gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg
echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] "https://deb.nodesource.com/node_${NODE_MAJOR}.x" nodistro main" \
| sudo tee /etc/apt/sources.list.d/nodesource.list
# PostgreSQL のインストールの準備
echo "deb https://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" | sudo tee /etc/apt/sources.list.d/pgdg.list
curl -s https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add -
# PGroonga のインストールの準備
sudo apt install --no-install-recommends software-properties-common wget
sudo add-apt-repository -y universe
sudo add-apt-repository -y ppa:groonga/ppa
wget https://packages.groonga.org/ubuntu/groonga-apt-source-latest-$(lsb_release --codename --short).deb
sudo apt install --no-install-recommends ./groonga-apt-source-latest-$(lsb_release --codename --short).deb
echo "deb http://apt.postgresql.org/pub/repos/apt/ $(lsb_release --codename --short)-pgdg main" | sudo tee /etc/apt/sources.list.d/pgdg.list
wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add -
# Redis のインストールの準備
curl -fsSL https://packages.redis.io/gpg | sudo gpg --dearmor -o /usr/share/keyrings/redis-archive-keyring.gpg
# 各ソフトウェアのインストール
sudo apt update
sudo apt install --no-install-recommends git nodejs postgresql-16 postgresql-16-pgdg-pgroonga redis-server
# pnpm を有効化
sudo corepack enable
# PostgreSQL, Redis を起動
systemctl enable --now postgresql redis-server
```
2. このリポジトリを複製し、リポジトリのディレクトリに移る
```bash
git clone --branch=main https://code.naskya.net/naskya/firefish
cd firefish
```
3. 設定ファイルのテンプレートを複製する
```bash
cp .config/example.yml .config/default.yml
```
4. PostgreSQL のユーザー名・データベース名・パスワードを決める(決めるだけでこの工程にコマンドの操作は無い)
5. `./config/default.yml` を編集する
```bash
vim .config/default.yml
```
`url` という項目を `example.com` から使いたいドメイン名に変更する
```yaml
# Final accessible URL seen by a user.
url: https://example.com/
```
データベースの設定を変える(データベース名・ユーザー名・パスワードはそれぞれさっき自分で決めたもの)
```yaml
db:
host: localhost # 変えない
port: 5432 # 変えない
db: firefish_db # ここをデータベース名に変える
user: firefish # ここをデータベースのユーザー名に変える
pass: very_strong_password # ここをデータベースのパスワードに変える
```
6. PostgreSQL のユーザーとデータベースを作る
```bash
POSTGRES_DB='データベース名'
POSTGRES_USER='ユーザー名'
POSTGRES_PASSWORD='パスワード'
sudo -u postgres psql --command="CREATE ROLE ${POSTGRES_USER} LOGIN PASSWORD '${POSTGRES_PASSWORD}';"
sudo -u postgres psql --command="CREATE DATABASE ${POSTGRES_DB} OWNER ${POSTGRES_USER} encoding = 'UTF8';"
```
7. PGroonga を有効化する
```bash
sudo -u postgres psql --dbname="${POSTGRES_DB}" --command='CREATE EXTENSION pgroonga;'
```
8. Firefish をビルドする
```bash
./update.sh --install --native
```
9. Firefish を起動するための systemd サービスを作る
`/etc/systemd/system/firefish.service` というテキストファイルを作り、Firefish をインストールしたディレクトリで `pnpm run start` を実行させるようにします。[私のブログ](https://blog.naskya.net/post/6kic0tebueju/#firefish-%E3%82%92%E8%B5%B7%E5%8B%95)や [systemd サービスに関する記事](https://wiki.archlinux.jp/index.php/Systemd#.E3.83.A6.E3.83.8B.E3.83.83.E3.83.88.E3.83.95.E3.82.A1.E3.82.A4.E3.83.AB)などを参考にしてください。
以下の設定を行えば大丈夫なはずです。
- 実行するユーザーをグループを指定する
- `which pnpm``pnpm` コマンドの場所(例えば `/usr/bin/pnpm`)を探し、`ExecStart` で `pnpm run start` を実行させる
- 例えば `ExecStart=/usr/bin/pnpm run start`
- リポジトリのディレクトリを `WorkingDirectory` に設定する
- `Environment="NODE_ENV=production"`
```bash
sudo vim /etc/systemd/system/firefish.service
```
10. Firefish のサービスを有効化・起動する
```bash
sudo systemctl enable --now firefish
```
これで `http://localhost:3000` で Web クライアントが動くので、リバースプロキシやファイヤーウォールを設定して外部からアクセス可能にすればよいです。その設定には[私のブログ](https://blog.naskya.net/post/6kic0tebueju/)や[本家 Firefish の README](https://git.joinfirefish.org/firefish/firefish#-getting-started)が参考になるかもしれません。
正常にインストールできなかった場合には自力で解決しようとせず、[私にコマンドの実行ログを送ってください](https://code.naskya.net/naskya/firefish/source-by/main/docs/trouble_shooting.md#私にコマンドの実行ログを送る)。
## コンテナイメージを用いる
実行環境として [Podman](https://podman.io/) と [Docker](https://www.docker.com/) をサポートしています。Docker を使う場合には以下の `podman`, `podman-compose`, `--podman` をそれぞれ `docker`, `docker-compose`, `--docker` に読み替えてください。
x86_64 アーキテクチャの Linux のマシン上では、[`registry.code.naskya.net/naskya/firefish`](https://view.registry.code.naskya.net/naskya/firefish) に上げられている [OCI コンテナイメージ](https://opencontainers.org/)を利用できます。それ以外の環境で使うには、自分でコンテナイメージをビルドする必要があります。
1. このリポジトリを複製し、リポジトリのディレクトリに移る
```bash
git clone --branch=main https://code.naskya.net/naskya/firefish
cd firefish
```
2. 設定ファイルのテンプレートを複製する
```bash
cp docker-compose.example.yml docker-compose.yml
cp .config/docker.example.env .config/docker.env
cp .config/example.yml .config/default.yml
```
3. PostgreSQL のユーザー名・データベース名・パスワードを決めて `./config/docker.env` に書く
```bash
vim .config/docker.env
```
4. `.config/default.yml` を編集する
```bash
vim .config/default.yml
```
`url` という項目を `example.com` から使いたいドメイン名に変更する
```yaml
# Final accessible URL seen by a user.
url: https://example.com/
```
データベースの設定を変える(データベース名・ユーザー名・パスワードはそれぞれさっき `.config/docker.env` に書いた `POSTGRES_DB`, `POSTGRES_USER`, `POSTGRES_PASSWORD`
```yaml
db:
host: localhost # ここを firefish_db に変える
port: 5432 # 変えない
db: firefish_db # ここをデータベース名に変える
user: firefish # ここをデータベースのユーザー名に変える
pass: very_strong_password # ここをデータベースのパスワードに変える
```
Redis の設定を変える
```yaml
redis:
host: localhost # ここを firefish_redis に変える
```
5. コンテナイメージをダウンロードまたはビルドする
```bash
./update.sh --install --podman
```
6. データベースのコンテナのみを起動する
```bash
podman-compose up db --detach
```
7. PGroonga を有効にする(`firefish` と `firefish_db` はそれぞれ自分が決めたユーザー名とデータベース名に変えて実行する)
```bash
podman-compose exec db psql --user=firefish --dbname=firefish_db --command='CREATE EXTENSION pgroonga;'
```
8. Firefish を起動する
```bash
podman-compose up --detach
```
これで `http://localhost:3000` で Web クライアントが動くので、リバースプロキシやファイヤーウォールを設定して外部からアクセス可能にすればよいです。その設定には[私のブログ](https://blog.naskya.net/post/6kic0tebueju/)や[本家 Firefish の README](https://git.joinfirefish.org/firefish/firefish#-getting-started)が参考になるかもしれません。
正常にインストールできなかった場合には自力で解決しようとせず、[私にコマンドの実行ログを送ってください](https://code.naskya.net/naskya/firefish/source-by/main/docs/trouble_shooting.md#私にコマンドの実行ログを送る)。

175
docs/migrate.md Normal file
View file

@ -0,0 +1,175 @@
# このフォークから本家版に移行する
## サーバーに Firefish を直接インストールしている場合
1. サーバーを停止する
```bash
sudo systemctl stop firefish
```
2. サーバーのバックアップを取る
3. Firefish がインストールされているディレクトリ (e.g., `/home/firefish/firefish`) の親ディレクトリ (e.g., `/home/firefish`) に移動する
```bash
cd /home/firefish
```
4. Firefish がインストールされているディレクトリ (e.g., `./firefish`) の名前を変える
```bash
mv firefish firefish.old
```
5. 元々 Firefish がインストールされていたディレクトリ (e.g., `./firefish`) と同じ名前でこのリポジトリをクローンする
```bash
git clone --branch=main https://code.naskya.net/naskya/firefish firefish
```
6. 必要なファイルを元のディレクトリからコピーする
```bash
rm -rf firefish/files firefish/custom firefish/.config
cp -r firefish.old/files firefish
cp -r firefish.old/custom firefish
cp -r firefish.old/.config firefish
```
7. 全文検索エンジンMeilisearch, Sonic, Elasticsearch のいずれか)を使用している場合には、`.config/default.yml` からその設定を削除またはコメントアウトする
先頭に `#` をつけると設定をコメントアウトできます。
```yaml
#sonic:
# host: localhost
# port: 1491
# auth: SecretPassword
# collection: notes
# bucket: default
```
全文検索エンジンは停止またはアンインストールしてしまってよいです。本家の Firefish に戻るつもりがあるなら停止を、そうでなければアンインストールをおすすめします。
停止コマンドの例
```bash
sudo systemctl disable --now sonic
```
8. PostgreSQL のバージョンを確認する
```bash
psql --version
```
9. PGroonga をインストールする
コマンドの例(詳しくは[この投稿](https://post.naskya.net/notes/9ldi29amfanomef5)を参考にしてください)
```bash
sudo apt install -y software-properties-common
sudo add-apt-repository -y universe
sudo add-apt-repository -y ppa:groonga/ppa
sudo apt install -y wget lsb-release
wget https://packages.groonga.org/ubuntu/groonga-apt-source-latest-$(lsb_release --codename --short).deb
sudo apt install -y -V ./groonga-apt-source-latest-$(lsb_release --codename --short).deb
echo "deb http://apt.postgresql.org/pub/repos/apt/ $(lsb_release --codename --short)-pgdg main" | sudo tee /etc/apt/sources.list.d/pgdg.list
wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add -
sudo apt update
sudo apt install -y -V postgresql-16-pgdg-pgroonga
```
10. `.config/default.yml` に書かれているデータベースの名前を確認する(以下の例では `firefish_db`
```yaml
db:
host: localhost
port: 5432
db: firefish_db # これ
```
11. 以下のコマンドを実行して PGroonga の拡張機能を有効にする(`firefish_db` の部分は自分のデータベース名に変えて実行)
```bash
sudo -u postgres psql --command="CREATE EXTENSION pgroonga;" --dbname=firefish_db
```
12. 新しい Firefish のディレクトリに入ってビルドする
```bash
cd firefish
./update.sh --install --native
```
13. サーバーを起動して動作を確認する
```bash
sudo systemctl start firefish
```
14. 元々 Firefish がインストールされていたディレクトリを削除する
```bash
cd ..
rm -rf firefish.old
```
## コンテナで Firefish を動かしている場合
Docker を使う場合には以下の `podman`, `podman-compose`, `--podman` をそれぞれ `docker`, `docker-compose`, `--docker` に読み替えてください。
1. サーバーを停止し、バックアップを取る
2. Firefish がインストールされているディレクトリ (e.g., `/home/firefish/firefish`) の親ディレクトリ (e.g., `/home/firefish`) に移動する
```bash
cd /home/firefish
```
3. Firefish がインストールされているディレクトリ (e.g., `./firefish`) の名前を変える
```bash
mv firefish firefish.old
```
4. 元々 Firefish がインストールされていたディレクトリ (e.g., `./firefish`) と同じ名前でこのリポジトリをクローンする
```bash
git clone --branch=main https://code.naskya.net/naskya/firefish firefish
```
5. 必要なファイルを元のディレクトリからコピーする
```bash
rm -rf firefish/files firefish/custom firefish/.config
cp -r firefish.old/files firefish
cp -r firefish.old/custom firefish
cp -r firefish.old/.config firefish
```
6. 以下のコマンドを実行して PGroonga を有効にする(`firefish` と `firefish_db` はそれぞれ `.config/docker.env``.config/default.yml` に書いた PostgreSQL のユーザー名とデータベース名に置き換える)
```bash
podman-compose up db --detach
podman-compose exec db psql --command='CREATE EXTENSION pgroonga;' --user=firefish --dbname=firefish_db
```
7. コンテナイメージをダウンロードまたはビルドする
```bash
./update.sh --install --podman
```
8. サーバーを起動して動作を確認する
```bash
podman-compose up --detach
```
9. 元々 Firefish がインストールされていたディレクトリを削除する
```bash
cd ..
rm -rf firefish.old
```

208
docs/migrate_back.md Normal file
View file

@ -0,0 +1,208 @@
# このフォークから本家版に移行する
## サーバーに Firefish を直接インストールしている場合
1. サーバーのバックアップを取る
2. サーバーを停止する
```bash
sudo systemctl stop firefish
```
3. Firefish がインストールされているディレクトリ (e.g., `/home/firefish/firefish`) へ移動する
```bash
cd /home/firefish/firefish
```
4. 最新版にアップデートする
```bash
./update.sh
```
5. `.config/default.yml` に書かれているデータベースの名前を確認する(以下の例では `firefish_db`
```yaml
db:
host: localhost
port: 5432
db: firefish_db # これ
```
6. 次のコマンドでデータベースをいじる前に、そのコマンドが正常に動作するか確認する(`firefish_db` の部分は自分のデータベース名に変更する)
```bash
printf 'BEGIN;\n%s\nROLLBACK;' "$(cat neko/revert.sql)" | sudo -u postgres psql --echo-all --set='ON_ERROR_STOP=1' --dbname=firefish_db
```
最後の行が `ROLLBACK` で終わっていれば問題ありません。そうでない場合には[私にコマンドの実行ログを送ってください](https://code.naskya.net/naskya/firefish/source-by/main/docs/trouble_shooting.md#私にコマンドの実行ログを送る)。
7. このフォークで加えられたデータベースへの変更を実際に取り消す(`firefish_db` の部分は自分のデータベース名に変更する)
```bash
sudo -u postgres psql --file=neko/revert.sql --dbname=firefish_db
```
8. PGroonga をアンインストールする
コマンドの例
```bash
sudo apt purge --remove postgresql-16-pgdg-pgroonga
sudo add-apt-repository --remove ppa:groonga/ppa
sudo apt-key del ACCC4CF8
sudo apt update
```
9. Firefish がインストールされているディレクトリの親ディレクトリ (e.g., `/home/firefish`) に行く
```bash
cd ..
```
10. Firefish がインストールされているディレクトリ (e.g., `./firefish`) の名前を変える
```bash
mv firefish firefish.old
```
11. Firefish がインストールされているディレクトリと同じ名前で本家版の Firefish を clone する
```bash
git clone https://git.joinfirefish.org/firefish/firefish.git firefish
```
12. 必要なファイルをコピーする
```bash
rm -rf firefish/files firefish/custom firefish/.config
cp -r firefish.old/files firefish
cp -r firefish.old/custom firefish
cp -r firefish.old/.config firefish
```
13. 新しい Firefish のディレクトリ (e.g., `./firefish`) に入り、`develop` ブランチに行く(実際には既に `develop` にいるはず)
```bash
cd firefish
git checkout develop
```
14. Firefish をビルドする
```bash
corepack prepare pnpm@latest --activate
pnpm install
NODE_ENV=production pnpm run build
pnpm run migrate
```
15. サーバーを起動して動作を確認する
```bash
sudo systemctl start firefish
```
16. 元々 Firefish がインストールされていたディレクトリを削除する
```bash
cd ..
rm -rf firefish.old
```
## コンテナで Firefish を動かしている場合
Docker を使う場合には以下の `podman`, `podman-compose`, `--podman` をそれぞれ `docker`, `docker-compose`, `--docker` に読み替えてください。
1. サーバーのバックアップを取る
2. Firefish がインストールされているディレクトリ (e.g., `/home/firefish/firefish`) へ移動する
```bash
cd /home/firefish/firefish
```
3. 最新版にアップデートする
```bash
./update.sh --podman
```
4. 一度サーバーを再起動し、自分のサーバーが起動したことを Web から確認したら再度停止してデータベースのコンテナのみを起動する
```bash
podman-compose down
podman-compose up --detach
# 少し待って、サーバーが起動したことを確認する
podman-compose down
podman-compose up db --detach
```
5. `.config/docker.env` に書かれているユーザー名とデータベース名を確認する(以下の例では `firefish_db`
```env
# db settings
POSTGRES_PASSWORD=very_strong_password
POSTGRES_USER=firefish # これがユーザー名
POSTGRES_DB=firefish_db # これがデータベース名
```
6. 次のコマンドでデータベースをいじる前に、そのコマンドが正常に動作するか確認する(`firefish` と `firefish_db` の部分は自分のユーザー名とデータベース名に変更する)
```bash
podman-compose exec db psql --user=firefish --dbname=firefish_db --echo-all --set='ON_ERROR_STOP=1' --command="$(printf 'BEGIN;\n%s\nROLLBACK;' "$(cat neko/revert.sql)")"
```
最後の行が `ROLLBACK` で終わっていれば問題ありません。そうでない場合には[私にコマンドの実行ログを送ってください](https://code.naskya.net/naskya/firefish/source-by/main/docs/trouble_shooting.md#私にコマンドの実行ログを送る)。
7. このフォークで加えられたデータベースへの変更を実際に取り消す(`mk1` の部分は自分のデータベース名に変更する)
```bash
podman-compose exec db psql --user=firefish --dbname=firefish_db --command="$(cat neko/revert.sql)"
```
8. Firefish がインストールされているディレクトリの親ディレクトリ (e.g., `/home/firefish`) に行く
```bash
cd ..
```
9. Firefish がインストールされているディレクトリ (e.g., `./firefish`) の名前を変える
```bash
mv firefish firefish.old
```
10. Firefish がインストールされているディレクトリと同じ名前で本家版の Firefish を clone する
```bash
git clone https://git.joinfirefish.org/firefish/firefish.git firefish
```
11. 必要なファイルをコピーする
```bash
rm -rf firefish/files firefish/custom firefish/.config
cp -r firefish.old/files firefish
cp -r firefish.old/custom firefish
cp -r firefish.old/.config firefish
```
12. 新しい Firefish のディレクトリ (e.g., `./firefish`) に入る
```bash
cd firefish
```
13. サーバーを起動して動作を確認する
```bash
podman-compose up --detach
```
14. 元々 Firefish がインストールされていたディレクトリを削除する
```bash
cd ..
rm -rf firefish.old
```

35
docs/trouble_shooting.md Normal file
View file

@ -0,0 +1,35 @@
# トラブルシュート
## 私にコマンドの実行ログを送る
実行したいコマンドの後ろに `|& tee /tmp/fflog` をつけてコマンドを実行します。例えば実行したいコマンドが `./update.sh` ならば
```bash
./update.sh |& tee /tmp/fflog
```
とし、実行したいコマンドが
```bash
printf 'BEGIN;\n%s\nROLLBACK;' "$(cat neko/revert.sql)" | sudo -u postgres psql --echo-all --set='ON_ERROR_STOP=1' --dbname=firefish_db
```
ならば
```bash
printf 'BEGIN;\n%s\nROLLBACK;' "$(cat neko/revert.sql)" | sudo -u postgres psql --echo-all --set='ON_ERROR_STOP=1' --dbname=firefish_db |& tee /tmp/fflog
```
とします。
するとコマンドの実行ログが `/tmp/fflog` に保存されるので、保存されたテキスト全体をサーバーの OS などの環境の情報とともに[私](https://post.naskya.net/@dev)に送ってください。
ログのうちの参考になりそうだと思った一部だけを抜粋して送ることは絶対にしないでください。必要な情報が欠落していてもう一度全体を送り直してもらうことになったり、私が対処方法の判断を誤ったりする可能性が高まったりします。
テキストが一つの投稿に収まる長さの場合はコードブロックの記法(``` で囲む)を用いて投稿してもらってよいです。ログが一投稿に収まらない長さの場合は [Pastebin](https://pastebin.com/) などのサービスを使って共有してください。投稿にログファイルを直接添付して送ることはしないでください。
ログを私に送ったら `/tmp/fflog` は削除してよいです。
```bash
rm /tmp/fflog
```

13
docs/update.md Normal file
View file

@ -0,0 +1,13 @@
# アップデート方法
必ず添付されているアップデートスクリプト (`update.sh`) を用いてアップデートしてください。それ以外のアップデート方法はサポートされていません。
1. サーバーを停止し、バックアップを取る
2. Firefish のリポジトリのディレクトリに移動する
3. アップデートスクリプトを実行し、表示される指示に従う
```bash
./update.sh
```
正常にアップデートできなかった場合には自力で解決しようとせず、[私にコマンドの実行ログを送ってください](https://code.naskya.net/naskya/firefish/source-by/main/docs/trouble_shooting.md#私にコマンドの実行ログを送る)。

View file

@ -266,9 +266,21 @@ if (!nativeBinding) {
}
const {
EnvConfig,
readEnvironmentConfig,
readServerConfig,
stringToAcct,
acctToString,
nativeRandomStr,
getFullApAccount,
isSelfHost,
extractHost,
toPuny,
toPunyOptional,
convertToHiddenPost,
sqlLikeEscape,
safeForSql,
formatMilliseconds,
genString,
IdConvertType,
convertId,
nativeGetTimestamp,
@ -276,9 +288,21 @@ const {
nativeInitIdGenerator,
} = nativeBinding;
module.exports.EnvConfig = EnvConfig;
module.exports.readEnvironmentConfig = readEnvironmentConfig;
module.exports.readServerConfig = readServerConfig;
module.exports.stringToAcct = stringToAcct;
module.exports.acctToString = acctToString;
module.exports.nativeRandomStr = nativeRandomStr;
module.exports.getFullApAccount = getFullApAccount;
module.exports.isSelfHost = isSelfHost;
module.exports.extractHost = extractHost;
module.exports.toPuny = toPuny;
module.exports.toPunyOptional = toPunyOptional;
module.exports.convertToHiddenPost = convertToHiddenPost;
module.exports.sqlLikeEscape = sqlLikeEscape;
module.exports.safeForSql = safeForSql;
module.exports.formatMilliseconds = formatMilliseconds;
module.exports.genString = genString;
module.exports.IdConvertType = IdConvertType;
module.exports.convertId = convertId;
module.exports.nativeGetTimestamp = nativeGetTimestamp;

View file

@ -1,3 +1,5 @@
# shellcheck disable=SC2148
color() {
if [ -t 1 ]; then
tput setaf "${1:-7}"

View file

@ -27,7 +27,7 @@
"clean": "pnpm node ./scripts/clean-built.mjs",
"clean-cargo": "pnpm node ./scripts/clean-cargo.mjs",
"clean-npm": "pnpm node ./scripts/clean-npm.mjs",
"clean-all": "pnpm run clean && pnpm run claen-cargo && pnpm run clean-npm",
"clean-all": "pnpm run clean && pnpm run clean-cargo && pnpm run clean-npm",
"cleanall": "pnpm run clean-all"
},
"resolutions": {

View file

@ -1436,6 +1436,7 @@ dependencies = [
"chrono",
"cuid2",
"derive_more",
"idna",
"jsonschema",
"napi",
"napi-build",
@ -1448,8 +1449,10 @@ dependencies = [
"sea-orm",
"serde",
"serde_json",
"serde_yaml",
"thiserror",
"tokio",
"url",
"utoipa",
]

View file

@ -8,7 +8,7 @@ members = ["migration"]
[features]
default = []
napi = ["dep:napi", "dep:napi-derive"]
napi = ["dep:napi-derive"]
[lib]
crate-type = ["cdylib", "lib"]
@ -19,6 +19,7 @@ cfg-if = "1.0.0"
chrono = "0.4.31"
cuid2 = "0.1.2"
derive_more = "0.99.17"
idna = "0.5.0"
jsonschema = "0.17.1"
once_cell = "1.19.0"
parse-display = "0.8.2"
@ -27,12 +28,14 @@ schemars = { version = "0.8.16", features = ["chrono"] }
sea-orm = { version = "0.12.10", features = ["sqlx-postgres", "runtime-tokio-rustls"] }
serde = { version = "1.0.193", features = ["derive"] }
serde_json = "1.0.108"
serde_yaml = "0.9.29"
thiserror = "1.0.52"
tokio = { version = "1.35.1", features = ["full"] }
url = "2.5.0"
utoipa = "4.1.0"
# Default enable napi4 feature, see https://nodejs.org/api/n-api.html#node-api-version-matrix
napi = { version = "2.14.1", default-features = false, features = ["napi9", "tokio_rt"], optional = true }
napi = { version = "2.14.1", default-features = false, features = ["napi9", "tokio_rt"] }
napi-derive = { version = "2.14.5", optional = true }
basen = "0.1.0"

View file

@ -0,0 +1,28 @@
#[cfg_attr(feature = "napi", napi_derive::napi)]
pub struct EnvConfig {
pub only_queue: bool,
pub only_server: bool,
pub no_daemons: bool,
pub disable_clustering: bool,
pub verbose: bool,
pub with_log_time: bool,
pub quiet: bool,
pub slow: bool,
}
#[cfg_attr(feature = "napi", napi_derive::napi)]
pub fn read_environment_config() -> EnvConfig {
let node_env = std::env::var("NODE_ENV").unwrap_or_default().to_lowercase();
let is_testing = node_env == "test" || node_env == "testing";
EnvConfig {
only_queue: std::env::var("MK_ONLY_QUEUE").is_ok(),
only_server: std::env::var("MK_ONLY_SERVER").is_ok(),
no_daemons: is_testing || std::env::var("MK_NO_DAEMONS").is_ok(),
disable_clustering: is_testing || std::env::var("MK_DISABLE_CLUSTERING").is_ok(),
verbose: std::env::var("MK_VERBOSE").is_ok(),
with_log_time: std::env::var("MK_WITH_LOG_TIME").is_ok(),
quiet: is_testing || std::env::var("MK_QUIET").is_ok(),
slow: std::env::var("MK_SLOW").is_ok(),
}
}

View file

@ -0,0 +1,2 @@
pub mod environment;
pub mod server;

View file

@ -0,0 +1,54 @@
use serde::Deserialize;
use serde_yaml;
use std::env;
use std::fs;
#[derive(Debug, PartialEq, Deserialize)]
#[serde(rename_all = "camelCase")]
#[cfg_attr(feature = "napi", napi_derive::napi(object))]
pub struct ServerConfig {
pub url: String,
pub db: DbConfig,
pub redis: RedisConfig,
pub cache_server: Option<RedisConfig>,
}
#[derive(Debug, PartialEq, Deserialize)]
#[cfg_attr(feature = "napi", napi_derive::napi(object))]
pub struct DbConfig {
pub host: String,
pub port: u32,
pub db: String,
pub user: String,
pub pass: String,
}
#[derive(Debug, PartialEq, Deserialize)]
#[cfg_attr(feature = "napi", napi_derive::napi(object))]
pub struct RedisConfig {
pub host: String,
pub port: u32,
pub user: Option<String>,
pub pass: Option<String>,
pub tls: Option<TlsConfig>,
#[serde(default)]
pub db: u32,
#[serde(default)]
pub prefix: String,
}
#[derive(Debug, PartialEq, Deserialize)]
#[serde(rename_all = "camelCase")]
#[cfg_attr(feature = "napi", napi_derive::napi(object))]
pub struct TlsConfig {
pub host: String,
pub reject_unauthorized: bool,
}
#[cfg_attr(feature = "napi", napi_derive::napi)]
pub fn read_server_config() -> ServerConfig {
let cwd = env::current_dir().unwrap();
let yml = fs::File::open(cwd.join("../../.config/default.yml"))
.expect("Failed to open '.config/default.yml'");
serde_yaml::from_reader(yml).expect("Failed to parse yaml")
}

View file

@ -1,3 +1,4 @@
pub mod config;
pub mod database;
pub mod macros;
pub mod model;

View file

@ -1,49 +0,0 @@
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Default)]
#[sea_orm(table_name = "antenna_note")]
pub struct Model {
#[sea_orm(primary_key, auto_increment = false)]
pub id: String,
#[sea_orm(column_name = "noteId")]
pub note_id: String,
#[sea_orm(column_name = "antennaId")]
pub antenna_id: String,
pub read: bool,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {
#[sea_orm(
belongs_to = "super::antenna::Entity",
from = "Column::AntennaId",
to = "super::antenna::Column::Id",
on_update = "NoAction",
on_delete = "Cascade"
)]
Antenna,
#[sea_orm(
belongs_to = "super::note::Entity",
from = "Column::NoteId",
to = "super::note::Column::Id",
on_update = "NoAction",
on_delete = "Cascade"
)]
Note,
}
impl Related<super::antenna::Entity> for Entity {
fn to() -> RelationDef {
Relation::Antenna.def()
}
}
impl Related<super::note::Entity> for Entity {
fn to() -> RelationDef {
Relation::Note.def()
}
}
impl ActiveModelBehavior for ActiveModel {}

View file

@ -51,6 +51,7 @@ pub mod registration_ticket;
pub mod registry_item;
pub mod relay;
pub mod renote_muting;
pub mod reply_muting;
pub mod sea_orm_active_enums;
pub mod signin;
pub mod sw_subscription;

View file

@ -61,7 +61,6 @@ pub struct Model {
pub thread_id: Option<String>,
#[sea_orm(column_name = "updatedAt")]
pub updated_at: Option<DateTimeWithTimeZone>,
#[sea_orm(column_name = "lang")]
pub lang: Option<String>,
}

View file

@ -49,6 +49,7 @@ pub use super::registration_ticket::Entity as RegistrationTicket;
pub use super::registry_item::Entity as RegistryItem;
pub use super::relay::Entity as Relay;
pub use super::renote_muting::Entity as RenoteMuting;
pub use super::reply_muting::Entity as ReplyMuting;
pub use super::signin::Entity as Signin;
pub use super::sw_subscription::Entity as SwSubscription;
pub use super::used_username::Entity as UsedUsername;

View file

@ -0,0 +1,21 @@
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.10
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[sea_orm(table_name = "reply_muting")]
pub struct Model {
#[sea_orm(primary_key, auto_increment = false)]
pub id: String,
#[sea_orm(column_name = "createdAt")]
pub created_at: DateTimeWithTimeZone,
#[sea_orm(column_name = "muteeId")]
pub mutee_id: String,
#[sea_orm(column_name = "muterId")]
pub muter_id: String,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {}
impl ActiveModelBehavior for ActiveModel {}

View file

@ -1,13 +1,10 @@
use napi_derive::napi;
#[derive(Clone, Eq, PartialEq, Debug)]
#[napi(object)]
#[cfg_attr(feature = "napi", napi_derive::napi(object))]
pub struct Acct {
pub username: String,
pub host: Option<String>,
}
#[napi]
#[cfg_attr(feature = "napi", napi_derive::napi)]
pub fn string_to_acct(acct: String) -> Acct {
let split: Vec<&str> = if let Some(stripped) = acct.strip_prefix('@') {
stripped
@ -27,7 +24,7 @@ pub fn string_to_acct(acct: String) -> Acct {
}
}
#[napi]
#[cfg_attr(feature = "napi", napi_derive::napi)]
pub fn acct_to_string(acct: Acct) -> String {
match acct.host {
Some(host) => format!("{}@{}", acct.username, host),

View file

@ -0,0 +1,64 @@
use crate::config::server::read_server_config;
use idna;
use url::Url;
#[cfg_attr(feature = "napi", napi_derive::napi)]
pub fn get_full_ap_account(username: String, host: Option<String>) -> String {
if host.is_none() {
format!("{}@{}", username, extract_host(read_server_config().url))
} else {
format!("{}@{}", username, to_puny(host.unwrap()))
}
}
#[cfg_attr(feature = "napi", napi_derive::napi)]
pub fn is_self_host(host: Option<String>) -> bool {
if let Some(x) = host {
extract_host(read_server_config().url) == to_puny(x)
} else {
true
}
}
#[cfg_attr(feature = "napi", napi_derive::napi)]
pub fn extract_host(uri: String) -> String {
to_puny(
Url::parse(uri.as_str())
.expect("Invalid uri")
.host_str()
.unwrap()
.to_string(),
)
}
#[cfg_attr(feature = "napi", napi_derive::napi)]
pub fn to_puny(host: String) -> String {
idna::domain_to_ascii(&host).expect("Failed to encode the host to punycode")
}
#[cfg_attr(feature = "napi", napi_derive::napi)]
pub fn to_puny_optional(host: Option<String>) -> Option<String> {
host.map(to_puny)
}
#[cfg(test)]
mod unit_test {
use super::{extract_host, to_puny};
use pretty_assertions::assert_eq;
#[test]
fn extract_host_test() {
assert_eq!(
extract_host("https://git.joinfirefish.org/firefish/firefish.git".to_string()),
"git.joinfirefish.org".to_string()
);
}
#[test]
fn to_puny_test() {
assert_eq!(
to_puny("何もかも.owari.shop".to_string()),
"xn--u8jyfb5762a.owari.shop".to_string()
);
}
}

View file

@ -0,0 +1,25 @@
use napi;
#[cfg_attr(feature = "napi", napi_derive::napi(object))]
pub struct Post {
pub text: Option<String>,
pub cw: Option<String>,
pub local_only: bool,
pub created_at: napi::JsDate,
/// FIXME: introduce enum type (and remove "hiddensomething" visibility if possible)
pub visibility: String,
}
#[cfg_attr(feature = "napi", napi_derive::napi)]
pub fn convert_to_hidden_post(original_post: Post) -> Post {
Post {
text: match original_post.text {
Some(s) if !s.is_empty() => Some(s),
_ => None,
},
cw: original_post.cw,
local_only: original_post.local_only,
created_at: original_post.created_at,
visibility: format!("hidden{}", original_post.visibility),
}
}

View file

@ -0,0 +1,41 @@
#[cfg_attr(feature = "napi", napi_derive::napi)]
pub fn sql_like_escape(src: String) -> String {
src.replace('%', r"\%").replace('_', r"\_")
}
#[cfg_attr(feature = "napi", napi_derive::napi)]
pub fn safe_for_sql(src: String) -> bool {
!src.contains([
'\0', '\x08', '\x09', '\x1a', '\n', '\r', '"', '\'', '\\', '%',
])
}
#[cfg(test)]
mod unit_test {
use super::{safe_for_sql, sql_like_escape};
use pretty_assertions::assert_eq;
#[test]
fn sql_like_escape_test() {
assert_eq!(sql_like_escape("".to_string()), "".to_string());
assert_eq!(sql_like_escape("abc".to_string()), "abc".to_string());
assert_eq!(sql_like_escape("a%bc".to_string()), r"a\%bc".to_string());
assert_eq!(
sql_like_escape("a呼%吸bc".to_string()),
r"a呼\%吸bc".to_string()
);
assert_eq!(
sql_like_escape("_اللغة العربية".to_string()),
r"\_اللغة العربية".to_string()
);
}
#[test]
fn safe_for_sql_test() {
assert!(safe_for_sql("123".to_string()));
assert!(safe_for_sql("人間".to_string()));
assert!(!safe_for_sql("人間\x09".to_string()));
assert!(!safe_for_sql("abc\ndef".to_string()));
assert!(!safe_for_sql("%something%".to_string()));
}
}

View file

@ -0,0 +1,52 @@
/// Convert milliseconds to human readable string
#[cfg_attr(feature = "napi", napi_derive::napi)]
pub fn format_milliseconds(milliseconds: i32) -> String {
let mut seconds = milliseconds / 1000;
let mut minutes = seconds / 60;
let mut hours = minutes / 60;
let days = hours / 24;
seconds %= 60;
minutes %= 60;
hours %= 24;
let mut buf = Vec::<String>::new();
if days > 0 {
buf.push(format!("{} day(s)", days));
}
if hours > 0 {
buf.push(format!("{} hour(s)", hours));
}
if minutes > 0 {
buf.push(format!("{} minute(s)", minutes));
}
if seconds > 0 {
buf.push(format!("{} second(s)", seconds));
}
buf.join(", ")
}
#[cfg(test)]
mod unit_test {
use super::format_milliseconds;
use pretty_assertions::assert_eq;
#[test]
fn format_milliseconds_test() {
assert_eq!(format_milliseconds(1000), "1 second(s)".to_string());
assert_eq!(
format_milliseconds(1387938),
"23 minute(s), 7 second(s)".to_string()
);
assert_eq!(
format_milliseconds(34200457),
"9 hour(s), 30 minute(s)".to_string()
);
assert_eq!(
format_milliseconds(998244353),
"11 day(s), 13 hour(s), 17 minute(s), 24 second(s)".to_string()
);
}
}

View file

@ -1,3 +1,7 @@
pub mod acct;
pub mod convert_host;
pub mod convert_to_hidden_post;
pub mod escape_sql;
pub mod format_milliseconds;
pub mod id;
pub mod random;

View file

@ -1,6 +1,7 @@
use rand::{distributions::Alphanumeric, thread_rng, Rng};
/// Generate random string based on [thread_rng] and [Alphanumeric].
#[cfg_attr(feature = "napi", napi_derive::napi)]
pub fn gen_string(length: u16) -> String {
thread_rng()
.sample_iter(Alphanumeric)
@ -9,12 +10,6 @@ pub fn gen_string(length: u16) -> String {
.collect()
}
#[cfg(feature = "napi")]
#[napi_derive::napi]
pub fn native_random_str(length: u16) -> String {
gen_string(length)
}
#[cfg(test)]
mod unit_test {
use pretty_assertions::{assert_eq, assert_ne};

View file

@ -3,7 +3,7 @@ import chalk from "chalk";
import Xev from "xev";
import Logger from "@/services/logger.js";
import { envOption } from "../env.js";
import { envOption } from "@/config/index.js";
// for typeorm
import "reflect-metadata";

View file

@ -10,7 +10,7 @@ import semver from "semver";
import Logger from "@/services/logger.js";
import loadConfig from "@/config/load.js";
import type { Config } from "@/config/types.js";
import { envOption } from "@/env.js";
import { envOption } from "@/config/index.js";
import { showMachineInfo } from "@/misc/show-machine-info.js";
import { db, initDb } from "@/db/postgre.js";

View file

@ -1,3 +1,5 @@
import load from "./load.js";
import { readEnvironmentConfig } from "native-utils/built/index.js";
export default load();
export const envOption = readEnvironmentConfig();

View file

@ -1,25 +0,0 @@
const envOption = {
onlyQueue: false,
onlyServer: false,
noDaemons: false,
disableClustering: false,
verbose: false,
withLogTime: false,
quiet: false,
slow: false,
};
for (const key of Object.keys(envOption) as (keyof typeof envOption)[]) {
if (
process.env[
`MK_${key.replace(/[A-Z]/g, (letter) => `_${letter}`).toUpperCase()}`
]
)
envOption[key] = true;
}
if (process.env.NODE_ENV === "test") envOption.disableClustering = true;
if (process.env.NODE_ENV === "test") envOption.quiet = true;
if (process.env.NODE_ENV === "test") envOption.noDaemons = true;
export { envOption };

View file

@ -2,8 +2,7 @@ import type { Antenna } from "@/models/entities/antenna.js";
import type { Note } from "@/models/entities/note.js";
import type { User } from "@/models/entities/user.js";
import { Blockings, Followings, UserProfiles } from "@/models/index.js";
import { getFullApAccount } from "@/misc/convert-host.js";
import { stringToAcct } from "native-utils/built/index.js";
import { getFullApAccount, stringToAcct } from "native-utils/built/index.js";
import type { Packed } from "@/misc/schema.js";
import { Cache } from "@/misc/cache.js";
import { getWordHardMute } from "@/misc/check-word-mute.js";

View file

@ -1,28 +0,0 @@
import { URL } from "node:url";
import config from "@/config/index.js";
import { toASCII } from "punycode";
export function getFullApAccount(username: string, host: string | null) {
return host
? `${username}@${toPuny(host)}`
: `${username}@${toPuny(config.host)}`;
}
export function isSelfHost(host: string) {
if (host == null) return true;
return toPuny(config.host) === toPuny(host);
}
export function extractDbHost(uri: string) {
const url = new URL(uri);
return toPuny(url.hostname);
}
export function toPuny(host: string) {
return toASCII(host.toLowerCase());
}
export function toPunyNullable(host: string | null | undefined): string | null {
if (host == null) return null;
return toASCII(host.toLowerCase());
}

View file

@ -1,17 +0,0 @@
export function convertMilliseconds(ms: number) {
let seconds = Math.round(ms / 1000);
let minutes = Math.round(seconds / 60);
let hours = Math.round(minutes / 60);
const days = Math.round(hours / 24);
seconds %= 60;
minutes %= 60;
hours %= 24;
const result = [];
if (days > 0) result.push(`${days} day(s)`);
if (hours > 0) result.push(`${hours} hour(s)`);
if (minutes > 0) result.push(`${minutes} minute(s)`);
if (seconds > 0) result.push(`${seconds} second(s)`);
return result.join(", ");
}

View file

@ -3,7 +3,7 @@ import { Emojis } from "@/models/index.js";
import type { Emoji } from "@/models/entities/emoji.js";
import type { Note } from "@/models/entities/note.js";
import { Cache } from "./cache.js";
import { isSelfHost, toPunyNullable } from "./convert-host.js";
import { isSelfHost, toPunyOptional } from "native-utils/built/index.js";
import { decodeReaction } from "./reaction-lib.js";
import config from "@/config/index.js";
import { query } from "@/prelude/url.js";
@ -35,7 +35,7 @@ function normalizeHost(
? null // 自ホスト指定
: src || noteUserHost; // 指定されたホスト || ノートなどの所有者のホスト (こっちがリアクションにマッチすることはない)
host = toPunyNullable(host);
host = toPunyOptional(host);
return host;
}
@ -47,7 +47,7 @@ function parseEmojiStr(emojiName: string, noteUserHost: string | null) {
const name = match[1];
// ホスト正規化
const host = toPunyNullable(normalizeHost(match[2], noteUserHost));
const host = toPunyOptional(normalizeHost(match[2], noteUserHost));
return { name, host };
}

View file

@ -1,21 +0,0 @@
export type Post = {
text: string | undefined;
cw: string | null;
localOnly: boolean;
createdAt: Date;
visibility: string;
};
export function parse(acct: any): Post {
return {
text: acct.text || undefined,
cw: acct.cw,
localOnly: acct.localOnly,
createdAt: new Date(acct.createdAt),
visibility: `hidden${acct.visibility || ""}`,
};
}
export function toJson(acct: Post): string {
return { text: acct.text, cw: acct.cw, localOnly: acct.localOnly }.toString();
}

View file

@ -1,7 +1,7 @@
import { emojiRegex } from "./emoji-regex.js";
import { fetchMeta } from "./fetch-meta.js";
import { Emojis } from "@/models/index.js";
import { toPunyNullable } from "./convert-host.js";
import { toPunyOptional } from "native-utils/built/index.js";
import { IsNull } from "typeorm";
export function convertReactions(reactions: Record<string, number>) {
@ -23,7 +23,7 @@ export async function toDbReaction(
): Promise<string> {
if (!reaction) return (await fetchMeta()).defaultReaction;
reacterHost = toPunyNullable(reacterHost);
reacterHost = toPunyOptional(reacterHost);
if (reaction.includes("❤") || reaction.includes("♥️")) return "❤️";

View file

@ -1,3 +0,0 @@
export function safeForSql(text: string): boolean {
return !/[\0\x08\x09\x1a\n\r"'\\\%]/g.test(text);
}

View file

@ -1,5 +0,0 @@
import { nativeRandomStr } from "native-utils/built/index.js";
export function secureRndstr(length = 32, _ = true): string {
return nativeRandomStr(length);
}

View file

@ -1,3 +0,0 @@
export function sqlLikeEscape(s: string) {
return s.replace(/([%_])/g, "\\$1");
}

View file

@ -1,7 +1,7 @@
import { db } from "@/db/postgre.js";
import { DriveFile } from "@/models/entities/drive-file.js";
import type { User } from "@/models/entities/user.js";
import { toPuny } from "@/misc/convert-host.js";
import { toPuny } from "native-utils/built/index.js";
import { awaitAll } from "@/prelude/await-all.js";
import type { Packed } from "@/misc/schema.js";
import config from "@/config/index.js";

View file

@ -5,7 +5,7 @@ import config from "@/config/index.js";
import type { DriveFile } from "@/models/entities/drive-file.js";
import type { IActivity } from "@/remote/activitypub/type.js";
import type { Webhook, webhookEventTypes } from "@/models/entities/webhook.js";
import { envOption } from "../env.js";
import { envOption } from "@/config/index.js";
import processDeliver from "./processors/deliver.js";
import processInbox from "./processors/inbox.js";

View file

@ -4,7 +4,7 @@ import * as fs from "node:fs";
import { queueLogger } from "../../logger.js";
import { addFile } from "@/services/drive/add-file.js";
import { format as dateFormat } from "date-fns";
import { getFullApAccount } from "@/misc/convert-host.js";
import { getFullApAccount } from "native-utils/built/index.js";
import { createTemp } from "@/misc/create-temp.js";
import { Users, Blockings } from "@/models/index.js";
import { MoreThan } from "typeorm";

View file

@ -4,7 +4,7 @@ import * as fs from "node:fs";
import { queueLogger } from "../../logger.js";
import { addFile } from "@/services/drive/add-file.js";
import { format as dateFormat } from "date-fns";
import { getFullApAccount } from "@/misc/convert-host.js";
import { getFullApAccount } from "native-utils/built/index.js";
import { createTemp } from "@/misc/create-temp.js";
import { Users, Followings, Mutings } from "@/models/index.js";
import { In, MoreThan, Not } from "typeorm";

View file

@ -4,7 +4,7 @@ import * as fs from "node:fs";
import { queueLogger } from "../../logger.js";
import { addFile } from "@/services/drive/add-file.js";
import { format as dateFormat } from "date-fns";
import { getFullApAccount } from "@/misc/convert-host.js";
import { getFullApAccount } from "native-utils/built/index.js";
import { createTemp } from "@/misc/create-temp.js";
import { Users, Mutings } from "@/models/index.js";
import { IsNull, MoreThan } from "typeorm";

View file

@ -4,7 +4,7 @@ import * as fs from "node:fs";
import { queueLogger } from "../../logger.js";
import { addFile } from "@/services/drive/add-file.js";
import { format as dateFormat } from "date-fns";
import { getFullApAccount } from "@/misc/convert-host.js";
import { getFullApAccount } from "native-utils/built/index.js";
import { createTemp } from "@/misc/create-temp.js";
import { Users, UserLists, UserListJoinings } from "@/models/index.js";
import { In } from "typeorm";

View file

@ -1,10 +1,9 @@
import type Bull from "bull";
import { queueLogger } from "../../logger.js";
import { stringToAcct } from "native-utils/built/index.js";
import { resolveUser } from "@/remote/resolve-user.js";
import { downloadTextFile } from "@/misc/download-text-file.js";
import { isSelfHost, toPuny } from "@/misc/convert-host.js";
import { isSelfHost, stringToAcct, toPuny } from "native-utils/built/index.js";
import { Users, DriveFiles } from "@/models/index.js";
import type { DbUserImportJobData } from "@/queue/types.js";
import block from "@/services/blocking/create.js";

View file

@ -1,4 +1,4 @@
import * as Post from "@/misc/post.js";
import { convertToHiddenPost } from "native-utils/built/index.js";
import create from "@/services/note/create.js";
import { Users } from "@/models/index.js";
import type { DbUserImportMastoPostJobData } from "@/queue/types.js";
@ -52,7 +52,8 @@ export async function importCkPost(
logger.error(`Skipped adding file to drive: ${url}`);
}
}
const { text, cw, localOnly, createdAt, visibility } = Post.parse(post);
const { text, cw, localOnly, createdAt, visibility } =
convertToHiddenPost(post);
let note = await Notes.findOneBy({
createdAt: createdAt,
text: text,

View file

@ -1,10 +1,9 @@
import { IsNull } from "typeorm";
import follow from "@/services/following/create.js";
import { stringToAcct } from "native-utils/built/index.js";
import { resolveUser } from "@/remote/resolve-user.js";
import { downloadTextFile } from "@/misc/download-text-file.js";
import { isSelfHost, toPuny } from "@/misc/convert-host.js";
import { isSelfHost, stringToAcct, toPuny } from "native-utils/built/index.js";
import { Users, DriveFiles } from "@/models/index.js";
import type { DbUserImportJobData } from "@/queue/types.js";
import { queueLogger } from "../../logger.js";

View file

@ -1,10 +1,9 @@
import type Bull from "bull";
import { queueLogger } from "../../logger.js";
import { stringToAcct } from "native-utils/built/index.js";
import { resolveUser } from "@/remote/resolve-user.js";
import { downloadTextFile } from "@/misc/download-text-file.js";
import { isSelfHost, toPuny } from "@/misc/convert-host.js";
import { isSelfHost, stringToAcct, toPuny } from "native-utils/built/index.js";
import { Users, DriveFiles, Mutings } from "@/models/index.js";
import type { DbUserImportJobData } from "@/queue/types.js";
import type { User } from "@/models/entities/user.js";

View file

@ -1,11 +1,10 @@
import type Bull from "bull";
import { queueLogger } from "../../logger.js";
import { stringToAcct } from "native-utils/built/index.js";
import { resolveUser } from "@/remote/resolve-user.js";
import { pushUserToUserList } from "@/services/user-list/push.js";
import { downloadTextFile } from "@/misc/download-text-file.js";
import { isSelfHost, toPuny } from "@/misc/convert-host.js";
import { isSelfHost, stringToAcct, toPuny } from "native-utils/built/index.js";
import {
DriveFiles,
Users,

View file

@ -4,7 +4,7 @@ import { registerOrFetchInstanceDoc } from "@/services/register-or-fetch-instanc
import Logger from "@/services/logger.js";
import { Instances } from "@/models/index.js";
import { fetchInstanceMetadata } from "@/services/fetch-instance-metadata.js";
import { toPuny } from "@/misc/convert-host.js";
import { toPuny } from "native-utils/built/index.js";
import { StatusError } from "@/misc/fetch.js";
import { shouldSkipInstance } from "@/misc/skipped-instances.js";
import type { DeliverJobData } from "@/queue/types.js";

View file

@ -6,7 +6,7 @@ import Logger from "@/services/logger.js";
import { registerOrFetchInstanceDoc } from "@/services/register-or-fetch-instance-doc.js";
import { Instances } from "@/models/index.js";
import { fetchMeta } from "@/misc/fetch-meta.js";
import { toPuny, extractDbHost } from "@/misc/convert-host.js";
import { toPuny, extractHost } from "native-utils/built/index.js";
import { getApId } from "@/remote/activitypub/type.js";
import { fetchInstanceMetadata } from "@/services/fetch-instance-metadata.js";
import type { InboxJobData } from "../types.js";
@ -158,7 +158,7 @@ export default async (job: Bull.Job<InboxJobData>): Promise<string> => {
}
// ブロックしてたら中断
const ldHost = extractDbHost(authUser.user.uri);
const ldHost = extractHost(authUser.user.uri);
if (await shouldBlockInstance(ldHost, meta)) {
return `Blocked request: ${ldHost}`;
}
@ -169,8 +169,8 @@ export default async (job: Bull.Job<InboxJobData>): Promise<string> => {
// activity.idがあればホストが署名者のホストであることを確認する
if (typeof activity.id === "string") {
const signerHost = extractDbHost(authUser.user.uri!);
const activityIdHost = extractDbHost(activity.id);
const signerHost = extractHost(authUser.user.uri!);
const activityIdHost = extractHost(activity.id);
if (signerHost !== activityIdHost) {
return `skip: signerHost(${signerHost}) !== activity.id host(${activityIdHost}`;
}

View file

@ -2,7 +2,7 @@ import { URL } from "url";
import httpSignature, { IParsedSignature } from "@peertube/http-signature";
import config from "@/config/index.js";
import { fetchMeta } from "@/misc/fetch-meta.js";
import { toPuny } from "@/misc/convert-host.js";
import { toPuny } from "native-utils/built/index.js";
import DbResolver from "@/remote/activitypub/db-resolver.js";
import { getApId } from "@/remote/activitypub/type.js";
import { shouldBlockInstance } from "@/misc/should-block-instance.js";

View file

@ -5,7 +5,7 @@ import type { IAnnounce } from "../../type.js";
import { getApId } from "../../type.js";
import { fetchNote, resolveNote } from "../../models/note.js";
import { apLogger } from "../../logger.js";
import { extractDbHost } from "@/misc/convert-host.js";
import { extractHost } from "native-utils/built/index.js";
import { getApLock } from "@/misc/app-lock.js";
import { parseAudience } from "../../audience.js";
import { StatusError } from "@/misc/fetch.js";
@ -30,7 +30,7 @@ export default async function (
}
// Interrupt if you block the announcement destination
if (await shouldBlockInstance(extractDbHost(uri))) return;
if (await shouldBlockInstance(extractHost(uri))) return;
const lock = await getApLock(uri);

View file

@ -4,7 +4,7 @@ import { createNote, fetchNote } from "../../models/note.js";
import type { IObject, ICreate } from "../../type.js";
import { getApId } from "../../type.js";
import { getApLock } from "@/misc/app-lock.js";
import { extractDbHost } from "@/misc/convert-host.js";
import { extractHost } from "native-utils/built/index.js";
import { StatusError } from "@/misc/fetch.js";
/**
@ -25,7 +25,7 @@ export default async function (
}
if (typeof note.id === "string") {
if (extractDbHost(actor.uri) !== extractDbHost(note.id)) {
if (extractHost(actor.uri) !== extractHost(note.id)) {
return "skip: host in actor.uri !== note.id";
}
}

View file

@ -38,7 +38,7 @@ import block from "./block/index.js";
import flag from "./flag/index.js";
import move from "./move/index.js";
import type { IObject, IActivity } from "../type.js";
import { extractDbHost } from "@/misc/convert-host.js";
import { extractHost } from "native-utils/built/index.js";
import { shouldBlockInstance } from "@/misc/should-block-instance.js";
export async function performActivity(
@ -71,7 +71,7 @@ async function performOneActivity(
if (actor.isSuspended) return;
if (typeof activity.id !== "undefined") {
const host = extractDbHost(getApId(activity));
const host = extractHost(getApId(activity));
if (await shouldBlockInstance(host)) return;
}

View file

@ -1,7 +1,7 @@
import type { CacheableRemoteUser } from "@/models/entities/user.js";
import type { IRead } from "../type.js";
import { getApId } from "../type.js";
import { isSelfHost, extractDbHost } from "@/misc/convert-host.js";
import { isSelfHost, extractHost } from "native-utils/built/index.js";
import { MessagingMessages } from "@/models/index.js";
import { readUserMessagingMessage } from "@/server/api/common/read-messaging-message.js";
@ -11,7 +11,7 @@ export const performReadActivity = async (
): Promise<string> => {
const id = await getApId(activity.object);
if (!isSelfHost(extractDbHost(id))) {
if (!isSelfHost(extractHost(id))) {
return `skip: Read to foreign host (${id})`;
}

View file

@ -14,7 +14,7 @@ import { extractPollFromQuestion } from "./question.js";
import vote from "@/services/note/polls/vote.js";
import { apLogger } from "../logger.js";
import { DriveFile } from "@/models/entities/drive-file.js";
import { extractDbHost, toPuny } from "@/misc/convert-host.js";
import { extractHost, toPuny } from "native-utils/built/index.js";
import {
Emojis,
Polls,
@ -54,7 +54,7 @@ import { langmap } from "@/misc/langmap.js";
const logger = apLogger;
export function validateNote(object: any, uri: string) {
const expectHost = extractDbHost(uri);
const expectHost = extractHost(uri);
if (object == null) {
return new Error("invalid Note: object is null");
@ -64,9 +64,9 @@ export function validateNote(object: any, uri: string) {
return new Error(`invalid Note: invalid object type ${getApType(object)}`);
}
if (object.id && extractDbHost(object.id) !== expectHost) {
if (object.id && extractHost(object.id) !== expectHost) {
return new Error(
`invalid Note: id has different host. expected: ${expectHost}, actual: ${extractDbHost(
`invalid Note: id has different host. expected: ${expectHost}, actual: ${extractHost(
object.id,
)}`,
);
@ -74,10 +74,10 @@ export function validateNote(object: any, uri: string) {
if (
object.attributedTo &&
extractDbHost(getOneApId(object.attributedTo)) !== expectHost
extractHost(getOneApId(object.attributedTo)) !== expectHost
) {
return new Error(
`invalid Note: attributedTo has different host. expected: ${expectHost}, actual: ${extractDbHost(
`invalid Note: attributedTo has different host. expected: ${expectHost}, actual: ${extractHost(
object.attributedTo,
)}`,
);
@ -422,11 +422,11 @@ export async function resolveNote(
if (uri == null) throw new Error("missing uri");
// Abort if origin host is blocked
if (await shouldBlockInstance(extractDbHost(uri)))
if (await shouldBlockInstance(extractHost(uri)))
throw new StatusError(
"host blocked",
451,
`host ${extractDbHost(uri)} is blocked`,
`host ${extractHost(uri)} is blocked`,
);
const lock = await getApLock(uri);

View file

@ -19,7 +19,7 @@ import { UserNotePining } from "@/models/entities/user-note-pining.js";
import { genId } from "@/misc/gen-id.js";
import { UserPublickey } from "@/models/entities/user-publickey.js";
import { isDuplicateKeyValueError } from "@/misc/is-duplicate-key-value-error.js";
import { toPuny } from "@/misc/convert-host.js";
import { toPuny } from "native-utils/built/index.js";
import { UserProfile } from "@/models/entities/user-profile.js";
import { toArray } from "@/prelude/array.js";
import { fetchInstanceMetadata } from "@/services/fetch-instance-metadata.js";

View file

@ -3,7 +3,7 @@ import { getJson } from "@/misc/fetch.js";
import type { ILocalUser } from "@/models/entities/user.js";
import { getInstanceActor } from "@/services/instance-actor.js";
import { fetchMeta } from "@/misc/fetch-meta.js";
import { extractDbHost, isSelfHost } from "@/misc/convert-host.js";
import { extractHost, isSelfHost } from "native-utils/built/index.js";
import { signedGet } from "./request.js";
import type { IObject, ICollection, IOrderedCollection } from "./type.js";
import { isCollectionOrOrderedCollection, getApId } from "./type.js";
@ -62,7 +62,7 @@ export default class Resolver {
if (typeof value !== "string") {
apLogger.debug("Object to resolve is not a string");
if (typeof value.id !== "undefined") {
const host = extractDbHost(getApId(value));
const host = extractHost(getApId(value));
if (await shouldBlockInstance(host)) {
throw new Error("instance is blocked");
}
@ -89,7 +89,7 @@ export default class Resolver {
}
this.history.add(value);
const host = extractDbHost(value);
const host = extractHost(value);
if (isSelfHost(host)) {
return await this.resolveLocal(value);
}

View file

@ -4,7 +4,7 @@ import { IsNull } from "typeorm";
import config from "@/config/index.js";
import type { User, IRemoteUser } from "@/models/entities/user.js";
import { Users } from "@/models/index.js";
import { toPuny } from "@/misc/convert-host.js";
import { toPuny } from "native-utils/built/index.js";
import webFinger from "./webfinger.js";
import { createPerson, updatePerson } from "./activitypub/models/person.js";
import { remoteLogger } from "./logger.js";

View file

@ -9,7 +9,7 @@ import renderKey from "@/remote/activitypub/renderer/key.js";
import { renderPerson } from "@/remote/activitypub/renderer/person.js";
import renderEmoji from "@/remote/activitypub/renderer/emoji.js";
import { inbox as processInbox } from "@/queue/index.js";
import { isSelfHost } from "@/misc/convert-host.js";
import { isSelfHost } from "native-utils/built/index.js";
import {
Notes,
Users,

View file

@ -1,3 +1,3 @@
import { secureRndstr } from "@/misc/secure-rndstr.js";
import { genString } from "native-utils/built/index.js";
export default () => secureRndstr(16, true);
export default () => genString(16);

View file

@ -5,7 +5,7 @@ import { Users, UsedUsernames } from "@/models/index.js";
import { UserProfile } from "@/models/entities/user-profile.js";
import { IsNull } from "typeorm";
import { genId } from "@/misc/gen-id.js";
import { toPunyNullable } from "@/misc/convert-host.js";
import { toPunyOptional } from "native-utils/built/index.js";
import { UserKeypair } from "@/models/entities/user-keypair.js";
import { UsedUsername } from "@/models/entities/used-username.js";
import { db } from "@/db/postgre.js";
@ -100,7 +100,7 @@ export async function signup(opts: {
createdAt: new Date(),
username: username,
usernameLower: username.toLowerCase(),
host: toPunyNullable(host),
host: toPunyOptional(host),
token: secret,
isAdmin:
(await Users.countBy({

View file

@ -1,9 +1,8 @@
import define from "@/server/api/define.js";
import { ApiError } from "@/server/api/error.js";
import { Emojis } from "@/models/index.js";
import { toPuny } from "@/misc/convert-host.js";
import { makePaginationQuery } from "@/server/api/common/make-pagination-query.js";
import { sqlLikeEscape } from "@/misc/sql-like-escape.js";
import { sqlLikeEscape, toPuny } from "native-utils/built/index.js";
export const meta = {
tags: ["admin", "emoji"],

View file

@ -2,7 +2,7 @@ import define from "@/server/api/define.js";
import { Emojis } from "@/models/index.js";
import { makePaginationQuery } from "../../../common/make-pagination-query.js";
import type { Emoji } from "@/models/entities/emoji.js";
//import { sqlLikeEscape } from "@/misc/sql-like-escape.js";
//import { sqlLikeEscape } from "native-utils/built/index.js";
import { ApiError } from "../../../error.js";
export const meta = {

View file

@ -1,6 +1,6 @@
import define from "@/server/api/define.js";
import { Instances } from "@/models/index.js";
import { toPuny } from "@/misc/convert-host.js";
import { toPuny } from "native-utils/built/index.js";
import { fetchInstanceMetadata } from "@/services/fetch-instance-metadata.js";
export const meta = {

View file

@ -1,6 +1,6 @@
import define from "@/server/api/define.js";
import { Instances } from "@/models/index.js";
import { toPuny } from "@/misc/convert-host.js";
import { toPuny } from "native-utils/built/index.js";
export const meta = {
tags: ["admin"],

View file

@ -1,6 +1,6 @@
import { Users } from "@/models/index.js";
import define from "@/server/api/define.js";
import { sqlLikeEscape } from "@/misc/sql-like-escape.js";
import { sqlLikeEscape } from "native-utils/built/index.js";
export const meta = {
tags: ["admin"],

View file

@ -4,7 +4,7 @@ import { createNote } from "@/remote/activitypub/models/note.js";
import DbResolver from "@/remote/activitypub/db-resolver.js";
import Resolver from "@/remote/activitypub/resolver.js";
import { ApiError } from "@/server/api/error.js";
import { extractDbHost } from "@/misc/convert-host.js";
import { extractHost } from "native-utils/built/index.js";
import { Users, Notes } from "@/models/index.js";
import type { Note } from "@/models/entities/note.js";
import type { CacheableLocalUser, User } from "@/models/entities/user.js";
@ -101,7 +101,7 @@ async function fetchAny(
me: CacheableLocalUser | null | undefined,
): Promise<SchemaType<(typeof meta)["res"]> | null> {
// Wait if blocked.
if (await shouldBlockInstance(extractDbHost(uri))) return null;
if (await shouldBlockInstance(extractHost(uri))) return null;
const dbResolver = new DbResolver();

View file

@ -2,7 +2,7 @@ import define from "@/server/api/define.js";
import { Apps } from "@/models/index.js";
import { genId } from "@/misc/gen-id.js";
import { unique } from "@/prelude/array.js";
import { secureRndstr } from "@/misc/secure-rndstr.js";
import { genString } from "native-utils/built/index.js";
export const meta = {
tags: ["app"],
@ -41,7 +41,7 @@ export default define(meta, paramDef, async (ps, user) => {
includeSecret: true,
});
// Generate secret
const secret = secureRndstr(32, true);
const secret = genString(32);
// for backward compatibility
const permission = unique(

View file

@ -3,7 +3,7 @@ import define from "@/server/api/define.js";
import { ApiError } from "@/server/api/error.js";
import { AuthSessions, AccessTokens, Apps } from "@/models/index.js";
import { genId } from "@/misc/gen-id.js";
import { secureRndstr } from "@/misc/secure-rndstr.js";
import { genString } from "native-utils/built/index.js";
export const meta = {
tags: ["auth"],
@ -38,10 +38,10 @@ export default define(meta, paramDef, async (ps, user) => {
}
// Generate access token
const accessToken = secureRndstr(32, true);
const accessToken = genString(32);
// Fetch exist access token
const exist = await AccessTokens.exist({
const exist = await AccessTokens.exists({
where: {
appId: session.appId,
userId: user.id,

View file

@ -2,7 +2,7 @@ import define from "@/server/api/define.js";
import { Brackets } from "typeorm";
import { makePaginationQuery } from "@/server/api/common/make-pagination-query.js";
import { Channels } from "@/models/index.js";
import { sqlLikeEscape } from "@/misc/sql-like-escape.js";
import { sqlLikeEscape } from "native-utils/built/index.js";
export const meta = {
tags: ["channels"],

View file

@ -1,7 +1,7 @@
import define from "@/server/api/define.js";
import { Instances } from "@/models/index.js";
import { fetchMeta } from "@/misc/fetch-meta.js";
import { sqlLikeEscape } from "@/misc/sql-like-escape.js";
import { sqlLikeEscape } from "native-utils/built/index.js";
export const meta = {
tags: ["federation"],

View file

@ -1,6 +1,6 @@
import define from "@/server/api/define.js";
import { Instances } from "@/models/index.js";
import { toPuny } from "@/misc/convert-host.js";
import { toPuny } from "native-utils/built/index.js";
export const meta = {
tags: ["federation"],

View file

@ -1,6 +1,6 @@
import define from "@/server/api/define.js";
import { Hashtags } from "@/models/index.js";
import { sqlLikeEscape } from "@/misc/sql-like-escape.js";
import { sqlLikeEscape } from "native-utils/built/index.js";
export const meta = {
tags: ["hashtags"],

View file

@ -3,7 +3,7 @@ import define from "@/server/api/define.js";
import { fetchMeta } from "@/misc/fetch-meta.js";
import { Notes } from "@/models/index.js";
import type { Note } from "@/models/entities/note.js";
import { safeForSql } from "@/misc/safe-for-sql.js";
import { safeForSql } from "native-utils/built/index.js";
import { normalizeForSearch } from "@/misc/normalize-for-search.js";
/*

View file

@ -1,7 +1,7 @@
import define from "@/server/api/define.js";
import { AccessTokens } from "@/models/index.js";
import { genId } from "@/misc/gen-id.js";
import { secureRndstr } from "@/misc/secure-rndstr.js";
import { genString } from "native-utils/built/index.js";
export const meta = {
tags: ["auth"],
@ -44,7 +44,7 @@ export const paramDef = {
export default define(meta, paramDef, async (ps, user) => {
// Generate access token
const accessToken = secureRndstr(32, true);
const accessToken = genString(32);
const now = new Date();

View file

@ -1,6 +1,6 @@
import { Brackets } from "typeorm";
import { Notes } from "@/models/index.js";
import { safeForSql } from "@/misc/safe-for-sql.js";
import { safeForSql } from "native-utils/built/index.js";
import { normalizeForSearch } from "@/misc/normalize-for-search.js";
import define from "@/server/api/define.js";
import { makePaginationQuery } from "@/server/api/common/make-pagination-query.js";

View file

@ -10,7 +10,7 @@ import { makePaginationQuery } from "@/server/api/common/make-pagination-query.j
import { generateVisibilityQuery } from "@/server/api/common/generate-visibility-query.js";
import { generateMutedUserQuery } from "@/server/api/common/generate-muted-user-query.js";
import { generateBlockedUserQuery } from "@/server/api/common/generate-block-query.js";
import { sqlLikeEscape } from "@/misc/sql-like-escape.js";
import { sqlLikeEscape } from "native-utils/built/index.js";
export const meta = {
tags: ["notes"],

View file

@ -44,9 +44,9 @@ async function translateCommitMsg(msg: string, targetLang: Language) {
if (targetLang.startsWith("ja")) {
const prefixes = {
container: "コンテナ (Podman/Docker)",
chore: "雑務",
dev: "開発",
docker: "Docker",
docs: "ドキュメント",
feat: "新機能",
fix: "修正",

View file

@ -1,6 +1,6 @@
import { IsNull } from "typeorm";
import { Users, Followings, UserProfiles } from "@/models/index.js";
import { toPunyNullable } from "@/misc/convert-host.js";
import { toPunyOptional } from "native-utils/built/index.js";
import define from "@/server/api/define.js";
import { ApiError } from "@/server/api/error.js";
import { makePaginationQuery } from "@/server/api/common/make-pagination-query.js";
@ -80,7 +80,7 @@ export default define(meta, paramDef, async (ps, me) => {
? { id: ps.userId }
: {
usernameLower: ps.username?.toLowerCase(),
host: toPunyNullable(ps.host) ?? IsNull(),
host: toPunyOptional(ps.host) ?? IsNull(),
},
);

View file

@ -1,6 +1,6 @@
import { IsNull } from "typeorm";
import { Users, Followings, UserProfiles } from "@/models/index.js";
import { toPunyNullable } from "@/misc/convert-host.js";
import { toPunyOptional } from "native-utils/built/index.js";
import define from "@/server/api/define.js";
import { ApiError } from "@/server/api/error.js";
import { makePaginationQuery } from "@/server/api/common/make-pagination-query.js";
@ -79,7 +79,7 @@ export default define(meta, paramDef, async (ps, me) => {
? { id: ps.userId }
: {
usernameLower: ps.username?.toLowerCase(),
host: toPunyNullable(ps.host) ?? IsNull(),
host: toPunyOptional(ps.host) ?? IsNull(),
},
);

View file

@ -2,7 +2,7 @@ import { Brackets } from "typeorm";
import { Followings, Users } from "@/models/index.js";
import type { User } from "@/models/entities/user.js";
import define from "@/server/api/define.js";
import { sqlLikeEscape } from "@/misc/sql-like-escape.js";
import { sqlLikeEscape } from "native-utils/built/index.js";
export const meta = {
tags: ["users"],

View file

@ -2,7 +2,7 @@ import { Brackets } from "typeorm";
import { UserProfiles, Users } from "@/models/index.js";
import type { User } from "@/models/entities/user.js";
import define from "@/server/api/define.js";
import { sqlLikeEscape } from "@/misc/sql-like-escape.js";
import { sqlLikeEscape } from "native-utils/built/index.js";
export const meta = {
tags: ["users"],

View file

@ -2,7 +2,7 @@ import Limiter from "ratelimiter";
import Logger from "@/services/logger.js";
import { redisClient } from "@/db/redis.js";
import type { IEndpointMeta } from "./endpoints.js";
import { convertMilliseconds } from "@/misc/convert-milliseconds.js";
import { formatMilliseconds } from "native-utils/built/index.js";
const logger = new Logger("limiter");
@ -78,7 +78,7 @@ export const limiter = (
if (info.remaining === 0) {
reject({
message: "RATE_LIMIT_EXCEEDED",
remainingTime: convertMilliseconds(info.resetMs - Date.now()),
remainingTime: formatMilliseconds(info.resetMs - Date.now()),
});
} else {
ok();

View file

@ -20,7 +20,7 @@ import { fetchMeta } from "@/misc/fetch-meta.js";
import { genIdenticon } from "@/misc/gen-identicon.js";
import { createTemp } from "@/misc/create-temp.js";
import { stringToAcct } from "native-utils/built/index.js";
import { envOption } from "@/env.js";
import { envOption } from "@/config/index.js";
import megalodon, { MegalodonInterface } from "megalodon";
import activityPub from "./activitypub.js";
import nodeinfo from "./nodeinfo.js";

View file

@ -2,7 +2,7 @@ import cluster from "node:cluster";
import chalk from "chalk";
import { default as convertColor } from "color-convert";
import { format as dateFormat } from "date-fns";
import { envOption } from "@/env.js";
import { envOption } from "@/config/index.js";
import config from "@/config/index.js";
import * as SyslogPro from "syslog-pro";

View file

@ -22,7 +22,7 @@ import renderNote from "@/remote/activitypub/renderer/note.js";
import renderCreate from "@/remote/activitypub/renderer/create.js";
import { renderActivity } from "@/remote/activitypub/renderer/index.js";
import { deliver } from "@/queue/index.js";
import { toPuny } from "@/misc/convert-host.js";
import { toPuny } from "native-utils/built/index.js";
import { Instances } from "@/models/index.js";
export async function createMessage(

View file

@ -1,7 +1,7 @@
import type { Instance } from "@/models/entities/instance.js";
import { Instances } from "@/models/index.js";
import { genId } from "@/misc/gen-id.js";
import { toPuny } from "@/misc/convert-host.js";
import { toPuny } from "native-utils/built/index.js";
import { Cache } from "@/misc/cache.js";
const cache = new Cache<Instance>("registerOrFetchInstanceDoc", 60 * 60);

View file

@ -229,13 +229,16 @@ function saveFields() {
});
}
const convertEmptyStringToNull = (x) =>
x === "" ? null : x == null ? undefined : x;
function save() {
os.apiWithDialog("i/update", {
name: profile.name ?? undefined,
description: profile.description ?? undefined,
location: profile.location ?? undefined,
birthday: profile.birthday ?? undefined,
lang: profile.lang ?? undefined,
name: convertEmptyStringToNull(profile.name),
description: convertEmptyStringToNull(profile.description),
location: convertEmptyStringToNull(profile.location),
birthday: convertEmptyStringToNull(profile.birthday),
lang: convertEmptyStringToNull(profile.lang),
isBot: !!profile.isBot,
isCat: !!profile.isCat,
speakAsCat: profile.isCat ? !!profile.speakAsCat : undefined,

View file

@ -10,7 +10,7 @@ OLD_COMMIT=$(git rev-parse --short HEAD)
say 'Pulling changes from the remote repo...'
run 'git checkout -- package.json packages/backend/assets'
run 'git pull --ff --no-edit --autostash --strategy-option theirs'
run 'git pull --ff --no-edit --autostash --strategy-option theirs origin main'
NEW_COMMIT=$(git rev-parse --short HEAD)