Compare commits

...

701 commits

Author SHA1 Message Date
naskya 88890153a1 Merge branch 'renovate/eslint-monorepo' into 'develop'
chore(deps): update dependency eslint to v9.3.0

Co-authored-by: CI <project_7_bot_1bfaee5701aed20091a86249a967a6c1@noreply.firefish.dev>

See merge request firefish/firefish!10867
2024-05-18 05:17:08 +00:00
naskya 23f47aac38
ci: buildah prune before build 2024-05-18 14:16:31 +09:00
naskya abc9d58f7c
refactor (backend-rs): use bb8 connection pool for Redis 2024-05-18 13:20:47 +09:00
Daniel Smith ab3ca2a20b
feat (backend-rs): impl bb8::ManageConnection for RedisConnectionManager
62597aa45a/redis/src/lib.rs

Co-authored-by: Kyle Huey <khuey@kylehuey.com>
2024-05-18 13:20:47 +09:00
naskya fdc77b74ae
chore: lint 2024-05-18 13:20:46 +09:00
naskya 61562a0943
refactor (backend-rs): use async redis commands 2024-05-18 13:20:43 +09:00
CI a298302f76 chore(deps): update dependency eslint to v9.3.0 2024-05-18 04:05:11 +00:00
naskya 42d4f2fd79 Merge branch 'renovate/lock-file-maintenance' into 'develop'
chore(deps): lock file maintenance

Co-authored-by: CI <project_7_bot_1bfaee5701aed20091a86249a967a6c1@noreply.firefish.dev>

See merge request firefish/firefish!10866
2024-05-17 20:53:33 +00:00
naskya 234e5b8619 Merge branch 'renovate/thiserror-1.x' into 'develop'
chore(deps): update rust crate thiserror to 1.0.61

Co-authored-by: CI <project_7_bot_1bfaee5701aed20091a86249a967a6c1@noreply.firefish.dev>

See merge request firefish/firefish!10864
2024-05-17 20:36:54 +00:00
naskya 1904fbaa43 Merge branch 'renovate/aws-sdk-2.x' into 'develop'
fix(deps): update dependency aws-sdk to v2.1623.0

Co-authored-by: CI <project_7_bot_1bfaee5701aed20091a86249a967a6c1@noreply.firefish.dev>

See merge request firefish/firefish!10865
2024-05-17 20:32:28 +00:00
naskya c1f5bbd2e8 Merge branch 'renovate/chart.js-4.x' into 'develop'
chore(deps): update dependency chart.js to v4.4.3

Co-authored-by: CI <project_7_bot_1bfaee5701aed20091a86249a967a6c1@noreply.firefish.dev>

See merge request firefish/firefish!10863
2024-05-17 20:26:13 +00:00
CI 282944a199 chore(deps): lock file maintenance 2024-05-17 20:20:57 +00:00
CI fcb4017700 fix(deps): update dependency aws-sdk to v2.1623.0 2024-05-17 20:20:15 +00:00
CI 18eaa915b8 chore(deps): update rust crate thiserror to 1.0.61 2024-05-17 20:19:52 +00:00
CI 52456a509e chore(deps): update dependency chart.js to v4.4.3 2024-05-17 20:19:03 +00:00
naskya 8c9a1abd32 Merge branch 'renovate/bull-4.x' into 'develop'
fix(deps): update dependency bull to v4.12.5

Co-authored-by: CI <project_7_bot_1bfaee5701aed20091a86249a967a6c1@noreply.firefish.dev>

See merge request firefish/firefish!10861
2024-05-17 19:54:25 +00:00
naskya 2ee48321fd Merge branch 'add-key' into 'develop'
fix: add key to v-for in MkNotificationFolded

Co-authored-by: Lhcfl <Lhcfl@outlook.com>

See merge request firefish/firefish!10862
2024-05-17 19:54:12 +00:00
Lhcfl 1d1e9105e5 fix: add key to v-for in MkNotificationFolded 2024-05-17 22:30:10 +08:00
CI e9287357f7 fix(deps): update dependency bull to v4.12.5 2024-05-17 12:05:08 +00:00
naskya 29023d7240
test (backend-rs): add unit test for word mute 2024-05-17 18:59:06 +09:00
naskya 4e50ffeb09
chore: format 2024-05-17 18:17:52 +09:00
naskya 2fdd165144
chore (backend-rs): add TODO comment 2024-05-17 18:12:27 +09:00
naskya 7e8d71003f Merge branch 'renovate/sass-1.x' into 'develop'
chore(deps): update dependency sass to v1.77.2

Co-authored-by: CI <project_7_bot_1bfaee5701aed20091a86249a967a6c1@noreply.firefish.dev>

See merge request firefish/firefish!10860
2024-05-17 09:04:47 +00:00
naskya 1e313846ca
fix (backend-rs): typo 2024-05-17 18:03:34 +09:00
naskya ffa08748d0
refactor (backend): don't check word mute twice 2024-05-17 17:59:45 +09:00
naskya 5e53f9a8cf
refactor (backend): port check-hit-antenna to backend-rs 2024-05-17 17:59:45 +09:00
naskya a4779f233b
refactor (backend): convert jsonb to array 2024-05-17 17:59:45 +09:00
CI fd5f27eb49 chore(deps): update dependency sass to v1.77.2 2024-05-17 04:05:06 +00:00
naskya 3d28acb2c9
chore (backend-rs): rename types and modules 2024-05-17 07:32:08 +09:00
naskya ff6ad1c2a9 Merge branch 'renovate/syn-2.x' into 'develop'
chore(deps): update rust crate syn to 2.0.64

Co-authored-by: CI <project_7_bot_1bfaee5701aed20091a86249a967a6c1@noreply.firefish.dev>

See merge request firefish/firefish!10856
2024-05-16 20:35:46 +00:00
naskya 20cd64281a Merge branch 'renovate/aws-sdk-2.x' into 'develop'
fix(deps): update dependency aws-sdk to v2.1622.0

Co-authored-by: CI <project_7_bot_1bfaee5701aed20091a86249a967a6c1@noreply.firefish.dev>

See merge request firefish/firefish!10858
2024-05-16 20:35:23 +00:00
naskya e47f5f0cfc Merge branch 'renovate/sharp-0.x' into 'develop'
fix(deps): update dependency sharp to v0.33.4

Co-authored-by: CI <project_7_bot_1bfaee5701aed20091a86249a967a6c1@noreply.firefish.dev>

See merge request firefish/firefish!10857
2024-05-16 20:35:03 +00:00
naskya 0ad1c581b0 Merge branch 'renovate/lock-file-maintenance' into 'develop'
chore(deps): lock file maintenance

Co-authored-by: CI <project_7_bot_1bfaee5701aed20091a86249a967a6c1@noreply.firefish.dev>

See merge request firefish/firefish!10859
2024-05-16 20:34:22 +00:00
CI ead1db27a5 chore(deps): lock file maintenance 2024-05-16 20:06:37 +00:00
CI 12be7266db fix(deps): update dependency aws-sdk to v2.1622.0 2024-05-16 20:05:57 +00:00
CI 7ad4ffecf3 fix(deps): update dependency sharp to v0.33.4 2024-05-16 20:05:36 +00:00
CI 4e39a85708 chore(deps): update rust crate syn to 2.0.64 2024-05-16 20:05:04 +00:00
naskya c21bd40a5c Merge branch 'renovate/vue-tsc-2.x' into 'develop'
chore(deps): update dependency vue-tsc to v2.0.19

Co-authored-by: CI <project_7_bot_1bfaee5701aed20091a86249a967a6c1@noreply.firefish.dev>

See merge request firefish/firefish!10855
2024-05-16 17:19:31 +00:00
CI 1b537da466 chore(deps): update dependency vue-tsc to v2.0.19 2024-05-16 12:05:04 +00:00
naskya 8cee026c72 Merge branch 'refactor/types' into 'develop'
refactor: types

Co-authored-by: Lhcfl <Lhcfl@outlook.com>

See merge request firefish/firefish!10762
2024-05-16 08:05:00 +00:00
naskya 0ee3db7788
chore (backend): sort dependencies in package.json 2024-05-16 17:00:33 +09:00
naskya ff63089128
ci: add firefish-js to backend deps 2024-05-16 16:59:04 +09:00
naskya 287dcece57
chore (client): remove debug prints 2024-05-16 16:47:35 +09:00
naskya 9298a6252d
Merge branch 'develop' into refactor/types 2024-05-16 16:45:03 +09:00
naskya 088dfd21e7
v20240516 2024-05-16 16:16:29 +09:00
naskya 03323e40fa
docs: update notice-for-admins.md 2024-05-16 15:09:40 +09:00
naskya c6e3506bd5
fix: remove unnecessary copy operation (close #10926) 2024-05-16 15:03:53 +09:00
naskya 128fc72778 Merge branch 'renovate/lock-file-maintenance' into 'develop'
chore(deps): lock file maintenance

Co-authored-by: CI <project_7_bot_1bfaee5701aed20091a86249a967a6c1@noreply.firefish.dev>

See merge request firefish/firefish!10853
2024-05-16 05:17:04 +00:00
CI 310059f6a0 chore(deps): lock file maintenance 2024-05-16 04:05:56 +00:00
naskya 7d4d1c1fbd
fix merge mistake 2024-05-16 08:45:50 +09:00
naskya dbd205972f Merge branch 'refactor/push-notification' into 'develop'
refactor: port push notification sender to backend-rs


See merge request firefish/firefish!10760
2024-05-15 22:19:58 +00:00
naskya 41b32c5535 refactor (backend): port push notification sender to backend-rs 2024-05-15 22:19:58 +00:00
naskya 56be2f034e Merge branch 'renovate/syn-2.x' into 'develop'
chore(deps): update rust crate syn to 2.0.63

Co-authored-by: CI <project_7_bot_1bfaee5701aed20091a86249a967a6c1@noreply.firefish.dev>

See merge request firefish/firefish!10851
2024-05-15 21:22:39 +00:00
naskya e15bcee86c Merge branch 'renovate/aws-sdk-2.x' into 'develop'
fix(deps): update dependency aws-sdk to v2.1621.0

Co-authored-by: CI <project_7_bot_1bfaee5701aed20091a86249a967a6c1@noreply.firefish.dev>

See merge request firefish/firefish!10852
2024-05-15 21:19:48 +00:00
naskya 43326cdf8d Merge branch 'renovate/serde-monorepo' into 'develop'
chore(deps): update rust crate serde to 1.0.202

Co-authored-by: CI <project_7_bot_1bfaee5701aed20091a86249a967a6c1@noreply.firefish.dev>

See merge request firefish/firefish!10850
2024-05-15 21:16:55 +00:00
CI 7d1947792d fix(deps): update dependency aws-sdk to v2.1621.0 2024-05-15 21:05:33 +00:00
CI d28fe77d9f chore(deps): update rust crate syn to 2.0.63 2024-05-15 21:05:04 +00:00
CI acc13e9b10 chore(deps): update rust crate serde to 1.0.202 2024-05-15 21:04:59 +00:00
naskya 4e31e11f81
docs: use permalink 2024-05-16 05:04:47 +09:00
naskya dddd2779c0
chore: update auto-generated files 2024-05-16 04:57:48 +09:00
naskya 832fc7cd1d
docs: update changelog 2024-05-16 04:56:26 +09:00
naskya a18ad132be
fix: remove $[center] MFM function 2024-05-16 04:51:51 +09:00
naskya 4b96063c23
chore: format 2024-05-16 04:22:41 +09:00
naskya 0de54e02f8
chore (backend): use literals and consts 2024-05-16 04:22:23 +09:00
naskya 101e50926b
chore: remove import assertion 2024-05-16 04:12:10 +09:00
naskya 9cf88f0df6
chore: remove import assertion 2024-05-16 03:49:31 +09:00
naskya efb6cc9132 Merge branch 'renovate/execa-9.x' into 'develop'
chore(deps): update dependency execa to v9.1.0

Co-authored-by: CI <project_7_bot_1bfaee5701aed20091a86249a967a6c1@noreply.firefish.dev>

See merge request firefish/firefish!10848
2024-05-15 18:36:18 +00:00
Hosted Weblate 58f3eb4924
Merge branch 'origin/develop' into Weblate 2024-05-15 18:29:30 +00:00
Gary O'Regan Kelly 5adc0e581d
locale: update translations (French)
Currently translated at 100.0% (1932 of 1932 strings)

Translation: Firefish/locales
Translate-URL: https://hosted.weblate.org/projects/firefish/locales/fr/
2024-05-15 20:29:25 +02:00
naskya c0b760cda5 Merge branch 'develop' into 'renovate/execa-9.x'
# Conflicts:
#   package.json
2024-05-15 18:17:35 +00:00
naskya eb967564f9 Merge branch 'renovate/aws-sdk-2.x' into 'develop'
fix(deps): update dependency aws-sdk to v2.1620.0

Co-authored-by: CI <project_7_bot_1bfaee5701aed20091a86249a967a6c1@noreply.firefish.dev>

See merge request firefish/firefish!10849
2024-05-15 18:13:47 +00:00
naskya 0085105e72 Merge branch 'renovate/websocket-1.x' into 'develop'
fix(deps): update dependency websocket to v1.0.35

Co-authored-by: CI <project_7_bot_1bfaee5701aed20091a86249a967a6c1@noreply.firefish.dev>

See merge request firefish/firefish!10847
2024-05-15 18:09:16 +00:00
naskya 217b3ecf80 Merge branch 'renovate/is-svg-5.x' into 'develop'
fix(deps): update dependency is-svg to v5.0.1

Co-authored-by: CI <project_7_bot_1bfaee5701aed20091a86249a967a6c1@noreply.firefish.dev>

See merge request firefish/firefish!10846
2024-05-15 18:07:52 +00:00
naskya ffeeb3b444 Merge branch 'renovate/bull-4.x' into 'develop'
fix(deps): update dependency bull to v4.12.4

Co-authored-by: CI <project_7_bot_1bfaee5701aed20091a86249a967a6c1@noreply.firefish.dev>

See merge request firefish/firefish!10845
2024-05-15 18:06:29 +00:00
naskya 2f00947a24 Merge branch 'renovate/swc-monorepo' into 'develop'
chore(deps): update swc monorepo

Co-authored-by: CI <project_7_bot_1bfaee5701aed20091a86249a967a6c1@noreply.firefish.dev>

See merge request firefish/firefish!10844
2024-05-15 18:05:01 +00:00
naskya 5608129913 Merge branch 'renovate/syn-2.x-lockfile' into 'develop'
chore(deps): update rust crate syn to v2.0.63

Co-authored-by: CI <project_7_bot_1bfaee5701aed20091a86249a967a6c1@noreply.firefish.dev>

See merge request firefish/firefish!10843
2024-05-15 18:02:51 +00:00
naskya 8923e1f2a7 Merge branch 'renovate/serde-monorepo' into 'develop'
chore(deps): update rust crate serde to v1.0.202

Co-authored-by: CI <project_7_bot_1bfaee5701aed20091a86249a967a6c1@noreply.firefish.dev>

See merge request firefish/firefish!10842
2024-05-15 17:54:30 +00:00
naskya 8765e6ba54
ci: update renovate config 2024-05-16 02:42:59 +09:00
naskya 7c72738983 Merge branch 'renovate/pnpm-9.x' into 'develop'
chore(deps): update pnpm to v9.1.1

Co-authored-by: CI <project_7_bot_1bfaee5701aed20091a86249a967a6c1@noreply.firefish.dev>

See merge request firefish/firefish!10841
2024-05-15 17:40:54 +00:00
naskya ff446de7e8 Merge branch 'renovate/vue-tsc-2.x' into 'develop'
chore(deps): update dependency vue-tsc to v2.0.18

Co-authored-by: CI <project_7_bot_1bfaee5701aed20091a86249a967a6c1@noreply.firefish.dev>

See merge request firefish/firefish!10840
2024-05-15 17:36:25 +00:00
naskya 411d00a7af Merge branch 'renovate/vue-draggable-plus-0.x' into 'develop'
chore(deps): update dependency vue-draggable-plus to v0.4.1

Co-authored-by: CI <project_7_bot_1bfaee5701aed20091a86249a967a6c1@noreply.firefish.dev>

See merge request firefish/firefish!10839
2024-05-15 17:35:22 +00:00
CI 65a1fa870b fix(deps): update dependency aws-sdk to v2.1620.0 2024-05-15 17:29:54 +00:00
CI 1d25c78866 chore(deps): update dependency execa to v9.1.0 2024-05-15 17:29:35 +00:00
CI 6067eaef04 fix(deps): update dependency websocket to v1.0.35 2024-05-15 17:29:14 +00:00
CI 92299423a3 fix(deps): update dependency is-svg to v5.0.1 2024-05-15 17:28:54 +00:00
CI 65a8984c09 fix(deps): update dependency bull to v4.12.4 2024-05-15 17:28:34 +00:00
CI 99eb364778 chore(deps): update swc monorepo 2024-05-15 17:28:11 +00:00
CI 266c81df1e chore(deps): update rust crate syn to v2.0.63 2024-05-15 17:27:48 +00:00
CI 6a2e91efa1 chore(deps): update rust crate serde to v1.0.202 2024-05-15 17:27:42 +00:00
CI 17cbb9cd1e chore(deps): update pnpm to v9.1.1 2024-05-15 17:26:51 +00:00
CI d6ebb55556 chore(deps): update dependency vue-tsc to v2.0.18 2024-05-15 17:26:26 +00:00
CI 4dd1cff80b chore(deps): update dependency vue-draggable-plus to v0.4.1 2024-05-15 17:26:06 +00:00
naskya 752c6dc75b
ci: update renovate config 2024-05-16 02:24:06 +09:00
naskya cede0fdae2 Merge branch 'renovate/node-20.x' into 'develop'
chore(deps): update dependency @types/node to v20.12.12

Co-authored-by: CI <project_7_bot_1bfaee5701aed20091a86249a967a6c1@noreply.firefish.dev>

See merge request firefish/firefish!10837
2024-05-15 15:01:54 +00:00
naskya 35d706e45d Merge branch 'renovate/swiper-11.x' into 'develop'
chore(deps): update dependency swiper to v11.1.3

Co-authored-by: CI <project_7_bot_1bfaee5701aed20091a86249a967a6c1@noreply.firefish.dev>

See merge request firefish/firefish!10838
2024-05-15 15:01:26 +00:00
CI 9075050a67 chore(deps): update dependency swiper to v11.1.3 2024-05-15 10:05:12 +00:00
CI edc2a7d890 chore(deps): update dependency @types/node to v20.12.12 2024-05-15 10:04:53 +00:00
naskya 28e2a24585
chore (backend-rs): cleanup 2024-05-15 16:45:35 +09:00
naskya 2884b2fb42
chore (backend-rs): apply clippy fix 2024-05-15 16:36:26 +09:00
naskya d8e1ab63c0
refactor: port system information checker to backend-rs
network stat is removed because it might be inaccurate and/or
it should be monitored by other system tools, but it may be added back
later if it is wanted
2024-05-15 16:26:46 +09:00
Gary O'Regan Kelly c2d5859755
locale: update translations (French)
Currently translated at 100.0% (1932 of 1932 strings)

Translation: Firefish/locales
Translate-URL: https://hosted.weblate.org/projects/firefish/locales/fr/
2024-05-14 20:01:54 +02:00
naskya 457bd22b7b
chore (deps): pin versions 2024-05-12 01:29:19 +09:00
naskya 6176c09509
ci: update renovate config 2024-05-12 01:20:17 +09:00
naskya e9068acddd Merge branch 'renovate/lock-file-maintenance' into 'develop'
chore(deps): lock file maintenance

Co-authored-by: CI <project_7_bot_1bfaee5701aed20091a86249a967a6c1@noreply.firefish.dev>

See merge request firefish/firefish!10834
2024-05-11 16:11:11 +00:00
CI 3ccacb7fce chore(deps): lock file maintenance 2024-05-11 16:07:50 +00:00
naskya 654ab006a6
ci: update config 2024-05-12 01:03:52 +09:00
naskya 1942d772db Merge branch 'renovate/node-20.x' into 'develop'
chore(deps): update node.js to v20

Co-authored-by: CI <project_7_bot_1bfaee5701aed20091a86249a967a6c1@noreply.firefish.dev>

See merge request firefish/firefish!10833
2024-05-11 15:59:23 +00:00
naskya bc08f0faa9 Merge branch 'renovate/semver-7.x' into 'develop'
fix(deps): update dependency semver to v7.6.2

Co-authored-by: CI <project_7_bot_1bfaee5701aed20091a86249a967a6c1@noreply.firefish.dev>

See merge request firefish/firefish!10829
2024-05-11 15:56:59 +00:00
naskya fa35d1f4dd
meta: remove node version info from backend-rs/package.json 2024-05-12 00:52:27 +09:00
naskya 4db42272e7 Merge branch 'renovate/eslint-plugin-file-progress-1.x' into 'develop'
chore(deps): update dependency eslint-plugin-file-progress to ^1.4.0

Co-authored-by: CI <project_7_bot_1bfaee5701aed20091a86249a967a6c1@noreply.firefish.dev>

See merge request firefish/firefish!10836
2024-05-11 15:51:49 +00:00
naskya cfa3263c46
Merge branch 'develop' into renovate/semver-7.x 2024-05-12 00:49:18 +09:00
naskya b09e418cf6 Merge branch 'renovate/msgpackr-1.x' into 'develop'
fix(deps): update dependency msgpackr to ^1.10.2

Co-authored-by: CI <project_7_bot_1bfaee5701aed20091a86249a967a6c1@noreply.firefish.dev>

See merge request firefish/firefish!10835
2024-05-11 15:47:12 +00:00
CI e5a5d715b6 chore(deps): update node.js to v20 2024-05-11 15:41:52 +00:00
CI 9ad61fe607 chore(deps): update dependency eslint-plugin-file-progress to ^1.4.0 2024-05-11 15:41:30 +00:00
CI d6983e92aa fix(deps): update dependency semver to v7.6.2 2024-05-11 15:40:50 +00:00
CI 07e2571c79 fix(deps): update dependency msgpackr to ^1.10.2 2024-05-11 15:40:29 +00:00
naskya 0a9289abe3 Merge branch 'renovate/sass-1.x' into 'develop'
chore(deps): update dependency sass to v1.77.1

Co-authored-by: CI <project_7_bot_1bfaee5701aed20091a86249a967a6c1@noreply.firefish.dev>

See merge request firefish/firefish!10814
2024-05-11 15:25:52 +00:00
naskya 9dda7f955a Merge branch 'renovate/type-fest-4.x' into 'develop'
chore(deps): update dependency type-fest to v4.18.2

Co-authored-by: CI <project_7_bot_1bfaee5701aed20091a86249a967a6c1@noreply.firefish.dev>

See merge request firefish/firefish!10831
2024-05-11 14:38:51 +00:00
naskya a14a4a5f9c Merge branch 'renovate/redocly-openapi-core-1.x' into 'develop'
fix(deps): update dependency @redocly/openapi-core to v1.12.2

Co-authored-by: CI <project_7_bot_1bfaee5701aed20091a86249a967a6c1@noreply.firefish.dev>

See merge request firefish/firefish!10826
2024-05-11 14:34:39 +00:00
naskya b3eb12ccff Merge branch 'renovate/eslint-sets-eslint-config-vue3-5.x' into 'develop'
chore(deps): update dependency @eslint-sets/eslint-config-vue3 to ^5.13.0

Co-authored-by: CI <project_7_bot_1bfaee5701aed20091a86249a967a6c1@noreply.firefish.dev>

See merge request firefish/firefish!10811
2024-05-11 14:34:16 +00:00
naskya 6cd511d473 Merge branch 'renovate/bull-4.x' into 'develop'
fix(deps): update dependency bull to v4.12.3

Co-authored-by: CI <project_7_bot_1bfaee5701aed20091a86249a967a6c1@noreply.firefish.dev>

See merge request firefish/firefish!10828
2024-05-11 14:30:51 +00:00
naskya a82ed86539 Merge branch 'renovate/prismjs-1.x' into 'develop'
chore(deps): update dependency @types/prismjs to ^1.26.4

Co-authored-by: CI <project_7_bot_1bfaee5701aed20091a86249a967a6c1@noreply.firefish.dev>

See merge request firefish/firefish!10825
2024-05-11 14:29:38 +00:00
naskya 30ce11f9fe Merge branch 'renovate/vue-tsc-2.x' into 'develop'
chore(deps): update dependency vue-tsc to v2.0.17

Co-authored-by: CI <project_7_bot_1bfaee5701aed20091a86249a967a6c1@noreply.firefish.dev>

See merge request firefish/firefish!10807
2024-05-11 14:29:22 +00:00
naskya afb2d30e65 Merge branch 'renovate/pg-8.x' into 'develop'
chore(deps): update dependency @types/pg to ^8.11.6

Co-authored-by: CI <project_7_bot_1bfaee5701aed20091a86249a967a6c1@noreply.firefish.dev>

See merge request firefish/firefish!10824
2024-05-11 14:27:40 +00:00
naskya fb3cd43102 Merge branch 'renovate/napi-rs-cli-2.x' into 'develop'
chore(deps): update dependency @napi-rs/cli to v2.18.3

Co-authored-by: CI <project_7_bot_1bfaee5701aed20091a86249a967a6c1@noreply.firefish.dev>

See merge request firefish/firefish!10823
2024-05-11 14:26:54 +00:00
CI 1cd58ce05d chore(deps): update dependency type-fest to v4.18.2 2024-05-11 14:08:58 +00:00
CI 55fdd8b5a2 chore(deps): update dependency sass to v1.77.1 2024-05-11 14:08:37 +00:00
CI 704d39f202 chore(deps): update dependency @eslint-sets/eslint-config-vue3 to ^5.13.0 2024-05-11 14:07:55 +00:00
CI ef95673d50 fix(deps): update dependency bull to v4.12.3 2024-05-11 14:07:10 +00:00
CI eb443c8494 fix(deps): update dependency @redocly/openapi-core to v1.12.2 2024-05-11 14:06:30 +00:00
CI 52e3b49533 chore(deps): update dependency vue-tsc to v2.0.17 2024-05-11 14:06:07 +00:00
CI 11ded9491e chore(deps): update dependency @types/prismjs to ^1.26.4 2024-05-11 14:05:47 +00:00
CI d3b899ccc3 chore(deps): update dependency @types/pg to ^8.11.6 2024-05-11 14:05:27 +00:00
CI 6d6c0fbca0 chore(deps): update dependency @napi-rs/cli to v2.18.3 2024-05-11 14:05:03 +00:00
naskya 2257721fe3 Merge branch 'renovate/execa-9.x' into 'develop'
chore(deps): update dependency execa to v9

Co-authored-by: CI <project_7_bot_1bfaee5701aed20091a86249a967a6c1@noreply.firefish.dev>

See merge request firefish/firefish!10822
2024-05-11 13:10:38 +00:00
naskya cd836daa9b Merge branch 'renovate/otpauth-9.x' into 'develop'
fix(deps): update dependency otpauth to ^9.2.4

Co-authored-by: CI <project_7_bot_1bfaee5701aed20091a86249a967a6c1@noreply.firefish.dev>

See merge request firefish/firefish!10809
2024-05-11 13:09:51 +00:00
naskya bf5a2c6ebb Merge branch 'renovate/eslint-monorepo' into 'develop'
chore(deps): update dependency eslint to ^9.2.0

Co-authored-by: CI <project_7_bot_1bfaee5701aed20091a86249a967a6c1@noreply.firefish.dev>

See merge request firefish/firefish!10812
2024-05-11 13:09:13 +00:00
naskya f0632d2a6b Merge branch 'renovate/cropperjs-2.x' into 'develop'
chore(deps): update dependency cropperjs to v2.0.0-beta.5

Co-authored-by: CI <project_7_bot_1bfaee5701aed20091a86249a967a6c1@noreply.firefish.dev>

See merge request firefish/firefish!10805
2024-05-11 13:06:28 +00:00
naskya d192a7c81a
ci: fix config 2024-05-11 21:35:56 +09:00
naskya 847cc47fc4
chore (deps): update cargo dependencies 2024-05-11 21:27:24 +09:00
naskya 59862f16b0
chore (deps): update bull-board 2024-05-11 21:21:50 +09:00
naskya 0e5e96c99a Merge branch 'renovate/vite-5.x' into 'develop'
chore(deps): update dependency vite to v5.2.11

Co-authored-by: CI <project_7_bot_1bfaee5701aed20091a86249a967a6c1@noreply.firefish.dev>

See merge request firefish/firefish!10806
2024-05-11 12:18:46 +00:00
naskya dbc24a0b8f
Merge branch 'develop' into renovate/vite-5.x 2024-05-11 21:16:34 +09:00
naskya 1eb26263c1 Merge branch 'renovate/ajv-8.x' into 'develop'
fix(deps): update dependency ajv to v8.13.0

Co-authored-by: CI <project_7_bot_1bfaee5701aed20091a86249a967a6c1@noreply.firefish.dev>

See merge request firefish/firefish!10819
2024-05-11 12:14:56 +00:00
naskya 639a838736 Merge branch 'renovate/systeminformation-5.x' into 'develop'
fix(deps): update dependency systeminformation to v5.22.8

Co-authored-by: CI <project_7_bot_1bfaee5701aed20091a86249a967a6c1@noreply.firefish.dev>

See merge request firefish/firefish!10810
2024-05-11 12:14:00 +00:00
naskya 811be1022a Merge branch 'renovate/tesseract.js-5.x' into 'develop'
fix(deps): update dependency tesseract.js to ^5.1.0

Co-authored-by: CI <project_7_bot_1bfaee5701aed20091a86249a967a6c1@noreply.firefish.dev>

See merge request firefish/firefish!10821
2024-05-11 12:12:27 +00:00
naskya 7f277878a6 Merge branch 'renovate/ws-8.x' into 'develop'
chore(deps): update dependency ws to v8.17.0

Co-authored-by: CI <project_7_bot_1bfaee5701aed20091a86249a967a6c1@noreply.firefish.dev>

See merge request firefish/firefish!10815
2024-05-11 12:11:49 +00:00
naskya 119cbe3e4f Merge branch 'renovate/bull-board-ui-5.x' into 'develop'
fix(deps): update dependency @bull-board/ui to v5.17.1

Co-authored-by: CI <project_7_bot_1bfaee5701aed20091a86249a967a6c1@noreply.firefish.dev>

See merge request firefish/firefish!10818
2024-05-11 12:10:23 +00:00
naskya 2e15165117 Merge branch 'renovate/rollup-4.x' into 'develop'
chore(deps): update dependency rollup to v4.17.2

Co-authored-by: CI <project_7_bot_1bfaee5701aed20091a86249a967a6c1@noreply.firefish.dev>

See merge request firefish/firefish!10813
2024-05-11 12:09:49 +00:00
naskya 1b526c651e Merge branch 'renovate/aws-sdk-2.x' into 'develop'
fix(deps): update dependency aws-sdk to v2.1618.0

Co-authored-by: CI <project_7_bot_1bfaee5701aed20091a86249a967a6c1@noreply.firefish.dev>

See merge request firefish/firefish!10820
2024-05-11 11:57:14 +00:00
naskya afe06edd16
ci: disable scans for now 2024-05-11 20:49:18 +09:00
naskya 3bdf4f9f9c
ci: GitLab CI's cache is slow 2024-05-11 20:35:01 +09:00
naskya 567ba873e3 Merge branch 'renovate/vue-monorepo' into 'develop'
chore(deps): update vue monorepo to v3.4.27

Co-authored-by: CI <project_7_bot_1bfaee5701aed20091a86249a967a6c1@noreply.firefish.dev>

See merge request firefish/firefish!10808
2024-05-11 11:19:43 +00:00
naskya 6eef39158e
ci: ignore unrelated files in sast 2024-05-11 19:55:12 +09:00
naskya f81739b8d1
container: cargo fetch using root Cargo.toml 2024-05-11 19:30:00 +09:00
naskya ff7fffc711
container: update Dockerfile to use cargo cache on deps updates 2024-05-11 19:19:28 +09:00
naskya daade3865b Merge branch 'renovate/nodemailer-6.x' into 'develop'
chore(deps): update dependency @types/nodemailer to v6.4.15

Co-authored-by: CI <project_7_bot_1bfaee5701aed20091a86249a967a6c1@noreply.firefish.dev>

See merge request firefish/firefish!10804
2024-05-11 09:48:00 +00:00
naskya 7b24210bd8
ci: add variables 2024-05-11 18:47:01 +09:00
naskya d77da088f8
ci: tweak config 2024-05-11 18:38:02 +09:00
naskya d6541a3ebb Merge branch 'renovate/jsrsasign-10.x' into 'develop'
chore(deps): update dependency @types/jsrsasign to v10.5.14

Co-authored-by: CI <project_7_bot_1bfaee5701aed20091a86249a967a6c1@noreply.firefish.dev>

See merge request firefish/firefish!10803
2024-05-11 08:48:59 +00:00
naskya 4fbf211e96
ci: add missing file copy 2024-05-11 10:35:00 +09:00
naskya 132615958b
ci: revise conditions 2024-05-11 10:32:10 +09:00
naskya 27c4b4c812
ci: fix typo 2024-05-11 10:28:39 +09:00
naskya 99f5063f4c
ci: reduce cargo builds 2024-05-11 10:27:09 +09:00
CI ef706fff9d chore(deps): update dependency execa to v9 2024-05-11 00:12:40 +00:00
CI 84528680df fix(deps): update dependency tesseract.js to ^5.1.0 2024-05-11 00:12:21 +00:00
CI 11b5f5cc17 fix(deps): update dependency aws-sdk to v2.1618.0 2024-05-11 00:11:48 +00:00
CI da752a158c fix(deps): update dependency ajv to v8.13.0 2024-05-11 00:11:28 +00:00
CI 693c9edb10 fix(deps): update dependency @bull-board/ui to v5.17.1 2024-05-11 00:11:09 +00:00
CI 4110842357 chore(deps): update dependency ws to v8.17.0 2024-05-11 00:10:10 +00:00
CI 8656bdb185 chore(deps): update dependency rollup to v4.17.2 2024-05-11 00:09:25 +00:00
CI af4426653e chore(deps): update dependency eslint to ^9.2.0 2024-05-11 00:09:02 +00:00
CI 3750ca426b fix(deps): update dependency systeminformation to v5.22.8 2024-05-11 00:08:21 +00:00
CI 3a8ca2a2d7 fix(deps): update dependency otpauth to ^9.2.4 2024-05-11 00:08:02 +00:00
CI ace081f163 chore(deps): update vue monorepo to v3.4.27 2024-05-11 00:07:41 +00:00
CI 184b0e4019 chore(deps): update dependency vite to v5.2.11 2024-05-11 00:06:56 +00:00
CI bec62cffc6 chore(deps): update dependency cropperjs to v2.0.0-beta.5 2024-05-11 00:06:36 +00:00
CI d5493f8e5d chore(deps): update dependency @types/nodemailer to v6.4.15 2024-05-11 00:06:17 +00:00
CI 1cb64b7fa8 chore(deps): update dependency @types/jsrsasign to v10.5.14 2024-05-11 00:05:58 +00:00
naskya 8f59f26aa0
ci: disable nodejs-scan
it doesn't work very well with this repository :(
2024-05-11 09:03:31 +09:00
naskya 6f6333f094
ci: edit sast config 2024-05-11 08:50:13 +09:00
naskya 96cbc6799c
ci: add container scanning 2024-05-11 08:41:33 +09:00
naskya d4f1e06535
ci: add sast-ruleset.toml 2024-05-11 07:59:37 +09:00
naskya f9e2bd2448
ci: enable Static Application Security Testing 2024-05-11 07:26:53 +09:00
naskya b07dc87af6
container: reorder build operations to use cargo build cache on deps updates 2024-05-11 05:39:09 +09:00
naskya aa266d91e0
chore (backend-rs): impl From<Acct> for String 2024-05-11 04:54:30 +09:00
naskya 8f8d62aa58
chore (backend): organize imports 2024-05-11 04:52:59 +09:00
naskya d1b33ad76f
chore (backend-rs): move acct to another directory 2024-05-11 04:31:59 +09:00
naskya eeb09028bd
docs: fix indent 2024-05-11 04:23:20 +09:00
naskya ded0de27c5 Merge branch 'renovate/swc-monorepo' into 'develop'
chore(deps): update dependency @swc/core to v1.5.5

Co-authored-by: CI <project_7_bot_1bfaee5701aed20091a86249a967a6c1@noreply.firefish.dev>

See merge request firefish/firefish!10802
2024-05-10 12:40:32 +00:00
naskya ac57e4c019
ci: update config 2024-05-10 21:39:13 +09:00
CI 291990d320 chore(deps): update dependency @swc/core to v1.5.5 2024-05-10 12:09:21 +00:00
naskya 2ca7bd65aa
ci: update config 2024-05-10 21:05:34 +09:00
naskya fb4e449139
ci: update renovate config 2024-05-10 21:00:29 +09:00
naskya 084c7f1c84 Merge branch 'renovate/biomejs-biome-1.x' into 'develop'
chore(deps): update dependency @biomejs/biome to v1.7.3

Co-authored-by: CI <project_7_bot_1bfaee5701aed20091a86249a967a6c1@noreply.firefish.dev>

See merge request firefish/firefish!10800
2024-05-10 07:52:04 +00:00
naskya 421030c38f
ci: fix rules 2024-05-10 16:34:58 +09:00
naskya f933525856
chore: fix dependencies 2024-05-10 16:25:22 +09:00
CI 9c8e5eabb4 chore(deps): update dependency @biomejs/biome to v1.7.3 2024-05-10 00:32:26 +00:00
naskya 4d3072929e
chore (backend-rs): update doctest comment 2024-05-10 06:59:05 +09:00
naskya 612ce48f44
chore (backend-rs): impl FromStr and Display for Acct 2024-05-10 06:55:51 +09:00
naskya 95fd20a46f
feat (macro-rs): add ts_only_warn macro 2024-05-10 06:54:26 +09:00
naskya 3886c5624b
ci: don't run cargo test with napi feature flag 2024-05-10 06:52:55 +09:00
naskya fc7de024c6
chore: let pnpm detect dependencies 2024-05-10 02:24:25 +09:00
naskya bd88c3399f
chore: update pnpm major version 2024-05-10 01:55:27 +09:00
Hosted Weblate 5b8a164b8d
Merge branch 'origin/develop' into Weblate 2024-05-09 18:21:47 +02:00
jolupa b3cc8cdb3c
locale: update translations (Catalan)
Currently translated at 100.0% (1932 of 1932 strings)

Translation: Firefish/locales
Translate-URL: https://hosted.weblate.org/projects/firefish/locales/ca/
2024-05-09 18:21:46 +02:00
naskya 8373623136
locale: add the Esperanto language 2024-05-10 01:19:13 +09:00
Hosted Weblate d04f85d4bd
Merge branch 'origin/develop' into Weblate 2024-05-09 18:07:17 +02:00
naskya 33853a3a9b
locale: update translations (Japanese)
Currently translated at 100.0% (1932 of 1932 strings)

Translation: Firefish/locales
Translate-URL: https://hosted.weblate.org/projects/firefish/locales/ja/
2024-05-09 18:07:12 +02:00
naskya 26a58c92df
ci: use cargo nextest 2024-05-09 23:19:35 +09:00
naskya 4a81106cf5
chore (backend): remove generate-native-user-token 2024-05-09 21:49:56 +09:00
naskya bdc5d02d27
fix (client): missing import 2024-05-09 19:13:43 +09:00
naskya 075d326d7b Merge branch 'fix/renote-time' into 'develop'
fix: incorrect renote time

Co-authored-by: Lhcfl <Lhcfl@outlook.com>

See merge request firefish/firefish!10798
2024-05-09 09:52:07 +00:00
Lhcfl 0a7f16c11f fix: renote time 2024-05-09 15:49:00 +08:00
naskya 3af8f86924
chore: lint 2024-05-09 02:06:10 +09:00
naskya 276cabbbe3
ci: fix clippy task 2024-05-09 01:15:09 +09:00
naskya af14bee31f
docs: update changelog 2024-05-09 00:41:49 +09:00
naskya b3d1be457b Merge branch 'fix/MkTime' into 'develop'
refactor MkTime: replace the awful ?: chain with if-else; fix: force update ticker when props.time changed

Co-authored-by: Lhcfl <Lhcfl@outlook.com>

See merge request firefish/firefish!10797
2024-05-08 12:11:31 +00:00
Hosted Weblate 347851d6bb
Merge branch 'origin/develop' into Weblate 2024-05-08 10:21:46 +02:00
jolupa abec71074b
locale: update translations (Catalan)
Currently translated at 100.0% (1930 of 1930 strings)

Translation: Firefish/locales
Translate-URL: https://hosted.weblate.org/projects/firefish/locales/ca/
2024-05-08 10:21:45 +02:00
Lhcfl 272e30be0c refactor: replace the awful ?: chain with if-else; fix: force update ticker when props.time changed
related: ed6f866a4f

Co-authored-by: kakkokari-gtyih <kakkokari-gtyih@users.noreply.github.com>
2024-05-08 10:52:32 +08:00
naskya 971f196627
ci: yet another fix 2024-05-08 08:27:54 +09:00
naskya 8cc0e40d35
ci: remove more unneeded paths 2024-05-08 07:16:32 +09:00
naskya beeea86253
ci: remove unneeded steps from clippy check 2024-05-08 06:54:43 +09:00
naskya 084a4bc63a
ci: add pull_policy 2024-05-08 06:46:41 +09:00
naskya cda31d3dc7
Revert "refactor (backend): port publishNotesStream to backend-rs"
This reverts commit 5382dc5da8.

It turns out this sends an inccorect time info to the stream
since JavaScript's Date object doesn't have timezone info

I'll revisit this in the future
2024-05-08 06:08:26 +09:00
naskya 907578e8f8
ci: fix config error 2024-05-08 05:28:41 +09:00
naskya 2923ea86de
ci: update workflow rules 2024-05-08 05:26:59 +09:00
naskya 226c990385
ci: use buildah caches 2024-05-08 05:26:36 +09:00
naskya 769f52c8ee Merge branch 'fix/reactive' into 'develop'
fix: use reactive MkTime

Co-authored-by: Lhcfl <Lhcfl@outlook.com>

See merge request firefish/firefish!10796
2024-05-07 19:59:12 +00:00
naskya 8a00d82f36
ci: add firefish-js 2024-05-08 04:49:13 +09:00
naskya 34ed877f57
ci: don't build the backend on client-only changes 2024-05-08 04:41:20 +09:00
Lhcfl f5074f35cc fix: use reactive MkTime 2024-05-08 03:00:07 +08:00
naskya a847dd55ad
ci: fix cargo clippy task 2024-05-08 03:58:21 +09:00
naskya 5382dc5da8
refactor (backend): port publishNotesStream to backend-rs 2024-05-08 02:15:07 +09:00
naskya 989e93f2a0
fix: migrate back from happy-dom to JSDOM (closes #10924 #10914 #10842)
this reverts commit 4565867b8b.
2024-05-08 01:52:15 +09:00
naskya df81cb6a85 Merge branch 'feat/collepse-reply-timeline' into 'develop'
feat: collepse renotes and replies in timeline

Co-authored-by: Lhcfl <Lhcfl@outlook.com>

Closes #10908

See merge request firefish/firefish!10788
2024-05-07 16:20:45 +00:00
Lhcfl 31168cc7b2 fix: use reacive MkSubNoteContent 2024-05-07 23:42:40 +08:00
Lhcfl 42886f054d fix: use reactive previewableCount 2024-05-07 23:31:45 +08:00
Lhcfl 1d0ea11eea fix: use note capture in MkNoteSimple 2024-05-07 23:23:19 +08:00
Lhcfl 24602c4745 update locales 2024-05-07 22:49:09 +08:00
Lhcfl 33923a59fa fix: use reactive MkNoteHeader 2024-05-07 22:37:09 +08:00
Lhcfl 8067ed4084 Merge branch 'develop' of https://firefish.dev/firefish/firefish into feat/collepse-reply-timeline 2024-05-07 22:34:45 +08:00
naskya 4277ad0b59
meta: update COPYING & include LICENSE in pre-built images 2024-05-07 20:54:47 +09:00
naskya fc65d8c1c3
docs: update api-change.md 2024-05-07 20:52:11 +09:00
naskya 3b3d457c3e
ci: restrict paths 2024-05-07 18:34:18 +09:00
naskya 1128e243d3
container: fix dockerignore 2024-05-07 18:01:05 +09:00
naskya 39e08f57e8
ci: remove unneeded argument 2024-05-07 18:01:05 +09:00
naskya 09ef642905
ci: skip builds if unneeded 2024-05-07 17:36:23 +09:00
naskya 1b8748bc8c
another attempt to build an image inside container inside container 2024-05-07 17:30:57 +09:00
naskya 82c98ae72f
ci: modify buildah args 2024-05-07 07:26:33 +09:00
naskya 5b3f93457b
dev: add renovate 2024-05-07 06:58:00 +09:00
naskya 4d9c0f8e7b
ci: fix syntax 2024-05-07 06:11:31 +09:00
naskya bf2b624bc9
ci: build OCI container image on develop 2024-05-07 05:52:43 +09:00
naskya 5261eb24b6
ci: restrict project path 2024-05-07 05:26:05 +09:00
naskya d440e9b388
ci: revise tasks 2024-05-07 04:58:59 +09:00
naskya 14b285f882 Merge branch 'refactor/is-safe-url' into 'develop'
refactor (backend): port isValidUrl to backend-rs


See merge request firefish/firefish!10795
2024-05-06 17:11:51 +00:00
naskya baa5c402db
ci: apt-get update first & fix paths 2024-05-07 01:54:29 +09:00
naskya 5b01d3574f
refactor (backend): port isValidUrl to backend-rs 2024-05-07 00:56:37 +09:00
naskya e3a98ebc72 Merge branch 'userLang' into 'develop'
Add server-side per-user UI language

Co-authored-by: eana <coder@apps.1a23.com>

See merge request firefish/firefish!10793
2024-05-06 15:31:18 +00:00
naskya 7fe7f90350
ci: revise build config 2024-05-07 00:22:51 +09:00
naskya 8ed942e00f
chore: update auto-generated files 2024-05-06 23:13:31 +09:00
naskya ddfdd038ad
chore: update downgrade.sql 2024-05-06 23:10:39 +09:00
naskya 7fdd44cf8d
locale: update translations 2024-05-06 23:07:57 +09:00
naskya 0c4826becf
dev: copy backend-rs/index.{js,d.ts} to built/index.{js,d.ts} if not exist
https://firefish.dev/firefish/firefish/-/merge_requests/10780#note_5685
2024-05-06 22:54:10 +09:00
naskya ecd8e3d109
ci: remove git clean flags 2024-05-06 22:51:42 +09:00
naskya a3b156441a
ci: temporary fix for cargo test failure due to missing meta.json 2024-05-06 19:38:35 +09:00
naskya ecbd8a8724
ci: save node_modules and target 2024-05-06 19:23:43 +09:00
naskya 442dc33a34
ci: exec build & cargo test only for now 2024-05-06 19:08:28 +09:00
naskya c8372767fa
ci: attempt to fix permission 2024-05-06 19:00:34 +09:00
naskya 8e497b41cf
messed up 2024-05-06 18:44:28 +09:00
naskya bfdf73caeb
ci: fix permisson 2024-05-06 18:38:54 +09:00
naskya 5b18f9761c
ci: fix .git 2024-05-06 18:29:31 +09:00
naskya 641ff742bb
ci: add dependencies of sea-orm-cli 2024-05-06 18:26:50 +09:00
naskya e6121946aa
ci: another fix 2024-05-06 18:11:30 +09:00
naskya c6212ff8f4
ci: use CI_JOB_TOKEN 2024-05-06 18:06:56 +09:00
naskya d582a84c57
ci: install postgresql client 2024-05-06 17:58:26 +09:00
naskya a7978e2b08
ci: non-interactive shell option 2024-05-06 17:46:45 +09:00
naskya 766bac3dee
ci: give alias for services 2024-05-06 17:14:47 +09:00
naskya 7360736966
ci: fix typo 2024-05-06 17:07:42 +09:00
naskya e797849e9b
ci: attempt to add a CI task for merge requests 2024-05-06 17:00:36 +09:00
eana ef57735e6a fix typo 2024-05-06 05:26:38 +00:00
eana e7c33835b2 Add server-side per-user UI language 2024-05-06 05:14:44 +00:00
naskya 4e83dbd01f Merge branch 'refactor/remove-gulp' into 'develop'
refactor: replace gulp with a simple script


See merge request firefish/firefish!10791
2024-05-06 04:45:17 +00:00
naskya dd74eabae1
refactor (backend): port nodeinfo fetcher to backend-rs 2024-05-06 08:12:21 +09:00
naskya 711618b42c
test (backend-rs): add tests for nodeinfo (de)serialization 2024-05-06 05:20:13 +09:00
naskya 510207b101
refactor (backend-rs): separate nodeinfo generator and schema 2024-05-06 04:23:38 +09:00
naskya 49825853c1
refactor (backend): port nodeinfo generator to backend-rs 2024-05-06 03:01:55 +09:00
naskya 359fef0a42
chore: replace old comments 2024-05-05 21:22:57 +09:00
naskya fda81a9f91
chore: use absolute path for file operations 2024-05-05 21:04:20 +09:00
naskya c505c6df36
fix: remove old locale files 2024-05-05 20:59:26 +09:00
naskya 9a4a75bf92 Merge branch 'fix/click' into 'develop'
fix: Click event of MenuParent unexpectedly goes to underlying element

Co-authored-by: Lhcfl <Lhcfl@outlook.com>

See merge request firefish/firefish!10792
2024-05-05 11:42:25 +00:00
Linca 5e5d01d407 fix: Click event of MenuParent unexpectedly goes to underlying element
Co-authored-by: Lhcfl <Lhcfl@outlook.com>
2024-05-05 11:42:25 +00:00
naskya d114b8ec1d
chore: format 2024-05-05 14:58:56 +09:00
naskya d2471b6db7
refactor (backend-rs): replace reqwest with isahc
reqwest is feature-rich, but we will need isahc http client for push notifications (!10760)
isahc http client is also good btw :)
2024-05-05 14:53:45 +09:00
naskya 341b43ed71
refactor: replace gulp with a simple script 2024-05-05 02:19:58 +09:00
naskya 6d64358674
fix (client): missing MFM function props not falling back correctly 2024-05-05 01:15:04 +09:00
naskya 4992999bb7
test (backend-rs): add tests 2024-05-04 22:59:49 +09:00
naskya 38c0de39b9
chore (backend-rs): add docs for functions in database/cache 2024-05-04 22:50:46 +09:00
naskya 722d090f8d
chore (backend-rs): remove unneeded 'static 2024-05-04 22:49:11 +09:00
naskya b185c0c87e
feat (backend-rs): add cache::delete_all 2024-05-04 21:24:20 +09:00
naskya 8c22b0d07f
test (backend-rs): fix version format 2024-05-04 16:17:33 +09:00
naskya 0f4c05a64f
ci: add 'ci' feature flag to backend-rs 2024-05-04 16:14:23 +09:00
naskya bc39badf51
chore (client): remove unused code 2024-05-04 16:08:41 +09:00
naskya 1ee8198c6f
v20240504 2024-05-04 16:04:31 +09:00
naskya 15c32d5510
docs: explicitly list Perl as a build dependency 2024-05-04 15:30:05 +09:00
naskya bf3f4906ac
docs: update dev docs 2024-05-04 14:59:11 +09:00
naskya dbe10f88b0
docs: update changelog 2024-05-04 14:54:42 +09:00
naskya 369b1d72df
fix/perf (backend): port latest version check to backend-rs, address excessive requests to firefish.dev 2024-05-04 14:44:20 +09:00
naskya e6ba0a002f
refactor (backend-rs): add cache::{get_one, set_one, delete_one} 2024-05-04 13:22:20 +09:00
naskya 37e03007f0
refactor (backend-rs): misc/redis_cache -> database/cache 2024-05-04 13:22:20 +09:00
naskya f66ecd0759 Merge branch 'fix/follow_me_with_host' into 'develop'
fix: follow-me generate wrong link for other server

Co-authored-by: 老周部落 <laozhoubuluo@gmail.com>

See merge request firefish/firefish!10785
2024-05-03 14:41:37 +00:00
naskya 5ab81c2799
docs: Docker -> Podman 2024-05-03 23:14:41 +09:00
老周部落 8a2f3b3e36
fix: follow-me generate wrong link for other server 2024-05-03 22:13:54 +08:00
naskya f7c576f8fb Merge branch 'fix/10915' into 'develop'
feat: add angle constraint for MkPullToRefresh

Co-authored-by: Lhcfl <Lhcfl@outlook.com>

Closes #10915

See merge request firefish/firefish!10784
2024-05-03 02:20:29 +00:00
Lhcfl 46d0679845 little patch 2024-05-03 00:56:10 +08:00
Lhcfl 4536ac0678 reviewed 2024-05-03 00:28:46 +08:00
Lhcfl 160e7f26a6 feat: collepse renotes and replies 2024-05-03 00:22:25 +08:00
naskya 4a0e4a4c91 Merge branch 'fix/notifications-swiper' into 'develop'
fix: notifications swiper not reload

Co-authored-by: Lhcfl <Lhcfl@outlook.com>

See merge request firefish/firefish!10782
2024-05-02 13:55:41 +00:00
naskya a8f659ab88
chore: format 2024-05-02 21:46:14 +09:00
naskya 64c07a2406
fix (backend): tell TypeORM that some columns are no longer indexed
should have done in caae8474a6
2024-05-02 21:45:58 +09:00
naskya 6e9ae06990 Merge branch 'fix/10918' into 'develop'
fix: escape ambiguous Mfm marks from html

Co-authored-by: Lhcfl <Lhcfl@outlook.com>

Closes #10918

See merge request firefish/firefish!10786
2024-05-02 11:28:08 +00:00
naskya caae8474a6
chore (backend): drop unused database indexes
Based on the PostgreSQL analitics on the following servers' database:

- dvd.chat
- iwshkey.com
- minazukey.uk
- post.naskya.net
- post.sup39.dev
- stelpolva.moe

Thank you all for your helps!
2024-05-02 19:31:53 +09:00
Lhcfl 9138c3726a dev: use reactiveState in foldNotification 2024-05-02 01:07:57 +08:00
Lhcfl 425b333474 set collapseReplyInTimeline default to false 2024-05-02 00:57:00 +08:00
Lhcfl d1c76b3882 feat: allow collepse replied posts in timeline 2024-05-02 00:53:52 +08:00
naskya d82f212c24
dev: update Makefile 2024-05-01 17:48:37 +09:00
naskya f85e6ebb19 Merge branch 'dontusetoreverse' into 'develop'
dev: use reverse instead of toReversed

Co-authored-by: Lhcfl <Lhcfl@outlook.com>

See merge request firefish/firefish!10787
2024-05-01 05:10:01 +00:00
Linca 587c64a906 dev: use reverse instead of toReversed
Co-authored-by: Lhcfl <Lhcfl@outlook.com>
2024-05-01 05:10:01 +00:00
Lhcfl a4a96f0026 fix: escape ambiguous Mfm marks from html 2024-05-01 11:47:14 +08:00
Hosted Weblate c0c5cb92cd
Merge branch 'origin/develop' into Weblate 2024-05-01 01:23:42 +02:00
Gary O'Regan Kelly fbfbffcb72
locale: update translations (French)
Currently translated at 100.0% (1927 of 1927 strings)

Co-authored-by: Gary O'Regan Kelly <gmoregan@icloud.com>
Translate-URL: https://hosted.weblate.org/projects/firefish/locales/fr/
Translation: Firefish/locales
2024-05-01 01:23:34 +02:00
Lhcfl 5ad9bd8ceb feat: add angle constraint for MkPullToRefresh 2024-04-30 19:51:40 +08:00
naskya c345d80f4a
container: make sure to use the correct backend-rs/built/index.js 2024-04-30 20:50:39 +09:00
Lhcfl f40c201670 remove forgotten debug parameter 2024-04-30 19:06:55 +08:00
Lhcfl c8c7abe6ef fix: notifications swiper not reload 2024-04-30 19:04:39 +08:00
naskya 1a46d1394c
docs: fix indent 2024-04-30 08:51:17 +09:00
naskya 32176f86b2
docs: fix typo 2024-04-30 07:11:01 +09:00
naskya 7d02a8852b
v20240430 2024-04-30 06:45:15 +09:00
naskya 1f8745b268
docs: update changelog & notice-for-admins.md 2024-04-30 05:51:12 +09:00
Laura Hausmann e790d6be90
fix (backend): hard code the @context value to avoid possible security risks
Co-authored-by: naskya <m@naskya.net>
2024-04-30 05:34:39 +09:00
naskya 0b6d0b525f
style: slightly increase the CW blur intensity 2024-04-29 19:05:16 +09:00
Hosted Weblate 1e9e5d1c18
Merge branch 'origin/develop' into Weblate 2024-04-29 08:07:20 +02:00
naskya 21e0439b5e
locale: update translations (Japanese)
Currently translated at 100.0% (1927 of 1927 strings)

Translation: Firefish/locales
Translate-URL: https://hosted.weblate.org/projects/firefish/locales/ja/
2024-04-29 08:07:11 +02:00
Linerly 74b843614a
locale: update translations (Indonesian)
Currently translated at 100.0% (1927 of 1927 strings)

Translation: Firefish/locales
Translate-URL: https://hosted.weblate.org/projects/firefish/locales/id/
2024-04-29 08:07:10 +02:00
jolupa 956592147c
locale: update translations (Catalan)
Currently translated at 100.0% (1927 of 1927 strings)

Translation: Firefish/locales
Translate-URL: https://hosted.weblate.org/projects/firefish/locales/ca/
2024-04-29 08:07:08 +02:00
naskya 2bf8eab74a
chore: lint 2024-04-29 11:56:31 +09:00
naskya 54b6e14621 Merge branch 'fix/pagination' into 'develop'
fix: MkPagination init -> reload

Co-authored-by: Lhcfl <Lhcfl@outlook.com>

See merge request firefish/firefish!10779
2024-04-28 20:22:13 +00:00
Lhcfl 5600799261 fix: MkPagination init -> reload 2024-04-28 21:24:54 +08:00
naskya c47d5d70e7
docs: update changelog 2024-04-28 17:25:08 +09:00
naskya f14a5bcb25
chore (minor): update example dev config file 2024-04-28 14:36:32 +09:00
naskya 4fcd5463e9 Merge branch 'feat/fold' into 'develop'
make foldNotification default as it's no longer an experimental feature

Co-authored-by: Lhcfl <Lhcfl@outlook.com>

See merge request firefish/firefish!10774
2024-04-28 01:52:19 +00:00
naskya ce0cec216c Merge branch 'feat/slash-quote' into 'develop'
feat: add slash quote

Co-authored-by: Lhcfl <Lhcfl@outlook.com>

See merge request firefish/firefish!10745
2024-04-28 00:03:20 +00:00
naskya 0272b08df8
locale: update en-US.yml 2024-04-28 09:02:03 +09:00
naskya a44e9f3e38 Merge branch 'addsuccess' into 'develop'
[UX]fix: add os.success for all copyToClipboard

Co-authored-by: Lhcfl <Lhcfl@outlook.com>

See merge request firefish/firefish!10777
2024-04-27 13:23:50 +00:00
Lhcfl 941a31c702 fix: add os.success for all copyToClipboard 2024-04-27 20:45:00 +08:00
naskya 64ca96d759 Merge branch 'fix/ptr' into 'develop'
fix: MkPullToRefresh not work

Co-authored-by: Lhcfl <Lhcfl@outlook.com>

Closes #10913

See merge request firefish/firefish!10776
2024-04-27 12:32:34 +00:00
Lhcfl fe1efed369 fix: MkPullToRefresh not work 2024-04-27 20:25:49 +08:00
Hosted Weblate aed2e9853a
Merge branch 'origin/develop' into Weblate 2024-04-27 08:07:14 +02:00
naskya 50d662d62b
locale: update translations (Japanese)
Currently translated at 100.0% (1926 of 1926 strings)

Translation: Firefish/locales
Translate-URL: https://hosted.weblate.org/projects/firefish/locales/ja/
2024-04-27 08:07:10 +02:00
jolupa 9c3ba0c593
locale: update translations (Catalan)
Currently translated at 100.0% (1926 of 1926 strings)

Translation: Firefish/locales
Translate-URL: https://hosted.weblate.org/projects/firefish/locales/ca/
2024-04-27 08:07:08 +02:00
Lhcfl c2a9b028a3 make foldNotification default as it's no longer an experimental feature 2024-04-27 13:18:01 +08:00
naskya cc560811cf
test (backend-rs): remove ava in favor of cargo test 2024-04-27 11:23:16 +09:00
naskya 28b9e35f2a
style: use yes/no instead of ok/cancel 2024-04-27 11:01:25 +09:00
naskya 51f9d20073
fix (client): don't interpret sample account ID as MFM 2024-04-27 10:58:17 +09:00
naskya 98cc23557f
chore (backend): remove unused value 2024-04-27 10:12:31 +09:00
naskya e5bac649c8
refactor (backend): flatten a type 2024-04-27 10:05:48 +09:00
naskya 53dfec57a8
refactor (backend): remove misc/langmap.ts 2024-04-27 09:15:59 +09:00
naskya 1b143ebfaa
Merge branch 'develop' into refactor/types 2024-04-27 09:09:17 +09:00
naskya 38cd4bafde
refactor (backend): port publishGroupMessagingStream to backend-rs 2024-04-27 08:36:12 +09:00
naskya d880601d56
container: install perl on build 2024-04-27 07:05:10 +09:00
naskya c89525d151
container (minor): update Dockerfile 2024-04-27 06:09:05 +09:00
naskya 2ccc45cfad
docs: update changelog 2024-04-27 06:04:22 +09:00
naskya aae505ad68
fix (backend): correctly fallback maxLogLevel to info 2024-04-27 05:48:56 +09:00
naskya f80ee9f36d
chore: update TypeScript dependencies
except for pnpm, gulp, adm-zip, @syuilo/aiscript, and Megalodon
2024-04-27 05:42:37 +09:00
naskya efcadf681f
chore: update Rust dependencies 2024-04-27 05:39:14 +09:00
naskya cd3c6a52dd
Merge branch 'develop' into feat/slash-quote 2024-04-27 05:20:02 +09:00
naskya a7a47f7d3b Merge branch 'feat/fold' into 'develop'
feat: rewrite MkPagination for fold

Co-authored-by: Lhcfl <Lhcfl@outlook.com>

See merge request firefish/firefish!10772
2024-04-26 20:12:11 +00:00
Lhcfl 564eb08386 feat: rewrite MkPagination for fold 2024-04-26 22:39:58 +08:00
Hosted Weblate dc8ddff007
Merge branch 'origin/develop' into Weblate 2024-04-26 07:16:01 +02:00
naskya 54f100032e
locale: update translations (Japanese)
Currently translated at 100.0% (1926 of 1926 strings)

Translation: Firefish/locales
Translate-URL: https://hosted.weblate.org/projects/firefish/locales/ja/
2024-04-26 07:15:54 +02:00
Linerly 98048699c1
locale: update translations (Indonesian)
Currently translated at 100.0% (1926 of 1926 strings)

Translation: Firefish/locales
Translate-URL: https://hosted.weblate.org/projects/firefish/locales/id/
2024-04-26 07:15:53 +02:00
naskya 37cf4f8361
chore (backend-rs): tweak logger config 2024-04-26 13:51:28 +09:00
naskya d40db1ee7e
refactor (backend): port publishBroadcastStream to backend-rs 2024-04-26 13:30:13 +09:00
naskya 0caba566e6
refactor (backend): port publishMessagingIndexStream to backend-rs 2024-04-26 13:11:11 +09:00
naskya 883645a581
chore (backend-rs): regenerate index.d.ts 2024-04-26 12:14:34 +09:00
naskya 3190f66740
chore (backend): convert TypeORM's simple-array into normal array 2024-04-26 12:03:12 +09:00
Lhcfl 8b73a1a6b3 revert changes of vite.config.ts 2024-04-26 10:37:02 +08:00
Lhcfl 41f9a0bda9 fix: Unrecognized language causes errors 2024-04-26 10:34:12 +08:00
Lhcfl 99f30ba01a change firefish-js to esm 2024-04-26 10:22:51 +08:00
Hosted Weblate bfcadaa094
Merge branch 'origin/develop' into Weblate 2024-04-26 00:31:59 +02:00
Salif Mehmed 18439f6e27
locale: update translations (Bulgarian)
Currently translated at 44.8% (864 of 1926 strings)

Translation: Firefish/locales
Translate-URL: https://hosted.weblate.org/projects/firefish/locales/bg/
2024-04-26 00:31:58 +02:00
naskya f806c47c7c
style: move QR code and remote follow URL to another category 2024-04-26 07:30:41 +09:00
naskya b58d940e71
fix (backend-rs): add openssl as a dependency 2024-04-26 07:13:09 +09:00
naskya 42f704b515
chore (backend-rs): remove whitespaces 2024-04-26 06:51:52 +09:00
naskya 8534154792
chore (backend-rs): add comment 2024-04-26 06:30:30 +09:00
naskya 1cfe3bfb73
refactor (backend): port publishChannelStream to backend-rs 2024-04-26 06:16:51 +09:00
naskya ba8e044f42
chore: format 2024-04-26 05:54:10 +09:00
naskya 79ab7bf787
refactor (backend): port publishAdminStream to backend-rs 2024-04-26 05:44:37 +09:00
naskya 23e57737a6 Merge branch 'feat/fold' into 'develop'
feat: For NotificationFold posts, set a smaller collepsing threshold

Co-authored-by: Lhcfl <Lhcfl@outlook.com>

See merge request firefish/firefish!10768
2024-04-25 19:14:51 +00:00
naskya 5c60e030e8 Merge branch 'refactor/get-image-size' into 'develop'
refactor (backend): port emoji-meta to backend-rs


See merge request firefish/firefish!10769
2024-04-25 19:04:39 +00:00
naskya 0512dca83c
fix (backend): import .js 2024-04-26 03:22:07 +09:00
naskya 1757560f36
Merge branch 'develop' into refactor/get-image-size 2024-04-26 02:21:25 +09:00
naskya 154ae05fb7 Merge branch 'chore/remove_unused_config' into 'develop'
chore: remove unused id config

Co-authored-by: 老周部落 <laozhoubuluo@gmail.com>

See merge request firefish/firefish!10770
2024-04-25 17:07:41 +00:00
naskya 8e5d31b606 Merge branch 'feat/qr_code' into 'develop'
feat: url and qr codes for account sharing

Co-authored-by: 老周部落 <laozhoubuluo@gmail.com>
Co-authored-by: Sal Rahman <salehen.rahman@gmail.com>

Closes #10474

See merge request firefish/firefish!10761
2024-04-25 17:04:03 +00:00
naskya 2d15d6dcfa
refactor (client): clean up conditions a bit 2024-04-26 02:03:15 +09:00
naskya 171e2f3973
chore: update lockfile 2024-04-26 01:56:43 +09:00
naskya fd40c3dd2e
locale: update en-US.yml 2024-04-26 01:51:48 +09:00
naskya 3b172a7762
chore (backend-rs): fix typo 2024-04-26 01:30:16 +09:00
naskya 13b648f6bf
fix (backend-rs): use proxy and proxyBypassHosts config 2024-04-26 01:27:23 +09:00
naskya 320f933e9d
chore (backend): to be on the safe side 2024-04-26 01:04:40 +09:00
Lhcfl fec1a800b6 Merge branch 'develop' of https://firefish.dev/firefish/firefish into refactor/types 2024-04-25 21:13:55 +08:00
老周部落 22b52ac3d3
feat: follow-me 2024-04-25 20:45:41 +08:00
老周部落 af109b45ef
locale: update translations 2024-04-25 20:40:59 +08:00
老周部落 ab357233db
chore: format 2024-04-25 20:40:18 +08:00
Sal Rahman 6aecf12067
QR code update 2024-04-25 20:38:31 +08:00
Sal Rahman fefed2d4a4
FfQrCode -> MkQrCode 2024-04-25 20:38:31 +08:00
Sal Rahman bb984a8608
Some work on the popup
Some more work on the modal

Some more work done

Some initial work on adding the QR code

Some more changes

Done
2024-04-25 20:38:30 +08:00
老周部落 5de6e07dfa
chore: remove unused id config 2024-04-25 20:34:31 +08:00
naskya 9a077c4beb Merge branch 'fix/edit-visibility' into 'develop'
fix: Client's strange behavior about visibility when editing a post

Co-authored-by: Lhcfl <Lhcfl@outlook.com>

See merge request firefish/firefish!10763
2024-04-25 10:37:13 +00:00
naskya 7c827682dd
locale: update en-US.yml 2024-04-25 19:36:37 +09:00
naskya 0b7385e16a
chore: format 2024-04-25 19:15:12 +09:00
naskya 214e999c8c
test (backend-rs): add test against animated gif 2024-04-25 19:14:16 +09:00
naskya b12d7e4c63
refactor (backend): port emoji-meta to backend-rs 2024-04-25 19:06:16 +09:00
Lhcfl 27c46d7df9 feat: For notification posts, set a smaller folding threshold 2024-04-25 15:53:29 +08:00
naskya b9c3dfbd3d
refactor? (backend): move consts to backend-rs 2024-04-25 14:50:04 +09:00
naskya 5891a90f71 Merge branch 'feat/fold' into 'develop'
feat: fold notifications

Co-authored-by: Lhcfl <Lhcfl@outlook.com>

Closes #10908

See merge request firefish/firefish!10767
2024-04-25 04:10:41 +00:00
naskya d82ad33730 Merge branch 'fix/import-custom-emojis' into 'develop'
fix: #10451 Emoji pack imports randomly stop processing files

Co-authored-by: Linca <lhcfllinca@gmail.com>
Co-authored-by: Lhcfl <Lhcfl@outlook.com>

Closes #10451

See merge request firefish/firefish!10757
2024-04-25 03:56:09 +00:00
naskya cdea55ab25
chore (backend): revise apLogger log levels (#10907) 2024-04-25 11:42:16 +09:00
naskya 76a45fa6ac
chore (backend): move download logs to debug 2024-04-25 11:08:45 +09:00
naskya d086b7b993
chore (backend): failed deliver logs should be info 2024-04-25 10:58:31 +09:00
naskya aa2b35a554
chore (backend): proper streaming logs 2024-04-25 10:53:27 +09:00
Lhcfl 19b94b2fb8 tiny style fix 2024-04-25 09:42:03 +08:00
Lhcfl e5a21bae13 use MkNote insteadof MkNoteSub 2024-04-25 09:32:59 +08:00
Lhcfl adafd62710 chore: format 2024-04-25 09:17:37 +08:00
Lhcfl 217d686997 fix: improve the display of Notes in notifications 2024-04-25 09:17:01 +08:00
naskya 92b1f5cb5f
refactor (minor, backend-rs): rearrange conditions 2024-04-25 10:00:58 +09:00
Lhcfl 42f6a6e3b6 reviewed 2024-04-25 08:55:18 +08:00
naskya 40a4109c76
chore (backend): add FIXME comment to questionable if statements 2024-04-25 09:36:22 +09:00
naskya 24e6e31b2b
fix (backend): update note_file table by post imports 2024-04-25 08:22:30 +09:00
naskya 7441f0861a
fix (backend): fix order of logger initialization 2024-04-25 08:15:40 +09:00
naskya a2316b7caf
chore (backend): move SQL query logs to trace level (#10907) 2024-04-25 08:05:45 +09:00
naskya 322b2392de
chore (backend): revise queue logs (#10907) 2024-04-25 08:03:53 +09:00
naskya bf9ab9c1ca
chore (backend-rs): add logs 2024-04-25 07:03:48 +09:00
naskya 339cbac191
chore (backend-rs): remove unneeded conversion 2024-04-25 03:10:51 +09:00
naskya bb5349d127
chore (client): use plural 2024-04-25 03:04:59 +09:00
naskya 93e55f146b
docs: update notice-for-admins.md 2024-04-25 02:27:59 +09:00
naskya eac0c1c47c
refactor (backend): revise log levels
* Add logging in backend-rs
* Change log levels to 'error', 'warning', 'info', 'debug', 'trace'
* Add `maxLogLevel` config and deprecate `logLevels`
* Move SQL queries to 'trace' level
2024-04-25 02:22:49 +09:00
Lhcfl 44ad4bce32 increase FETCH_LIMIT 2024-04-24 22:21:49 +08:00
Lhcfl 861b66c6ad update translations 2024-04-24 22:15:38 +08:00
Lhcfl feafaffd12 remove duplicate i18n 2024-04-24 21:40:48 +08:00
Lhcfl 7e9633a36b feat: fold notifications 2024-04-24 21:33:56 +08:00
naskya e67b3e8ad5 Merge branch 'fix/user_menu' into 'develop'
fix: user menu not display to guest because of 'id' not exist

Co-authored-by: 老周部落 <laozhoubuluo@gmail.com>

See merge request firefish/firefish!10766
2024-04-24 12:36:18 +00:00
老周部落 dbad2a485f
fix: user menu not display to guest because of 'id' not exist 2024-04-24 19:29:24 +08:00
naskya 4816d75e23
v20240424 2024-04-24 15:42:23 +09:00
naskya 9bf17dcb3c
docs: update changelog 2024-04-24 15:32:00 +09:00
Hosted Weblate ac9a07d25a
Merge branch 'origin/develop' into Weblate 2024-04-24 08:08:56 +02:00
老周部落 89a8547cb1
locale: update translations (Chinese (Simplified))
Currently translated at 100.0% (1918 of 1918 strings)

Translation: Firefish/locales
Translate-URL: https://hosted.weblate.org/projects/firefish/locales/zh_Hans/
2024-04-24 08:08:50 +02:00
jolupa f895e002ea
locale: update translations (Catalan)
Currently translated at 100.0% (1918 of 1918 strings)

Translation: Firefish/locales
Translate-URL: https://hosted.weblate.org/projects/firefish/locales/ca/
2024-04-24 08:08:49 +02:00
naskya fbd980aeb8 Merge branch 'fix/reply-count' into 'develop'
fix: Replying to a post sometimes doesn't increase post's reply count

Co-authored-by: Lhcfl <Lhcfl@outlook.com>

See merge request firefish/firefish!10764
2024-04-24 06:02:37 +00:00
Linca 71e4621e26 fix: Replying to a post sometimes doesn't increase post's reply count
Co-authored-by: Lhcfl <Lhcfl@outlook.com>
2024-04-24 06:02:37 +00:00
naskya cc37177bb5
fix (backend-rs): use server's local time 2024-04-24 13:43:18 +09:00
naskya 109884f6d8
refactor (backend): port note watch/unwatch to backend-rs 2024-04-24 13:23:27 +09:00
Lhcfl 3bed093344 fix: Visible users are not automatically filled when editing 2024-04-24 11:36:56 +08:00
Lhcfl 243adaaa0d feat: disable visibilityPicker for edit 2024-04-24 11:36:28 +08:00
naskya 879d499486
refactor (backend-rs): never throw an error on ID generation 2024-04-24 07:37:16 +09:00
naskya 9db729d734
refactor (backend-rs): separate gen_id and gen_id_at 2024-04-24 07:02:06 +09:00
naskya a2958f6da8
refactor (backend): port daemons/janitor.ts to backend-rs 2024-04-24 06:39:40 +09:00
naskya 9ef5350a00
chore (backend): remove promisify 2024-04-24 05:19:03 +09:00
naskya e8b39be387
chore (backend): remove unused function 2024-04-24 05:01:35 +09:00
naskya 88280e3bfd
refactor (backend): port publishMessagingStream to backend-rs 2024-04-24 04:13:29 +09:00
naskya ee04e30f25
chore (backend-rs): remove unused stuff
it should be reworked
2024-04-24 03:01:47 +09:00
Lhcfl 267670af96 move schema & langmap from backend to firefish-js 2024-04-24 02:00:34 +08:00
Lhcfl ea60895bf8 Merge branch 'develop' of https://firefish.dev/firefish/firefish into refactor/types 2024-04-24 00:13:07 +08:00
Lhcfl 5994a1d615 fix types 2024-04-24 00:02:28 +08:00
naskya 9eccdba075
chore (backend-rs): move add_note_to_antenna to misc 2024-04-24 00:23:13 +09:00
naskya cac438b965 Merge branch 'fix/use-pagination-in-note' into 'develop'
fix: use pagination in note

Co-authored-by: Lhcfl <Lhcfl@outlook.com>

Closes #10906

See merge request firefish/firefish!10754
2024-04-23 14:59:40 +00:00
Lhcfl 067810b1be
docs (minor): update api-change.md 2024-04-23 23:59:03 +09:00
naskya 50b7c71ed6
chore (backend): use type import 2024-04-23 23:56:34 +09:00
naskya 8f68693510 Merge branch 'fix/authorize-follow' into 'develop'
fix: authorize-follow not working due to redeclare 'acct'

Co-authored-by: 老周部落 <laozhoubuluo@gmail.com>

See merge request firefish/firefish!10759
2024-04-23 14:55:04 +00:00
Lhcfl 9a42745926 fix soft mute languages 2024-04-23 22:41:18 +08:00
Lhcfl c0afa4a2f7 fix types 2024-04-23 22:40:57 +08:00
Lhcfl 62f5c84ca6 fix types 2024-04-23 21:30:55 +08:00
老周部落 6a0ad409cd
fix: authorize-follow not working due to redeclare 'acct' 2024-04-23 20:07:52 +08:00
Lhcfl 850201ff71 Merge branch 'develop' of https://firefish.dev/firefish/firefish into refactor/types 2024-04-23 18:37:50 +08:00
Lhcfl 6f324a3dcd refactor: move ColdDeviceStorage into a module 2024-04-23 16:56:39 +08:00
naskya ccd26a826f
locale: update translations (Japanese)
Currently translated at 100.0% (1918 of 1918 strings)

Translation: Firefish/locales
Translate-URL: https://hosted.weblate.org/projects/firefish/locales/ja/
2024-04-23 08:10:34 +02:00
Lhcfl 4bb97f2a3c Merge branch 'develop' of https://firefish.dev/firefish/firefish into fix/use-pagination-in-note 2024-04-23 10:45:50 +08:00
Lhcfl 93bee484bb fix types of pizzax.ts 2024-04-23 10:44:06 +08:00
Lhcfl 07d39cb5ac Merge branch 'develop' of https://firefish.dev/firefish/firefish into refactor/types 2024-04-23 10:28:11 +08:00
Hosted Weblate 192929f0f4
Merge branch 'origin/develop' into Weblate 2024-04-23 04:05:25 +02:00
Salif Mehmed be2837551c
locale: update translations (Bulgarian)
Currently translated at 44.6% (855 of 1916 strings)

Translation: Firefish/locales
Translate-URL: https://hosted.weblate.org/projects/firefish/locales/bg/
2024-04-23 04:05:24 +02:00
naskya d9e46f7fa6 Merge branch 'refactor/isSignedIn' into 'develop'
refactor: make isSignedIn a function

Co-authored-by: Lhcfl <Lhcfl@outlook.com>

See merge request firefish/firefish!10758
2024-04-22 18:10:00 +00:00
naskya c6d0fe52d2
chore (client): remove unnecessary non-null assertions 2024-04-22 22:44:43 +09:00
naskya 08926ceb8c
dev (backend-rs): add basic Redis cache setter/getter 2024-04-22 21:56:38 +09:00
Lhcfl a49f9c33ef chore: format 2024-04-22 11:02:35 +08:00
Lhcfl 1d3b67eafb fix types of pizzax 2024-04-22 11:01:46 +08:00
Lhcfl 29da813170 fix typo 2024-04-22 10:54:16 +08:00
Lhcfl c5a344c2a0 refactor: make isSignedIn a function 2024-04-22 10:36:02 +08:00
Lhcfl a856523119 fix: add slashquote in MkRenoteButton 2024-04-22 09:09:39 +08:00
Lhcfl 83e3f96ced Merge branch 'develop' of https://firefish.dev/firefish/firefish into feat/slash-quote 2024-04-22 09:01:19 +08:00
Linca a52340fa53 fix: disable extensions that cannot be used as emojis 2024-04-22 00:31:38 +00:00
naskya 9acd130a22
chore (backend): update meta cache every 5 mins 2024-04-22 08:57:00 +09:00
naskya 0c1e7cdd72
refactor (backend): port should-block-instance to backend-rs 2024-04-22 08:31:28 +09:00
naskya cbd15fb2ca Merge branch 'redis' into 'develop'
Make Redis accessible from backend-rs


See merge request firefish/firefish!10753
2024-04-21 22:23:29 +00:00
naskya a6498b0491 Merge branch 'feat/alt_warning' into 'develop'
feat(client): attachment without description opened when choose add alt

Co-authored-by: 老周部落 <laozhoubuluo@gmail.com>

See merge request firefish/firefish!10699
2024-04-21 22:20:45 +00:00
naskya 9ced0d96ad
chore (client): don't make a new post as soon as you add descriptions 2024-04-22 07:20:07 +09:00
naskya 9a4988eaad
chore (client): fix type errors 2024-04-22 07:14:56 +09:00
naskya fbdc068115
locale: update en-US.yml 2024-04-22 07:09:54 +09:00
naskya 23ec206aee
Merge branch 'develop' into feat/alt_warning 2024-04-22 06:59:58 +09:00
naskya 07444ae7c1
Merge branch 'develop' into redis 2024-04-22 06:42:53 +09:00
naskya 455ecdf743 Merge branch 'docs/add_dependencies' into 'develop'
docs: add minimum dependencies

Co-authored-by: 老周部落 <laozhoubuluo@gmail.com>

See merge request firefish/firefish!10703
2024-04-21 21:40:34 +00:00
naskya d98c564ead
docs: move the dependencies section to the top 2024-04-22 06:39:20 +09:00
naskya 56aac15a6b
docs (minor): paraphrase a bit 2024-04-22 06:32:07 +09:00
naskya 280dddf464 Merge branch 'fix/download-url-agent' into 'develop'
fix: download-url should use proxy bypass hosts

Co-authored-by: 老周部落 <laozhoubuluo@gmail.com>

See merge request firefish/firefish!10739
2024-04-21 21:27:55 +00:00
naskya 1347c6ff04
Merge branch 'develop' into redis 2024-04-22 06:23:17 +09:00
naskya b3cc01c440 Merge branch 'feat/show-MkRemoteCaution-in-history' into 'develop'
feat: show MkRemoteCaution in note history page

Co-authored-by: Lhcfl <Lhcfl@outlook.com>

See merge request firefish/firefish!10743
2024-04-21 21:23:02 +00:00
naskya ebaefb9697
chore (minor, client): remove redundant attribute 2024-04-22 06:02:08 +09:00
naskya d9982a0b6a
Merge branch 'develop' into feat/show-MkRemoteCaution-in-history 2024-04-22 06:01:19 +09:00
naskya 0cb2e94d99 Merge branch 'fix/feed' into 'develop'
fix: notes in rss feed do not display HTML

Co-authored-by: Lhcfl <Lhcfl@outlook.com>

See merge request firefish/firefish!10744
2024-04-21 21:00:18 +00:00
naskya 78b6228b3c
Merge branch 'develop' into fix/use-pagination-in-note 2024-04-22 05:58:55 +09:00
naskya d1817d9a22 Merge branch 'feat/antenna_limit' into 'develop'
feat: antenna limit

Co-authored-by: 老周部落 <laozhoubuluo@gmail.com>

Closes #10894

See merge request firefish/firefish!10740
2024-04-21 20:58:09 +00:00
naskya c9de5f6095
docs: update api-changes.md 2024-04-22 05:56:46 +09:00
naskya c4658801aa
chore: regenerate entities 2024-04-22 05:54:32 +09:00
naskya a107d8c1ec
fix (backend): update import 2024-04-22 05:52:56 +09:00
naskya 4c91e8e37f
Merge branch 'develop' into feat/antenna_limit 2024-04-22 05:51:22 +09:00
naskya 8140694a31
chore (backend): add comment 2024-04-22 05:47:22 +09:00
naskya 509690d84d
chore: meRenoteCount -> myRenoteCount 2024-04-22 05:43:58 +09:00
naskya b9e88ce490
docs: edit api-changes.md 2024-04-22 05:42:40 +09:00
naskya dc53447fa3
chore (API): we still use the word 'renote' in our code/API 2024-04-22 05:42:40 +09:00
naskya 0c9dc92f07
fix (backend-rs): use hostname for Redis key prefix and host for Redis streams
I suspect this is a bug, but I keep this behavior for compatibility for now
2024-04-22 05:16:14 +09:00
naskya 9fef36e80c
chore (backend): add comment 2024-04-22 01:56:34 +09:00
naskya b4ae877462
chore: format 2024-04-22 01:54:44 +09:00
naskya 2a30b4a536
Revert "refactor (backend): it turns out that sending the entire note object was redundant"
This reverts commit 3b65ebcb3e.
2024-04-22 01:47:56 +09:00
naskya 3039458c4c
chore (backend-rs): config::server::CONFIG -> config::CONFIG 2024-04-22 00:36:06 +09:00
naskya 574d3b3fe5
chore (backend-rs): in the current implementation we need to call to_string() anyway
There's room for refactoring
2024-04-22 00:30:59 +09:00
naskya 137d0fe3e5
chore (backend-rs): make variable names clearer 2024-04-22 00:29:37 +09:00
naskya 5c6f1c818a
chore (backend-rs): clean redundant match expression 2024-04-22 00:21:10 +09:00
naskya 2d8f4b945f
chore (backend-rs): remove unneeded serialize derivation for now 2024-04-22 00:17:33 +09:00
naskya 77e6479a67
chore (backend-rs): separate one-to-one/group chat stream types 2024-04-22 00:14:19 +09:00
naskya ec940bb068
chore (backend-rs): publish -> publish_to_stream 2024-04-21 22:43:09 +09:00
naskya 9b91035a79
chore (backend-rs): remove unneeded reference 2024-04-21 22:40:58 +09:00
naskya ce672f4edd
dev: add cargo test to pnpm scripts
mocha test has been unmaintained for a long time and is very broken :(
2024-04-21 22:36:05 +09:00
naskya 2fa0ca355d
Merge branch 'develop' into redis 2024-04-21 22:25:50 +09:00
naskya 131b3686d4 Merge branch 'feat/drive-file-usage-hints' into 'develop'
feat: Add usageHint field to DriveFile

Co-authored-by: yumeko <yumeko@mainichi.social>

See merge request firefish/firefish!10750
2024-04-21 12:58:37 +00:00
Lhcfl 09bcbb0ff0 Update docs/api-change.md 2024-04-21 11:27:08 +08:00
Lhcfl cdc3b5181a Update api-change.md 2024-04-21 11:24:41 +08:00
Lhcfl 70e4d78f90 Merge branch 'develop' of https://firefish.dev/firefish/firefish into fix/use-pagination-in-note 2024-04-21 11:18:24 +08:00
Lhcfl 1dfc7c443b fix: Extensions should be converted to lowercase 2024-04-21 11:14:19 +08:00
Lhcfl 3917818c76 fix: #10451 Emoji pack imports randomly stop processing files 2024-04-21 11:05:19 +08:00
naskya 6b008c651a
chore (backend): remove (technically) incorrect TypeORM decorator field 2024-04-21 11:09:18 +09:00
naskya d2dbfb37c7
chore (backend): reflect entity changes to the schema and repository 2024-04-21 10:59:02 +09:00
naskya 96481f1353
chore: update downgrade.sql 2024-04-21 10:48:31 +09:00
naskya c936102a4c
chore (backend-rs): regenerate entities and index.js/d.ts 2024-04-21 10:45:47 +09:00
naskya 43570a54aa
chore: format 2024-04-21 10:44:54 +09:00
naskya 4d34e14dd8
Merge branch 'develop' into feat/drive-file-usage-hints 2024-04-21 10:42:25 +09:00
naskya 28f7ac1acd
fix (backend): typo 2024-04-21 10:31:00 +09:00
naskya 9f3396af21
chore (backend): translate Japanese comments into English 2024-04-21 10:30:13 +09:00
naskya dac4043dd9
v20240421 2024-04-21 10:09:45 +09:00
naskya d1e898c0d0
docs: update changelog 2024-04-21 09:32:05 +09:00
mei23 dc02a07774
fix (backend): add Cache-Control to Bull Dashboard 2024-04-21 09:29:00 +09:00
naskya 2760e7feee
chore (minor): use ** in lieu of Math.pow 2024-04-21 06:40:53 +09:00
naskya 488323cc8e
chore: format 2024-04-21 05:57:05 +09:00
naskya 3b65ebcb3e
refactor (backend): it turns out that sending the entire note object was redundant 2024-04-21 05:55:45 +09:00
naskya 6349705fb2
test (backend-rs): add db connection tests 2024-04-21 00:48:03 +09:00
Lhcfl 4e20fe589d fix: style may be missing in mobile mode 2024-04-20 22:29:55 +08:00
naskya b08175fb83
Merge branch 'develop' into redis 2024-04-20 23:15:58 +09:00
Lhcfl e5e2eedfb4 fix: Some SubNote incorrectly added replies 2024-04-20 22:04:25 +08:00
naskya a2699e6687
chore (backend): fix imports 2024-04-20 23:04:12 +09:00
Lhcfl 39a229b875 fix: use MkPagination for replies 2024-04-20 20:55:47 +08:00
Lhcfl 35c7dccb49 fix: use MkPagination in notes for Quote, Boost, Reaction 2024-04-20 15:56:13 +08:00
naskya 77ded03330
Merge branch 'develop' into redis 2024-04-20 10:17:11 +09:00
naskya dd3ad89b64 Merge branch 'refactor/types' into 'develop'
revert unnecessary MaybeRef in components

Co-authored-by: Lhcfl <Lhcfl@outlook.com>

See merge request firefish/firefish!10751
2024-04-20 01:07:25 +00:00
naskya 4fb2cab617 Merge branch 'fix/emoji-picker' into 'develop'
fix: use settings from reactionPicker for non-reaction emoji picker

Co-authored-by: Lhcfl <Lhcfl@outlook.com>

Closes #10905

See merge request firefish/firefish!10752
2024-04-20 01:06:53 +00:00
naskya 2b0668dacd
fix (backend-rs): Redis streaming 2024-04-20 09:54:02 +09:00
naskya f13df7e202
chore (backend-rs): make entities deserializable 2024-04-20 09:27:58 +09:00
naskya f33c7c6c94
fix (backend): host and hostname was messed up 2024-04-20 08:48:48 +09:00
naskya d41b462a89
fix (backend): fix redis key prefix 2024-04-20 07:43:09 +09:00
naskya ccbd6178e4
refactor (backend): port add-note-to-antenna to backend-rs
I hit this bug: https://github.com/napi-rs/napi-rs/issues/2060
2024-04-20 07:18:39 +09:00
naskya f486caf244
fix (backend-rs): use correct Redis key prefix 2024-04-20 06:53:45 +09:00
naskya b017f9ce94
fix (backend-rs): use cache server if present 2024-04-20 06:53:34 +09:00
naskya b157cbe79a
chore (backend-rs): make entities serializable 2024-04-20 04:41:47 +09:00
naskya 5c4a773ecf
chore (backend): qualify Node.js builtin modules 2024-04-20 03:09:18 +09:00
naskya ccb17977c3
chore (minor, backend): use type import 2024-04-20 03:08:35 +09:00
naskya def62ff1ce
chore (minor, backend): remove unused import 2024-04-20 03:04:34 +09:00
naskya 56038b174d
fix (backend): correct import path 2024-04-20 03:01:13 +09:00
naskya 037b7950a2
refactor (backend): clean the booting process a bit 2024-04-20 02:58:39 +09:00
naskya e82a07c03d
dev (backend-rs): make backend-rs accessible to Redis 2024-04-20 02:47:32 +09:00
naskya e60898b4b0
refactor (backend-rs): separate postgresql connector from database/mod.rs 2024-04-20 02:46:44 +09:00
naskya 3b89a8bfa6
refactor (backend): port config loader to backend-rs completely 2024-04-20 02:46:37 +09:00
Lhcfl 207855b0e8 fix: use settings from reactionPicker for non-reaction emoji picker 2024-04-20 01:05:30 +08:00
Lhcfl 781c98dda7 revert unnecessary .value for MkLink 2024-04-20 00:18:36 +08:00
Lhcfl ab221c98a7 revert unnecessary MaybeRef in components 2024-04-20 00:05:37 +08:00
yumeko 6c46bb56fd
Switch DriveFile's usageHint field to an enum type 2024-04-19 18:24:48 +03:00
naskya 1be5373dfc
chore (backend-rs): make exported enum compatible w/ TypeScript's string enum 2024-04-19 21:59:35 +09:00
yumeko 968657d26e
Run format 2024-04-19 07:54:11 +03:00
yumeko 913de651db
When updating (remote) user avatar/banner, clear usageHint for the previous drivefile, if any 2024-04-19 07:25:42 +03:00
yumeko 4aeb0d95cc
Add DriveFile usageHint field to rust model as well 2024-04-19 07:03:09 +03:00
yumeko c0f93de94b
Set file usage hints on local avatar/banner uploads as well + export "valid" values as type 2024-04-19 06:29:28 +03:00
yumeko 4823abd3a9
Add usageHint field to DriveFile, and fill accordingly when operating on Persons 2024-04-19 03:41:36 +03:00
naskya c6e2776298
chore (backend): remove a horrible and unused function 2024-04-19 03:42:49 +09:00
naskya 4b7724ed1f Merge branch 'cw-text' into 'develop'
Increase CW character limit

Closes #10876

See merge request firefish/firefish!10746
2024-04-17 20:47:40 +00:00
naskya 9d87679800
chore: lint 2024-04-18 05:34:23 +09:00
naskya 78092cd4be
dev (client): update eslint rules 2024-04-18 05:32:42 +09:00
naskya ff08d044b5
chore: format 2024-04-18 05:22:54 +09:00
naskya bce88ec199 Merge branch 'refactor/types' into 'develop'
Refactor/types

Co-authored-by: Lhcfl <Lhcfl@outlook.com>

See merge request firefish/firefish!10737
2024-04-17 20:21:52 +00:00
naskya 1b076c96d7
Merge branch 'develop' into refactor/types 2024-04-18 05:10:20 +09:00
naskya c19c439ac1
fix (backend): add hasPoll to packed note 2024-04-18 05:08:14 +09:00
naskya cc452da6c5
Merge branch 'develop' into refactor/types 2024-04-18 05:03:32 +09:00
naskya 30969ad817
refactor (backend): port get-note-summary to backend-rs
I removed trim() as it wasn't strictly neccessary
2024-04-18 05:02:00 +09:00
naskya 8337863ed3
chore: format 2024-04-18 05:01:49 +09:00
naskya 6a94d1b65d Merge branch 'fix/server-stats-disable' into 'develop'
Fix check for whether stats are disabled in meta in server machine stats job

Co-authored-by: yumeko <yumeko@mainichi.social>

See merge request firefish/firefish!10748
2024-04-17 18:20:35 +00:00
naskya c471aa30ae Merge branch 'fix/pinned-user-null' into 'develop'
Fix internal error in api/pinned-users if one or more name fails to resolve

Co-authored-by: yumeko <yumeko@mainichi.social>

See merge request firefish/firefish!10747
2024-04-17 18:19:17 +00:00
yumeko a411f4e4d9 Fix internal error in api/pinned-users if one or more name fails to resolve 2024-04-17 18:19:17 +00:00
naskya 22f4278ab5
meta: update issue/merge request templates 2024-04-17 23:14:43 +09:00
naskya ec7578e78e
docs: specify max-old-space-size in example config files 2024-04-17 19:49:02 +09:00
naskya adaaae1583
Merge branch 'develop' into refactor/types 2024-04-17 19:07:43 +09:00
naskya 8489066130
fix (client): list layout on mobile 2024-04-17 19:06:29 +09:00
naskya 17fb05430e
fix (backend, Mastodon API): add 'meta.original' field to media attachments
addresses https://github.com/whitescent/Mastify/pull/102
2024-04-17 17:46:23 +09:00
yumeko bf3c0717b9 Fix check for whether stats are disabled in meta in server machine stats job 2024-04-16 15:29:18 +00:00
naskya 082948bfe0
Merge branch 'develop' into refactor/types 2024-04-16 22:29:04 +09:00
Hosted Weblate 86f2e32c66
Merge branch 'origin/develop' into Weblate 2024-04-16 05:02:06 +02:00
Gary O'Regan Kelly 7af2cca2d4
locale: update translations (French)
Currently translated at 100.0% (1920 of 1920 strings)

Translation: Firefish/locales
Translate-URL: https://hosted.weblate.org/projects/firefish/locales/fr/
2024-04-16 05:01:56 +02:00
naskya 07384a4f0f
feat (backend): increase CW character limit (close #10876) 2024-04-16 09:14:44 +09:00
naskya 77a2bcfc4b
chore: remove unused items from example config file 2024-04-16 09:01:12 +09:00
naskya fd333250c9
chore (backend): set proxyRemoteFiles to true by default (close #9426) 2024-04-16 08:56:05 +09:00
naskya 38192052c9
meta: update issue/merge request templates 2024-04-16 05:40:27 +09:00
naskya 80b80277e2
fix (pug): random MOTD not showing 2024-04-16 01:50:42 +09:00
naskya 71c158fbd3
refactor (backend): port env.ts to backend-rs 2024-04-15 17:28:20 +09:00
Lhcfl a07483996e feat: add slash quote 2024-04-15 15:12:51 +08:00
naskya 0f3126196f
refactor (backend): port reaction-lib to backend-rs 2024-04-15 10:02:44 +09:00
naskya 2731003bc9
refactor (backend): port emoji-regex to backend-rs 2024-04-15 05:37:09 +09:00
naskya 74875f174b
chore (minor, backend): use a template literal 2024-04-15 04:34:36 +09:00
naskya 884c69f377
chore (minor, backend): organize imports 2024-04-15 04:34:00 +09:00
naskya f412d7ace3
chore (backend): remove 'quiet' settings 2024-04-15 04:31:04 +09:00
naskya eb5dccacfe
chore (firefish-js): remove bun lockfile
Why is it there?
2024-04-15 04:18:44 +09:00
naskya 21225f7137
chore: update dependencies 2024-04-15 04:09:33 +09:00
naskya fca48b2a81
refactor (backend): port safe-for-sql, sql-like-escape to backend-rs 2024-04-14 20:29:44 +09:00
sup39 b71da18b03
refactor (backend): port fetch-meta to backend-rs
Co-authored-by: naskya <m@naskya.net>
2024-04-14 20:16:22 +09:00
Lhcfl 241c824ab5 fix: use better ]]> replacer 2024-04-14 16:44:12 +08:00
Lhcfl 54d9916fec fix: rss feed no HTML 2024-04-14 16:34:33 +08:00
Lhcfl f0a50bc288 feat: show MkRemoteCaution in note history page 2024-04-14 13:59:27 +08:00
naskya ceca260c92
refactor (backend): port convert-milliseconds to backend-rs 2024-04-14 14:54:32 +09:00
sup39 70aa3704ef
refactor (backend): port password hashing/verification to backend-rs
Co-authored-by: naskya <m@naskya.net>
2024-04-14 14:41:01 +09:00
naskya baa57d7c17
dev: add rust-analyzer to recommended VSCode extensitons 2024-04-14 13:38:44 +09:00
naskya 19b45866c8
docs: update packages/README.md 2024-04-14 13:35:35 +09:00
Lhcfl 8a62bf90f5 fix: MkMenu.vue 2024-04-14 03:42:30 +08:00
Lhcfl cee3a13f51 fix types 2024-04-14 03:15:31 +08:00
naskya aea6659d0b
fix: workaround the issue that profile pages don't load if the version is older than 20240212
(v20240212 is vulnerable btw)
2024-04-14 02:35:25 +09:00
Lhcfl 1a6ba246f2 add other FormInput 2024-04-13 23:21:31 +08:00
Lhcfl 16880b1231 fix types 2024-04-13 23:08:58 +08:00
Lhcfl 3e43819ba1 rewrite MkFormDialog 2024-04-13 23:08:46 +08:00
Lhcfl bb9a58ce34 Merge branch 'develop' of https://firefish.dev/firefish/firefish into refactor/types 2024-04-13 20:08:41 +08:00
Lhcfl baba86a203 Downgrade vue-tsc
new versions of vue-tsc have perfomance issues:
see https://github.com/vuejs/language-tools/issues/4223
2024-04-13 17:55:40 +08:00
Lhcfl f95e2d1c51 Merge branch 'develop' of https://firefish.dev/firefish/firefish into refactor/types 2024-04-13 15:39:44 +08:00
Lhcfl 2b88ef18a5 fix type errors of components 2024-04-13 15:37:23 +08:00
老周部落 5eff4da27b
feat: antenna limit 2024-04-13 10:02:32 +08:00
老周部落 f44a2937d4
fix: download-url should use proxy bypass hosts 2024-04-13 09:00:08 +08:00
Lhcfl 393ab2590d fix type errors of components 2024-04-12 22:02:03 +08:00
Lhcfl f422842aef use Number.parseInt 2024-04-12 16:55:34 +08:00
Lhcfl 695cb452bc chore: Temporarily disable organizeImports 2024-04-12 16:50:07 +08:00
Lhcfl e4927c9b9b fix types of components 2024-04-12 16:37:32 +08:00
Lhcfl ea6ef881c2 fix types of components 2024-04-12 14:08:20 +08:00
Lhcfl 2bf51abc12 fix type of MkModalPageWindow 2024-04-12 11:31:11 +08:00
Lhcfl 5da03666b2 fix types of component 2024-04-12 11:16:26 +08:00
Lhcfl f3b189a70c refactor: rewrite MkMediaCaption 2024-04-12 09:46:04 +08:00
Lhcfl 95b91c6396 chore: remove unused type imports 2024-04-12 09:40:34 +08:00
Lhcfl 9c75dd0b26 refactor: define more cleared type of os.ts 2024-04-12 09:33:27 +08:00
Lhcfl 38668c4c11 fix type errors 2024-04-12 01:32:04 +08:00
Lhcfl e6b7eca775 fix type errors of components 2024-04-12 01:12:18 +08:00
Lhcfl 8e28f0e97c Merge branch 'develop' of https://firefish.dev/firefish/firefish into refactor/types 2024-04-12 00:23:21 +08:00
Lhcfl d8b4eb6f5e fix: gallery posts not show & fix types 2024-04-12 00:22:49 +08:00
Lhcfl ef94ba1474 refactor: fix type errrors of some components 2024-04-12 00:20:51 +08:00
Lhcfl c4a093209f fix type of MkEmojiPicker 2024-04-11 18:00:37 +08:00
Lhcfl db37eb4ad1 refactor: rewrite MkFolder 2024-04-11 17:29:07 +08:00
Lhcfl 3716e7f74c fix: expose toggleContent for it was a method 2024-04-11 17:11:48 +08:00
Lhcfl 7a4e6334f1 fix type error 2024-04-11 17:01:37 +08:00
Lhcfl 783f5481bb remove unnecessary assertion 2024-04-11 16:58:58 +08:00
Lhcfl 3ddd68097a fix type errors 2024-04-11 00:48:35 +08:00
Lhcfl f275fc9cdf refactor: fix type errors of MkCropperDialog 2024-04-11 00:14:36 +08:00
Lhcfl 18ba024cbb chore: format 2024-04-11 00:00:54 +08:00
Lhcfl 43aeec32ce fix types error of MkContextMenu 2024-04-10 23:36:42 +08:00
Lhcfl 909125e519 refactor: rewrite MkContainer using composition api 2024-04-10 23:35:14 +08:00
Lhcfl 124b2244d6 fix type errors of MkCode & MkModalWindow 2024-04-10 22:50:30 +08:00
Lhcfl 0b7ab1b90d fix type errors of MkChatPreview 2024-04-10 22:45:01 +08:00
Lhcfl 6982843716 fix: channel editor cannot remove channel banner 2024-04-10 22:25:45 +08:00
Lhcfl 17a945b8b1 fix type errors of channel 2024-04-10 22:25:11 +08:00
Lhcfl f7f7959ba6 refactor: fix waring for MkCaptcha 2024-04-10 22:24:16 +08:00
Lhcfl c1155f169a fix type warnings of MkCaptcha 2024-04-10 20:42:48 +08:00
Lhcfl b59186f093 fix type errors of MkButton 2024-04-10 20:41:31 +08:00
Lhcfl 1a1d817772 fix type errors of MkAvatars 2024-04-10 20:37:34 +08:00
Lhcfl 73537ec6fa fix type errors of MkAutoComplete 2024-04-10 20:36:20 +08:00
Lhcfl 6ac6a4cfa9 refactor: fix type errors of MkAnnouncements 2024-04-10 20:10:43 +08:00
Lhcfl b6baded2e3 refactor: fix MkAcct type 2024-04-10 17:58:02 +08:00
Lhcfl 23145c61af refactor: fix type of MkAbuseReport 2024-04-10 17:55:52 +08:00
Lhcfl 268b7aeb3f refactor: Fix type errors of mfm.ts 2024-04-10 17:16:25 +08:00
老周部落 46bf02cdd5
chore: format 2024-03-31 06:44:00 +08:00
老周部落 f346d2e2f6
feat: add showAddFileDescriptionAtFirstPost and allow cancel adding alt-text midway 2024-03-31 06:42:33 +08:00
naskya 2267e90d3b
refactor (client): await asynchronous processes, remove duplicate code 2024-03-31 06:41:20 +08:00
老周部落 db0bd21edc
feat(client): attachment without description opened when choose add alt 2024-03-31 06:41:07 +08:00
老周部落 de4da3c1fd
docs: add minimum dependencies 2024-03-19 22:51:05 +08:00
770 changed files with 25667 additions and 20420 deletions

View file

@ -1,195 +1,11 @@
#━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# Firefish configuration
#━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# ┌─────┐
#───┘ URL └─────────────────────────────────────────────────────
# Final accessible URL seen by a user.
url: https://example.tld/
# ONCE YOU HAVE STARTED THE INSTANCE, DO NOT CHANGE THE
# URL SETTINGS AFTER THAT!
# ┌───────────────────────┐
#───┘ Port and TLS settings └───────────────────────────────────
#
# Misskey requires a reverse proxy to support HTTPS connections.
#
# +----- https://example.tld/ ------------+
# +------+ |+-------------+ +----------------+|
# | User | ---> || Proxy (443) | ---> | Misskey (3000) ||
# +------+ |+-------------+ +----------------+|
# +---------------------------------------+
#
# You need to set up a reverse proxy. (e.g. nginx)
# An encrypted connection with HTTPS is highly recommended
# because tokens may be transferred in GET requests.
# The port that your Misskey server should listen on.
url: http://localhost:3000
port: 3000
# ┌──────────────────────────┐
#───┘ PostgreSQL configuration └────────────────────────────────
db:
host: postgres
port: 5432
# Database name
db: postgres
# Auth
user: postgres
pass: test
# Whether disable Caching queries
#disableCache: true
# Extra Connection options
#extra:
# ssl: true
# ┌─────────────────────┐
#───┘ Redis configuration └─────────────────────────────────────
db: firefish_db
user: firefish
pass: password
redis:
host: redis
port: 6379
#family: 0 # 0=Both, 4=IPv4, 6=IPv6
#pass: example-pass
#prefix: example-prefix
#db: 1
# ┌─────────────────────────────┐
#───┘ Elasticsearch configuration └─────────────────────────────
#elasticsearch:
# host: localhost
# port: 9200
# ssl: false
# user:
# pass:
# ┌───────────────┐
#───┘ ID generation └───────────────────────────────────────────
# You can select the ID generation method.
# You don't usually need to change this setting, but you can
# change it according to your preferences.
# Available methods:
# aid ... Short, Millisecond accuracy
# meid ... Similar to ObjectID, Millisecond accuracy
# ulid ... Millisecond accuracy
# objectid ... This is left for backward compatibility
# ONCE YOU HAVE STARTED THE INSTANCE, DO NOT CHANGE THE
# ID SETTINGS AFTER THAT!
id: 'aid'
# ┌─────────────────────┐
#───┘ Other configuration └─────────────────────────────────────
# Max note length, should be < 8000.
#maxNoteLength: 3000
# Whether disable HSTS
#disableHsts: true
# Number of worker processes
#clusterLimit: 1
# Job concurrency per worker
# deliverJobConcurrency: 128
# inboxJobConcurrency: 16
# Job rate limiter
# deliverJobPerSec: 128
# inboxJobPerSec: 16
# Job attempts
# deliverJobMaxAttempts: 12
# inboxJobMaxAttempts: 8
# IP address family used for outgoing request (ipv4, ipv6 or dual)
#outgoingAddressFamily: ipv4
# Syslog option
#syslog:
# host: localhost
# port: 514
# Proxy for HTTP/HTTPS
#proxy: http://127.0.0.1:3128
#proxyBypassHosts: [
# 'example.com',
# '192.0.2.8'
#]
# Proxy for SMTP/SMTPS
#proxySmtp: http://127.0.0.1:3128 # use HTTP/1.1 CONNECT
#proxySmtp: socks4://127.0.0.1:1080 # use SOCKS4
#proxySmtp: socks5://127.0.0.1:1080 # use SOCKS5
# Media Proxy
#mediaProxy: https://example.com/proxy
# Proxy remote files (default: false)
#proxyRemoteFiles: true
#allowedPrivateNetworks: [
# '127.0.0.1/32'
#]
# Upload or download file size limits (bytes)
#maxFileSize: 262144000
# Managed hosting settings
# !!!!!!!!!!
# >>>>>> NORMAL SELF-HOSTERS, STAY AWAY! <<<<<<
# >>>>>> YOU DON'T NEED THIS! <<<<<<
# !!!!!!!!!!
# Each category is optional, but if each item in each category is mandatory!
# If you mess this up, that's on you, you've been warned...
#maxUserSignups: 100
#isManagedHosting: true
#deepl:
# managed: true
# authKey: ''
# isPro: false
#
#email:
# managed: true
# address: 'example@email.com'
# host: 'email.com'
# port: 587
# user: 'example@email.com'
# pass: ''
# useImplicitSslTls: false
#
#objectStorage:
# managed: true
# baseUrl: ''
# bucket: ''
# prefix: ''
# endpoint: ''
# region: ''
# accessKey: ''
# secretKey: ''
# useSsl: true
# connnectOverProxy: false
# setPublicReadOnUpload: true
# s3ForcePathStyle: true
# !!!!!!!!!!
# >>>>>> AGAIN, NORMAL SELF-HOSTERS, STAY AWAY! <<<<<<
# >>>>>> YOU DON'T NEED THIS, ABOVE SETTINGS ARE FOR MANAGED HOSTING ONLY! <<<<<<
# !!!!!!!!!!
# Seriously. Do NOT fill out the above settings if you're self-hosting.
# They're much better off being set from the control panel.

View file

@ -13,16 +13,8 @@ redis:
host: firefish_redis
port: 6379
id: 'aid'
#allowedPrivateNetworks: [
# '10.69.1.0/24'
#]
logLevel: [
'error',
'success',
'warning',
'debug',
'info'
]
maxLogLevel: 'debug'

View file

@ -145,16 +145,11 @@ reservedUsernames: [
# IP address family used for outgoing request (ipv4, ipv6 or dual)
#outgoingAddressFamily: ipv4
# Log Option
# Production env: ['error', 'success', 'warning', 'info']
# Debug/Test env or Troubleshooting: ['error', 'success', 'warning', 'debug' ,'info']
# Production env which storage space or IO is tight: ['error', 'warning']
logLevel: [
'error',
'success',
'warning',
'info'
]
# Log level (error, warning, info, debug, trace)
# Production env: info
# Production env whose storage space or IO is tight: warning
# Debug/Test env or Troubleshooting: debug (or trace)
maxLogLevel: info
# Syslog option
#syslog:
@ -178,19 +173,13 @@ logLevel: [
# Media Proxy
#mediaProxy: https://example.com/proxy
# Proxy remote files (default: false)
# Proxy remote files (default: true)
#proxyRemoteFiles: true
#allowedPrivateNetworks: [
# '127.0.0.1/32'
#]
# TWA
#twa:
# nameSpace: android_app
# packageName: tld.domain.twa
# sha256CertFingerprints: ['AB:CD:EF']
# Upload or download file size limits (bytes)
#maxFileSize: 262144000

View file

@ -51,12 +51,11 @@ title.svg
/dev
/docs
/scripts
!/scripts/copy-assets.mjs
biome.json
COPYING
CODE_OF_CONDUCT.md
CONTRIBUTING.md
Dockerfile
LICENSE
Procfile
README.md
SECURITY.md

267
.gitlab-ci.yml Normal file
View file

@ -0,0 +1,267 @@
image: docker.io/rust:slim-bookworm
services:
- name: docker.io/groonga/pgroonga:latest-alpine-12-slim
alias: postgres
pull_policy: if-not-present
- name: docker.io/redis:7-alpine
alias: redis
pull_policy: if-not-present
workflow:
rules:
- if: $CI_PROJECT_PATH == 'firefish/firefish' || $CI_MERGE_REQUEST_PROJECT_PATH == 'firefish/firefish'
changes:
paths:
- packages/**/*
- locales/**/*
- scripts/**/*
- package.json
- Cargo.toml
- Cargo.lock
- Dockerfile
- .dockerignore
when: always
- when: never
stages:
- dependency
- test
- build
variables:
POSTGRES_DB: 'firefish_db'
POSTGRES_USER: 'firefish'
POSTGRES_PASSWORD: 'password'
POSTGRES_HOST_AUTH_METHOD: 'trust'
DEBIAN_FRONTEND: 'noninteractive'
CARGO_PROFILE_DEV_OPT_LEVEL: '0'
CARGO_PROFILE_DEV_LTO: 'off'
CARGO_PROFILE_DEV_DEBUG: 'none'
CARGO_TERM_COLOR: 'always'
GIT_CLEAN_FLAGS: -ffdx -e node_modules/ -e built/ -e target/ -e packages/backend-rs/built/
default:
before_script:
- apt-get update && apt-get -y upgrade
- apt-get -y --no-install-recommends install curl
- curl -fsSL 'https://deb.nodesource.com/setup_18.x' | bash -
- apt-get install -y --no-install-recommends build-essential clang mold python3 perl nodejs postgresql-client
- corepack enable
- corepack prepare pnpm@latest --activate
- cp .config/ci.yml .config/default.yml
- cp ci/cargo/config.toml /usr/local/cargo/config.toml
- export PGPASSWORD="${POSTGRES_PASSWORD}"
- psql --host postgres --user "${POSTGRES_USER}" --dbname "${POSTGRES_DB}" --command 'CREATE EXTENSION pgroonga'
test:build:
stage: test
rules:
- if: $TEST == 'false'
when: never
- if: $CI_COMMIT_BRANCH == 'develop' || $CI_PIPELINE_SOURCE == 'merge_request_event'
changes:
paths:
- packages/backend-rs/**/*
- packages/macro-rs/**/*
- scripts/**/*
- package.json
- Cargo.toml
- Cargo.lock
when: always
needs:
- job: cargo:clippy
optional: true
- job: cargo:test
optional: true
script:
- pnpm install --frozen-lockfile
- pnpm run build:debug
- pnpm run migrate
test:build:backend_ts_only:
stage: test
rules:
- if: $TEST == 'false'
when: never
- if: $CI_COMMIT_BRANCH == 'develop' || $CI_PIPELINE_SOURCE == 'merge_request_event'
changes:
paths:
- packages/backend-rs/**/*
- packages/macro-rs/**/*
- scripts/**/*
- package.json
- Cargo.toml
- Cargo.lock
when: never
- if: $CI_COMMIT_BRANCH == 'develop' || $CI_PIPELINE_SOURCE == 'merge_request_event'
changes:
paths:
- packages/backend/**/*
- packages/firefish-js/**/*
- packages/megalodon/**/*
when: always
before_script:
- apt-get update && apt-get -y upgrade
- apt-get -y --no-install-recommends install curl
- curl -fsSL 'https://deb.nodesource.com/setup_18.x' | bash -
- apt-get install -y --no-install-recommends build-essential python3 nodejs postgresql-client
- corepack enable
- corepack prepare pnpm@latest --activate
- mkdir -p packages/backend-rs/built
- cp packages/backend-rs/index.js packages/backend-rs/built/index.js
- cp packages/backend-rs/index.d.ts packages/backend-rs/built/index.d.ts
- cp .config/ci.yml .config/default.yml
- export PGPASSWORD="${POSTGRES_PASSWORD}"
- psql --host postgres --user "${POSTGRES_USER}" --dbname "${POSTGRES_DB}" --command 'CREATE EXTENSION pgroonga'
script:
- pnpm install --frozen-lockfile
- pnpm --filter 'backend' --filter 'firefish-js' --filter 'megalodon' run build:debug
- pnpm run migrate
test:build:client_only:
stage: test
rules:
- if: $TEST == 'false'
when: never
- if: $CI_COMMIT_BRANCH == 'develop' || $CI_PIPELINE_SOURCE == 'merge_request_event'
changes:
paths:
- packages/backend-rs/**/*
- packages/macro-rs/**/*
- scripts/**/*
- package.json
- Cargo.toml
- Cargo.lock
when: never
- if: $CI_COMMIT_BRANCH == 'develop' || $CI_PIPELINE_SOURCE == 'merge_request_event'
changes:
paths:
- packages/client/**/*
- packages/firefish-js/**/*
- packages/sw/**/*
- locales/**/*
when: always
services: []
before_script:
- apt-get update && apt-get -y upgrade
- apt-get -y --no-install-recommends install curl
- curl -fsSL 'https://deb.nodesource.com/setup_18.x' | bash -
- apt-get install -y --no-install-recommends build-essential python3 perl nodejs
- corepack enable
- corepack prepare pnpm@latest --activate
- cp .config/ci.yml .config/default.yml
script:
- pnpm install --frozen-lockfile
- pnpm --filter 'firefish-js' --filter 'client' --filter 'sw' run build:debug
build:container:
stage: build
image: docker.io/debian:bookworm-slim
services: []
rules:
- if: $BUILD == 'false'
when: never
- if: $CI_COMMIT_BRANCH == 'develop'
changes:
paths:
- packages/**/*
- locales/**/*
- scripts/copy-assets.mjs
- package.json
- Cargo.toml
- Cargo.lock
- Dockerfile
- .dockerignore
when: always
needs:
- job: test:build
optional: true
- job: test:build:backend_ts_only
optional: true
- job: test:build:client_only
optional: true
before_script:
- apt-get update && apt-get -y upgrade
- apt-get install -y --no-install-recommends buildah ca-certificates fuse-overlayfs
- buildah prune --all
- echo "${CI_REGISTRY_PASSWORD}" | buildah login --username "${CI_REGISTRY_USER}" --password-stdin "${CI_REGISTRY}"
- export IMAGE_TAG="${CI_REGISTRY}/${CI_PROJECT_PATH}/develop:not-for-production"
- export IMAGE_CACHE="${CI_REGISTRY}/${CI_PROJECT_PATH}/develop/cache"
script:
- |-
buildah build \
--isolation chroot \
--device /dev/fuse:rw \
--security-opt seccomp=unconfined \
--security-opt apparmor=unconfined \
--cap-add all \
--platform linux/amd64 \
--layers \
--cache-to "${IMAGE_CACHE}" \
--cache-from "${IMAGE_CACHE}" \
--tag "${IMAGE_TAG}" \
.
- buildah inspect "${IMAGE_TAG}"
- buildah push "${IMAGE_TAG}"
cargo:test:
stage: test
rules:
- if: $TEST == 'false'
when: never
- if: $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == 'main'
when: never
- if: $CI_COMMIT_BRANCH == 'develop' || $CI_PIPELINE_SOURCE == 'merge_request_event'
changes:
paths:
- packages/backend-rs/**/*
- packages/macro-rs/**/*
- Cargo.toml
- Cargo.lock
when: always
script:
- curl -LsSf https://get.nexte.st/latest/linux | tar zxf - -C /usr/local/cargo/bin
- pnpm install --frozen-lockfile
- mkdir -p packages/backend-rs/built
- cp packages/backend-rs/index.js packages/backend-rs/built/index.js
- cp packages/backend-rs/index.d.ts packages/backend-rs/built/index.d.ts
- pnpm --filter='!backend-rs' run build:debug
- cargo test --doc
- cargo nextest run
cargo:clippy:
stage: test
rules:
- if: $TEST == 'false'
when: never
- if: $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == 'main'
when: never
- if: $CI_COMMIT_BRANCH == 'develop' || $CI_PIPELINE_SOURCE == 'merge_request_event'
changes:
paths:
- packages/backend-rs/**/*
- packages/macro-rs/**/*
- Cargo.toml
- Cargo.lock
when: always
services: []
before_script:
- apt-get update && apt-get -y upgrade
- apt-get install -y --no-install-recommends build-essential clang mold perl
- cp ci/cargo/config.toml /usr/local/cargo/config.toml
- rustup component add clippy
script:
- cargo clippy -- -D warnings
renovate:
stage: dependency
image:
name: docker.io/renovate/renovate:37-slim
entrypoint: [""]
rules:
- if: $RENOVATE && $CI_PIPELINE_SOURCE == 'schedule'
services: []
before_script: []
script:
- renovate --platform gitlab --token "${API_TOKEN}" --endpoint "${CI_SERVER_URL}/api/v4" "${CI_PROJECT_PATH}"

View file

@ -3,30 +3,47 @@
🔒 Found a security vulnerability? [Please disclose it responsibly.](https://firefish.dev/firefish/firefish/-/blob/develop/SECURITY.md)
🤝 By submitting this issue, you agree to follow our [Contribution Guidelines.](https://firefish.dev/firefish/firefish/-/blob/develop/CONTRIBUTING.md) -->
**What happened?** _(Please give us a brief description of what happened.)_
## What happened? <!-- Please give us a brief description of what happened. -->
**What did you expect to happen?** _(Please give us a brief description of what you expected to happen.)_
**Version** _(What version of firefish is your instance running? You can find this by clicking your instance's logo at the bottom left and then clicking instance information.)_
## What did you expect to happen? <!-- Please give us a brief description of what you expected to happen. -->
**Instance** _(What instance of firefish are you using?)_
**What type of issue is this?** _(If this happens on your device and has to do with the user interface, it's client-side. If this happens on either with the API or the backend, or you got a server-side error in the client, it's server-side.)_
## Version <!-- What version of firefish is your instance running? You can find this by clicking your instance's logo at the bottom left and then clicking instance information. -->
**What browser are you using? (Client-side issues only)**
**What operating system are you using? (Client-side issues only)**
## What type of issue is this? <!-- If this happens on your device and has to do with the user interface, it's client-side. If this happens on either with the API or the backend, or you got a server-side error in the client, it's server-side. -->
**How do you deploy Firefish on your server? (Server-side issues only)**
- [ ] server-side
- [ ] client-side
- [ ] not sure
**What operating system are you using? (Server-side issues only)**
<details>
**Relevant log output** _(Please copy and paste any relevant log output. You can find your log by inspecting the page, and going to the "console" tab. This will be automatically formatted into code, so no need for backticks.)_
### Instance <!-- What instance of firefish are you using? -->
**Contribution Guidelines**
### What browser are you using? (client-side issues only)
### What operating system are you using? (client-side issues only)
### How do you deploy Firefish on your server? (server-side issues only)
### What operating system are you using? (Server-side issues only)
### Relevant log output <!-- Please copy and paste any relevant log output. You can find your log by inspecting the page, and going to the "console" tab. -->
</details>
## Contribution Guidelines
By submitting this issue, you agree to follow our [Contribution Guidelines](https://firefish.dev/firefish/firefish/-/blob/develop/CONTRIBUTING.md)
- [ ] I agree to follow this project's Contribution Guidelines
- [ ] I have searched the issue tracker for similar issues, and this is not a duplicate.
**Are you willing to fix this bug?** (optional)
## Are you willing to fix this bug? (optional)
- [ ] Yes. I will fix this bug and open a merge request if the change is agreed upon.

View file

@ -3,18 +3,22 @@
🔒 Found a security vulnerability? [Please disclose it responsibly.](https://firefish.dev/firefish/firefish/-/blob/develop/SECURITY.md)
🤝 By submitting this feature request, you agree to follow our [Contribution Guidelines.](https://firefish.dev/firefish/firefish/-/blob/develop/CONTRIBUTING.md) -->
**What feature would you like implemented?** _(Please give us a brief description of what you'd like.)_
## What feature would you like implemented? <!-- Please give us a brief description of what you'd like. -->
**Why should we add this feature?** _(Please give us a brief description of why your feature is important.)_
**Version** _(What version of firefish is your instance running? You can find this by clicking your instance's logo at the bottom left and then clicking instance information.)_
## Why should we add this feature? <!-- Please give us a brief description of why your feature is important. -->
**Instance** _(What instance of firefish are you using?)_
**Contribution Guidelines**
## Version <!-- What version of firefish is your instance running? You can find this by clicking your instance's logo at the bottom left and then clicking instance information. -->
## Instance <!-- What instance of firefish are you using? -->
## Contribution Guidelines
By submitting this issue, you agree to follow our [Contribution Guidelines](https://firefish.dev/firefish/firefish/-/blob/develop/CONTRIBUTING.md)
- [ ] I agree to follow this project's Contribution Guidelines
- [ ] I have searched the issue tracker for similar requests, and this is not a duplicate.
**Are you willing to implement this feature?** (optional)
## Are you willing to implement this feature? (optional)
- [ ] Yes. I will implement this feature and open a merge request if the change is agreed upon.

View file

@ -1,8 +1,9 @@
<!-- Thanks for taking the time to make Firefish better! It's not required, but please consider using [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) when making your commits. If you use VSCode, please use the [Conventional Commits extension](https://marketplace.visualstudio.com/items?itemName=vivaxy.vscode-conventional-commits). -->
**What does this PR do?** _(Please give us a brief description of what this PR does.)_
## What does this PR do? <!-- Please give us a brief description of what this PR does. -->
**Contribution Guidelines**
## Contribution Guidelines
By submitting this merge request, you agree to follow our [Contribution Guidelines](https://firefish.dev/firefish/firefish/-/blob/develop/CONTRIBUTING.md)
- [ ] This change is reviewed in an issue / This is a minor bug fix
- [ ] I agree to follow this project's Contribution Guidelines

View file

@ -12,6 +12,7 @@
"esbenp.prettier-vscode",
"redhat.vscode-yaml",
"yoavbls.pretty-ts-errors",
"biomejs.biome"
"biomejs.biome",
"rust-lang.rust-analyzer"
]
}

View file

@ -26,14 +26,14 @@ RsaSignature2017 implementation by Transmute Industries Inc
License: MIT
https://github.com/transmute-industries/RsaSignature2017/blob/master/LICENSE
Machine learning model for sensitive images by Infinite Red, Inc.
License: MIT
https://github.com/infinitered/nsfwjs/blob/master/LICENSE
Chiptune2.js by Simon Gündling
License: MIT
https://github.com/deskjet/chiptune2.js#license
bb8-redis by Kyle Huey
License: MIT
https://github.com/djc/bb8/blob/62597aa45ac1746780b08cb6a68cf7d65452a23a/LICENSE
Licenses for all softwares and software libraries installed via the Node Package Manager ("npm") can be found by running the following shell command in the root directory of this repository:
pnpm licenses list

2313
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -5,35 +5,47 @@ resolver = "2"
[workspace.dependencies]
macro_rs = { path = "packages/macro-rs" }
napi = { version = "2.16.2", default-features = false }
napi-derive = "2.16.2"
napi-build = "2.1.2"
napi = { version = "2.16.6", default-features = false }
napi-derive = "2.16.4"
napi-build = "2.1.3"
argon2 = "0.5.3"
async-trait = "0.1.80"
basen = "0.1.0"
cfg-if = "1.0.0"
chrono = "0.4.37"
bb8 = "0.8.3"
bcrypt = "0.15.1"
chrono = "0.4.38"
convert_case = "0.6.0"
cuid2 = "0.1.2"
emojis = "0.6.2"
idna = "0.5.0"
jsonschema = "0.17.1"
image = "0.25.1"
isahc = "1.7.2"
nom-exif = "1.2.0"
once_cell = "1.19.0"
parse-display = "0.9.0"
openssl = "0.10.64"
pretty_assertions = "1.4.0"
proc-macro2 = "1.0.79"
proc-macro2 = "1.0.82"
quote = "1.0.36"
rand = "0.8.5"
redis = { version = "0.25.3", default-features = false }
regex = "1.10.4"
schemars = "0.8.16"
rmp-serde = "1.3.0"
sea-orm = "0.12.15"
serde = "1.0.197"
serde_json = "1.0.115"
serde = "1.0.202"
serde_json = "1.0.117"
serde_yaml = "0.9.34"
syn = "2.0.58"
thiserror = "1.0.58"
strum = "0.26.2"
syn = "2.0.64"
sysinfo = "0.30.12"
thiserror = "1.0.61"
tokio = "1.37.0"
tokio-test = "0.4.4"
tracing = "0.1.40"
tracing-subscriber = "0.3.18"
url = "2.5.0"
urlencoding = "2.1.3"
web-push = { git = "https://github.com/pimeys/rust-web-push", rev = "40febe4085e3cef9cdfd539c315e3e945aba0656" }
[profile.release]
lto = true

View file

@ -3,11 +3,16 @@ FROM docker.io/node:20-alpine as build
WORKDIR /firefish
# Install compilation dependencies
RUN apk update && apk add --no-cache build-base linux-headers curl ca-certificates python3
RUN apk update && apk add --no-cache build-base linux-headers curl ca-certificates python3 perl
RUN curl --proto '=https' --tlsv1.2 --silent --show-error --fail https://sh.rustup.rs | sh -s -- -y
ENV PATH="/root/.cargo/bin:${PATH}"
# Copy only the cargo dependency-related files first, to cache efficiently
# Copy only backend-rs dependency-related files first, to cache efficiently
COPY package.json pnpm-workspace.yaml ./
COPY packages/backend-rs/package.json packages/backend-rs/package.json
COPY packages/backend-rs/npm/linux-x64-musl/package.json packages/backend-rs/npm/linux-x64-musl/package.json
COPY packages/backend-rs/npm/linux-arm64-musl/package.json packages/backend-rs/npm/linux-arm64-musl/package.json
COPY Cargo.toml Cargo.toml
COPY Cargo.lock Cargo.lock
COPY packages/backend-rs/Cargo.toml packages/backend-rs/Cargo.toml
@ -15,33 +20,36 @@ COPY packages/backend-rs/src/lib.rs packages/backend-rs/src/
COPY packages/macro-rs/Cargo.toml packages/macro-rs/Cargo.toml
COPY packages/macro-rs/src/lib.rs packages/macro-rs/src/
# Install cargo dependencies
RUN cargo fetch --locked --manifest-path /firefish/packages/backend-rs/Cargo.toml
# Configure pnpm, and install backend-rs dependencies
RUN corepack enable && corepack prepare pnpm@latest --activate && pnpm --filter backend-rs install
RUN cargo fetch --locked --manifest-path Cargo.toml
# Copy in the rest of the rust files
COPY packages/backend-rs packages/backend-rs/
# COPY packages/macro-rs packages/macro-rs/
# Compile backend-rs
RUN NODE_ENV='production' pnpm run --filter backend-rs build
# Copy/Overwrite index.js to mitigate the bug in napi-rs codegen
COPY packages/backend-rs/index.js packages/backend-rs/built/index.js
# Copy only the dependency-related files first, to cache efficiently
COPY package.json pnpm*.yaml ./
COPY packages/backend/package.json packages/backend/package.json
COPY packages/client/package.json packages/client/package.json
COPY packages/sw/package.json packages/sw/package.json
COPY packages/firefish-js/package.json packages/firefish-js/package.json
COPY packages/megalodon/package.json packages/megalodon/package.json
COPY packages/backend-rs/package.json packages/backend-rs/package.json
COPY packages/backend-rs/npm/linux-x64-musl/package.json packages/backend-rs/npm/linux-x64-musl/package.json
COPY packages/backend-rs/npm/linux-arm64-musl/package.json packages/backend-rs/npm/linux-arm64-musl/package.json
COPY pnpm-lock.yaml ./
# Configure pnpm, and install dev mode dependencies for compilation
RUN corepack enable && corepack prepare pnpm@latest --activate && pnpm install --frozen-lockfile
# Install dev mode dependencies for compilation
RUN pnpm install --frozen-lockfile
# Copy in the rest of the rust files
COPY packages/backend-rs packages/backend-rs/
# Compile backend-rs
RUN NODE_ENV='production' pnpm run --filter backend-rs build
# Copy in the rest of the files to compile
# Copy in the rest of the files to build
COPY . ./
RUN NODE_ENV='production' pnpm run --filter firefish-js build
RUN NODE_ENV='production' pnpm run --recursive --parallel --filter '!backend-rs' --filter '!firefish-js' build && pnpm run gulp
# Build other workspaces
RUN NODE_ENV='production' pnpm run --recursive --filter '!backend-rs' build && pnpm run build:assets
# Trim down the dependencies to only those for production
RUN find . -path '*/node_modules/*' -delete && pnpm install --prod --frozen-lockfile

View file

@ -3,7 +3,7 @@ export
.PHONY: pre-commit
pre-commit: format entities napi-index
pre-commit: format entities napi
.PHONY: format
format:
@ -11,11 +11,12 @@ format:
.PHONY: entities
entities:
pnpm --filter=backend run build:debug
pnpm run migrate
$(MAKE) -C ./packages/backend-rs regenerate-entities
.PHONY: napi-index
napi-index:
.PHONY: napi
napi:
$(MAKE) -C ./packages/backend-rs update-index

View file

@ -1,7 +1,7 @@
{
"$schema": "https://biomejs.dev/schemas/1.6.4/schema.json",
"organizeImports": {
"enabled": true
"enabled": false
},
"linter": {
"enabled": true,
@ -14,14 +14,15 @@
},
"overrides": [
{
"include": ["*.vue"],
"include": ["*.vue", "packages/client/*.ts"],
"linter": {
"rules": {
"style": {
"useImportType": "warn",
"useShorthandFunctionType": "warn",
"useTemplate": "warn",
"noNonNullAssertion": "off"
"noNonNullAssertion": "off",
"useNodejsImportProtocol": "off"
}
}
}

3
ci/cargo/config.toml Normal file
View file

@ -0,0 +1,3 @@
[target.x86_64-unknown-linux-gnu]
linker = "/usr/bin/clang"
rustflags = ["-C", "link-arg=--ld-path=/usr/bin/mold"]

View file

@ -1,4 +1,4 @@
COMPOSE='docker compose'
COMPOSE='podman-compose'
POSTGRES_PASSWORD=password
POSTGRES_USER=firefish
POSTGRES_DB=firefish_db

View file

@ -7,6 +7,8 @@
- Node.js
- pnpm
- Rust toolchain
- Python 3
- Perl
- FFmpeg
- Container runtime
- [Docker](https://docs.docker.com/get-docker/)
@ -31,7 +33,7 @@ You can refer to [local-installation.md](./local-installation.md) to install the
1. Copy example config file
```sh
cp dev/config.example.env dev/config.env
# If you use container runtime other than Docker, you need to modify the "COMPOSE" variable
# If you use container runtime other than Podman, you need to modify the "COMPOSE" variable
# vim dev/config.env
```
1. Create `.config/default.yml` with the following content
@ -51,12 +53,7 @@ You can refer to [local-installation.md](./local-installation.md) to install the
host: localhost
port: 26379
logLevel: [
'error',
'success',
'warning',
'info'
]
maxlogLevel: 'debug' # or 'trace'
```
1. Start database containers
```sh
@ -84,6 +81,19 @@ You can refer to [local-installation.md](./local-installation.md) to install the
DONE * [core boot] Now listening on port 3000 on http://localhost:3000
```
## Update auto-generated files in `package/backend-rs`
You need to install `sea-orm-cli` to regenerate database entities.
```sh
cargo install sea-orm-cli
```
```sh
make entities
make napi
```
## Reset the environment
You can recreate a fresh local Firefish environment by recreating the database containers:

View file

@ -42,6 +42,8 @@ cargo --version
### PostgreSQL and PGroonga
Firefish requires PostgreSQL v12 or later. We recommend that you install v12.x for the same reason as Node.js.
PostgreSQL install instructions can be found at [this page](https://www.postgresql.org/download/).
```sh
@ -139,12 +141,7 @@ sudo apt install ffmpeg
host: localhost
port: 6379
logLevel: [
'error',
'success',
'warning',
'info'
]
maxLogLevel: 'debug' # or 'trace'
```
## 4. Build and start Firefish

View file

@ -17,6 +17,7 @@ services:
# - web
environment:
NODE_ENV: production
NODE_OPTIONS: --max-old-space-size=3072
volumes:
- ./custom:/firefish/custom:ro
- ./files:/firefish/files

View file

@ -2,6 +2,27 @@
Breaking changes are indicated by the :warning: icon.
## v20240516
- :warning: `server-info` (an endpoint to get server hardware information) now requires credentials.
- :warning: `net` (server's default network interface) has been removed from `admin/server-info`.
- Adding `lang` to the response of `i` and the request parameter of `i/update`.
## v20240504
- :warning: Removed `release` endpoint.
## v20240424
- Added `antennaLimit` field to the response of `meta` and `admin/meta`, and the request of `admin/update-meta` (optional).
- Added `filter` optional parameter to `notes/renotes` endpoint to filter the types of renotes. It can take the following values:
- `all` (default)
- `renote`
- `quote`
- :warning: Removed the following optional parameters in `notes/reactions`, as they were never taken into account due to a bug:
- `sinceId`
- `untilId`
## v20240413
- :warning: Removed `patrons` endpoint.

View file

@ -5,6 +5,36 @@ Critical security updates are indicated by the :warning: icon.
- Server administrators should check [notice-for-admins.md](./notice-for-admins.md) as well.
- Third-party client/bot developers may want to check [api-change.md](./api-change.md) as well.
## [v20240516](https://firefish.dev/firefish/firefish/-/merge_requests/10854/commits)
- Improve timeline UX (you can restore the original appearance by settings)
- Remove `$[center]` MFM function
- This function was suddenly added last year (https://firefish.dev/firefish/firefish/-/commit/1a971efa689323d54eebb4d3646e102fb4d1d95a), but according to the [MFM spec](https://github.com/misskey-dev/mfm.js/blob/6aaf68089023c6adebe44123eebbc4dcd75955e0/docs/syntax.md#fn), `$[something]` must be an inline element (while `center` is a block element), so such a syntax is not expected by MFM renderers. Please use `<center></center>` instead.
- Fix bugs
## [v20240504](https://firefish.dev/firefish/firefish/-/merge_requests/10790/commits)
- Fix bugs
## :warning: [v20240430](https://firefish.dev/firefish/firefish/-/merge_requests/10781/commits)
- Add ability to group similar notifications
- Add features to share links to an account in the three dots menu on the profile page
- Improve server logs
- Fix bugs (including a critical security issue)
- We are very thankful to @tesaguri and Laura Hausmann for helping to fix the security issue.
## [v20240424](https://firefish.dev/firefish/firefish/-/merge_requests/10765/commits)
- Improve the usability of the feature to prevent forgetting to write alt texts
- Add a server-wide setting for the maximum number of antennas each user can create
- Fix bugs (including a medium severity security issue)
- We are very thankful to @mei23 for kindly sharing the information about the security issue.
## [v20240421](https://firefish.dev/firefish/firefish/-/merge_requests/10756/commits)
- Fix bugs
## [v20240413](https://firefish.dev/firefish/firefish/-/merge_requests/10741/commits)
- Add "Media" tab to user page

View file

@ -1,6 +1,14 @@
BEGIN;
DELETE FROM "migrations" WHERE name IN (
'UserprofileJsonbToArray1714270605574',
'DropUnusedUserprofileColumns1714259023878',
'AntennaJsonbToArray1714192520471',
'AddUserProfileLanguage1714888400293',
'DropUnusedIndexes1714643926317',
'AlterAkaType1714099399879',
'AddDriveFileUsage1713451569342',
'ConvertCwVarcharToText1713225866247',
'FixChatFileConstraint1712855579316',
'DropTimeZone1712425488543',
'ExpandNoteEdit1711936358554',
@ -22,6 +30,77 @@ DELETE FROM "migrations" WHERE name IN (
'RemoveNativeUtilsMigration1705877093218'
);
-- userprofile-jsonb-to-array
ALTER TABLE "user_profile" RENAME COLUMN "mutedInstances" TO "mutedInstances_old";
ALTER TABLE "user_profile" ADD COLUMN "mutedInstances" jsonb NOT NULL DEFAULT '[]';
UPDATE "user_profile" SET "mutedInstances" = to_jsonb("mutedInstances_old");
ALTER TABLE "user_profile" DROP COLUMN "mutedInstances_old";
ALTER TABLE "user_profile" RENAME COLUMN "mutedWords" TO "mutedWords_old";
ALTER TABLE "user_profile" ADD COLUMN "mutedWords" jsonb NOT NULL DEFAULT '[]';
CREATE TEMP TABLE "BCrsGgLCUeMMLARy" ("userId" character varying(32), "kws" jsonb NOT NULL DEFAULT '[]');
INSERT INTO "BCrsGgLCUeMMLARy" ("userId", "kws") SELECT "userId", jsonb_agg("X"."w") FROM (SELECT "userId", to_jsonb(string_to_array(unnest("mutedWords_old"), ' ')) AS "w" FROM "user_profile") AS "X" GROUP BY "userId";
UPDATE "user_profile" SET "mutedWords" = "kws" FROM "BCrsGgLCUeMMLARy" WHERE "user_profile"."userId" = "BCrsGgLCUeMMLARy"."userId";
ALTER TABLE "user_profile" DROP COLUMN "mutedWords_old";
-- drop-unused-userprofile-columns
ALTER TABLE "user_profile" ADD "room" jsonb NOT NULL DEFAULT '{}';
COMMENT ON COLUMN "user_profile"."room" IS 'The room data of the User.';
ALTER TABLE "user_profile" ADD "clientData" jsonb NOT NULL DEFAULT '{}';
COMMENT ON COLUMN "user_profile"."clientData" IS 'The client-specific data of the User.';
-- antenna-jsonb-to-array
UPDATE "antenna" SET "instances" = '{""}' WHERE "instances" = '{}';
ALTER TABLE "antenna" RENAME COLUMN "instances" TO "instances_old";
ALTER TABLE "antenna" ADD COLUMN "instances" jsonb NOT NULL DEFAULT '[]';
UPDATE "antenna" SET "instances" = to_jsonb("instances_old");
ALTER TABLE "antenna" DROP COLUMN "instances_old";
UPDATE "antenna" SET "keywords" = '{""}' WHERE "keywords" = '{}';
ALTER TABLE "antenna" RENAME COLUMN "keywords" TO "keywords_old";
ALTER TABLE "antenna" ADD COLUMN "keywords" jsonb NOT NULL DEFAULT '[]';
CREATE TEMP TABLE "QvPNcMitBFkqqBgm" ("id" character varying(32), "kws" jsonb NOT NULL DEFAULT '[]');
INSERT INTO "QvPNcMitBFkqqBgm" ("id", "kws") SELECT "id", jsonb_agg("X"."w") FROM (SELECT "id", to_jsonb(string_to_array(unnest("keywords_old"), ' ')) AS "w" FROM "antenna") AS "X" GROUP BY "id";
UPDATE "antenna" SET "keywords" = "kws" FROM "QvPNcMitBFkqqBgm" WHERE "antenna"."id" = "QvPNcMitBFkqqBgm"."id";
ALTER TABLE "antenna" DROP COLUMN "keywords_old";
UPDATE "antenna" SET "excludeKeywords" = '{""}' WHERE "excludeKeywords" = '{}';
ALTER TABLE "antenna" RENAME COLUMN "excludeKeywords" TO "excludeKeywords_old";
ALTER TABLE "antenna" ADD COLUMN "excludeKeywords" jsonb NOT NULL DEFAULT '[]';
CREATE TEMP TABLE "MZvVSjHzYcGXmGmz" ("id" character varying(32), "kws" jsonb NOT NULL DEFAULT '[]');
INSERT INTO "MZvVSjHzYcGXmGmz" ("id", "kws") SELECT "id", jsonb_agg("X"."w") FROM (SELECT "id", to_jsonb(string_to_array(unnest("excludeKeywords_old"), ' ')) AS "w" FROM "antenna") AS "X" GROUP BY "id";
UPDATE "antenna" SET "excludeKeywords" = "kws" FROM "MZvVSjHzYcGXmGmz" WHERE "antenna"."id" = "MZvVSjHzYcGXmGmz"."id";
ALTER TABLE "antenna" DROP COLUMN "excludeKeywords_old";
-- drop-unused-indexes
CREATE INDEX "IDX_01f4581f114e0ebd2bbb876f0b" ON "note_reaction" ("createdAt");
CREATE INDEX "IDX_0610ebcfcfb4a18441a9bcdab2" ON "poll" ("userId");
CREATE INDEX "IDX_25dfc71b0369b003a4cd434d0b" ON "note" ("attachedFileTypes");
CREATE INDEX "IDX_2710a55f826ee236ea1a62698f" ON "hashtag" ("mentionedUsersCount");
CREATE INDEX "IDX_4c02d38a976c3ae132228c6fce" ON "hashtag" ("mentionedRemoteUsersCount");
CREATE INDEX "IDX_51c063b6a133a9cb87145450f5" ON "note" ("fileIds");
CREATE INDEX "IDX_54ebcb6d27222913b908d56fd8" ON "note" ("mentions");
CREATE INDEX "IDX_7fa20a12319c7f6dc3aed98c0a" ON "poll" ("userHost");
CREATE INDEX "IDX_88937d94d7443d9a99a76fa5c0" ON "note" ("tags");
CREATE INDEX "IDX_b11a5e627c41d4dc3170f1d370" ON "notification" ("createdAt");
CREATE INDEX "IDX_c8dfad3b72196dd1d6b5db168a" ON "drive_file" ("createdAt");
CREATE INDEX "IDX_d57f9030cd3af7f63ffb1c267c" ON "hashtag" ("attachedUsersCount");
CREATE INDEX "IDX_e5848eac4940934e23dbc17581" ON "drive_file" ("uri");
CREATE INDEX "IDX_fa99d777623947a5b05f394cae" ON "user" ("tags");
-- alter-aka-type
ALTER TABLE "user" RENAME COLUMN "alsoKnownAs" TO "alsoKnownAsOld";
ALTER TABLE "user" ADD COLUMN "alsoKnownAs" text;
UPDATE "user" SET "alsoKnownAs" = array_to_string("alsoKnownAsOld", ',');
COMMENT ON COLUMN "user"."alsoKnownAs" IS 'URIs the user is known as too';
ALTER TABLE "user" DROP COLUMN "alsoKnownAsOld";
-- AddDriveFileUsage
ALTER TABLE "drive_file" DROP COLUMN "usageHint";
DROP TYPE "drive_file_usage_hint_enum";
-- convert-cw-varchar-to-text
DROP INDEX "IDX_8e3bbbeb3df04d1a8105da4c8f";
ALTER TABLE "note" ALTER COLUMN "cw" TYPE character varying(512);
CREATE INDEX "IDX_8e3bbbeb3df04d1a8105da4c8f" ON "note" USING "pgroonga" ("cw" pgroonga_varchar_full_text_search_ops_v2);
-- fix-chat-file-constraint
ALTER TABLE "messaging_message" DROP CONSTRAINT "FK_535def119223ac05ad3fa9ef64b";
ALTER TABLE "messaging_message" ADD CONSTRAINT "FK_535def119223ac05ad3fa9ef64b" FOREIGN KEY ("fileId") REFERENCES "drive_file"("id") ON DELETE CASCADE ON UPDATE NO ACTION;
@ -728,9 +807,6 @@ CREATE SEQUENCE public.__chart_day__users_id_seq
CACHE 1;
ALTER SEQUENCE public.__chart_day__users_id_seq OWNED BY public.__chart_day__users.id;
-- drop-user-profile-language
ALTER TABLE "user_profile" ADD COLUMN "lang" character varying(32);
-- emoji-moderator
ALTER TABLE "user" DROP COLUMN "emojiModPerm";
DROP TYPE "public"."user_emojimodperm_enum";

View file

@ -1,9 +1,37 @@
# Install Firefish
This document shows an example procedure for installing Firefish on Debian 12. Note that there is much room for customizing the server setup; this document merely demonstrates a simple installation.
Firefish depends on the following software.
## Runtime dependencies
- At least [NodeJS](https://nodejs.org/en/) v18.17.0 (v20/v21 recommended)
- At least [PostgreSQL](https://www.postgresql.org/) v12 (v16 recommended) with [PGroonga](https://pgroonga.github.io/) extension
- At least [Redis](https://redis.io/) v7
- Web Proxy (one of the following)
- Caddy (recommended)
- Nginx (recommended)
- Apache
- [FFmpeg](https://ffmpeg.org/) for video transcoding (**optional**)
- Caching server (**optional**, one of the following)
- [DragonflyDB](https://www.dragonflydb.io/)
- [KeyDB](https://keydb.dev/)
- Another [Redis](https://redis.io/) server
## Build dependencies
- At least [Rust](https://www.rust-lang.org/) v1.74
- C/C++ compiler & build tools
- `build-essential` on Debian/Ubuntu Linux
- `base-devel` on Arch Linux
- [Python 3](https://www.python.org/)
- [Perl](https://www.perl.org/)
This document shows an example procedure for installing these dependencies and Firefish on Debian 12. Note that there is much room for customizing the server setup; this document merely demonstrates a simple installation.
If you want to use the pre-built container image, please refer to [`install-container.md`](./install-container.md).
If you do not prepare your environment as document, be sure to meet the minimum dependencies given at the bottom of the page.
Make sure that you can use the `sudo` command before proceeding.
## 1. Install dependencies
@ -154,7 +182,7 @@ sudo apt install ffmpeg
1. Build
```sh
pnpm install --frozen-lockfile
NODE_ENV=production pnpm run build
NODE_ENV=production NODE_OPTIONS='--max-old-space-size=3072' pnpm run build
```
1. Execute database migrations
```sh
@ -242,6 +270,7 @@ In this instruction, we use [Caddy](https://caddyserver.com/) to make the Firefi
WorkingDirectory=/home/firefish/firefish
Environment="NODE_ENV=production"
Environment="npm_config_cache=/tmp"
Environment="NODE_OPTIONS=--max-old-space-size=3072"
# uncomment the following line if you use jemalloc (note that the path varies on different environments)
# Environment="LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libjemalloc.so.2"
StandardOutput=journal
@ -297,7 +326,7 @@ cd ~/firefish
- To add custom locales, place them in the `./custom/locales/` directory. If you name your custom locale the same as an existing locale, it will overwrite it. If you give it a unique name, it will be added to the list. Also make sure that the first part of the filename matches the locale you're basing it on. (Example: `en-FOO.yml`)
- To add custom error images, place them in the `./custom/assets/badges` directory, replacing the files already there.
- To add custom sounds, place only mp3 files in the `./custom/assets/sounds` directory.
- To update custom assets without rebuilding, just run `pnpm run gulp`.
- To update custom assets without rebuilding, just run `pnpm run build:assets`.
- To block ChatGPT, CommonCrawl, or other crawlers from indexing your instance, uncomment the respective rules in `./custom/robots.txt`.
## Tips & Tricks

View file

@ -2,6 +2,39 @@
You can skip intermediate versions when upgrading from an old version, but please read the notices and follow the instructions for each intermediate version before [upgrading](./upgrade.md).
## v20240516
### For all users
Firefish is now compatible with [Node v22](https://nodejs.org/en/blog/announcements/v22-release-announce). The pre-built OCI container image will still be using the latest LTS version (v20.13.1 as of now).
## v20240430
### For all users
You can control the verbosity of the server log by adding `maxLogLevel` in `.config/default.yml`. `logLevels` has been deprecated in favor of this setting. (see also: <https://firefish.dev/firefish/firefish/-/blob/eac0c1c47cd23789dcc395ab08b074934409fd96/.config/example.yml#L152>)
### For systemd/pm2 users
- You need to install Perl to build Firefish. Since Git depends on Perl in many packaging systems, you probably already have Perl installed on your system. You can check the Perl version by this command:
```sh
perl --version
```
- Not only Firefish but also Node.js has recently fixed a few security issues:
- https://nodejs.org/en/blog/vulnerability/april-2024-security-releases
- https://nodejs.org/en/blog/vulnerability/april-2024-security-releases-2
So, it is highly recommended that you upgrade your Node.js version as well. The new versions are
- Node v18.20.2 (v18.x LTS)
- Node v20.12.2 (v20.x LTS)
- Node v21.7.3 (v21.x)
You can check your Node.js version by this command:
```sh
node --version
```
[Node v22](https://nodejs.org/en/blog/announcements/v22-release-announce) was also released several days ago, but we have not yet tested Firefish with this version.
## v20240413
### For all users

View file

@ -1,101 +0,0 @@
/**
* Gulp tasks
*/
const fs = require("fs");
const gulp = require("gulp");
const replace = require("gulp-replace");
const terser = require("gulp-terser");
const cssnano = require("gulp-cssnano");
const meta = require("./package.json");
gulp.task("copy:backend:views", () =>
gulp
.src("./packages/backend/src/server/web/views/**/*")
.pipe(gulp.dest("./packages/backend/built/server/web/views")),
);
gulp.task("copy:backend:custom", () =>
gulp
.src("./custom/assets/**/*")
.pipe(gulp.dest("./packages/backend/assets/")),
);
gulp.task("copy:client:fonts", () =>
gulp
.src("./packages/client/node_modules/three/examples/fonts/**/*")
.pipe(gulp.dest("./built/_client_dist_/fonts/")),
);
gulp.task("copy:client:locales", async (cb) => {
fs.mkdirSync("./built/_client_dist_/locales", { recursive: true });
const { default: locales } = await import("./locales/index.mjs");
const v = { _version_: meta.version };
for (const [lang, locale] of Object.entries(locales)) {
fs.writeFileSync(
`./built/_client_dist_/locales/${lang}.${meta.version}.json`,
JSON.stringify({ ...locale, ...v }),
"utf-8",
);
}
cb();
});
gulp.task("build:backend:script", async () => {
const { default: locales } = await import("./locales/index.mjs");
return gulp
.src([
"./packages/backend/src/server/web/boot.js",
"./packages/backend/src/server/web/bios.js",
"./packages/backend/src/server/web/cli.js",
])
.pipe(replace("SUPPORTED_LANGS", JSON.stringify(Object.keys(locales))))
.pipe(
terser({
toplevel: true,
}),
)
.pipe(gulp.dest("./packages/backend/built/server/web/"));
});
gulp.task("build:backend:style", () => {
return gulp
.src([
"./packages/backend/src/server/web/style.css",
"./packages/backend/src/server/web/bios.css",
"./packages/backend/src/server/web/cli.css",
])
.pipe(
cssnano({
zindex: false,
}),
)
.pipe(gulp.dest("./packages/backend/built/server/web/"));
});
gulp.task(
"build",
gulp.parallel(
"copy:client:locales",
"copy:backend:views",
"copy:backend:custom",
"build:backend:script",
"build:backend:style",
"copy:client:fonts",
),
);
gulp.task("default", gulp.task("build"));
gulp.task("watch", () => {
gulp.watch(
["./packages/*/src/**/*"],
{ ignoreInitial: false },
gulp.task("build"),
);
});

View file

@ -19,7 +19,7 @@ deleteAndEditConfirm: Сигурни ли сте, че искате да изт
copyUsername: Копиране на потребителското име
searchUser: Търсене на потребител
reply: Отговор
showMore: Покажи още
showMore: Показване на повече
loadMore: Зареди още
followRequestAccepted: Заявката за последване е приета
importAndExport: Импорт/експорт на данни
@ -69,7 +69,7 @@ renameFile: Преименуване на файла
_widgets:
activity: Дейност
notifications: Известия
timeline: Инфопоток
timeline: Хронология
clock: Часовник
trends: Актуални
photos: Снимки
@ -187,7 +187,7 @@ notesAndReplies: Публикации и отговори
noSuchUser: Потребителят не е намерен
pinnedPages: Закачени страници
pinLimitExceeded: Не може да закачаш повече публикации
flagShowTimelineReplies: Показване на отговори в инфопотока
flagShowTimelineReplies: Показване на отговори в хронологията
followersCount: Брой последователи
receivedReactionsCount: Брой получени реакции
federation: Федерация
@ -336,11 +336,15 @@ _pages:
title: Заглавие
my: Моите страници
pageSetting: Настройки на страницата
url: Адрес на страницата
summary: Кратко обобщение
alignCenter: Центриране на елементите
variables: Променливи
_deck:
_columns:
notifications: Известия
mentions: Споменавания
tl: Инфопоток
tl: Хронология
direct: Директни съобщения
list: Списък
antenna: Антена
@ -375,7 +379,7 @@ basicSettings: Основни настройки
otherSettings: Други настройки
openInWindow: Отваряне в прозорец
profile: Профил
timeline: Инфопоток
timeline: Хронология
noAccountDescription: Този потребител все още не е написал своята биография.
login: Вход
loggingIn: Вписване
@ -398,7 +402,7 @@ sendMessage: Изпращане на съобщение
jumpToPrevious: Премини към предишно
newer: по-ново
older: по-старо
showLess: Покажи по-малко
showLess: Показване на по-малко
youGotNewFollower: те последва
receiveFollowRequest: Заявка за последване получена
mention: Споменаване
@ -558,12 +562,12 @@ _visibility:
specified: Директна
localOnly: Само местни
public: Общодостъпна
publicDescription: Публикацията ще бъде видима във всички публични инфопотоци
publicDescription: Публикацията ще бъде видима във всички публични хронологии
home: Скрита
localOnlyDescription: Не е видима за отдалечени потребители
specifiedDescription: Видима само за определени потребители
followersDescription: Видима само за последователите ти и споменатите потребители
homeDescription: Публикуване само в началния инфопоток
homeDescription: Публикуване само в началната хронология
explore: Разглеждане
theme: Теми
wallpaper: Тапет
@ -594,21 +598,21 @@ _tutorial:
да разберат дали искат да видят вашите публикации или да ви следват.
title: Как се използва Firefish
step1_1: Добре дошли!
step5_1: Инфопотоци, инфопотоци навсякъде!
step5_1: Хронологии, хронологии навсякъде!
step3_1: Сега е време да последвате няколко хора!
step1_2: Нека да ви настроим. Ще бъдете готови за нула време!
step5_3: Началният {icon} инфопоток е мястото, където можете да видите публикации
step5_3: Началната {icon} хронология е мястото, където можете да видите публикации
от акаунтите, които следвате.
step6_1: И така, какво е това място?
step5_7: Глобалният {icon} инфопоток е мястото, където можете да видите публикации
step5_7: Глобалната {icon} хронология е мястото, където можете да видите публикации
от всеки друг свързан сървър.
step4_2: За първата си публикация някои хора обичат да правят публикация {introduction}
или просто „Здравей свят!“
step5_2: Вашият сървър има активирани {timelines} различни инфопотоци.
step5_4: Местният {icon} инфопоток е мястото, където можете да видите публикации
step5_2: Вашият сървър има активирани {timelines} различни хронологии.
step5_4: Местната {icon} хронология е мястото, където можете да видите публикации
от всички останали на този сървър.
step5_5: Социалният {icon} инфопоток е комбинация от Началния и Местния инфопоток.
step5_6: Препоръчаният {icon} инфопоток е мястото, където можете да видите публикации
step5_5: Социалната {icon} хронология е комбинация от Началната и Местната хронология.
step5_6: Препоръчаната {icon} хронология е мястото, където можете да видите публикации
от сървъри, препоръчани от администраторите.
step6_4: Сега отидете, изследвайте и се забавлявайте!
step6_3: Всеки сървър работи по различни начини и не всички сървъри работят с Firefish.
@ -754,7 +758,7 @@ _feeds:
general: Общи
metadata: Метаданни
disk: Диск
featured: Представени
featured: Препоръчано
yearsOld: на {age} години
reload: Опресняване
invites: Покани
@ -778,8 +782,8 @@ uploadFromUrl: Качване от URL адрес
instanceName: Име на сървъра
instanceDescription: Описание на сървъра
accept: Приемане
enableLocalTimeline: Включване на местния инфопоток
enableGlobalTimeline: Включване на глобалния инфопоток
enableLocalTimeline: Включване на местната хронология
enableGlobalTimeline: Включване на глобалната хронология
removeMember: Премахване на член
isAdmin: Администратор
isModerator: Модератор
@ -862,8 +866,8 @@ apply: Прилагане
selectAccount: Избор на акаунт
muteThread: Заглушаване на нишката
ffVisibility: Видимост на Последвани/Последователи
renoteMute: Заглушаване на подсилванията в инфопотоците
replyMute: Заглушаване на отговорите в инфопотоците
renoteMute: Заглуш. на подсилванията в хронолог.
replyMute: Заглуш. на отговорите в хронолог.
blockConfirm: Сигурни ли сте, че искате да блокирате този акаунт?
appearance: Облик
fontSize: Размер на шрифта
@ -893,7 +897,7 @@ charts: Диаграми
disablePagesScript: Изключване на AiScript в Страниците
updatedAt: Обновено на
privateDescription: Видима само за теб
enableTimelineStreaming: Автоматично обновяване на инфопотоците
enableTimelineStreaming: Автоматично обновяване на хронологиите
toEdit: Редактиране
showEmojisInReactionNotifications: Показване на емоджита в известията за реакции
rememberNoteVisibility: Запомняне на настройките за видимост на публикациите
@ -932,3 +936,19 @@ clientSettings: Настройки за устройството
behavior: Поведение
detectPostLanguage: Автоматично откриване на езика и показване на бутон за превеждане
за публикации на чужди езици
replyUnmute: Отмяна на заглушаването на отговорите
searchWords: Думи за търсене / ID или URL за поглеждане
reloadConfirm: Искате ли да опресните хронологията?
enableRecommendedTimeline: Включване на препоръчаната хронология
showGapBetweenNotesInTimeline: Показване на празнина между публикациите в хронологията
lookup: Поглеждане
media: Мултимедия
welcomeBackWithName: Добре дошли отново, {name}
reduceUiAnimation: Намаляване на UI анимациите
clickToFinishEmailVerification: Моля, натиснете [{ok}], за да завършите потвърждаването
на ел. поща.
_cw:
show: Показване на съдържанието
remoteFollow: Отдалечено последване
messagingUnencryptedInfo: Чатовете във Firefish не са шифровани от край до край. Не
споделяйте чувствителна информация през Firefish.

View file

@ -738,6 +738,7 @@ _notification:
reacted: Ha reaccionat a la teva publicació
renoted: Ha impulsat la teva publicació
voted: Ha votat a la teva enquesta
andCountUsers: I {count} usuaris més {acted}
_deck:
_columns:
notifications: "Notificacions"
@ -2289,3 +2290,20 @@ autocorrectNoteLanguage: Mostra un avís si l'idioma de la publicació no coinci
amb el resultat de l'idioma detectat automàticament
noteEditHistory: Historial d'edicions
media: Multimèdia
antennaLimit: El nombre màxim d'antenes que pot crear un usuari
showAddFileDescriptionAtFirstPost: Obra de forma automàtica un formulari per escriure
una descripció quant intentes publicar un fitxer que no en té
remoteFollow: Seguiment remot
cannotEditVisibility: No pots canviar la visibilitat
useThisAccountConfirm: Vols continuar amb aquest compte?
inputAccountId: Sisplau introdueix el teu compte (per exemple @firefish@info.firefish.dev)
getQrCode: Mostrar el codi QR
copyRemoteFollowUrl: Còpia la adreça URL del seguidor remot
foldNotification: Agrupar les notificacions similars
slashQuote: Cita encadenada
i18nServerInfo: Els nous clients els trobares en {language} per defecte.
i18nServerChange: Fes servir {language} en comptes.
i18nServerSet: Fes servir {language} per els nous clients.
mergeThreadInTimeline: Fusiona diferents publicacions en un mateix fil a les línies
de temps
mergeRenotesInTimeline: Agrupa diferents impulsos d'una mateixa publicació

View file

@ -394,6 +394,7 @@ enableRegistration: "Enable new user registration"
invite: "Invite"
driveCapacityPerLocalAccount: "Drive capacity per local user"
driveCapacityPerRemoteAccount: "Drive capacity per remote user"
antennaLimit: "The maximum number of antennas that each user can create"
inMb: "In megabytes"
iconUrl: "Icon URL"
bannerUrl: "Banner image URL"
@ -644,6 +645,7 @@ deletedNote: "Deleted post"
invisibleNote: "Invisible post"
enableInfiniteScroll: "Automatically load more"
visibility: "Visiblility"
cannotEditVisibility: "You can't edit the visibility"
poll: "Poll"
useCw: "Hide content"
enablePlayer: "Open video player"
@ -764,6 +766,9 @@ confirmToUnclipAlreadyClippedNote: "This post is already part of the \"{name}\"
public: "Public"
i18nInfo: "Firefish is being translated into various languages by volunteers. You
can help at {link}."
i18nServerInfo: "New clients will be in {language} by default."
i18nServerChange: "Use {language} instead."
i18nServerSet: "Use {language} for new clients."
manageAccessTokens: "Manage access tokens"
accountInfo: "Account Info"
notesCount: "Number of posts"
@ -1009,6 +1014,8 @@ isSystemAccount: "This account is created and automatically operated by the syst
Please do not moderate, edit, delete, or otherwise tamper with this account, or
it may break your server."
typeToConfirm: "Please enter {x} to confirm"
useThisAccountConfirm: "Do you want to continue with this account?"
inputAccountId: "Please input your account (e.g., @firefish@info.firefish.dev)"
deleteAccount: "Delete account"
document: "Documentation"
numberOfPageCache: "Number of cached pages"
@ -1155,6 +1162,9 @@ addRe: "Add \"re:\" at the beginning of comment in reply to a post with a conten
confirm: "Confirm"
importZip: "Import ZIP"
exportZip: "Export ZIP"
getQrCode: "Show QR code"
remoteFollow: "Remote follow"
copyRemoteFollowUrl: "Copy remote follow URL"
emojiPackCreator: "Emoji pack creator"
indexable: "Indexable"
indexableDescription: "Allow built-in search to show your public posts"
@ -1226,6 +1236,8 @@ publishTimelinesDescription: "If enabled, the Local and Global timelines will be
on {url} even when signed out."
noAltTextWarning: "Some attached file(s) have no description. Did you forget to write?"
showNoAltTextWarning: "Show a warning if you attempt to post files without a description"
showAddFileDescriptionAtFirstPost: "Automatically open a form to write a description when you
attempt to post files without a description"
_emojiModPerm:
unauthorized: "None"
@ -2143,6 +2155,7 @@ _notification:
reacted: "reacted to your post"
renoted: "boosted your post"
voted: "voted on your poll"
andCountUsers: "and {count} more users {acted}"
_types:
all: "All"
follow: "New followers"
@ -2229,3 +2242,7 @@ autocorrectNoteLanguage: "Show a warning if the post language does not match the
incorrectLanguageWarning: "It looks like your post is in {detected}, but you selected
{current}.\nWould you like to set the language to {detected} instead?"
noteEditHistory: "Post edit history"
slashQuote: "Chain quote"
foldNotification: "Group similar notifications"
mergeThreadInTimeline: "Merge multiple posts in the same thread in timelines"
mergeRenotesInTimeline: "Group multiple boosts of the same post"

1
locales/eo.yml Normal file
View file

@ -0,0 +1 @@
_lang_: "Esperanto"

View file

@ -928,6 +928,8 @@ colored: "Coloré"
label: "Étiquette"
localOnly: "Local seulement"
account: "Comptes"
getQrCode: "Afficher le code QR"
_emailUnavailable:
used: "Adresse non disponible"
format: "Le format de cette adresse de courriel est invalide"
@ -1140,8 +1142,8 @@ _wordMute:
mutedNotes: "Publications masquées"
muteLangsDescription2: Utiliser les codes de langue (i.e en, fr, ja, zh).
lang: Langue
langDescription: Cacher du fil de publication les publications qui correspondent
à ces langues.
langDescription: Cachez les publications qui correspondent à la langue définie dans
le fil d'actualité.
muteLangs: Langues filtrées
muteLangsDescription: Séparer avec des espaces ou des retours à la ligne pour une
condition OU (OR).
@ -1258,7 +1260,7 @@ _tutorial:
step2_2: "En fournissant quelques informations sur qui vous êtes, il sera plus facile
pour les autres de savoir s'ils veulent voir vos publcations ou s'abonner à vous."
step3_1: "Maintenant il est temps de vous abonner à des gens!"
step3_2: "Vos fils d'actualités Principal et Social sont basés sur les personnes
step3_2: "Vos fils d'actualité Principal et Social sont basés sur les personnes
que vous êtes abonné, alors essayez de vous abonner à quelques comptes pour commencer.\n
Cliquez sur le cercle « plus » en haut à droite d'un profil pour vous abonner."
step4_1: "On y va."
@ -1834,6 +1836,7 @@ _notification:
reacted: a réagit à votre publication
renoted: a boosté votre publication
voted: a voté pour votre sondage
andCountUsers: et {count} utilisateur(s) de plus {acted}
_deck:
alwaysShowMainColumn: "Toujours afficher la colonne principale"
columnAlign: "Aligner les colonnes"
@ -2318,3 +2321,20 @@ markLocalFilesNsfwByDefaultDescription: Indépendamment de ce réglage, les util
peuvent supprimer le drapeau « sensible » (NSFW) eux-mêmes. Les fichiers existants
ne sont pas affectés.
noteEditHistory: Historique des publications
media: Multimédia
antennaLimit: Le nombre maximal d'antennes que chaque utilisateur peut créer
showAddFileDescriptionAtFirstPost: Ouvrez automatiquement un formulaire pour écrire
une description lorsque vous tentez de publier des fichiers sans description
foldNotification: Grouper les notifications similaires
cannotEditVisibility: Vous ne pouvez pas modifier la visibilité
useThisAccountConfirm: Voulez-vous continuer avec ce compte?
inputAccountId: Veuillez saisir votre compte (par exemple, @firefish@info.firefish.dev)
remoteFollow: Abonnement à distance
copyRemoteFollowUrl: Copier l'URL d'abonnement à distance
slashQuote: Citation enchaînée
i18nServerInfo: Les nouveaux clients seront en {language} par défaut.
i18nServerChange: Utilisez {language} à la place.
i18nServerSet: Utilisez {language} pour les nouveaux clients.
mergeThreadInTimeline: Fusionner plusieurs publications dans le même fil dans les
fils d'actualité
mergeRenotesInTimeline: Regrouper plusieurs boosts du même publication

View file

@ -1825,6 +1825,7 @@ _notification:
reacted: mereaksi postinganmu
renoted: memposting ulang postinganmu
voted: memilih di angketmu
andCountUsers: dan {count} lebih banyak pengguna {acted}
_deck:
alwaysShowMainColumn: "Selalu tampilkan kolom utama"
columnAlign: "Luruskan kolom"
@ -2267,3 +2268,14 @@ markLocalFilesNsfwByDefaultDescription: Terlepas dari pengaturan ini, pengguna d
menghapus sendiri tanda NSFW. Berkas yang ada tidak berpengaruh.
noteEditHistory: Riwayat penyuntingan kiriman
media: Media
antennaLimit: Jumlah antena maksimum yang dapat dibuat oleh setiap pengguna
showAddFileDescriptionAtFirstPost: Buka formulir secara otomatis untuk menulis deskripsi
ketika mencoba mengirim berkas tanpa deskripsi
remoteFollow: Ikuti jarak jauh
foldNotification: Kelompokkan notifikasi yang sama
getQrCode: Tampilkan kode QR
cannotEditVisibility: Kamu tidak bisa menyunting keterlihatan
useThisAccountConfirm: Apakah kamu ingin melanjutkan dengan akun ini?
inputAccountId: Silakan memasukkan akunmu (misalnya, @firefish@info.firefish.dev)
copyRemoteFollowUrl: Salin URL ikuti jarak jauh
slashQuote: Kutipan rantai

View file

@ -685,6 +685,9 @@ unclip: "クリップ解除"
confirmToUnclipAlreadyClippedNote: "この投稿はすでにクリップ「{name}」に含まれています。投稿をこのクリップから除外しますか?"
public: "公開"
i18nInfo: "Firefishは有志によって様々な言語に翻訳されています。{link}で翻訳に協力できます。"
i18nServerInfo: "新しい端末では{language}が既定の言語になります。"
i18nServerChange: "{language}に変更する。"
i18nServerSet: "新しい端末での表示言語を{language}にします。"
manageAccessTokens: "アクセストークンの管理"
accountInfo: "アカウント情報"
notesCount: "投稿の数"
@ -1902,6 +1905,7 @@ _notification:
reacted: がリアクションしました
renoted: がブーストしました
voted: が投票しました
andCountUsers: と{count}人{acted}
_deck:
alwaysShowMainColumn: "常にメインカラムを表示"
columnAlign: "カラムの寄せ"
@ -2057,3 +2061,15 @@ incorrectLanguageWarning: "この投稿は{detected}で書かれていると判
markLocalFilesNsfwByDefault: このサーバーの全てのファイルをデフォルトでNSFWに設定する
markLocalFilesNsfwByDefaultDescription: この設定が有効でも、ユーザーは自分でNSFWのフラグを外すことができます。また、この設定は既存のファイルには影響しません。
noteEditHistory: 編集履歴
showAddFileDescriptionAtFirstPost: 説明の無い添付ファイルを投稿しようとした際に説明を書く画面を自動で開く
antennaLimit: 各ユーザーが作れるアンテナの最大数
inputAccountId: 'あなたのアカウントを入力してください(例: @firefish@info.firefish.dev'
remoteFollow: リモートフォロー
cannotEditVisibility: 公開範囲は変更できません
useThisAccountConfirm: このアカウントで操作を続けますか?
getQrCode: QRコードを表示
copyRemoteFollowUrl: リモートからフォローするURLをコピー
foldNotification: 同じ種類の通知をまとめて表示する
slashQuote: 繋げて引用
mergeRenotesInTimeline: タイムラインで同じ投稿のブーストをまとめる
mergeThreadInTimeline: タイムラインで同じスレッドの投稿をまとめる

View file

@ -340,6 +340,7 @@ invite: "邀请"
driveCapacityPerLocalAccount: "每个本地用户的网盘容量"
driveCapacityPerRemoteAccount: "每个远程用户的网盘容量"
inMb: "以兆字节 (MegaByte) 为单位"
antennaLimit: "每个用户最多可以创建的天线数量"
iconUrl: "图标 URL"
bannerUrl: "横幅图 URL"
backgroundImageUrl: "背景图 URL"
@ -563,6 +564,7 @@ deletedNote: "已删除的帖子"
invisibleNote: "隐藏的帖子"
enableInfiniteScroll: "滚动页面以载入更多内容"
visibility: "可见性"
cannotEditVisibility: "不能编辑帖子的可见性"
poll: "调查问卷"
useCw: "隐藏内容"
enablePlayer: "打开播放器"
@ -665,6 +667,9 @@ unclip: "移除便签"
confirmToUnclipAlreadyClippedNote: "本帖已包含在便签 \"{name}\" 里。您想要将本帖从该便签中移除吗?"
public: "公开"
i18nInfo: "Firefish 已经被志愿者们翻译成了各种语言。如果您也有兴趣,可以通过 {link} 帮助翻译。"
i18nServerInfo: "新客户端将默认使用 {language}。"
i18nServerChange: "改为 {language}。"
i18nServerSet: "设定新客户端使用 {language}。"
manageAccessTokens: "管理访问令牌"
accountInfo: "账号信息"
notesCount: "帖子数量"
@ -877,6 +882,8 @@ driveCapOverrideCaption: "输入 0 或以下的值将容量重置为默认值。
requireAdminForView: "您需要使用管理员账号登录才能查看。"
isSystemAccount: "该账号由系统自动创建。请不要修改、编辑、删除或以其它方式篡改这个账号,否则可能会破坏您的服务器。"
typeToConfirm: "输入 {x} 以确认操作"
useThisAccountConfirm: "您想使用此帐户继续执行此操作吗?"
inputAccountId: "请输入您的帐户(例如 @firefish@info.firefish.dev "
deleteAccount: "删除账号"
document: "文档"
numberOfPageCache: "缓存页数"
@ -1385,7 +1392,7 @@ _poll:
_visibility:
public: "公开"
publicDescription: "您的帖子将出现在公共时间线上"
home: "公开"
home: "悄悄公开"
homeDescription: "仅发送至首页时间线"
followers: "仅关注者"
followersDescription: "仅对您的关注者和提及的用户可见"
@ -1786,6 +1793,7 @@ _notification:
reacted: 回应了您的帖子
voted: 在您的问卷调查中投了票
renoted: 转发了您的帖子
andCountUsers: "和其他 {count} 名用户{acted}"
_deck:
alwaysShowMainColumn: "总是显示主列"
columnAlign: "列对齐"
@ -1971,6 +1979,9 @@ origin: 起源
confirm: 确认
importZip: 导入 ZIP
exportZip: 导出 ZIP
getQrCode: "获取二维码"
remoteFollow: "远程关注"
copyRemoteFollowUrl: "复制远程关注 URL"
emojiPackCreator: 表情包创建工具
objectStorageS3ForcePathStyleDesc: 打开此选项可构建格式为 "s3.amazonaws.com/<bucket>/" 而非 "<bucket>.s3.amazonaws.com"
的端点 URL。
@ -2053,7 +2064,12 @@ searchRangeDescription: "如果您要过滤时间段,请按以下格式输入
messagingUnencryptedInfo: "Firefish 上的聊天没有经过端到端加密,请不要在聊天中分享您的敏感信息。"
noAltTextWarning: 有些附件没有描述。您是否忘记写描述了?
showNoAltTextWarning: 当您尝试发布没有描述的帖子附件时显示警告
showAddFileDescriptionAtFirstPost: 当您首次尝试发布没有描述的帖子附件时自动弹出添加描述页面
autocorrectNoteLanguage: 当帖子语言不符合自动检测的结果的时候显示警告
incorrectLanguageWarning: "看上去您帖子使用的语言是{detected},但您选择的语言是{current}。\n要改为以{detected}发帖吗?"
noteEditHistory: "帖子编辑历史"
media: 媒体
slashQuote: "斜杠引用"
foldNotification: "将通知按同类型分组"
mergeThreadInTimeline: "将时间线内的连续回复合并成一串"
mergeRenotesInTimeline: "合并同一个帖子的转发"

View file

@ -661,6 +661,9 @@ unclip: "解除摘錄"
confirmToUnclipAlreadyClippedNote: "此貼文已包含在摘錄「{name}」中。 你想將貼文從這個摘錄中排除嗎?"
public: "公開"
i18nInfo: "Firefish已經被志願者們翻譯成各種語言版本如果想要幫忙的話可以進入{link}幫助翻譯。"
i18nServerInfo: "新客戶端將默認使用 {language}。"
i18nServerChange: "改為 {language}。"
i18nServerSet: "設定新客戶端使用 {language}。"
manageAccessTokens: "管理存取權杖"
accountInfo: "帳戶資訊"
notesCount: "貼文數量"

View file

@ -1,22 +1,23 @@
{
"name": "firefish",
"version": "20240413",
"version": "20240516",
"repository": {
"type": "git",
"url": "https://firefish.dev/firefish/firefish.git"
},
"packageManager": "pnpm@8.15.6",
"packageManager": "pnpm@9.1.1",
"private": true,
"scripts": {
"rebuild": "pnpm run clean && pnpm run build",
"build": "pnpm node ./scripts/build.mjs && pnpm run gulp",
"build": "pnpm --recursive --color run build && pnpm node ./scripts/copy-index.mjs && pnpm run build:assets",
"build:assets": "pnpm node ./scripts/copy-assets.mjs",
"build:debug": "pnpm run clean && pnpm --recursive --color run build:debug && pnpm node ./scripts/copy-index-dev.mjs && pnpm run build:assets",
"start": "pnpm --filter backend run start",
"start:container": "pnpm run gulp && pnpm run migrate && pnpm run start",
"start:container": "pnpm run build:assets && pnpm run migrate && pnpm run start",
"start:test": "pnpm --filter backend run start:test",
"init": "pnpm run migrate",
"migrate": "pnpm --filter backend run migration:run",
"revertmigration": "pnpm --filter backend run migration:revert",
"gulp": "gulp build",
"watch": "pnpm run dev",
"dev": "pnpm node ./scripts/dev.mjs",
"dev:staging": "NODE_OPTIONS=--max_old_space_size=3072 NODE_ENV=development pnpm run build && pnpm run start",
@ -24,9 +25,10 @@
"lint:ts": "pnpm --filter !firefish-js -r --parallel run lint",
"lint:rs": "cargo clippy --fix --allow-dirty --allow-staged && cargo fmt --all --",
"debug": "pnpm run build:debug && pnpm run start",
"build:debug": "pnpm run clean && pnpm node ./scripts/dev-build.mjs && pnpm run gulp",
"mocha": "pnpm --filter backend run mocha",
"test": "pnpm run mocha",
"test": "pnpm run test:ts && pnpm run test:rs",
"test:ts": "pnpm run mocha",
"test:rs": "cargo test",
"format": "pnpm run format:ts; pnpm run format:rs",
"format:ts": "pnpm -r --parallel run format",
"format:rs": "cargo fmt --all --",
@ -36,21 +38,17 @@
"clean-all": "pnpm run clean && pnpm run clean-cargo && pnpm run clean-npm"
},
"dependencies": {
"js-yaml": "4.1.0",
"gulp": "4.0.2",
"gulp-cssnano": "2.1.3",
"gulp-replace": "1.1.4",
"gulp-terser": "2.1.0"
"js-yaml": "4.1.0"
},
"devDependencies": {
"@biomejs/biome": "1.6.4",
"@biomejs/cli-darwin-arm64": "^1.6.4",
"@biomejs/cli-darwin-x64": "^1.6.4",
"@biomejs/cli-linux-arm64": "^1.6.4",
"@biomejs/cli-linux-x64": "^1.6.4",
"@types/node": "20.12.7",
"execa": "8.0.1",
"pnpm": "8.15.6",
"@biomejs/biome": "1.7.3",
"@biomejs/cli-darwin-arm64": "1.7.3",
"@biomejs/cli-darwin-x64": "1.7.3",
"@biomejs/cli-linux-arm64": "1.7.3",
"@biomejs/cli-linux-x64": "1.7.3",
"@types/node": "20.12.12",
"execa": "9.1.0",
"pnpm": "9.1.1",
"typescript": "5.4.5"
}
}

View file

@ -4,6 +4,7 @@ This directory contains all of the packages Firefish uses.
- `backend`: Main backend code written in TypeScript for NodeJS
- `backend-rs`: Backend code written in Rust, bound to NodeJS by [NAPI-RS](https://napi.rs/)
- `macro-rs`: Procedural macros for backend-rs
- `client`: Web interface written in Vue3 and TypeScript
- `sw`: Web [Service Worker](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API) written in TypeScript
- `firefish-js`: TypeScript SDK for both backend and client

View file

@ -1,12 +0,0 @@
target
Cargo.lock
.cargo
.github
npm
.eslintrc
rustfmt.toml
yarn.lock
*.node
.yarn
__test__
renovate.json

View file

@ -7,6 +7,7 @@ rust-version = "1.74"
[features]
default = []
napi = ["dep:napi", "dep:napi-derive"]
ci = []
[lib]
crate-type = ["cdylib", "lib"]
@ -17,29 +18,41 @@ macro_rs = { workspace = true }
napi = { workspace = true, optional = true, default-features = false, features = ["napi9", "tokio_rt", "chrono_date", "serde-json"] }
napi-derive = { workspace = true, optional = true }
argon2 = { workspace = true, features = ["std"] }
async-trait = { workspace = true }
basen = { workspace = true }
cfg-if = { workspace = true }
bb8 = { workspace = true }
bcrypt = { workspace = true }
chrono = { workspace = true }
cuid2 = { workspace = true }
emojis = { workspace = true }
idna = { workspace = true }
jsonschema = { workspace = true }
image = { workspace = true }
isahc = { workspace = true }
nom-exif = { workspace = true }
once_cell = { workspace = true }
parse-display = { workspace = true }
openssl = { workspace = true, features = ["vendored"] }
rand = { workspace = true }
redis = { workspace = true, default-features = false, features = ["streams", "tokio-comp"] }
regex = { workspace = true }
schemars = { workspace = true, features = ["chrono"] }
rmp-serde = { workspace = true }
sea-orm = { workspace = true, features = ["sqlx-postgres", "runtime-tokio-rustls"] }
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true }
serde_yaml = { workspace = true }
strum = { workspace = true, features = ["derive"] }
sysinfo = { workspace = true }
thiserror = { workspace = true }
tokio = { workspace = true, features = ["full"] }
tracing = { workspace = true }
tracing-subscriber = { workspace = true }
url = { workspace = true }
urlencoding = { workspace = true }
web-push = { workspace = true }
[dev-dependencies]
pretty_assertions = { workspace = true }
tokio-test = { workspace = true }
[build-dependencies]
napi-build = { workspace = true }

View file

@ -6,6 +6,7 @@ SRC += $(call recursive_wildcard, src, *)
.PHONY: regenerate-entities
regenerate-entities:
rm --recursive --force src/model/entity
sea-orm-cli generate entity \
--output-dir='src/model/entity' \
--database-url='postgres://$(POSTGRES_USER):$(POSTGRES_PASSWORD)@localhost:25432/$(POSTGRES_DB)' \
@ -16,8 +17,9 @@ regenerate-entities:
jsname=$$(printf '%s\n' "$${base%.*}" | perl -pe 's/(^|_)./uc($$&)/ge;s/_//g'); \
attribute=$$(printf 'cfg_attr(feature = "napi", napi_derive::napi(object, js_name = "%s", use_nullable = true))' "$${jsname}"); \
sed -i "s/NAPI_EXTRA_ATTR_PLACEHOLDER/$${attribute}/" "$${file}"; \
sed -i 's/#\[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)\]/#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]\n#[serde(rename_all = "camelCase")]/' "$${file}"; \
done
sed -i 's/#\[derive(Debug, Clone, PartialEq, Eq, EnumIter, DeriveActiveEnum)\]/#[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum)]\n#[cfg_attr(not(feature = "napi"), derive(Clone))]\n#[cfg_attr(feature = "napi", napi_derive::napi)]/' \
sed -i 's/#\[derive(Debug, Clone, PartialEq, Eq, EnumIter, DeriveActiveEnum)\]/#[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum, serde::Serialize, serde::Deserialize)]\n#[serde(rename_all = "camelCase")]\n#[cfg_attr(not(feature = "napi"), derive(Clone))]\n#[cfg_attr(feature = "napi", napi_derive::napi(string_enum = "camelCase"))]/' \
src/model/entity/sea_orm_active_enums.rs
cargo fmt --all --

View file

@ -1,32 +0,0 @@
import test from "ava";
import {
convertId,
IdConvertType,
nativeInitIdGenerator,
nativeCreateId,
nativeRandomStr,
} from "../built/index.js";
test("convert to mastodon id", (t) => {
t.is(convertId("9gf61ehcxv", IdConvertType.MastodonId), "960365976481219");
t.is(
convertId("9fbr9z0wbrjqyd3u", IdConvertType.MastodonId),
"2083785058661759970208986",
);
t.is(
convertId("9fbs680oyviiqrol9md73p8g", IdConvertType.MastodonId),
"5878598648988104013828532260828151168",
);
});
test("create cuid2 with timestamp prefix", (t) => {
nativeInitIdGenerator(16, "");
t.not(nativeCreateId(Date.now()), nativeCreateId(Date.now()));
t.is(nativeCreateId(Date.now()).length, 16);
});
test("create random string", (t) => {
t.not(nativeRandomStr(16), nativeRandomStr(16));
t.is(nativeRandomStr(24).length, 24);
});

View file

@ -3,6 +3,31 @@
/* auto-generated by NAPI-RS */
export const SECOND: number
export const MINUTE: number
export const HOUR: number
export const DAY: number
export const USER_ONLINE_THRESHOLD: number
export const USER_ACTIVE_THRESHOLD: number
/**
* List of file types allowed to be viewed directly in the browser
* Anything not included here will be responded as application/octet-stream
* SVG is not allowed because it generates XSS <- we need to fix this and later allow it to be viewed directly
* https://github.com/sindresorhus/file-type/blob/main/supported.js
* https://github.com/sindresorhus/file-type/blob/main/core.js
* https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Containers
*/
export const FILE_TYPE_BROWSERSAFE: string[]
export interface EnvConfig {
onlyQueue: boolean
onlyServer: boolean
noDaemons: boolean
disableClustering: boolean
verbose: boolean
withLogTime: boolean
slow: boolean
}
export function loadEnv(): EnvConfig
export interface ServerConfig {
url: string
port: number
@ -16,10 +41,9 @@ export interface ServerConfig {
proxySmtp?: string
proxyBypassHosts?: Array<string>
allowedPrivateNetworks?: Array<string>
/** `NapiValue` is not implemented for `u64` */
maxFileSize?: number
accessLog?: string
clusterLimits?: WorkerConfig
clusterLimits?: WorkerConfigInternal
cuid?: IdConfig
outgoingAddress?: string
deliverJobConcurrency?: number
@ -28,7 +52,9 @@ export interface ServerConfig {
inboxJobPerSec?: number
deliverJobMaxAttempts?: number
inboxJobMaxAttempts?: number
/** deprecated */
logLevel?: Array<string>
maxLogLevel?: string
syslog?: SysLogConfig
proxyRemoteFiles?: boolean
mediaProxy?: string
@ -60,13 +86,17 @@ export interface RedisConfig {
pass?: string
tls?: TlsConfig
db: number
prefix: string
prefix?: string
}
export interface TlsConfig {
host: string
rejectUnauthorized: boolean
}
export interface WorkerConfig {
web: number
queue: number
}
export interface WorkerConfigInternal {
web?: number
queue?: number
}
@ -111,30 +141,186 @@ export interface ObjectStorageConfig {
setPublicReadOnUpload?: boolean
s3ForcePathStyle?: boolean
}
export function readServerConfig(): ServerConfig
export interface Config {
url: string
port: number
bind?: string
disableHsts?: boolean
db: DbConfig
redis: RedisConfig
cacheServer?: RedisConfig
proxy?: string
proxySmtp?: string
proxyBypassHosts?: Array<string>
allowedPrivateNetworks?: Array<string>
maxFileSize?: number
accessLog?: string
clusterLimits: WorkerConfig
cuid?: IdConfig
outgoingAddress?: string
deliverJobConcurrency?: number
inboxJobConcurrency?: number
deliverJobPerSec?: number
inboxJobPerSec?: number
deliverJobMaxAttempts?: number
inboxJobMaxAttempts?: number
/** deprecated */
logLevel?: Array<string>
maxLogLevel?: string
syslog?: SysLogConfig
proxyRemoteFiles?: boolean
mediaProxy?: string
summalyProxyUrl?: string
reservedUsernames?: Array<string>
maxUserSignups?: number
isManagedHosting?: boolean
maxNoteLength: number
maxCaptionLength: number
deepl?: DeepLConfig
libreTranslate?: LibreTranslateConfig
email?: EmailConfig
objectStorage?: ObjectStorageConfig
version: string
host: string
hostname: string
redisKeyPrefix: string
scheme: string
wsScheme: string
apiUrl: string
wsUrl: string
authUrl: string
driveUrl: string
userAgent: string
clientEntry: Manifest
}
export interface Manifest {
file: string
name: string
src: string
isEntry: boolean
isDynamicEntry: boolean
imports: Array<string>
dynamicImports: Array<string>
css: Array<string>
assets: Array<string>
}
export function loadConfig(): Config
export interface Acct {
username: string
host: string | null
}
export function stringToAcct(acct: string): Acct
export function acctToString(acct: Acct): string
export interface NoteLike {
fileIds: Array<string>
userId: string | null
text: string | null
cw: string | null
renoteId: string | null
replyId: string | null
}
export function checkWordMute(note: NoteLike, mutedWordLists: Array<Array<string>>, mutedPatterns: Array<string>): Promise<boolean>
export function initializeRustLogger(): void
export function showServerInfo(): void
/**
* Checks if a server is blocked.
*
* ## Argument
* `host` - punycoded instance host
*/
export function isBlockedServer(host: string): Promise<boolean>
/**
* Checks if a server is silenced.
*
* ## Argument
* `host` - punycoded instance host
*/
export function isSilencedServer(host: string): Promise<boolean>
/**
* Checks if a server is allowlisted.
* Returns `Ok(true)` if private mode is disabled.
*
* ## Argument
* `host` - punycoded instance host
*/
export function isAllowedServer(host: string): Promise<boolean>
export function checkWordMute(note: NoteLike, mutedWords: Array<string>, mutedPatterns: Array<string>): Promise<boolean>
export function getFullApAccount(username: string, host?: string | undefined | null): string
export function isSelfHost(host?: string | undefined | null): boolean
export function isSameOrigin(uri: string): boolean
export function extractHost(uri: string): string
export function toPuny(host: string): string
export function isUnicodeEmoji(s: string): boolean
export function sqlLikeEscape(src: string): string
export function safeForSql(src: string): boolean
/** Convert milliseconds to a human readable string */
export function formatMilliseconds(milliseconds: number): string
export interface ImageSize {
width: number
height: number
}
export function getImageSizeFromUrl(url: string): Promise<ImageSize>
/** TODO: handle name collisions better */
export interface NoteLikeForAllTexts {
fileIds: Array<string>
userId: string
text: string | null
cw: string | null
renoteId: string | null
replyId: string | null
}
export interface NoteLikeForGetNoteSummary {
fileIds: Array<string>
text: string | null
cw: string | null
hasPoll: boolean
}
export function getNoteSummary(note: NoteLikeForGetNoteSummary): string
export function isQuote(note: Note): boolean
export function isSafeUrl(url: string): boolean
export function latestVersion(): Promise<string>
export function toMastodonId(firefishId: string): string | null
export function fromMastodonId(mastodonId: string): string | null
export function fetchMeta(useCache: boolean): Promise<Meta>
export interface PugArgs {
img: string | null
title: string
instanceName: string
desc: string | null
icon: string | null
splashIcon: string | null
themeColor: string | null
randomMotd: string
privateMode: boolean | null
}
export function metaToPugArgs(meta: Meta): PugArgs
export function nyaify(text: string, lang?: string | undefined | null): string
export function hashPassword(password: string): string
export function verifyPassword(password: string, hash: string): boolean
export function isOldPasswordAlgorithm(hash: string): boolean
export interface DecodedReaction {
reaction: string
name: string | null
host: string | null
}
export function decodeReaction(reaction: string): DecodedReaction
export function countReactions(reactions: Record<string, number>): Record<string, number>
export function toDbReaction(reaction?: string | undefined | null, host?: string | undefined | null): Promise<string>
/** Delete all entries in the "attestation_challenge" table created at more than 5 minutes ago */
export function removeOldAttestationChallenges(): Promise<void>
export interface Cpu {
model: string
cores: number
}
export interface Memory {
/** Total memory amount in bytes */
total: number
/** Used memory amount in bytes */
used: number
/** Available (for (re)use) memory amount in bytes */
available: number
}
export interface Storage {
/** Total storage space in bytes */
total: number
/** Used storage space in bytes */
used: number
}
export function cpuInfo(): Cpu
export function cpuUsage(): number
export function memoryUsage(): Memory
export function storageUsage(): Storage | null
export interface AbuseUserReport {
id: string
createdAt: Date
@ -196,7 +382,6 @@ export interface Antenna {
name: string
src: AntennaSrcEnum
userListId: string | null
keywords: Json
withFile: boolean
expression: string | null
notify: boolean
@ -204,8 +389,9 @@ export interface Antenna {
withReplies: boolean
userGroupJoiningId: string | null
users: Array<string>
excludeKeywords: Json
instances: Json
instances: Array<string>
keywords: Array<string>
excludeKeywords: Array<string>
}
export interface App {
id: string
@ -300,6 +486,7 @@ export interface DriveFile {
webpublicType: string | null
requestHeaders: Json | null
requestIp: string | null
usageHint: DriveFileUsageHintEnum | null
}
export interface DriveFolder {
id: string
@ -505,6 +692,7 @@ export interface Meta {
donationLink: string | null
moreUrls: Json
markLocalFilesNsfwByDefault: boolean
antennaLimit: number
}
export interface Migrations {
id: number
@ -724,81 +912,85 @@ export interface ReplyMuting {
muteeId: string
muterId: string
}
export const enum AntennaSrcEnum {
All = 0,
Group = 1,
Home = 2,
Instances = 3,
List = 4,
Users = 5
export enum AntennaSrcEnum {
All = 'all',
Group = 'group',
Home = 'home',
Instances = 'instances',
List = 'list',
Users = 'users'
}
export const enum MutedNoteReasonEnum {
Manual = 0,
Other = 1,
Spam = 2,
Word = 3
export enum DriveFileUsageHintEnum {
UserAvatar = 'userAvatar',
UserBanner = 'userBanner'
}
export const enum NoteVisibilityEnum {
Followers = 0,
Hidden = 1,
Home = 2,
Public = 3,
Specified = 4
export enum MutedNoteReasonEnum {
Manual = 'manual',
Other = 'other',
Spam = 'spam',
Word = 'word'
}
export const enum NotificationTypeEnum {
App = 0,
Follow = 1,
FollowRequestAccepted = 2,
GroupInvited = 3,
Mention = 4,
PollEnded = 5,
PollVote = 6,
Quote = 7,
Reaction = 8,
ReceiveFollowRequest = 9,
Renote = 10,
Reply = 11
export enum NoteVisibilityEnum {
Followers = 'followers',
Hidden = 'hidden',
Home = 'home',
Public = 'public',
Specified = 'specified'
}
export const enum PageVisibilityEnum {
Followers = 0,
Public = 1,
Specified = 2
export enum NotificationTypeEnum {
App = 'app',
Follow = 'follow',
FollowRequestAccepted = 'followRequestAccepted',
GroupInvited = 'groupInvited',
Mention = 'mention',
PollEnded = 'pollEnded',
PollVote = 'pollVote',
Quote = 'quote',
Reaction = 'reaction',
ReceiveFollowRequest = 'receiveFollowRequest',
Renote = 'renote',
Reply = 'reply'
}
export const enum PollNotevisibilityEnum {
Followers = 0,
Home = 1,
Public = 2,
Specified = 3
export enum PageVisibilityEnum {
Followers = 'followers',
Public = 'public',
Specified = 'specified'
}
export const enum RelayStatusEnum {
Accepted = 0,
Rejected = 1,
Requesting = 2
export enum PollNotevisibilityEnum {
Followers = 'followers',
Home = 'home',
Public = 'public',
Specified = 'specified'
}
export const enum UserEmojimodpermEnum {
Add = 0,
Full = 1,
Mod = 2,
Unauthorized = 3
export enum RelayStatusEnum {
Accepted = 'accepted',
Rejected = 'rejected',
Requesting = 'requesting'
}
export const enum UserProfileFfvisibilityEnum {
Followers = 0,
Private = 1,
Public = 2
export enum UserEmojimodpermEnum {
Add = 'add',
Full = 'full',
Mod = 'mod',
Unauthorized = 'unauthorized'
}
export const enum UserProfileMutingnotificationtypesEnum {
App = 0,
Follow = 1,
FollowRequestAccepted = 2,
GroupInvited = 3,
Mention = 4,
PollEnded = 5,
PollVote = 6,
Quote = 7,
Reaction = 8,
ReceiveFollowRequest = 9,
Renote = 10,
Reply = 11
export enum UserProfileFfvisibilityEnum {
Followers = 'followers',
Private = 'private',
Public = 'public'
}
export enum UserProfileMutingnotificationtypesEnum {
App = 'app',
Follow = 'follow',
FollowRequestAccepted = 'followRequestAccepted',
GroupInvited = 'groupInvited',
Mention = 'mention',
PollEnded = 'pollEnded',
PollVote = 'pollVote',
Quote = 'quote',
Reaction = 'reaction',
ReceiveFollowRequest = 'receiveFollowRequest',
Renote = 'renote',
Reply = 'reply'
}
export interface Signin {
id: string
@ -856,10 +1048,10 @@ export interface User {
isDeleted: boolean
driveCapacityOverrideMb: number | null
movedToUri: string | null
alsoKnownAs: string | null
speakAsCat: boolean
emojiModPerm: UserEmojimodpermEnum
isIndexable: boolean
alsoKnownAs: Array<string> | null
}
export interface UserGroup {
id: string
@ -937,7 +1129,6 @@ export interface UserProfile {
twoFactorSecret: string | null
twoFactorEnabled: boolean
password: string | null
clientData: Json
autoAcceptFollowed: boolean
alwaysMarkNsfw: boolean
carefulBot: boolean
@ -945,21 +1136,21 @@ export interface UserProfile {
securityKeysAvailable: boolean
usePasswordLessLogin: boolean
pinnedPageId: string | null
room: Json
injectFeaturedNote: boolean
enableWordMute: boolean
mutedWords: Json
mutingNotificationTypes: Array<UserProfileMutingnotificationtypesEnum>
noCrawle: boolean
receiveAnnouncementEmail: boolean
emailNotificationTypes: Json
mutedInstances: Json
publicReactions: boolean
ffVisibility: UserProfileFfvisibilityEnum
moderationNote: string
preventAiLearning: boolean
isIndexable: boolean
mutedPatterns: Array<string>
mutedInstances: Array<string>
mutedWords: Array<string>
lang: string | null
}
export interface UserPublickey {
userId: string
@ -985,8 +1176,151 @@ export interface Webhook {
latestSentAt: Date | null
latestStatus: number | null
}
/** Initializes Cuid2 generator. Must be called before any [create_id]. */
export function initIdGenerator(length: number, fingerprint: string): void
export function updateAntennasOnNewNote(note: Note, noteAuthor: Acct, noteMutedUsers: Array<string>): Promise<void>
export function fetchNodeinfo(host: string): Promise<Nodeinfo>
export function nodeinfo_2_1(): Promise<any>
export function nodeinfo_2_0(): Promise<any>
/** NodeInfo schema version 2.0. https://nodeinfo.diaspora.software/docson/index.html#/ns/schema/2.0 */
export interface Nodeinfo {
/** The schema version, must be 2.0. */
version: string
/** Metadata about server software in use. */
software: Software20
/** The protocols supported on this server. */
protocols: Array<Protocol>
/** The third party sites this server can connect to via their application API. */
services: Services
/** Whether this server allows open self-registration. */
openRegistrations: boolean
/** Usage statistics for this server. */
usage: Usage
/** Free form key value pairs for software specific values. Clients should not rely on any specific key present. */
metadata: Record<string, any>
}
/** Metadata about server software in use (version 2.0). */
export interface Software20 {
/** The canonical name of this server software. */
name: string
/** The version of this server software. */
version: string
}
export enum Protocol {
Activitypub = 'activitypub',
Buddycloud = 'buddycloud',
Dfrn = 'dfrn',
Diaspora = 'diaspora',
Libertree = 'libertree',
Ostatus = 'ostatus',
Pumpio = 'pumpio',
Tent = 'tent',
Xmpp = 'xmpp',
Zot = 'zot'
}
/** The third party sites this server can connect to via their application API. */
export interface Services {
/** The third party sites this server can retrieve messages from for combined display with regular traffic. */
inbound: Array<Inbound>
/** The third party sites this server can publish messages to on the behalf of a user. */
outbound: Array<Outbound>
}
/** The third party sites this server can retrieve messages from for combined display with regular traffic. */
export enum Inbound {
Atom1 = 'atom1',
Gnusocial = 'gnusocial',
Imap = 'imap',
Pnut = 'pnut',
Pop3 = 'pop3',
Pumpio = 'pumpio',
Rss2 = 'rss2',
Twitter = 'twitter'
}
/** The third party sites this server can publish messages to on the behalf of a user. */
export enum Outbound {
Atom1 = 'atom1',
Blogger = 'blogger',
Buddycloud = 'buddycloud',
Diaspora = 'diaspora',
Dreamwidth = 'dreamwidth',
Drupal = 'drupal',
Facebook = 'facebook',
Friendica = 'friendica',
Gnusocial = 'gnusocial',
Google = 'google',
Insanejournal = 'insanejournal',
Libertree = 'libertree',
Linkedin = 'linkedin',
Livejournal = 'livejournal',
Mediagoblin = 'mediagoblin',
Myspace = 'myspace',
Pinterest = 'pinterest',
Pnut = 'pnut',
Posterous = 'posterous',
Pumpio = 'pumpio',
Redmatrix = 'redmatrix',
Rss2 = 'rss2',
Smtp = 'smtp',
Tent = 'tent',
Tumblr = 'tumblr',
Twitter = 'twitter',
Wordpress = 'wordpress',
Xmpp = 'xmpp'
}
/** Usage statistics for this server. */
export interface Usage {
users: Users
localPosts: number | null
localComments: number | null
}
/** statistics about the users of this server. */
export interface Users {
total: number | null
activeHalfyear: number | null
activeMonth: number | null
}
export function watchNote(watcherId: string, noteAuthorId: string, noteId: string): Promise<void>
export function unwatchNote(watcherId: string, noteId: string): Promise<void>
export enum PushNotificationKind {
Generic = 'generic',
Chat = 'chat',
ReadAllChats = 'readAllChats',
ReadAllChatsInTheRoom = 'readAllChatsInTheRoom',
ReadNotifications = 'readNotifications',
ReadAllNotifications = 'readAllNotifications'
}
export function sendPushNotification(receiverUserId: string, kind: PushNotificationKind, content: any): Promise<void>
export function publishToChannelStream(channelId: string, userId: string): Promise<void>
export enum ChatEvent {
Message = 'message',
Read = 'read',
Deleted = 'deleted',
Typing = 'typing'
}
export function publishToChatStream(senderUserId: string, receiverUserId: string, kind: ChatEvent, object: any): Promise<void>
export enum ChatIndexEvent {
Message = 'message',
Read = 'read'
}
export function publishToChatIndexStream(userId: string, kind: ChatIndexEvent, object: any): Promise<void>
export interface PackedEmoji {
id: string
aliases: Array<string>
name: string
category: string | null
host: string | null
url: string
license: string | null
width: number | null
height: number | null
}
export function publishToBroadcastStream(emoji: PackedEmoji): Promise<void>
export function publishToGroupChatStream(groupId: string, kind: ChatEvent, object: any): Promise<void>
export interface AbuseUserReportLike {
id: string
targetUserId: string
reporterId: string
comment: string
}
export function publishToModerationStream(moderatorId: string, report: AbuseUserReportLike): Promise<void>
export function getTimestamp(id: string): number
/**
* The generated ID results in the form of `[8 chars timestamp] + [cuid2]`.
@ -996,5 +1330,9 @@ export function getTimestamp(id: string): number
*
* Ref: https://github.com/paralleldrive/cuid2#parameterized-length
*/
export function genId(date?: Date | undefined | null): string
export function secureRndstr(length?: number | undefined | null): string
export function genId(): string
/** Generate an ID using a specific datetime */
export function genIdAt(date: Date): string
/** Generate random string based on [thread_rng] and [Alphanumeric]. */
export function generateSecureRandomString(length: number): string
export function generateUserToken(): string

View file

@ -310,21 +310,57 @@ if (!nativeBinding) {
throw new Error(`Failed to load native binding`)
}
const { readServerConfig, stringToAcct, acctToString, checkWordMute, getFullApAccount, isSelfHost, isSameOrigin, extractHost, toPuny, toMastodonId, fromMastodonId, nyaify, AntennaSrcEnum, MutedNoteReasonEnum, NoteVisibilityEnum, NotificationTypeEnum, PageVisibilityEnum, PollNotevisibilityEnum, RelayStatusEnum, UserEmojimodpermEnum, UserProfileFfvisibilityEnum, UserProfileMutingnotificationtypesEnum, initIdGenerator, getTimestamp, genId, secureRndstr } = nativeBinding
const { SECOND, MINUTE, HOUR, DAY, USER_ONLINE_THRESHOLD, USER_ACTIVE_THRESHOLD, FILE_TYPE_BROWSERSAFE, loadEnv, loadConfig, stringToAcct, acctToString, initializeRustLogger, showServerInfo, isBlockedServer, isSilencedServer, isAllowedServer, checkWordMute, getFullApAccount, isSelfHost, isSameOrigin, extractHost, toPuny, isUnicodeEmoji, sqlLikeEscape, safeForSql, formatMilliseconds, getImageSizeFromUrl, getNoteSummary, isQuote, isSafeUrl, latestVersion, toMastodonId, fromMastodonId, fetchMeta, metaToPugArgs, nyaify, hashPassword, verifyPassword, isOldPasswordAlgorithm, decodeReaction, countReactions, toDbReaction, removeOldAttestationChallenges, cpuInfo, cpuUsage, memoryUsage, storageUsage, AntennaSrcEnum, DriveFileUsageHintEnum, MutedNoteReasonEnum, NoteVisibilityEnum, NotificationTypeEnum, PageVisibilityEnum, PollNotevisibilityEnum, RelayStatusEnum, UserEmojimodpermEnum, UserProfileFfvisibilityEnum, UserProfileMutingnotificationtypesEnum, updateAntennasOnNewNote, fetchNodeinfo, nodeinfo_2_1, nodeinfo_2_0, Protocol, Inbound, Outbound, watchNote, unwatchNote, PushNotificationKind, sendPushNotification, publishToChannelStream, ChatEvent, publishToChatStream, ChatIndexEvent, publishToChatIndexStream, publishToBroadcastStream, publishToGroupChatStream, publishToModerationStream, getTimestamp, genId, genIdAt, generateSecureRandomString, generateUserToken } = nativeBinding
module.exports.readServerConfig = readServerConfig
module.exports.SECOND = SECOND
module.exports.MINUTE = MINUTE
module.exports.HOUR = HOUR
module.exports.DAY = DAY
module.exports.USER_ONLINE_THRESHOLD = USER_ONLINE_THRESHOLD
module.exports.USER_ACTIVE_THRESHOLD = USER_ACTIVE_THRESHOLD
module.exports.FILE_TYPE_BROWSERSAFE = FILE_TYPE_BROWSERSAFE
module.exports.loadEnv = loadEnv
module.exports.loadConfig = loadConfig
module.exports.stringToAcct = stringToAcct
module.exports.acctToString = acctToString
module.exports.initializeRustLogger = initializeRustLogger
module.exports.showServerInfo = showServerInfo
module.exports.isBlockedServer = isBlockedServer
module.exports.isSilencedServer = isSilencedServer
module.exports.isAllowedServer = isAllowedServer
module.exports.checkWordMute = checkWordMute
module.exports.getFullApAccount = getFullApAccount
module.exports.isSelfHost = isSelfHost
module.exports.isSameOrigin = isSameOrigin
module.exports.extractHost = extractHost
module.exports.toPuny = toPuny
module.exports.isUnicodeEmoji = isUnicodeEmoji
module.exports.sqlLikeEscape = sqlLikeEscape
module.exports.safeForSql = safeForSql
module.exports.formatMilliseconds = formatMilliseconds
module.exports.getImageSizeFromUrl = getImageSizeFromUrl
module.exports.getNoteSummary = getNoteSummary
module.exports.isQuote = isQuote
module.exports.isSafeUrl = isSafeUrl
module.exports.latestVersion = latestVersion
module.exports.toMastodonId = toMastodonId
module.exports.fromMastodonId = fromMastodonId
module.exports.fetchMeta = fetchMeta
module.exports.metaToPugArgs = metaToPugArgs
module.exports.nyaify = nyaify
module.exports.hashPassword = hashPassword
module.exports.verifyPassword = verifyPassword
module.exports.isOldPasswordAlgorithm = isOldPasswordAlgorithm
module.exports.decodeReaction = decodeReaction
module.exports.countReactions = countReactions
module.exports.toDbReaction = toDbReaction
module.exports.removeOldAttestationChallenges = removeOldAttestationChallenges
module.exports.cpuInfo = cpuInfo
module.exports.cpuUsage = cpuUsage
module.exports.memoryUsage = memoryUsage
module.exports.storageUsage = storageUsage
module.exports.AntennaSrcEnum = AntennaSrcEnum
module.exports.DriveFileUsageHintEnum = DriveFileUsageHintEnum
module.exports.MutedNoteReasonEnum = MutedNoteReasonEnum
module.exports.NoteVisibilityEnum = NoteVisibilityEnum
module.exports.NotificationTypeEnum = NotificationTypeEnum
@ -334,7 +370,27 @@ module.exports.RelayStatusEnum = RelayStatusEnum
module.exports.UserEmojimodpermEnum = UserEmojimodpermEnum
module.exports.UserProfileFfvisibilityEnum = UserProfileFfvisibilityEnum
module.exports.UserProfileMutingnotificationtypesEnum = UserProfileMutingnotificationtypesEnum
module.exports.initIdGenerator = initIdGenerator
module.exports.updateAntennasOnNewNote = updateAntennasOnNewNote
module.exports.fetchNodeinfo = fetchNodeinfo
module.exports.nodeinfo_2_1 = nodeinfo_2_1
module.exports.nodeinfo_2_0 = nodeinfo_2_0
module.exports.Protocol = Protocol
module.exports.Inbound = Inbound
module.exports.Outbound = Outbound
module.exports.watchNote = watchNote
module.exports.unwatchNote = unwatchNote
module.exports.PushNotificationKind = PushNotificationKind
module.exports.sendPushNotification = sendPushNotification
module.exports.publishToChannelStream = publishToChannelStream
module.exports.ChatEvent = ChatEvent
module.exports.publishToChatStream = publishToChatStream
module.exports.ChatIndexEvent = ChatIndexEvent
module.exports.publishToChatIndexStream = publishToChatIndexStream
module.exports.publishToBroadcastStream = publishToBroadcastStream
module.exports.publishToGroupChatStream = publishToGroupChatStream
module.exports.publishToModerationStream = publishToModerationStream
module.exports.getTimestamp = getTimestamp
module.exports.genId = genId
module.exports.secureRndstr = secureRndstr
module.exports.genIdAt = genIdAt
module.exports.generateSecureRandomString = generateSecureRandomString
module.exports.generateUserToken = generateUserToken

View file

@ -22,25 +22,14 @@
}
},
"devDependencies": {
"@napi-rs/cli": "2.18.1",
"ava": "6.1.2"
},
"ava": {
"timeout": "3m"
},
"engines": {
"node": ">= 10"
"@napi-rs/cli": "2.18.3"
},
"scripts": {
"artifacts": "napi artifacts",
"build": "napi build --features napi --platform --release ./built/",
"build:debug": "napi build --features napi --platform ./built/",
"build": "napi build --features napi --no-const-enum --platform --release ./built/",
"build:debug": "napi build --features napi --no-const-enum --platform ./built/",
"prepublishOnly": "napi prepublish -t npm",
"test": "pnpm run cargo:test && pnpm run build:debug && ava",
"universal": "napi universal",
"version": "napi version",
"cargo:test": "pnpm run cargo:unit && pnpm run cargo:integration",
"cargo:unit": "cargo test unit_test && cargo test -F napi unit_test",
"cargo:integration": "cargo test int_test"
"version": "napi version"
}
}

View file

@ -0,0 +1,67 @@
#[crate::export]
pub const SECOND: i32 = 1000;
#[crate::export]
pub const MINUTE: i32 = 60 * SECOND;
#[crate::export]
pub const HOUR: i32 = 60 * MINUTE;
#[crate::export]
pub const DAY: i32 = 24 * HOUR;
#[crate::export]
pub const USER_ONLINE_THRESHOLD: i32 = 10 * MINUTE;
#[crate::export]
pub const USER_ACTIVE_THRESHOLD: i32 = 3 * DAY;
/// List of file types allowed to be viewed directly in the browser
/// Anything not included here will be responded as application/octet-stream
/// SVG is not allowed because it generates XSS <- we need to fix this and later allow it to be viewed directly
/// https://github.com/sindresorhus/file-type/blob/main/supported.js
/// https://github.com/sindresorhus/file-type/blob/main/core.js
/// https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Containers
#[crate::export]
pub const FILE_TYPE_BROWSERSAFE: [&str; 41] = [
// Images
"image/png",
"image/gif", // TODO: deprecated, but still used by old posts, new gifs should be converted to webp in the future
"image/jpeg",
"image/webp", // TODO: make this the default image format
"image/apng",
"image/bmp",
"image/tiff",
"image/x-icon",
"image/avif", // not as good supported now, but its good to introduce initial support for the future
// OggS
"audio/opus",
"video/ogg",
"audio/ogg",
"application/ogg",
// ISO/IEC base media file format
"video/quicktime",
"video/mp4", // TODO: we need to check for av1 later
"video/vnd.avi", // also av1
"audio/mp4",
"video/x-m4v",
"audio/x-m4a",
"video/3gpp",
"video/3gpp2",
"video/3gp2",
"audio/3gpp",
"audio/3gpp2",
"audio/3gp2",
"video/mpeg",
"audio/mpeg",
"video/webm",
"audio/webm",
"audio/aac",
"audio/x-flac",
"audio/flac",
"audio/vnd.wave",
"audio/mod",
"audio/x-mod",
"audio/s3m",
"audio/x-s3m",
"audio/xm",
"audio/x-xm",
"audio/it",
"audio/x-it",
];

View file

@ -0,0 +1,27 @@
// FIXME: Are these options used?
#[crate::export(object)]
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 slow: bool,
}
#[crate::export]
pub fn load_env() -> EnvConfig {
let node_env = std::env::var("NODE_ENV").unwrap_or_default().to_lowercase();
let is_testing = node_env == "test";
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(),
slow: std::env::var("MK_SLOW").is_ok(),
}
}

View file

@ -1 +1,5 @@
pub use server::CONFIG;
pub mod constant;
pub mod environment;
pub mod server;

View file

@ -6,7 +6,7 @@ use std::fs;
#[derive(Clone, Debug, PartialEq, Deserialize)]
#[serde(rename_all = "camelCase")]
#[crate::export(object, use_nullable = false)]
pub struct ServerConfig {
struct ServerConfig {
pub url: String,
pub port: u16,
/// host to listen on
@ -22,10 +22,10 @@ pub struct ServerConfig {
pub proxy_bypass_hosts: Option<Vec<String>>,
pub allowed_private_networks: Option<Vec<String>>,
/// `NapiValue` is not implemented for `u64`
// TODO: i64 -> u64 (NapiValue is not implemented for u64)
pub max_file_size: Option<i64>,
pub access_log: Option<String>,
pub cluster_limits: Option<WorkerConfig>,
pub cluster_limits: Option<WorkerConfigInternal>,
pub cuid: Option<IdConfig>,
pub outgoing_address: Option<String>,
@ -36,8 +36,11 @@ pub struct ServerConfig {
pub deliver_job_max_attempts: Option<u32>,
pub inbox_job_max_attempts: Option<u32>,
/// deprecated
pub log_level: Option<Vec<String>>,
pub max_log_level: Option<String>,
pub syslog: Option<SysLogConfig>,
pub proxy_remote_files: Option<bool>,
@ -82,8 +85,7 @@ pub struct RedisConfig {
pub tls: Option<TlsConfig>,
#[serde(default)]
pub db: u32,
#[serde(default)]
pub prefix: String,
pub prefix: Option<String>,
}
#[derive(Clone, Debug, PartialEq, Deserialize)]
@ -94,10 +96,16 @@ pub struct TlsConfig {
pub reject_unauthorized: bool,
}
#[crate::export(object, use_nullable = false)]
pub struct WorkerConfig {
pub web: u32,
pub queue: u32,
}
#[derive(Clone, Debug, PartialEq, Deserialize)]
#[serde(rename_all = "camelCase")]
#[crate::export(object, use_nullable = false)]
pub struct WorkerConfig {
pub struct WorkerConfigInternal {
pub web: Option<u32>,
pub queue: Option<u32>,
}
@ -167,17 +175,212 @@ pub struct ObjectStorageConfig {
pub s3_force_path_style: Option<bool>,
}
#[crate::export]
pub fn read_server_config() -> ServerConfig {
#[crate::export(object, use_nullable = false)]
pub struct Config {
// ServerConfig (from default.yml)
pub url: String,
pub port: u16,
pub bind: Option<String>,
pub disable_hsts: Option<bool>,
pub db: DbConfig,
pub redis: RedisConfig,
pub cache_server: Option<RedisConfig>,
pub proxy: Option<String>,
pub proxy_smtp: Option<String>,
pub proxy_bypass_hosts: Option<Vec<String>>,
pub allowed_private_networks: Option<Vec<String>>,
pub max_file_size: Option<i64>,
pub access_log: Option<String>,
pub cluster_limits: WorkerConfig,
pub cuid: Option<IdConfig>,
pub outgoing_address: Option<String>,
pub deliver_job_concurrency: Option<u32>,
pub inbox_job_concurrency: Option<u32>,
pub deliver_job_per_sec: Option<u32>,
pub inbox_job_per_sec: Option<u32>,
pub deliver_job_max_attempts: Option<u32>,
pub inbox_job_max_attempts: Option<u32>,
/// deprecated
pub log_level: Option<Vec<String>>,
pub max_log_level: Option<String>,
pub syslog: Option<SysLogConfig>,
pub proxy_remote_files: Option<bool>,
pub media_proxy: Option<String>,
pub summaly_proxy_url: Option<String>,
pub reserved_usernames: Option<Vec<String>>,
pub max_user_signups: Option<u32>,
pub is_managed_hosting: Option<bool>,
pub max_note_length: u32,
pub max_caption_length: u32,
pub deepl: Option<DeepLConfig>,
pub libre_translate: Option<LibreTranslateConfig>,
pub email: Option<EmailConfig>,
pub object_storage: Option<ObjectStorageConfig>,
// Mixin
pub version: String,
pub host: String,
pub hostname: String,
pub redis_key_prefix: String,
pub scheme: String,
pub ws_scheme: String,
pub api_url: String,
pub ws_url: String,
pub auth_url: String,
pub drive_url: String,
pub user_agent: String,
pub client_entry: Manifest,
}
#[derive(Clone, Debug, PartialEq, Deserialize)]
#[serde(rename_all = "camelCase")]
struct Meta {
pub version: String,
}
#[derive(Clone, Debug, PartialEq, Deserialize)]
struct ManifestJson {
#[serde(rename = "src/init.ts")]
pub init_ts: Manifest,
}
#[derive(Clone, Debug, PartialEq, Deserialize)]
#[serde(rename_all = "camelCase")]
#[crate::export(object, use_nullable = false)]
pub struct Manifest {
pub file: String,
pub name: String,
pub src: String,
pub is_entry: bool,
pub is_dynamic_entry: bool,
pub imports: Vec<String>,
pub dynamic_imports: Vec<String>,
pub css: Vec<String>,
pub assets: Vec<String>,
}
fn read_config_file() -> ServerConfig {
let cwd = env::current_dir().unwrap();
let yml = fs::File::open(cwd.join("../../.config/default.yml"))
.expect("Failed to open '.config/default.yml'");
let mut data: ServerConfig = serde_yaml::from_reader(yml).expect("Failed to parse yaml");
let mut data: ServerConfig =
serde_yaml::from_reader(yml).expect("Failed to parse .config/default.yml");
data.url = url::Url::parse(&data.url)
.expect("Config url is invalid")
.origin()
.ascii_serialization();
if data.bind.is_none() {
data.bind = std::env::var("BIND").ok()
}
data
}
pub static SERVER_CONFIG: Lazy<ServerConfig> = Lazy::new(read_server_config);
fn read_meta() -> Meta {
let cwd = env::current_dir().unwrap();
let meta_json = fs::File::open(cwd.join("../../built/meta.json"))
.expect("Failed to open 'built/meta.json'");
serde_json::from_reader(meta_json).expect("Failed to parse built/meta.json")
}
fn read_manifest() -> Manifest {
let cwd = env::current_dir().unwrap();
let manifest_json = fs::File::open(cwd.join("../../built/_client_dist_/manifest.json"))
.expect("Failed to open 'built/_client_dist_/manifest.json'");
let manifest: ManifestJson = serde_json::from_reader(manifest_json)
.expect("Failed to parse built/_client_dist_/manifest.json");
manifest.init_ts
}
#[crate::export]
pub fn load_config() -> Config {
let server_config = read_config_file();
let version = read_meta().version;
let manifest = read_manifest();
let url = url::Url::parse(&server_config.url).expect("Config url is invalid");
let hostname = url
.host_str()
.expect("Hostname is missing in the config url")
.to_owned();
let host = match url.port() {
Some(port) => format!("{}:{}", hostname, port),
None => hostname.clone(),
};
let scheme = url.scheme().to_owned();
let ws_scheme = scheme.replace("http", "ws");
let cluster_limits = match server_config.cluster_limits {
Some(cl) => WorkerConfig {
web: cl.web.unwrap_or(1),
queue: cl.queue.unwrap_or(1),
},
None => WorkerConfig { web: 1, queue: 1 },
};
let redis_key_prefix = if let Some(cache_server) = &server_config.cache_server {
cache_server.prefix.clone()
} else {
server_config.redis.prefix.clone()
}
.unwrap_or(hostname.clone());
Config {
url: server_config.url,
port: server_config.port,
bind: server_config.bind,
disable_hsts: server_config.disable_hsts,
db: server_config.db,
redis: server_config.redis,
cache_server: server_config.cache_server,
proxy: server_config.proxy,
proxy_smtp: server_config.proxy_smtp,
proxy_bypass_hosts: server_config.proxy_bypass_hosts,
allowed_private_networks: server_config.allowed_private_networks,
max_file_size: server_config.max_file_size,
access_log: server_config.access_log,
cluster_limits,
cuid: server_config.cuid,
outgoing_address: server_config.outgoing_address,
deliver_job_concurrency: server_config.deliver_job_concurrency,
inbox_job_concurrency: server_config.inbox_job_concurrency,
deliver_job_per_sec: server_config.deliver_job_per_sec,
inbox_job_per_sec: server_config.inbox_job_per_sec,
deliver_job_max_attempts: server_config.deliver_job_max_attempts,
inbox_job_max_attempts: server_config.inbox_job_max_attempts,
log_level: server_config.log_level,
max_log_level: server_config.max_log_level,
syslog: server_config.syslog,
proxy_remote_files: server_config.proxy_remote_files,
media_proxy: server_config.media_proxy,
summaly_proxy_url: server_config.summaly_proxy_url,
reserved_usernames: server_config.reserved_usernames,
max_user_signups: server_config.max_user_signups,
is_managed_hosting: server_config.is_managed_hosting,
max_note_length: server_config.max_note_length.unwrap_or(3000),
max_caption_length: server_config.max_caption_length.unwrap_or(1500),
deepl: server_config.deepl,
libre_translate: server_config.libre_translate,
email: server_config.email,
object_storage: server_config.object_storage,
ws_url: format!("{}://{}", ws_scheme, host),
api_url: format!("{}://{}/api", scheme, host),
auth_url: format!("{}://{}/auth", scheme, host),
drive_url: format!("{}://{}/files", scheme, host),
user_agent: format!("Firefish/{} ({})", version, url),
version,
host,
hostname,
redis_key_prefix,
scheme,
ws_scheme,
client_entry: manifest,
}
}
pub static CONFIG: Lazy<Config> = Lazy::new(load_config);

View file

@ -0,0 +1,308 @@
use crate::database::{redis_conn, redis_key, RedisConnError};
use redis::{AsyncCommands, RedisError};
use serde::{Deserialize, Serialize};
#[derive(strum::Display, Debug)]
pub enum Category {
#[strum(serialize = "fetchUrl")]
FetchUrl,
#[strum(serialize = "blocking")]
Block,
#[strum(serialize = "following")]
Follow,
#[cfg(test)]
#[strum(serialize = "usedOnlyForTesting")]
Test,
}
#[derive(thiserror::Error, Debug)]
pub enum Error {
#[error("Redis error: {0}")]
RedisErr(#[from] RedisError),
#[error("Redis connection error: {0}")]
RedisConnErr(#[from] RedisConnError),
#[error("Data serialization error: {0}")]
SerializeErr(#[from] rmp_serde::encode::Error),
#[error("Data deserialization error: {0}")]
DeserializeErr(#[from] rmp_serde::decode::Error),
}
#[inline]
fn prefix_key(key: &str) -> String {
redis_key(format!("cache:{}", key))
}
#[inline]
fn categorize(category: Category, key: &str) -> String {
format!("{}:{}", category, key)
}
#[inline]
fn wildcard(category: Category) -> String {
prefix_key(&categorize(category, "*"))
}
/// Sets a Redis cache.
///
/// This overwrites the exsisting cache with the same key.
///
/// ## Arguments
///
/// * `key` - key (will be prefixed automatically)
/// * `value` - (de)serializable value
/// * `expire_seconds` - TTL
///
/// ## Example
///
/// ```
/// # use backend_rs::database::cache;
/// # tokio_test::block_on(async {
/// let key = "apple";
/// let data = "I want to cache this string".to_string();
///
/// // caches the data for 10 seconds
/// cache::set(key, &data, 10).await;
///
/// // get the cache
/// let cached_data = cache::get::<String>(key).await.unwrap();
/// assert_eq!(data, cached_data.unwrap());
/// # })
/// ```
pub async fn set<V: for<'a> Deserialize<'a> + Serialize>(
key: &str,
value: &V,
expire_seconds: u64,
) -> Result<(), Error> {
redis_conn()
.await?
.set_ex(
prefix_key(key),
rmp_serde::encode::to_vec(&value)?,
expire_seconds,
)
.await?;
Ok(())
}
/// Gets a Redis cache.
///
/// If the Redis connection is fine, this returns `Ok(data)` where `data`
/// is the cached value. Returns `Ok(None)` if there is no value corresponding to `key`.
///
/// ## Arguments
///
/// * `key` - key (will be prefixed automatically)
///
/// ## Example
///
/// ```
/// # use backend_rs::database::cache;
/// # tokio_test::block_on(async {
/// let key = "banana";
/// let data = "I want to cache this string".to_string();
///
/// // set cache
/// cache::set(key, &data, 10).await.unwrap();
///
/// // get cache
/// let cached_data = cache::get::<String>(key).await.unwrap();
/// assert_eq!(data, cached_data.unwrap());
///
/// // get nonexistent (or expired) cache
/// let no_cache = cache::get::<String>("nonexistent").await.unwrap();
/// assert!(no_cache.is_none());
/// # })
/// ```
pub async fn get<V: for<'a> Deserialize<'a> + Serialize>(key: &str) -> Result<Option<V>, Error> {
let serialized_value: Option<Vec<u8>> = redis_conn().await?.get(prefix_key(key)).await?;
Ok(match serialized_value {
Some(v) => Some(rmp_serde::from_slice::<V>(v.as_ref())?),
None => None,
})
}
/// Deletes a Redis cache.
///
/// If the Redis connection is fine, this returns `Ok(())`
/// regardless of whether the cache exists.
///
/// ## Arguments
///
/// * `key` - key (will be prefixed automatically)
///
/// ## Example
///
/// ```
/// # use backend_rs::database::cache;
/// # tokio_test::block_on(async {
/// let key = "chocolate";
/// let value = "I want to cache this string".to_string();
///
/// // set cache
/// cache::set(key, &value, 10).await.unwrap();
///
/// // delete the cache
/// cache::delete("foo").await.unwrap();
/// cache::delete("nonexistent").await.unwrap(); // this is okay
///
/// // the cache is gone
/// let cached_value = cache::get::<String>("foo").await.unwrap();
/// assert!(cached_value.is_none());
/// # })
/// ```
pub async fn delete(key: &str) -> Result<(), Error> {
Ok(redis_conn().await?.del(prefix_key(key)).await?)
}
/// Sets a Redis cache under a `category`.
///
/// The usage is the same as [set], except that you need to
/// use [get_one] and [delete_one] to get/delete the cache.
///
/// ## Arguments
///
/// * `category` - one of [Category]
/// * `key` - key (will be prefixed automatically)
/// * `value` - (de)serializable value
/// * `expire_seconds` - TTL
pub async fn set_one<V: for<'a> Deserialize<'a> + Serialize>(
category: Category,
key: &str,
value: &V,
expire_seconds: u64,
) -> Result<(), Error> {
set(&categorize(category, key), value, expire_seconds).await
}
/// Gets a Redis cache under a `category`.
///
/// The usage is basically the same as [get].
///
/// ## Arguments
///
/// * `category` - one of [Category]
/// * `key` - key (will be prefixed automatically)
pub async fn get_one<V: for<'a> Deserialize<'a> + Serialize>(
category: Category,
key: &str,
) -> Result<Option<V>, Error> {
get(&categorize(category, key)).await
}
/// Deletes a Redis cache under a `category`.
///
/// The usage is basically the same as [delete].
///
/// ## Arguments
///
/// * `category` - one of [Category]
/// * `key` - key (will be prefixed automatically)
pub async fn delete_one(category: Category, key: &str) -> Result<(), Error> {
delete(&categorize(category, key)).await
}
/// Deletes all Redis caches under a `category`.
///
/// ## Arguments
///
/// * `category` - one of [Category]
pub async fn delete_all(category: Category) -> Result<(), Error> {
let mut redis = redis_conn().await?;
let keys: Vec<Vec<u8>> = redis.keys(wildcard(category)).await?;
if !keys.is_empty() {
redis.del(keys).await?
}
Ok(())
}
// TODO: get_all()
#[cfg(test)]
mod unit_test {
use super::{delete_all, get, get_one, set, set_one, Category::Test};
use crate::database::cache::delete_one;
use pretty_assertions::assert_eq;
#[tokio::test]
async fn set_get_expire() {
#[derive(serde::Deserialize, serde::Serialize, PartialEq, Debug)]
struct Data {
id: u32,
kind: String,
}
let key_1 = "CARGO_TEST_CACHE_KEY_1";
let value_1: Vec<i32> = vec![1, 2, 3, 4, 5];
let key_2 = "CARGO_TEST_CACHE_KEY_2";
let value_2 = "Hello fedizens".to_string();
let key_3 = "CARGO_TEST_CACHE_KEY_3";
let value_3 = Data {
id: 1000000007,
kind: "prime number".to_string(),
};
set(key_1, &value_1, 1).await.unwrap();
set(key_2, &value_2, 1).await.unwrap();
set(key_3, &value_3, 1).await.unwrap();
let cached_value_1: Vec<i32> = get(key_1).await.unwrap().unwrap();
let cached_value_2: String = get(key_2).await.unwrap().unwrap();
let cached_value_3: Data = get(key_3).await.unwrap().unwrap();
assert_eq!(value_1, cached_value_1);
assert_eq!(value_2, cached_value_2);
assert_eq!(value_3, cached_value_3);
// wait for the cache to expire
std::thread::sleep(std::time::Duration::from_millis(1100));
let expired_value_1: Option<Vec<i32>> = get(key_1).await.unwrap();
let expired_value_2: Option<Vec<i32>> = get(key_2).await.unwrap();
let expired_value_3: Option<Vec<i32>> = get(key_3).await.unwrap();
assert!(expired_value_1.is_none());
assert!(expired_value_2.is_none());
assert!(expired_value_3.is_none());
}
#[tokio::test]
async fn use_category() {
let key_1 = "fire";
let key_2 = "fish";
let key_3 = "awawa";
let value_1 = "hello".to_string();
let value_2 = 998244353u32;
let value_3 = 'あ';
set_one(Test, key_1, &value_1, 5 * 60).await.unwrap();
set_one(Test, key_2, &value_2, 5 * 60).await.unwrap();
set_one(Test, key_3, &value_3, 5 * 60).await.unwrap();
assert_eq!(
get_one::<String>(Test, key_1).await.unwrap().unwrap(),
value_1
);
assert_eq!(get_one::<u32>(Test, key_2).await.unwrap().unwrap(), value_2);
assert_eq!(
get_one::<char>(Test, key_3).await.unwrap().unwrap(),
value_3
);
delete_one(Test, key_1).await.unwrap();
assert!(get_one::<String>(Test, key_1).await.unwrap().is_none());
assert!(get_one::<u32>(Test, key_2).await.unwrap().is_some());
assert!(get_one::<char>(Test, key_3).await.unwrap().is_some());
delete_all(Test).await.unwrap();
assert!(get_one::<String>(Test, key_1).await.unwrap().is_none());
assert!(get_one::<u32>(Test, key_2).await.unwrap().is_none());
assert!(get_one::<char>(Test, key_3).await.unwrap().is_none());
}
}

View file

@ -1,34 +1,8 @@
use crate::config::server::SERVER_CONFIG;
use sea_orm::{Database, DbConn, DbErr};
pub use postgresql::db_conn;
pub use redis::key as redis_key;
pub use redis::redis_conn;
pub use redis::RedisConnError;
static DB_CONN: once_cell::sync::OnceCell<DbConn> = once_cell::sync::OnceCell::new();
async fn init_database() -> Result<&'static DbConn, DbErr> {
let database_uri = format!(
"postgres://{}:{}@{}:{}/{}",
SERVER_CONFIG.db.user,
urlencoding::encode(&SERVER_CONFIG.db.pass),
SERVER_CONFIG.db.host,
SERVER_CONFIG.db.port,
SERVER_CONFIG.db.db,
);
let conn = Database::connect(database_uri).await?;
Ok(DB_CONN.get_or_init(move || conn))
}
pub async fn db_conn() -> Result<&'static DbConn, DbErr> {
match DB_CONN.get() {
Some(conn) => Ok(conn),
None => init_database().await,
}
}
#[cfg(test)]
mod unit_test {
use super::db_conn;
#[tokio::test]
async fn connect_test() {
assert!(db_conn().await.is_ok());
}
}
pub mod cache;
pub mod postgresql;
pub mod redis;

View file

@ -0,0 +1,43 @@
use crate::config::CONFIG;
use once_cell::sync::OnceCell;
use sea_orm::{ConnectOptions, Database, DbConn, DbErr};
use tracing::log::LevelFilter;
static DB_CONN: OnceCell<DbConn> = OnceCell::new();
async fn init_database() -> Result<&'static DbConn, DbErr> {
let database_uri = format!(
"postgres://{}:{}@{}:{}/{}",
CONFIG.db.user,
urlencoding::encode(&CONFIG.db.pass),
CONFIG.db.host,
CONFIG.db.port,
CONFIG.db.db,
);
let option: ConnectOptions = ConnectOptions::new(database_uri)
.sqlx_logging_level(LevelFilter::Trace)
.to_owned();
tracing::info!("Initializing PostgreSQL connection");
let conn = Database::connect(option).await?;
Ok(DB_CONN.get_or_init(move || conn))
}
pub async fn db_conn() -> Result<&'static DbConn, DbErr> {
match DB_CONN.get() {
Some(conn) => Ok(conn),
None => init_database().await,
}
}
#[cfg(test)]
mod unit_test {
use super::db_conn;
#[tokio::test]
async fn connect() {
assert!(db_conn().await.is_ok());
assert!(db_conn().await.is_ok());
}
}

View file

@ -0,0 +1,138 @@
use crate::config::CONFIG;
use async_trait::async_trait;
use bb8::{ManageConnection, Pool, PooledConnection, RunError};
use redis::{aio::MultiplexedConnection, Client, ErrorKind, IntoConnectionInfo, RedisError};
use tokio::sync::OnceCell;
/// A `bb8::ManageConnection` for `redis::Client::get_multiplexed_async_connection`.
#[derive(Clone, Debug)]
pub struct RedisConnectionManager {
client: Client,
}
impl RedisConnectionManager {
/// Create a new `RedisConnectionManager`.
/// See `redis::Client::open` for a description of the parameter types.
pub fn new<T: IntoConnectionInfo>(info: T) -> Result<Self, RedisError> {
Ok(Self {
client: Client::open(info.into_connection_info()?)?,
})
}
}
#[async_trait]
impl ManageConnection for RedisConnectionManager {
type Connection = MultiplexedConnection;
type Error = RedisError;
async fn connect(&self) -> Result<Self::Connection, Self::Error> {
self.client.get_multiplexed_async_connection().await
}
async fn is_valid(&self, conn: &mut Self::Connection) -> Result<(), Self::Error> {
let pong: String = redis::cmd("PING").query_async(conn).await?;
match pong.as_str() {
"PONG" => Ok(()),
_ => Err((ErrorKind::ResponseError, "ping request").into()),
}
}
fn has_broken(&self, _: &mut Self::Connection) -> bool {
false
}
}
static CONN_POOL: OnceCell<Pool<RedisConnectionManager>> = OnceCell::const_new();
async fn init_conn_pool() -> Result<(), RedisError> {
let redis_url = {
let mut params = vec!["redis://".to_owned()];
let redis = if let Some(cache_server) = &CONFIG.cache_server {
cache_server
} else {
&CONFIG.redis
};
if let Some(user) = &redis.user {
params.push(user.to_string())
}
if let Some(pass) = &redis.pass {
params.push(format!(":{}@", pass))
}
params.push(redis.host.to_string());
params.push(format!(":{}", redis.port));
params.push(format!("/{}", redis.db));
params.concat()
};
tracing::info!("Initializing connection manager");
let manager = RedisConnectionManager::new(redis_url)?;
tracing::info!("Creating connection pool");
let pool = Pool::builder().build(manager).await?;
CONN_POOL.get_or_init(|| async { pool }).await;
Ok(())
}
#[derive(thiserror::Error, Debug)]
pub enum RedisConnError {
#[error("Failed to initialize Redis connection pool: {0}")]
RedisErr(RedisError),
#[error("Redis connection pool error: {0}")]
Bb8PoolErr(RunError<RedisError>),
}
pub async fn redis_conn(
) -> Result<PooledConnection<'static, RedisConnectionManager>, RedisConnError> {
if !CONN_POOL.initialized() {
let init_res = init_conn_pool().await;
if let Err(err) = init_res {
return Err(RedisConnError::RedisErr(err));
}
}
CONN_POOL
.get()
.unwrap()
.get()
.await
.map_err(RedisConnError::Bb8PoolErr)
}
/// prefix redis key
#[inline]
pub fn key(key: impl ToString) -> String {
format!("{}:{}", CONFIG.redis_key_prefix, key.to_string())
}
#[cfg(test)]
mod unit_test {
use super::redis_conn;
use pretty_assertions::assert_eq;
use redis::AsyncCommands;
#[tokio::test]
async fn connect() {
assert!(redis_conn().await.is_ok());
assert!(redis_conn().await.is_ok());
}
#[tokio::test]
async fn access() {
let mut redis = redis_conn().await.unwrap();
let key = "CARGO_UNIT_TEST_KEY";
let value = "CARGO_UNIT_TEST_VALUE";
assert_eq!(
redis.set::<&str, &str, String>(key, value).await.unwrap(),
"OK"
);
assert_eq!(redis.get::<&str, String>(key).await.unwrap(), value);
assert_eq!(redis.del::<&str, u32>(key).await.unwrap(), 1);
}
}

View file

@ -0,0 +1,105 @@
use std::fmt;
use std::str::FromStr;
#[derive(Debug, PartialEq)]
#[crate::export(object)]
pub struct Acct {
pub username: String,
pub host: Option<String>,
}
impl FromStr for Acct {
type Err = ();
/// This never throw errors. Feel free to `.unwrap()` the result.
fn from_str(value: &str) -> Result<Self, Self::Err> {
let split: Vec<&str> = if let Some(stripped) = value.strip_prefix('@') {
stripped
} else {
value
}
.split('@')
.collect();
Ok(Self {
username: split[0].to_string(),
host: if split.len() == 1 {
None
} else {
Some(split[1].to_string())
},
})
}
}
impl fmt::Display for Acct {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let result = match &self.host {
Some(host) => format!("{}@{}", self.username, host),
None => self.username.clone(),
};
write!(f, "{result}")
}
}
impl From<Acct> for String {
fn from(value: Acct) -> Self {
value.to_string()
}
}
#[crate::ts_only_warn("Use `acct.parse().unwrap()` or `Acct::from_str(acct).unwrap()` instead.")]
#[crate::export]
pub fn string_to_acct(acct: &str) -> Acct {
Acct::from_str(acct).unwrap()
}
#[crate::ts_only_warn("Use `acct.to_string()` instead.")]
#[crate::export]
pub fn acct_to_string(acct: &Acct) -> String {
acct.to_string()
}
#[cfg(test)]
mod unit_test {
use super::Acct;
use pretty_assertions::assert_eq;
use std::str::FromStr;
#[test]
fn test_acct_to_string() {
let remote_acct = Acct {
username: "firefish".to_string(),
host: Some("example.com".to_string()),
};
let local_acct = Acct {
username: "MisakaMikoto".to_string(),
host: None,
};
assert_eq!(remote_acct.to_string(), "firefish@example.com");
assert_ne!(remote_acct.to_string(), "mastodon@example.com");
assert_eq!(local_acct.to_string(), "MisakaMikoto");
assert_ne!(local_acct.to_string(), "ShiraiKuroko");
}
#[test]
fn test_string_to_acct() {
let remote_acct = Acct {
username: "firefish".to_string(),
host: Some("example.com".to_string()),
};
let local_acct = Acct {
username: "MisakaMikoto".to_string(),
host: None,
};
assert_eq!(
Acct::from_str("@firefish@example.com").unwrap(),
remote_acct
);
assert_eq!(Acct::from_str("firefish@example.com").unwrap(), remote_acct);
assert_eq!(Acct::from_str("@MisakaMikoto").unwrap(), local_acct);
assert_eq!(Acct::from_str("MisakaMikoto").unwrap(), local_acct);
}
}

View file

@ -0,0 +1 @@
pub mod acct;

View file

@ -0,0 +1,51 @@
use crate::config::CONFIG;
use tracing::Level;
use tracing_subscriber::FmtSubscriber;
#[crate::export(js_name = "initializeRustLogger")]
pub fn initialize_logger() {
let mut builder = FmtSubscriber::builder();
if let Some(max_level) = &CONFIG.max_log_level {
builder = builder.with_max_level(match max_level.as_str() {
"error" => Level::ERROR,
"warning" => Level::WARN,
"info" => Level::INFO,
"debug" => Level::DEBUG,
"trace" => Level::TRACE,
_ => Level::INFO, // Fallback
});
} else if let Some(levels) = &CONFIG.log_level {
// `logLevel` config is Deprecated
if levels.contains(&"trace".to_string()) {
builder = builder.with_max_level(Level::TRACE);
} else if levels.contains(&"debug".to_string()) {
builder = builder.with_max_level(Level::DEBUG);
} else if levels.contains(&"info".to_string()) {
builder = builder.with_max_level(Level::INFO);
} else if levels.contains(&"warning".to_string()) {
builder = builder.with_max_level(Level::WARN);
} else if levels.contains(&"error".to_string()) {
builder = builder.with_max_level(Level::ERROR);
} else {
// Fallback
builder = builder.with_max_level(Level::INFO);
}
} else {
// Fallback
builder = builder.with_max_level(Level::INFO);
};
let subscriber = builder
.without_time()
.with_level(true)
.with_ansi(true)
.with_target(true)
.with_thread_names(true)
.with_line_number(true)
.log_internal_errors(true)
.compact()
.finish();
tracing::subscriber::set_global_default(subscriber).expect("Failed to initialize the logger");
}

View file

@ -0,0 +1,2 @@
pub mod log;
pub mod system_info;

View file

@ -0,0 +1,39 @@
use std::sync::{Mutex, MutexGuard, OnceLock, PoisonError};
use sysinfo::System;
pub type SysinfoPoisonError = PoisonError<MutexGuard<'static, System>>;
// TODO: handle this in a more proper way when we move the entry point to backend-rs
pub fn system() -> Result<MutexGuard<'static, System>, SysinfoPoisonError> {
pub static SYSTEM: OnceLock<Mutex<System>> = OnceLock::new();
SYSTEM.get_or_init(|| Mutex::new(System::new_all())).lock()
}
#[crate::export]
pub fn show_server_info() -> Result<(), SysinfoPoisonError> {
let system_info = system()?;
tracing::info!(
"Hostname: {}",
System::host_name().unwrap_or("unknown".to_string())
);
tracing::info!(
"OS: {}",
System::long_os_version().unwrap_or("unknown".to_string())
);
tracing::info!(
"Kernel: {}",
System::kernel_version().unwrap_or("unknown".to_string())
);
tracing::info!(
"CPU architecture: {}",
System::cpu_arch().unwrap_or("unknown".to_string())
);
tracing::info!("CPU threads: {}", system_info.cpus().len());
tracing::info!("Total memory: {} MiB", system_info.total_memory() / 1048576);
tracing::info!("Free memory: {} MiB", system_info.free_memory() / 1048576);
tracing::info!("Total swap: {} MiB", system_info.total_swap() / 1048576);
tracing::info!("Free swap: {} MiB", system_info.free_swap() / 1048576);
Ok(())
}

View file

@ -1,7 +1,10 @@
pub use macro_rs::export;
pub use macro_rs::{export, ts_only_warn};
pub mod config;
pub mod database;
pub mod federation;
pub mod init;
pub mod misc;
pub mod model;
pub mod service;
pub mod util;

View file

@ -1,74 +0,0 @@
#[derive(Debug, PartialEq)]
#[crate::export(object)]
pub struct Acct {
pub username: String,
pub host: Option<String>,
}
#[crate::export]
pub fn string_to_acct(acct: &str) -> Acct {
let split: Vec<&str> = if let Some(stripped) = acct.strip_prefix('@') {
stripped
} else {
acct
}
.split('@')
.collect();
Acct {
username: split[0].to_string(),
host: if split.len() == 1 {
None
} else {
Some(split[1].to_string())
},
}
}
#[crate::export]
pub fn acct_to_string(acct: &Acct) -> String {
match &acct.host {
Some(host) => format!("{}@{}", acct.username, host),
None => acct.username.clone(),
}
}
#[cfg(test)]
mod unit_test {
use super::{acct_to_string, string_to_acct, Acct};
use pretty_assertions::assert_eq;
#[test]
fn test_acct_to_string() {
let remote_acct = Acct {
username: "firefish".to_string(),
host: Some("example.com".to_string()),
};
let local_acct = Acct {
username: "MisakaMikoto".to_string(),
host: None,
};
assert_eq!(acct_to_string(&remote_acct), "firefish@example.com");
assert_ne!(acct_to_string(&remote_acct), "mastodon@example.com");
assert_eq!(acct_to_string(&local_acct), "MisakaMikoto");
assert_ne!(acct_to_string(&local_acct), "ShiraiKuroko");
}
#[test]
fn test_string_to_acct() {
let remote_acct = Acct {
username: "firefish".to_string(),
host: Some("example.com".to_string()),
};
let local_acct = Acct {
username: "MisakaMikoto".to_string(),
host: None,
};
assert_eq!(string_to_acct("@firefish@example.com"), remote_acct);
assert_eq!(string_to_acct("firefish@example.com"), remote_acct);
assert_eq!(string_to_acct("@MisakaMikoto"), local_acct);
assert_eq!(string_to_acct("MisakaMikoto"), local_acct);
}
}

View file

@ -0,0 +1,175 @@
use crate::config::CONFIG;
use crate::database::{cache, db_conn};
use crate::federation::acct::Acct;
use crate::misc::get_note_all_texts::{all_texts, NoteLike};
use crate::model::entity::{antenna, blocking, following, note, sea_orm_active_enums::*};
use sea_orm::{ColumnTrait, DbErr, EntityTrait, QueryFilter, QuerySelect};
#[derive(thiserror::Error, Debug)]
pub enum AntennaCheckError {
#[error("Database error: {0}")]
DbErr(#[from] DbErr),
#[error("Cache error: {0}")]
CacheErr(#[from] cache::Error),
#[error("User profile not found: {0}")]
UserProfileNotFoundErr(String),
}
fn match_all(space_separated_words: &str, text: &str, case_sensitive: bool) -> bool {
if case_sensitive {
space_separated_words
.split_whitespace()
.all(|word| text.contains(word))
} else {
space_separated_words
.to_lowercase()
.split_whitespace()
.all(|word| text.to_lowercase().contains(word))
}
}
pub async fn check_hit_antenna(
antenna: &antenna::Model,
note: note::Model,
note_author: &Acct,
) -> Result<bool, AntennaCheckError> {
if note.visibility == NoteVisibilityEnum::Specified {
return Ok(false);
}
if antenna.with_file && note.file_ids.is_empty() {
return Ok(false);
}
if !antenna.with_replies && note.reply_id.is_some() {
return Ok(false);
}
if antenna.src == AntennaSrcEnum::Users {
let is_from_one_of_specified_authors = antenna
.users
.iter()
.map(|s| s.parse::<Acct>().unwrap())
.any(|acct| acct.username == note_author.username && acct.host == note_author.host);
if !is_from_one_of_specified_authors {
return Ok(false);
}
} else if antenna.src == AntennaSrcEnum::Instances {
let is_from_one_of_specified_servers = antenna.instances.iter().any(|host| {
host.to_ascii_lowercase()
== note_author
.host
.clone()
.unwrap_or(CONFIG.host.clone())
.to_ascii_lowercase()
});
if !is_from_one_of_specified_servers {
return Ok(false);
}
}
// "Home", "Group", "List" sources are currently disabled
let note_texts = all_texts(NoteLike {
file_ids: note.file_ids,
user_id: note.user_id.clone(),
text: note.text,
cw: note.cw,
renote_id: note.renote_id,
reply_id: note.reply_id,
})
.await?;
let has_keyword = antenna.keywords.iter().any(|words| {
note_texts
.iter()
.any(|text| match_all(words, text, antenna.case_sensitive))
});
if !has_keyword {
return Ok(false);
}
let has_excluded_word = antenna.exclude_keywords.iter().any(|words| {
note_texts
.iter()
.any(|text| match_all(words, text, antenna.case_sensitive))
});
if has_excluded_word {
return Ok(false);
}
let db = db_conn().await?;
let blocked_user_ids: Vec<String> = cache::get_one(cache::Category::Block, &note.user_id)
.await?
.unwrap_or({
// cache miss
let blocks = blocking::Entity::find()
.select_only()
.column(blocking::Column::BlockeeId)
.filter(blocking::Column::BlockerId.eq(&note.user_id))
.into_tuple::<String>()
.all(db)
.await?;
cache::set_one(cache::Category::Block, &note.user_id, &blocks, 10 * 60).await?;
blocks
});
// if the antenna owner is blocked by the note author, return false
if blocked_user_ids.contains(&antenna.user_id) {
return Ok(false);
}
if [NoteVisibilityEnum::Home, NoteVisibilityEnum::Followers].contains(&note.visibility) {
let following_user_ids: Vec<String> =
cache::get_one(cache::Category::Follow, &antenna.user_id)
.await?
.unwrap_or({
// cache miss
let following = following::Entity::find()
.select_only()
.column(following::Column::FolloweeId)
.filter(following::Column::FollowerId.eq(&antenna.user_id))
.into_tuple::<String>()
.all(db)
.await?;
cache::set_one(
cache::Category::Follow,
&antenna.user_id,
&following,
10 * 60,
)
.await?;
following
});
// if the antenna owner is not following the note author, return false
if !following_user_ids.contains(&note.user_id) {
return Ok(false);
}
}
Ok(true)
}
#[cfg(test)]
mod unit_test {
use super::match_all;
use pretty_assertions::assert_eq;
#[test]
fn test_match_all() {
assert_eq!(match_all("Apple", "apple and banana", false), true);
assert_eq!(match_all("Apple", "apple and banana", true), false);
assert_eq!(match_all("Apple Banana", "apple and banana", false), true);
assert_eq!(match_all("Apple Banana", "apple and cinnamon", true), false);
assert_eq!(
match_all("Apple Banana", "apple and cinnamon", false),
false
);
}
}

View file

@ -0,0 +1,50 @@
use crate::misc::meta::fetch_meta;
use sea_orm::DbErr;
/// Checks if a server is blocked.
///
/// ## Argument
/// `host` - punycoded instance host
#[crate::export]
pub async fn is_blocked_server(host: &str) -> Result<bool, DbErr> {
Ok(fetch_meta(true)
.await?
.blocked_hosts
.iter()
.any(|blocked_host| {
host == blocked_host || host.ends_with(format!(".{}", blocked_host).as_str())
}))
}
/// Checks if a server is silenced.
///
/// ## Argument
/// `host` - punycoded instance host
#[crate::export]
pub async fn is_silenced_server(host: &str) -> Result<bool, DbErr> {
Ok(fetch_meta(true)
.await?
.silenced_hosts
.iter()
.any(|silenced_host| {
host == silenced_host || host.ends_with(format!(".{}", silenced_host).as_str())
}))
}
/// Checks if a server is allowlisted.
/// Returns `Ok(true)` if private mode is disabled.
///
/// ## Argument
/// `host` - punycoded instance host
#[crate::export]
pub async fn is_allowed_server(host: &str) -> Result<bool, DbErr> {
let meta = fetch_meta(true).await?;
if !meta.private_mode.unwrap_or(false) {
return Ok(true);
}
if let Some(allowed_hosts) = meta.allowed_hosts {
return Ok(allowed_hosts.contains(&host.to_string()));
}
Ok(false)
}

View file

@ -1,79 +1,7 @@
use crate::database::db_conn;
use crate::model::entity::{drive_file, note};
use crate::misc::get_note_all_texts::{all_texts, NoteLike};
use once_cell::sync::Lazy;
use regex::Regex;
use sea_orm::{prelude::*, QuerySelect};
#[crate::export(object)]
pub struct NoteLike {
pub file_ids: Vec<String>,
pub user_id: Option<String>,
pub text: Option<String>,
pub cw: Option<String>,
pub renote_id: Option<String>,
pub reply_id: Option<String>,
}
async fn all_texts(note: NoteLike) -> Result<Vec<String>, DbErr> {
let db = db_conn().await?;
let mut texts: Vec<String> = vec![];
if let Some(text) = note.text {
texts.push(text);
}
if let Some(cw) = note.cw {
texts.push(cw);
}
texts.extend(
drive_file::Entity::find()
.select_only()
.column(drive_file::Column::Comment)
.filter(drive_file::Column::Id.is_in(note.file_ids))
.into_tuple::<Option<String>>()
.all(db)
.await?
.into_iter()
.flatten(),
);
if let Some(renote_id) = note.renote_id {
if let Some((text, cw)) = note::Entity::find_by_id(renote_id)
.select_only()
.columns([note::Column::Text, note::Column::Cw])
.into_tuple::<(Option<String>, Option<String>)>()
.one(db)
.await?
{
if let Some(t) = text {
texts.push(t);
}
if let Some(c) = cw {
texts.push(c);
}
}
}
if let Some(reply_id) = note.reply_id {
if let Some((text, cw)) = note::Entity::find_by_id(reply_id)
.select_only()
.columns([note::Column::Text, note::Column::Cw])
.into_tuple::<(Option<String>, Option<String>)>()
.one(db)
.await?
{
if let Some(t) = text {
texts.push(t);
}
if let Some(c) = cw {
texts.push(c);
}
}
}
Ok(texts)
}
use sea_orm::DbErr;
fn convert_regex(js_regex: &str) -> String {
static RE: Lazy<Regex> = Lazy::new(|| Regex::new(r"^/(.+)/(.*)$").unwrap());
@ -82,14 +10,13 @@ fn convert_regex(js_regex: &str) -> String {
fn check_word_mute_impl(
texts: &[String],
muted_word_lists: &[Vec<String>],
muted_words: &[String],
muted_patterns: &[String],
) -> bool {
muted_word_lists.iter().any(|muted_word_list| {
muted_words.iter().any(|item| {
texts.iter().any(|text| {
let text_lower = text.to_lowercase();
muted_word_list
.iter()
item.split_whitespace()
.all(|muted_word| text_lower.contains(&muted_word.to_lowercase()))
})
}) || muted_patterns.iter().any(|muted_pattern| {
@ -102,16 +29,138 @@ fn check_word_mute_impl(
#[crate::export]
pub async fn check_word_mute(
note: NoteLike,
muted_word_lists: Vec<Vec<String>>,
muted_patterns: Vec<String>,
muted_words: &[String],
muted_patterns: &[String],
) -> Result<bool, DbErr> {
if muted_word_lists.is_empty() && muted_patterns.is_empty() {
if muted_words.is_empty() && muted_patterns.is_empty() {
Ok(false)
} else {
Ok(check_word_mute_impl(
&all_texts(note).await?,
&muted_word_lists,
&muted_patterns,
muted_words,
muted_patterns,
))
}
}
#[cfg(test)]
mod unit_test {
use super::check_word_mute_impl;
#[test]
fn test_word_mute_match() {
let texts = vec![
"The quick brown fox jumps over the lazy dog.".to_string(),
"色は匂へど 散りぬるを 我が世誰ぞ 常ならむ".to_string(),
"😇".to_string(),
];
let hiragana_1 = r#"/[\u{3040}-\u{309f}]/u"#.to_string();
let hiragana_2 = r#"/[あ-ん]/u"#.to_string();
let katakana_1 = r#"/[\u{30a1}-\u{30ff}]/u"#.to_string();
let katakana_2 = r#"/[ア-ン]/u"#.to_string();
let emoji = r#"/[\u{1f300}-\u{1f5ff}\u{1f900}-\u{1f9ff}\u{1f600}-\u{1f64f}\u{1f680}-\u{1f6ff}\u{2600}-\u{26ff}\u{2700}-\u{27bf}\u{1f1e6}-\u{1f1ff}\u{1f191}-\u{1f251}\u{1f004}\u{1f0cf}\u{1f170}-\u{1f171}\u{1f17e}-\u{1f17f}\u{1f18e}\u{3030}\u{2b50}\u{2b55}\u{2934}-\u{2935}\u{2b05}-\u{2b07}\u{2b1b}-\u{2b1c}\u{3297}\u{3299}\u{303d}\u{00a9}\u{00ae}\u{2122}\u{23f3}\u{24c2}\u{23e9}-\u{23ef}\u{25b6}\u{23f8}-\u{23fa}]/u"#.to_string();
assert!(check_word_mute_impl(&texts, &[], &["/the/i".to_string()]));
assert!(!check_word_mute_impl(&texts, &[], &["/the/".to_string()]));
assert!(check_word_mute_impl(&texts, &[], &["/QuICk/i".to_string()]));
assert!(!check_word_mute_impl(&texts, &[], &["/QuICk/".to_string()]));
assert!(check_word_mute_impl(
&texts,
&[
"".to_string(),
"有為の奥山 今日越えて 浅き夢見し 酔ひもせず".to_string()
],
&[]
));
assert!(!check_word_mute_impl(
&texts,
&["有為の奥山 今日越えて 浅き夢見し 酔ひもせず".to_string()],
&[]
));
assert!(!check_word_mute_impl(
&texts,
&[
"有為の奥山".to_string(),
"今日越えて".to_string(),
"浅き夢見し".to_string(),
"酔ひもせず".to_string()
],
&[]
));
assert!(check_word_mute_impl(
&texts,
&["yellow fox".to_string(), "mastodon".to_string()],
&[hiragana_1.clone()]
));
assert!(check_word_mute_impl(
&texts,
&["yellow fox".to_string(), "mastodon".to_string()],
&[hiragana_2.clone()]
));
assert!(!check_word_mute_impl(
&texts,
&["yellow fox".to_string(), "mastodon".to_string()],
&[katakana_1.clone()]
));
assert!(!check_word_mute_impl(
&texts,
&["yellow fox".to_string(), "mastodon".to_string()],
&[katakana_2.clone()]
));
assert!(check_word_mute_impl(
&texts,
&["brown fox".to_string(), "mastodon".to_string()],
&[katakana_1.clone()]
));
assert!(check_word_mute_impl(
&texts,
&["brown fox".to_string(), "mastodon".to_string()],
&[katakana_2.clone()]
));
assert!(check_word_mute_impl(
&texts,
&["yellow fox".to_string(), "dog".to_string()],
&[katakana_1.clone()]
));
assert!(check_word_mute_impl(
&texts,
&["yellow fox".to_string(), "dog".to_string()],
&[katakana_2.clone()]
));
assert!(check_word_mute_impl(
&texts,
&["yellow fox".to_string(), "mastodon".to_string()],
&[hiragana_1.clone(), katakana_1.clone()]
));
assert!(check_word_mute_impl(
&texts,
&["😇".to_string(), "🥲".to_string(), "🥴".to_string()],
&[]
));
assert!(!check_word_mute_impl(
&texts,
&["🙂".to_string(), "🥲".to_string(), "🥴".to_string()],
&[]
));
assert!(check_word_mute_impl(&texts, &[], &[emoji.clone()]));
}
}

View file

@ -1,4 +1,4 @@
use crate::config::server::SERVER_CONFIG;
use crate::config::CONFIG;
#[derive(thiserror::Error, Debug)]
pub enum Error {
@ -14,21 +14,21 @@ pub enum Error {
pub fn get_full_ap_account(username: &str, host: Option<&str>) -> Result<String, Error> {
Ok(match host {
Some(host) => format!("{}@{}", username, to_puny(host)?),
None => format!("{}@{}", username, extract_host(&SERVER_CONFIG.url)?),
None => format!("{}@{}", username, extract_host(&CONFIG.url)?),
})
}
#[crate::export]
pub fn is_self_host(host: Option<&str>) -> Result<bool, Error> {
Ok(match host {
Some(host) => extract_host(&SERVER_CONFIG.url)? == to_puny(host)?,
Some(host) => extract_host(&CONFIG.url)? == to_puny(host)?,
None => true,
})
}
#[crate::export]
pub fn is_same_origin(uri: &str) -> Result<bool, Error> {
Ok(url::Url::parse(uri)?.origin().ascii_serialization() == SERVER_CONFIG.url)
Ok(url::Url::parse(uri)?.origin().ascii_serialization() == CONFIG.url)
}
#[crate::export]

View file

@ -0,0 +1,31 @@
#[inline]
#[crate::export]
pub fn is_unicode_emoji(s: &str) -> bool {
emojis::get(s).is_some()
}
#[cfg(test)]
mod unit_test {
use super::is_unicode_emoji;
#[test]
fn test_unicode_emoji_check() {
assert!(is_unicode_emoji(""));
assert!(is_unicode_emoji("👍"));
assert!(is_unicode_emoji(""));
assert!(is_unicode_emoji("♥️"));
assert!(is_unicode_emoji("❤️"));
assert!(is_unicode_emoji("💙"));
assert!(is_unicode_emoji("🩷"));
assert!(is_unicode_emoji("🖖🏿"));
assert!(is_unicode_emoji("🏃‍➡️"));
assert!(is_unicode_emoji("👩‍❤️‍👨"));
assert!(is_unicode_emoji("👩‍👦‍👦"));
assert!(is_unicode_emoji("🏳️‍🌈"));
assert!(!is_unicode_emoji("⭐⭐"));
assert!(!is_unicode_emoji("x"));
assert!(!is_unicode_emoji("\t"));
assert!(!is_unicode_emoji(":meow_aww:"));
}
}

View file

@ -0,0 +1,36 @@
#[crate::export]
pub fn sql_like_escape(src: &str) -> String {
src.replace('%', r"\%").replace('_', r"\_")
}
#[crate::export]
pub fn safe_for_sql(src: &str) -> 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(""), "");
assert_eq!(sql_like_escape("abc"), "abc");
assert_eq!(sql_like_escape("a%bc"), r"a\%bc");
assert_eq!(sql_like_escape("a呼%吸bc"), r"a呼\%吸bc");
assert_eq!(sql_like_escape("a呼%吸b%_c"), r"a呼\%吸b\%\_c");
assert_eq!(sql_like_escape("_اللغة العربية"), r"\_اللغة العربية");
}
#[test]
fn safe_for_sql_test() {
assert!(safe_for_sql("123"));
assert!(safe_for_sql("人間"));
assert!(!safe_for_sql("人間\x09"));
assert!(!safe_for_sql("abc\ndef"));
assert!(!safe_for_sql("%something%"));
}
}

View file

@ -0,0 +1,46 @@
/// Convert milliseconds to a human readable string
#[crate::export]
pub fn format_milliseconds(milliseconds: u32) -> 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> = vec![];
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)");
assert_eq!(format_milliseconds(1387938), "23 minute(s), 7 second(s)");
assert_eq!(format_milliseconds(34200457), "9 hour(s), 30 minute(s)");
assert_eq!(
format_milliseconds(998244353),
"11 day(s), 13 hour(s), 17 minute(s), 24 second(s)"
);
}
}

View file

@ -0,0 +1,219 @@
use crate::database::cache;
use crate::util::http_client;
use image::{io::Reader, ImageError, ImageFormat};
use isahc::ReadResponseExt;
use nom_exif::{parse_jpeg_exif, EntryValue, ExifTag};
use std::io::Cursor;
use tokio::sync::Mutex;
#[derive(thiserror::Error, Debug)]
pub enum Error {
#[error("Redis cache error: {0}")]
CacheErr(#[from] cache::Error),
#[error("HTTP client aquisition error: {0}")]
HttpClientErr(#[from] http_client::Error),
#[error("Isahc error: {0}")]
IsahcErr(#[from] isahc::Error),
#[error("HTTP error: {0}")]
HttpErr(String),
#[error("Image decoding error: {0}")]
ImageErr(#[from] ImageError),
#[error("Image decoding error: {0}")]
IoErr(#[from] std::io::Error),
#[error("Exif extraction error: {0}")]
ExifErr(#[from] nom_exif::Error),
#[error("Emoji meta attempt limit exceeded: {0}")]
TooManyAttempts(String),
#[error("Unsupported image type: {0}")]
UnsupportedImageErr(String),
}
const BROWSER_SAFE_IMAGE_TYPES: [ImageFormat; 8] = [
ImageFormat::Png,
ImageFormat::Jpeg,
ImageFormat::Gif,
ImageFormat::WebP,
ImageFormat::Tiff,
ImageFormat::Bmp,
ImageFormat::Ico,
ImageFormat::Avif,
];
static MTX_GUARD: Mutex<()> = Mutex::const_new(());
#[derive(Debug, PartialEq)]
#[crate::export(object)]
pub struct ImageSize {
pub width: u32,
pub height: u32,
}
#[crate::export]
pub async fn get_image_size_from_url(url: &str) -> Result<ImageSize, Error> {
let attempted: bool;
{
let _ = MTX_GUARD.lock().await;
attempted = cache::get_one::<bool>(cache::Category::FetchUrl, url)
.await?
.is_some();
if !attempted {
cache::set_one(cache::Category::FetchUrl, url, &true, 10 * 60).await?;
}
}
if attempted {
tracing::warn!("attempt limit exceeded: {}", url);
return Err(Error::TooManyAttempts(url.to_string()));
}
tracing::info!("retrieving image size from {}", url);
let mut response = http_client::client()?.get(url)?;
if !response.status().is_success() {
tracing::info!("status: {}", response.status());
tracing::debug!("response body: {:#?}", response.body());
return Err(Error::HttpErr(format!("Failed to get image from {}", url)));
}
let image_bytes = response.bytes()?;
let reader = Reader::new(Cursor::new(&image_bytes)).with_guessed_format()?;
let format = reader.format();
if format.is_none() || !BROWSER_SAFE_IMAGE_TYPES.contains(&format.unwrap()) {
return Err(Error::UnsupportedImageErr(format!("{:?}", format)));
}
let size = reader.into_dimensions()?;
let res = ImageSize {
width: size.0,
height: size.1,
};
if format.unwrap() != ImageFormat::Jpeg {
return Ok(res);
}
// handle jpeg orientation
// https://magnushoff.com/articles/jpeg-orientation/
let exif = parse_jpeg_exif(&*image_bytes)?;
if exif.is_none() {
return Ok(res);
}
let orientation = exif.unwrap().get_value(&ExifTag::Orientation)?;
let rotated =
orientation.is_some() && matches!(orientation.unwrap(), EntryValue::U32(v) if v >= 5);
if !rotated {
return Ok(res);
}
Ok(ImageSize {
width: size.1,
height: size.0,
})
}
#[cfg(test)]
mod unit_test {
use super::{get_image_size_from_url, ImageSize};
use crate::database::cache;
use pretty_assertions::assert_eq;
#[tokio::test]
async fn test_get_image_size() {
let png_url_1 = "https://firefish.dev/firefish/firefish/-/raw/5891a90f71a8b9d5ea99c683ade7e485c685d642/packages/backend/assets/splash.png";
let png_url_2 = "https://firefish.dev/firefish/firefish/-/raw/5891a90f71a8b9d5ea99c683ade7e485c685d642/packages/backend/assets/notification-badges/at.png";
let png_url_3 = "https://firefish.dev/firefish/firefish/-/raw/5891a90f71a8b9d5ea99c683ade7e485c685d642/packages/backend/assets/api-doc.png";
let rotated_jpeg_url = "https://firefish.dev/firefish/firefish/-/raw/5891a90f71a8b9d5ea99c683ade7e485c685d642/packages/backend/test/resources/rotate.jpg";
let webp_url_1 = "https://firefish.dev/firefish/firefish/-/raw/5891a90f71a8b9d5ea99c683ade7e485c685d642/custom/assets/badges/error.webp";
let webp_url_2 = "https://firefish.dev/firefish/firefish/-/raw/5891a90f71a8b9d5ea99c683ade7e485c685d642/packages/backend/assets/screenshots/1.webp";
let ico_url = "https://firefish.dev/firefish/firefish/-/raw/5891a90f71a8b9d5ea99c683ade7e485c685d642/packages/backend/assets/favicon.ico";
let gif_url = "https://firefish.dev/firefish/firefish/-/raw/b9c3dfbd3d473cb2cee20c467eeae780bc401271/packages/backend/test/resources/anime.gif";
let mp3_url = "https://firefish.dev/firefish/firefish/-/blob/5891a90f71a8b9d5ea99c683ade7e485c685d642/packages/backend/assets/sounds/aisha/1.mp3";
// delete caches in case you run this test multiple times
cache::delete_all(cache::Category::FetchUrl).await.unwrap();
let png_size_1 = ImageSize {
width: 1024,
height: 1024,
};
let png_size_2 = ImageSize {
width: 96,
height: 96,
};
let png_size_3 = ImageSize {
width: 1024,
height: 354,
};
let rotated_jpeg_size = ImageSize {
width: 256,
height: 512,
};
let webp_size_1 = ImageSize {
width: 256,
height: 256,
};
let webp_size_2 = ImageSize {
width: 1080,
height: 2340,
};
let ico_size = ImageSize {
width: 256,
height: 256,
};
let gif_size = ImageSize {
width: 256,
height: 256,
};
assert_eq!(
png_size_1,
get_image_size_from_url(png_url_1).await.unwrap()
);
assert_eq!(
png_size_2,
get_image_size_from_url(png_url_2).await.unwrap()
);
assert_eq!(
png_size_3,
get_image_size_from_url(png_url_3).await.unwrap()
);
assert_eq!(
rotated_jpeg_size,
get_image_size_from_url(rotated_jpeg_url).await.unwrap()
);
assert_eq!(
webp_size_1,
get_image_size_from_url(webp_url_1).await.unwrap()
);
assert_eq!(
webp_size_2,
get_image_size_from_url(webp_url_2).await.unwrap()
);
assert_eq!(ico_size, get_image_size_from_url(ico_url).await.unwrap());
assert_eq!(gif_size, get_image_size_from_url(gif_url).await.unwrap());
assert!(get_image_size_from_url(mp3_url).await.is_err());
}
#[tokio::test]
async fn too_many_attempts() {
let url = "https://firefish.dev/firefish/firefish/-/raw/5891a90f71a8b9d5ea99c683ade7e485c685d642/packages/backend/assets/splash.png";
// delete caches in case you run this test multiple times
cache::delete_one(cache::Category::FetchUrl, url)
.await
.unwrap();
assert!(get_image_size_from_url(url).await.is_ok());
assert!(get_image_size_from_url(url).await.is_err());
}
}

View file

@ -0,0 +1,79 @@
use crate::database::db_conn;
use crate::model::entity::{drive_file, note};
use sea_orm::{prelude::*, QuerySelect};
/// TODO: handle name collisions better
#[crate::export(object, js_name = "NoteLikeForAllTexts")]
pub struct NoteLike {
pub file_ids: Vec<String>,
pub user_id: String,
pub text: Option<String>,
pub cw: Option<String>,
pub renote_id: Option<String>,
pub reply_id: Option<String>,
}
pub async fn all_texts(note: NoteLike) -> Result<Vec<String>, DbErr> {
let db = db_conn().await?;
let mut texts: Vec<String> = vec![];
if let Some(text) = note.text {
texts.push(text);
}
if let Some(cw) = note.cw {
texts.push(cw);
}
texts.extend(
drive_file::Entity::find()
.select_only()
.column(drive_file::Column::Comment)
.filter(drive_file::Column::Id.is_in(note.file_ids))
.into_tuple::<Option<String>>()
.all(db)
.await?
.into_iter()
.flatten(),
);
if let Some(renote_id) = &note.renote_id {
if let Some((text, cw)) = note::Entity::find_by_id(renote_id)
.select_only()
.columns([note::Column::Text, note::Column::Cw])
.into_tuple::<(Option<String>, Option<String>)>()
.one(db)
.await?
{
if let Some(t) = text {
texts.push(t);
}
if let Some(c) = cw {
texts.push(c);
}
} else {
tracing::warn!("nonexistent renote id: {:#?}", renote_id);
}
}
if let Some(reply_id) = &note.reply_id {
if let Some((text, cw)) = note::Entity::find_by_id(reply_id)
.select_only()
.columns([note::Column::Text, note::Column::Cw])
.into_tuple::<(Option<String>, Option<String>)>()
.one(db)
.await?
{
if let Some(t) = text {
texts.push(t);
}
if let Some(c) = cw {
texts.push(c);
}
} else {
tracing::warn!("nonexistent reply id: {:#?}", reply_id);
}
}
Ok(texts)
}

View file

@ -0,0 +1,94 @@
use serde::{Deserialize, Serialize};
// TODO: handle name collisions in a better way
#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
#[crate::export(object, js_name = "NoteLikeForGetNoteSummary")]
pub struct NoteLike {
pub file_ids: Vec<String>,
pub text: Option<String>,
pub cw: Option<String>,
pub has_poll: bool,
}
#[crate::export]
pub fn get_note_summary(note: NoteLike) -> String {
let mut buf: Vec<String> = vec![];
if let Some(cw) = note.cw {
buf.push(cw)
} else if let Some(text) = note.text {
buf.push(text)
}
match note.file_ids.len() {
0 => (),
1 => buf.push("📎".to_string()),
n => buf.push(format!("📎 ({})", n)),
};
if note.has_poll {
buf.push("📊".to_string())
}
buf.join(" ")
}
#[cfg(test)]
mod unit_test {
use super::{get_note_summary, NoteLike};
use pretty_assertions::assert_eq;
#[test]
fn test_note_summary() {
let note = NoteLike {
file_ids: vec![],
text: Some("Hello world!".to_string()),
cw: None,
has_poll: false,
};
assert_eq!(get_note_summary(note), "Hello world!");
let note_with_cw = NoteLike {
file_ids: vec![],
text: Some("Hello world!".to_string()),
cw: Some("Content warning".to_string()),
has_poll: false,
};
assert_eq!(get_note_summary(note_with_cw), "Content warning");
let note_with_file_and_cw = NoteLike {
file_ids: vec!["9s7fmcqogiq4igin".to_string()],
text: None,
cw: Some("Selfie, no ec".to_string()),
has_poll: false,
};
assert_eq!(get_note_summary(note_with_file_and_cw), "Selfie, no ec 📎");
let note_with_files_only = NoteLike {
file_ids: vec![
"9s7fmcqogiq4igin".to_string(),
"9s7qrld5u14cey98".to_string(),
"9s7gebs5zgts4kca".to_string(),
"9s5z3e4vefqd29ee".to_string(),
],
text: None,
cw: None,
has_poll: false,
};
assert_eq!(get_note_summary(note_with_files_only), "📎 (4)");
let note_all = NoteLike {
file_ids: vec![
"9s7fmcqogiq4igin".to_string(),
"9s7qrld5u14cey98".to_string(),
"9s7gebs5zgts4kca".to_string(),
"9s5z3e4vefqd29ee".to_string(),
],
text: Some("Hello world!".to_string()),
cw: Some("Content warning".to_string()),
has_poll: true,
};
assert_eq!(get_note_summary(note_all), "Content warning 📎 (4) 📊");
}
}

View file

@ -0,0 +1,9 @@
use crate::model::entity::note;
// https://github.com/napi-rs/napi-rs/issues/2060
type Note = note::Model;
#[crate::export]
pub fn is_quote(note: Note) -> bool {
note.renote_id.is_some() && (note.text.is_some() || note.has_poll || !note.file_ids.is_empty())
}

View file

@ -0,0 +1,34 @@
#[crate::export]
pub fn is_safe_url(url: &str) -> bool {
if let Ok(url) = url.parse::<url::Url>() {
if url.host_str().unwrap_or_default() == "unix"
|| !["http", "https"].contains(&url.scheme())
|| ![None, Some(80), Some(443)].contains(&url.port())
{
return false;
}
true
} else {
false
}
}
#[cfg(test)]
mod unit_test {
use super::is_safe_url;
#[test]
fn safe_url() {
assert!(is_safe_url("http://firefish.dev/firefish/firefish"));
assert!(is_safe_url("https://firefish.dev/firefish/firefish"));
assert!(is_safe_url("http://firefish.dev:80/firefish/firefish"));
assert!(is_safe_url("https://firefish.dev:80/firefish/firefish"));
assert!(is_safe_url("http://firefish.dev:443/firefish/firefish"));
assert!(is_safe_url("https://firefish.dev:443/firefish/firefish"));
assert!(!is_safe_url("https://unix/firefish/firefish"));
assert!(!is_safe_url("https://firefish.dev:35/firefish/firefish"));
assert!(!is_safe_url("ftp://firefish.dev/firefish/firefish"));
assert!(!is_safe_url("nyaa"));
assert!(!is_safe_url(""));
}
}

View file

@ -0,0 +1,111 @@
use crate::database::cache;
use crate::util::http_client;
use isahc::ReadResponseExt;
use serde::{Deserialize, Serialize};
#[derive(thiserror::Error, Debug)]
pub enum Error {
#[error("Cache error: {0}")]
CacheErr(#[from] cache::Error),
#[error("Isahc error: {0}")]
IsahcErr(#[from] isahc::Error),
#[error("HTTP client aquisition error: {0}")]
HttpClientErr(#[from] http_client::Error),
#[error("HTTP error: {0}")]
HttpErr(String),
#[error("Response parsing error: {0}")]
IoErr(#[from] std::io::Error),
#[error("Failed to deserialize JSON: {0}")]
JsonErr(#[from] serde_json::Error),
}
const UPSTREAM_PACKAGE_JSON_URL: &str =
"https://firefish.dev/firefish/firefish/-/raw/main/package.json";
async fn get_latest_version() -> Result<String, Error> {
#[derive(Debug, Deserialize, Serialize)]
struct Response {
version: String,
}
let mut response = http_client::client()?.get(UPSTREAM_PACKAGE_JSON_URL)?;
if !response.status().is_success() {
tracing::info!("status: {}", response.status());
tracing::debug!("response body: {:#?}", response.body());
return Err(Error::HttpErr(
"Failed to fetch version from Firefish GitLab".to_string(),
));
}
let res_parsed: Response = serde_json::from_str(&response.text()?)?;
Ok(res_parsed.version)
}
#[crate::export]
pub async fn latest_version() -> Result<String, Error> {
let version: Option<String> =
cache::get_one(cache::Category::FetchUrl, UPSTREAM_PACKAGE_JSON_URL).await?;
if let Some(v) = version {
tracing::trace!("use cached value: {}", v);
Ok(v)
} else {
tracing::trace!("cache is expired, fetching the latest version");
let fetched_version = get_latest_version().await?;
tracing::trace!("fetched value: {}", fetched_version);
cache::set_one(
cache::Category::FetchUrl,
UPSTREAM_PACKAGE_JSON_URL,
&fetched_version,
3 * 60 * 60,
)
.await?;
Ok(fetched_version)
}
}
#[cfg(test)]
mod unit_test {
use super::{latest_version, UPSTREAM_PACKAGE_JSON_URL};
use crate::database::cache;
fn validate_version(version: String) {
// version: YYYYMMDD or YYYYMMDD-X
assert!(version.len() >= 8);
assert!(version[..8].chars().all(|c| c.is_ascii_digit()));
// YYYY
assert!(&version[..4] >= "2024");
// MM
assert!(&version[4..6] >= "01");
assert!(&version[4..6] <= "12");
// DD
assert!(&version[6..8] >= "01");
assert!(&version[6..8] <= "31");
// -X
if version.len() > 8 {
assert!(version.chars().nth(8).unwrap() == '-');
assert!(version[9..].chars().all(|c| c.is_ascii_digit()));
}
}
#[tokio::test]
async fn check_version() {
// delete caches in case you run this test multiple times
cache::delete_one(cache::Category::FetchUrl, UPSTREAM_PACKAGE_JSON_URL)
.await
.unwrap();
// fetch from firefish.dev
validate_version(latest_version().await.unwrap());
// use cache
validate_version(latest_version().await.unwrap());
}
}

View file

@ -1,6 +1,6 @@
#[crate::export]
pub fn to_mastodon_id(firefish_id: &str) -> Option<String> {
let decoded: [u8; 16] = basen::BASE36.decode_var_len(&firefish_id.to_ascii_lowercase())?;
let decoded: [u8; 16] = basen::BASE36.decode_var_len(firefish_id)?;
Some(basen::BASE10.encode_var_len(&decoded))
}

View file

@ -0,0 +1,83 @@
use crate::database::db_conn;
use crate::model::entity::meta;
use rand::prelude::*;
use sea_orm::{prelude::*, ActiveValue};
use std::sync::Mutex;
type Meta = meta::Model;
static CACHE: Mutex<Option<Meta>> = Mutex::new(None);
fn update_cache(meta: &Meta) {
let _ = CACHE.lock().map(|mut cache| *cache = Some(meta.clone()));
}
#[crate::export]
pub async fn fetch_meta(use_cache: bool) -> Result<Meta, DbErr> {
// try using cache
if use_cache {
if let Some(cache) = CACHE.lock().ok().and_then(|cache| cache.clone()) {
return Ok(cache);
}
}
// try fetching from db
let db = db_conn().await?;
let meta = meta::Entity::find().one(db).await?;
if let Some(meta) = meta {
update_cache(&meta);
return Ok(meta);
}
// create a new meta object and insert into db
let meta = meta::Entity::insert(meta::ActiveModel {
id: ActiveValue::Set("x".to_owned()),
..Default::default()
})
.exec_with_returning(db)
.await?;
update_cache(&meta);
Ok(meta)
}
#[crate::export(object)]
pub struct PugArgs {
pub img: Option<String>,
pub title: String,
pub instance_name: String,
pub desc: Option<String>,
pub icon: Option<String>,
pub splash_icon: Option<String>,
pub theme_color: Option<String>,
pub random_motd: String,
pub private_mode: Option<bool>,
}
#[crate::export]
pub fn meta_to_pug_args(meta: Meta) -> PugArgs {
let mut rng = rand::thread_rng();
let splash_icon = meta
.custom_splash_icons
.choose(&mut rng)
.map(|s| s.to_owned())
.or_else(|| meta.icon_url.to_owned());
let random_motd = meta
.custom_motd
.choose(&mut rng)
.map(|s| s.to_owned())
.unwrap_or_else(|| "Loading...".to_owned());
let name = meta.name.unwrap_or_else(|| "Firefish".to_owned());
PugArgs {
img: meta.banner_url,
title: name.clone(),
instance_name: name.clone(),
desc: meta.description,
icon: meta.icon_url,
splash_icon,
theme_color: meta.theme_color,
random_motd,
private_mode: meta.private_mode,
}
}

View file

@ -1,5 +1,20 @@
pub mod acct;
pub mod check_hit_antenna;
pub mod check_server_block;
pub mod check_word_mute;
pub mod convert_host;
pub mod emoji;
pub mod escape_sql;
pub mod format_milliseconds;
pub mod get_image_size;
pub mod get_note_all_texts;
pub mod get_note_summary;
pub mod is_quote;
pub mod is_safe_url;
pub mod latest_version;
pub mod mastodon_id;
pub mod meta;
pub mod nyaify;
pub mod password;
pub mod reaction;
pub mod remove_old_attestation_challenges;
pub mod system_info;

View file

@ -0,0 +1,69 @@
use argon2::{
password_hash,
password_hash::{rand_core::OsRng, PasswordHash, PasswordHasher, PasswordVerifier, SaltString},
Argon2,
};
#[crate::export]
pub fn hash_password(password: &str) -> Result<String, password_hash::errors::Error> {
let salt = SaltString::generate(&mut OsRng);
Ok(Argon2::default()
.hash_password(password.as_bytes(), &salt)?
.to_string())
}
#[derive(thiserror::Error, Debug)]
pub enum VerifyError {
#[error("An error occured while bcrypt verification: {0}")]
BcryptError(#[from] bcrypt::BcryptError),
#[error("Invalid argon2 password hash: {0}")]
InvalidArgon2Hash(#[from] password_hash::Error),
#[error("An error occured while argon2 verification: {0}")]
Argon2Error(#[from] argon2::Error),
}
#[crate::export]
pub fn verify_password(password: &str, hash: &str) -> Result<bool, VerifyError> {
if is_old_password_algorithm(hash) {
Ok(bcrypt::verify(password, hash)?)
} else {
let parsed_hash = PasswordHash::new(hash)?;
Ok(Argon2::default()
.verify_password(password.as_bytes(), &parsed_hash)
.is_ok())
}
}
#[inline]
#[crate::export]
pub fn is_old_password_algorithm(hash: &str) -> bool {
// bcrypt hashes start with $2[ab]$
hash.starts_with("$2")
}
#[cfg(test)]
mod unit_test {
use super::{hash_password, is_old_password_algorithm, verify_password};
#[test]
fn verify_password_test() {
let password = "omWc*%sD^fn7o2cXmc9e2QasBdrbRuhNB*gx!J5";
let hash = hash_password(password).unwrap();
assert!(verify_password(password, hash.as_str()).unwrap());
let argon2_hash = "$argon2id$v=19$m=19456,t=2,p=1$jty3puDFd4ENv/lgHn3ROQ$kRHDdEoVv2rruvnF731E74NxnYlvj5FMgePdGIIq3Jk";
let argon2_invalid_hash = "$argon2id$v=19$m=19456,t=2,p=1$jty3puDFd4ENv/lgHn3ROQ$kRHDdEoVv2rruvnF731E74NxnYlvj4FMgePdGIIq3Jk";
let bcrypt_hash = "$2a$12$WzUc.20jgbHmQjUMqTr8vOhKqYbS1BUvubapv/GLjCK1IN.h4e4la";
let bcrypt_invalid_hash = "$2a$12$WzUc.20jgbHmQjUMqTr7vOhKqYbS1BUvubapv/GLjCK1IN.h4e4la";
assert!(!is_old_password_algorithm(argon2_hash));
assert!(is_old_password_algorithm(bcrypt_hash));
assert!(verify_password(password, argon2_hash).unwrap());
assert!(verify_password(password, bcrypt_hash).unwrap());
assert!(!verify_password(password, argon2_invalid_hash).unwrap());
assert!(!verify_password(password, bcrypt_invalid_hash).unwrap());
}
}

View file

@ -0,0 +1,195 @@
use crate::database::db_conn;
use crate::misc::{convert_host::to_puny, emoji::is_unicode_emoji, meta::fetch_meta};
use crate::model::entity::emoji;
use once_cell::sync::Lazy;
use regex::Regex;
use sea_orm::prelude::*;
use std::collections::HashMap;
#[derive(PartialEq, Debug)]
#[crate::export(object)]
pub struct DecodedReaction {
pub reaction: String,
pub name: Option<String>,
pub host: Option<String>,
}
#[crate::export]
pub fn decode_reaction(reaction: &str) -> DecodedReaction {
// Misskey allows you to include "+" and "-" in emoji shortcodes
// MFM spec: https://github.com/misskey-dev/mfm.js/blob/6aaf68089023c6adebe44123eebbc4dcd75955e0/docs/syntax.md?plain=1#L583
// Misskey's implementation: https://github.com/misskey-dev/misskey/blob/bba3097765317cbf95d09627961b5b5dce16a972/packages/backend/src/core/ReactionService.ts#L68
static RE: Lazy<Regex> =
Lazy::new(|| Regex::new(r"^:([0-9A-Za-z_+-]+)(?:@([0-9A-Za-z_.-]+))?:$").unwrap());
if let Some(captures) = RE.captures(reaction) {
let name = &captures[1];
let host = captures.get(2).map(|s| s.as_str());
DecodedReaction {
reaction: format!(":{}@{}:", name, host.unwrap_or(".")),
name: Some(name.to_owned()),
host: host.map(|s| s.to_owned()),
}
} else {
DecodedReaction {
reaction: reaction.to_owned(),
name: None,
host: None,
}
}
}
#[crate::export]
pub fn count_reactions(reactions: &HashMap<String, u32>) -> HashMap<String, u32> {
let mut res = HashMap::<String, u32>::new();
for (reaction, count) in reactions.iter() {
if count > &0 {
let decoded = decode_reaction(reaction).reaction;
let total = res.entry(decoded).or_insert(0);
*total += count;
}
}
res
}
#[derive(thiserror::Error, Debug)]
pub enum Error {
#[error("Idna error: {0}")]
IdnaError(#[from] idna::Errors),
#[error("Database error: {0}")]
DbError(#[from] DbErr),
}
#[crate::export]
pub async fn to_db_reaction(reaction: Option<&str>, host: Option<&str>) -> Result<String, Error> {
if let Some(reaction) = reaction {
// FIXME: Is it okay to do this only here?
// This was introduced in https://firefish.dev/firefish/firefish/-/commit/af730e75b6fc1a57ca680ce83459d7e433b130cf
if reaction.contains('❤') || reaction.contains("♥️") {
return Ok("❤️".to_owned());
}
if is_unicode_emoji(reaction) {
return Ok(reaction.to_owned());
}
static RE: Lazy<Regex> =
Lazy::new(|| Regex::new(r"^:([0-9A-Za-z_+-]+)(?:@\.)?:$").unwrap());
if let Some(captures) = RE.captures(reaction) {
let name = &captures[1];
let db = db_conn().await?;
if let Some(host) = host {
// remote emoji
let ascii_host = to_puny(host)?;
// TODO: Does SeaORM have the `exists` method?
if emoji::Entity::find()
.filter(emoji::Column::Name.eq(name))
.filter(emoji::Column::Host.eq(&ascii_host))
.one(db)
.await?
.is_some()
{
return Ok(format!(":{name}@{ascii_host}:"));
}
tracing::info!("nonexistent remote custom emoji: :{name}@{ascii_host}:");
} else {
// local emoji
// TODO: Does SeaORM have the `exists` method?
if emoji::Entity::find()
.filter(emoji::Column::Name.eq(name))
.filter(emoji::Column::Host.is_null())
.one(db)
.await?
.is_some()
{
return Ok(format!(":{name}:"));
}
tracing::info!("nonexistent local custom emoji: :{name}:");
}
};
};
Ok(fetch_meta(true).await?.default_reaction)
}
#[cfg(test)]
mod unit_test {
use super::{decode_reaction, DecodedReaction};
use pretty_assertions::{assert_eq, assert_ne};
#[test]
fn test_decode_reaction() {
let unicode_emoji_1 = DecodedReaction {
reaction: "".to_string(),
name: None,
host: None,
};
let unicode_emoji_2 = DecodedReaction {
reaction: "🩷".to_string(),
name: None,
host: None,
};
assert_eq!(decode_reaction(""), unicode_emoji_1);
assert_eq!(decode_reaction("🩷"), unicode_emoji_2);
assert_ne!(decode_reaction(""), unicode_emoji_2);
assert_ne!(decode_reaction("🩷"), unicode_emoji_1);
let unicode_emoji_3 = DecodedReaction {
reaction: "🖖🏿".to_string(),
name: None,
host: None,
};
assert_eq!(decode_reaction("🖖🏿"), unicode_emoji_3);
let local_emoji = DecodedReaction {
reaction: ":meow_melt_tears@.:".to_string(),
name: Some("meow_melt_tears".to_string()),
host: None,
};
assert_eq!(decode_reaction(":meow_melt_tears:"), local_emoji);
let remote_emoji_1 = DecodedReaction {
reaction: ":meow_uwu@some-domain.example.org:".to_string(),
name: Some("meow_uwu".to_string()),
host: Some("some-domain.example.org".to_string()),
};
assert_eq!(
decode_reaction(":meow_uwu@some-domain.example.org:"),
remote_emoji_1
);
let remote_emoji_2 = DecodedReaction {
reaction: ":C++23@xn--eckwd4c7c.example.org:".to_string(),
name: Some("C++23".to_string()),
host: Some("xn--eckwd4c7c.example.org".to_string()),
};
assert_eq!(
decode_reaction(":C++23@xn--eckwd4c7c.example.org:"),
remote_emoji_2
);
let invalid_reaction_1 = DecodedReaction {
reaction: ":foo".to_string(),
name: None,
host: None,
};
assert_eq!(decode_reaction(":foo"), invalid_reaction_1);
let invalid_reaction_2 = DecodedReaction {
reaction: ":foo&@example.com:".to_string(),
name: None,
host: None,
};
assert_eq!(decode_reaction(":foo&@example.com:"), invalid_reaction_2);
}
}

View file

@ -0,0 +1,19 @@
// TODO: We want to get rid of this
use crate::database::db_conn;
use crate::model::entity::attestation_challenge;
use chrono::{Duration, Local};
use sea_orm::{ColumnTrait, DbErr, EntityTrait, QueryFilter};
/// Delete all entries in the "attestation_challenge" table created at more than 5 minutes ago
#[crate::export]
pub async fn remove_old_attestation_challenges() -> Result<(), DbErr> {
let res = attestation_challenge::Entity::delete_many()
.filter(attestation_challenge::Column::CreatedAt.lt(Local::now() - Duration::minutes(5)))
.exec(db_conn().await?)
.await?;
tracing::info!("{} attestation challenges are removed", res.rows_affected);
Ok(())
}

View file

@ -0,0 +1,90 @@
use crate::init::system_info::{system, SysinfoPoisonError};
use sysinfo::{Disks, MemoryRefreshKind};
// TODO: i64 -> u64 (we can't export u64 to Node.js)
#[crate::export(object)]
pub struct Cpu {
pub model: String,
// TODO: u16 -> usize (we can't export usize to Node.js)
pub cores: u16,
}
#[crate::export(object)]
pub struct Memory {
/// Total memory amount in bytes
pub total: i64,
/// Used memory amount in bytes
pub used: i64,
/// Available (for (re)use) memory amount in bytes
pub available: i64,
}
#[crate::export(object)]
pub struct Storage {
/// Total storage space in bytes
pub total: i64,
/// Used storage space in bytes
pub used: i64,
}
#[crate::export]
pub fn cpu_info() -> Result<Cpu, SysinfoPoisonError> {
let system_info = system()?;
Ok(Cpu {
model: match system_info.cpus() {
[] => {
tracing::debug!("failed to get CPU info");
"unknown".to_string()
}
cpus => cpus[0].brand().to_string(),
},
cores: system_info.cpus().len() as u16,
})
}
#[crate::export]
pub fn cpu_usage() -> Result<f32, SysinfoPoisonError> {
let mut system_info = system()?;
system_info.refresh_cpu_usage();
let total_cpu_usage: f32 = system_info.cpus().iter().map(|cpu| cpu.cpu_usage()).sum();
let cpu_threads = system_info.cpus().len();
Ok(total_cpu_usage / (cpu_threads as f32))
}
#[crate::export]
pub fn memory_usage() -> Result<Memory, SysinfoPoisonError> {
let mut system_info = system()?;
system_info.refresh_memory_specifics(MemoryRefreshKind::new().with_ram());
Ok(Memory {
total: system_info.total_memory() as i64,
used: system_info.used_memory() as i64,
available: system_info.available_memory() as i64,
})
}
#[crate::export]
pub fn storage_usage() -> Option<Storage> {
// Get the first disk that is actualy used.
let disks = Disks::new_with_refreshed_list();
let disk = disks
.iter()
.find(|disk| disk.available_space() > 0 && disk.total_space() > disk.available_space());
if let Some(disk) = disk {
let total = disk.total_space() as i64;
let available = disk.available_space() as i64;
return Some(Storage {
total,
used: total - available,
});
}
tracing::debug!("failed to get stats");
None
}

View file

@ -2,7 +2,8 @@
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
#[sea_orm(table_name = "abuse_user_report")]
#[cfg_attr(
feature = "napi",

View file

@ -2,7 +2,8 @@
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
#[sea_orm(table_name = "access_token")]
#[cfg_attr(
feature = "napi",

View file

@ -2,7 +2,8 @@
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
#[sea_orm(table_name = "ad")]
#[cfg_attr(
feature = "napi",

View file

@ -2,7 +2,8 @@
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
#[sea_orm(table_name = "announcement")]
#[cfg_attr(
feature = "napi",

View file

@ -2,7 +2,8 @@
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
#[sea_orm(table_name = "announcement_read")]
#[cfg_attr(
feature = "napi",

View file

@ -3,7 +3,8 @@
use super::sea_orm_active_enums::AntennaSrcEnum;
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
#[sea_orm(table_name = "antenna")]
#[cfg_attr(
feature = "napi",
@ -20,8 +21,6 @@ pub struct Model {
pub src: AntennaSrcEnum,
#[sea_orm(column_name = "userListId")]
pub user_list_id: Option<String>,
#[sea_orm(column_type = "JsonBinary")]
pub keywords: Json,
#[sea_orm(column_name = "withFile")]
pub with_file: bool,
pub expression: Option<String>,
@ -33,10 +32,10 @@ pub struct Model {
#[sea_orm(column_name = "userGroupJoiningId")]
pub user_group_joining_id: Option<String>,
pub users: Vec<String>,
#[sea_orm(column_name = "excludeKeywords", column_type = "JsonBinary")]
pub exclude_keywords: Json,
#[sea_orm(column_type = "JsonBinary")]
pub instances: Json,
pub instances: Vec<String>,
pub keywords: Vec<String>,
#[sea_orm(column_name = "excludeKeywords")]
pub exclude_keywords: Vec<String>,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]

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

@ -2,7 +2,8 @@
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
#[sea_orm(table_name = "app")]
#[cfg_attr(
feature = "napi",

View file

@ -2,7 +2,8 @@
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
#[sea_orm(table_name = "attestation_challenge")]
#[cfg_attr(
feature = "napi",

View file

@ -2,7 +2,8 @@
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
#[sea_orm(table_name = "auth_session")]
#[cfg_attr(
feature = "napi",

View file

@ -2,7 +2,8 @@
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
#[sea_orm(table_name = "blocking")]
#[cfg_attr(
feature = "napi",

View file

@ -2,7 +2,8 @@
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
#[sea_orm(table_name = "channel")]
#[cfg_attr(
feature = "napi",

View file

@ -2,7 +2,8 @@
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
#[sea_orm(table_name = "channel_following")]
#[cfg_attr(
feature = "napi",

View file

@ -2,7 +2,8 @@
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
#[sea_orm(table_name = "channel_note_pining")]
#[cfg_attr(
feature = "napi",

View file

@ -2,7 +2,8 @@
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
#[sea_orm(table_name = "clip")]
#[cfg_attr(
feature = "napi",

View file

@ -2,7 +2,8 @@
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
#[sea_orm(table_name = "clip_note")]
#[cfg_attr(
feature = "napi",

View file

@ -1,8 +1,10 @@
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
use super::sea_orm_active_enums::DriveFileUsageHintEnum;
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
#[sea_orm(table_name = "drive_file")]
#[cfg_attr(
feature = "napi",
@ -52,6 +54,8 @@ pub struct Model {
pub request_headers: Option<Json>,
#[sea_orm(column_name = "requestIp")]
pub request_ip: Option<String>,
#[sea_orm(column_name = "usageHint")]
pub usage_hint: Option<DriveFileUsageHintEnum>,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]

View file

@ -2,7 +2,8 @@
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
#[sea_orm(table_name = "drive_folder")]
#[cfg_attr(
feature = "napi",

View file

@ -2,7 +2,8 @@
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
#[sea_orm(table_name = "emoji")]
#[cfg_attr(
feature = "napi",

View file

@ -2,7 +2,8 @@
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
#[sea_orm(table_name = "follow_request")]
#[cfg_attr(
feature = "napi",

View file

@ -2,7 +2,8 @@
use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
#[sea_orm(table_name = "following")]
#[cfg_attr(
feature = "napi",

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