Compare commits

...

131 Commits

Author SHA1 Message Date
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 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 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
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 a2699e6687
chore (backend): fix imports 2024-04-20 23:04:12 +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 5c4a773ecf
chore (backend): qualify Node.js builtin modules 2024-04-20 03:09:18 +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
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
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
naskya 5ba88f3d6f
v20240413 2024-04-13 21:34:20 +09:00
naskya 008d8d8f5c
docs: update changelog 2024-04-13 21:30:38 +09:00
Lhcfl bb9a58ce34 Merge branch 'develop' of https://firefish.dev/firefish/firefish into refactor/types 2024-04-13 20:08:41 +08:00
naskya ad58ae8f30
refactor: remove /api/patrons endpoint 2024-04-13 20:03:02 +09:00
naskya 799ad1f3f8
dev (minor, backend-rs): format Makefile 2024-04-13 19:49:36 +09:00
naskya d57c9dc289
fix (client): set displayMode to true for block math expressions 2024-04-13 19:48:35 +09:00
naskya d0de0d14b2
docs: fix indent 2024-04-13 19:42:03 +09: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
Hosted Weblate a86be6a9cb
Merge branch 'origin/develop' into Weblate 2024-04-13 09:02:03 +02:00
jolupa 68be7d6be9
locale: update translations (Catalan)
Currently translated at 100.0% (1920 of 1920 strings)

Translation: Firefish/locales
Translate-URL: https://hosted.weblate.org/projects/firefish/locales/ca/
2024-04-13 09:01:54 +02: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
343 changed files with 4412 additions and 3248 deletions

View File

@ -178,19 +178,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

@ -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"
]
}

207
Cargo.lock generated
View File

@ -59,9 +59,9 @@ checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd"
[[package]]
name = "allocator-api2"
version = "0.2.16"
version = "0.2.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5"
checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f"
[[package]]
name = "android-tzdata"
@ -132,6 +132,18 @@ version = "1.0.82"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519"
[[package]]
name = "argon2"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c3610892ee6e0cbce8ae2700349fcf8f98adb0dbfbee85aec3c9179d29cc072"
dependencies = [
"base64ct",
"blake2",
"cpufeatures",
"password-hash",
]
[[package]]
name = "arrayvec"
version = "0.7.4"
@ -190,11 +202,14 @@ checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80"
name = "backend-rs"
version = "0.0.0"
dependencies = [
"argon2",
"async-trait",
"basen",
"bcrypt",
"cfg-if",
"chrono",
"cuid2",
"emojis",
"idna",
"jsonschema",
"macro_rs",
@ -238,6 +253,12 @@ version = "0.21.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
[[package]]
name = "base64"
version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9475866fec1451be56a3c2400fd081ff546538961565ccb5b7142cbd22bc7a51"
[[package]]
name = "base64ct"
version = "1.6.0"
@ -250,6 +271,19 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1dbe4bb73fd931c4d1aaf53b35d1286c8a948ad00ec92c8e3c856f15fd027f43"
[[package]]
name = "bcrypt"
version = "0.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e65938ed058ef47d92cf8b346cc76ef48984572ade631927e9937b5ffc7662c7"
dependencies = [
"base64 0.22.0",
"blowfish",
"getrandom",
"subtle",
"zeroize",
]
[[package]]
name = "bigdecimal"
version = "0.3.1"
@ -303,6 +337,15 @@ dependencies = [
"wyz",
]
[[package]]
name = "blake2"
version = "0.10.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe"
dependencies = [
"digest",
]
[[package]]
name = "block-buffer"
version = "0.10.4"
@ -312,6 +355,16 @@ dependencies = [
"generic-array",
]
[[package]]
name = "blowfish"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e412e2cd0f2b2d93e02543ceae7917b3c70331573df19ee046bcbc35e45e87d7"
dependencies = [
"byteorder",
"cipher",
]
[[package]]
name = "borsh"
version = "1.4.0"
@ -384,9 +437,9 @@ checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9"
[[package]]
name = "cc"
version = "1.0.92"
version = "1.0.94"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2678b2e3449475e95b0aa6f9b506a28e61b3dc8996592b983695e8ebb58a8b41"
checksum = "17f6e324229dc011159fcc089755d1e2e216a90d43a7dea6853ca740b84f35e7"
[[package]]
name = "cfg-if"
@ -412,7 +465,17 @@ dependencies = [
"num-traits",
"serde",
"wasm-bindgen",
"windows-targets 0.52.4",
"windows-targets 0.52.5",
]
[[package]]
name = "cipher"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
dependencies = [
"crypto-common",
"inout",
]
[[package]]
@ -633,13 +696,22 @@ checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125"
[[package]]
name = "either"
version = "1.10.0"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a"
checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2"
dependencies = [
"serde",
]
[[package]]
name = "emojis"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ee61eb945bff65ee7d19d157d39c67c33290ff0742907413fd5eefd29edc979"
dependencies = [
"phf",
]
[[package]]
name = "encoding_rs"
version = "0.8.34"
@ -1075,6 +1147,15 @@ dependencies = [
"syn 2.0.58",
]
[[package]]
name = "inout"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5"
dependencies = [
"generic-array",
]
[[package]]
name = "ipnet"
version = "2.9.0"
@ -1122,7 +1203,7 @@ checksum = "2a071f4f7efc9a9118dfb627a0a94ef247986e1ab8606a4c806ae2b3aa3b6978"
dependencies = [
"ahash 0.8.11",
"anyhow",
"base64",
"base64 0.21.7",
"bytecount",
"clap",
"fancy-regex",
@ -1175,7 +1256,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19"
dependencies = [
"cfg-if",
"windows-targets 0.52.4",
"windows-targets 0.52.5",
]
[[package]]
@ -1296,9 +1377,9 @@ dependencies = [
[[package]]
name = "napi-build"
version = "2.1.2"
version = "2.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f9130fccc5f763cf2069b34a089a18f0d0883c66aceb81f2fad541a3d823c43"
checksum = "e1c0f5d67ee408a4685b61f5ab7e58605c8ae3f2b4189f0127d804ff13d5560a"
[[package]]
name = "napi-derive"
@ -1350,9 +1431,9 @@ dependencies = [
[[package]]
name = "num"
version = "0.4.1"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b05180d69e3da0e530ba2a1dae5110317e49e3b7f3d41be227dc5f92e49ee7af"
checksum = "3135b08af27d103b0a51f2ae0f8632117b7b185ccf931445affa8df530576a41"
dependencies = [
"num-bigint",
"num-complex",
@ -1559,6 +1640,17 @@ dependencies = [
"syn 2.0.58",
]
[[package]]
name = "password-hash"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166"
dependencies = [
"base64ct",
"rand_core",
"subtle",
]
[[package]]
name = "paste"
version = "1.0.14"
@ -1580,6 +1672,24 @@ version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
[[package]]
name = "phf"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc"
dependencies = [
"phf_shared",
]
[[package]]
name = "phf_shared"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b"
dependencies = [
"siphasher",
]
[[package]]
name = "pin-project-lite"
version = "0.2.14"
@ -1801,7 +1911,7 @@ version = "0.11.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62"
dependencies = [
"base64",
"base64 0.21.7",
"bytes",
"encoding_rs",
"futures-core",
@ -1947,7 +2057,7 @@ version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c"
dependencies = [
"base64",
"base64 0.21.7",
]
[[package]]
@ -2231,6 +2341,12 @@ version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f27f6278552951f1f2b8cf9da965d10969b2efdea95a6ec47987ab46edfe263a"
[[package]]
name = "siphasher"
version = "0.3.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d"
[[package]]
name = "slab"
version = "0.4.9"
@ -2398,7 +2514,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ed31390216d20e538e447a7a9b959e06ed9fc51c37b514b46eb758016ecd418"
dependencies = [
"atoi",
"base64",
"base64 0.21.7",
"bigdecimal",
"bitflags 2.5.0",
"byteorder",
@ -2445,7 +2561,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c824eb80b894f926f89a0b9da0c7f435d27cdd35b8c655b114e58223918577e"
dependencies = [
"atoi",
"base64",
"base64 0.21.7",
"bigdecimal",
"bitflags 2.5.0",
"byteorder",
@ -3041,7 +3157,7 @@ version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
dependencies = [
"windows-targets 0.52.4",
"windows-targets 0.52.5",
]
[[package]]
@ -3059,7 +3175,7 @@ version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets 0.52.4",
"windows-targets 0.52.5",
]
[[package]]
@ -3079,17 +3195,18 @@ dependencies = [
[[package]]
name = "windows-targets"
version = "0.52.4"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b"
checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb"
dependencies = [
"windows_aarch64_gnullvm 0.52.4",
"windows_aarch64_msvc 0.52.4",
"windows_i686_gnu 0.52.4",
"windows_i686_msvc 0.52.4",
"windows_x86_64_gnu 0.52.4",
"windows_x86_64_gnullvm 0.52.4",
"windows_x86_64_msvc 0.52.4",
"windows_aarch64_gnullvm 0.52.5",
"windows_aarch64_msvc 0.52.5",
"windows_i686_gnu 0.52.5",
"windows_i686_gnullvm",
"windows_i686_msvc 0.52.5",
"windows_x86_64_gnu 0.52.5",
"windows_x86_64_gnullvm 0.52.5",
"windows_x86_64_msvc 0.52.5",
]
[[package]]
@ -3100,9 +3217,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.4"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9"
checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263"
[[package]]
name = "windows_aarch64_msvc"
@ -3112,9 +3229,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.4"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675"
checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6"
[[package]]
name = "windows_i686_gnu"
@ -3124,9 +3241,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
[[package]]
name = "windows_i686_gnu"
version = "0.52.4"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3"
checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9"
[[package]]
name = "windows_i686_msvc"
@ -3136,9 +3259,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
[[package]]
name = "windows_i686_msvc"
version = "0.52.4"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02"
checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf"
[[package]]
name = "windows_x86_64_gnu"
@ -3148,9 +3271,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.4"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03"
checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9"
[[package]]
name = "windows_x86_64_gnullvm"
@ -3160,9 +3283,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.4"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177"
checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596"
[[package]]
name = "windows_x86_64_msvc"
@ -3172,9 +3295,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.4"
version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8"
checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
[[package]]
name = "winnow"

View File

@ -7,14 +7,17 @@ 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-build = "2.1.3"
argon2 = "0.5.3"
async-trait = "0.1.80"
basen = "0.1.0"
bcrypt = "0.15.1"
cfg-if = "1.0.0"
chrono = "0.4.37"
convert_case = "0.6.0"
cuid2 = "0.1.2"
emojis = "0.6.1"
idna = "0.5.0"
jsonschema = "0.17.1"
once_cell = "1.19.0"

View File

@ -1,7 +1,7 @@
{
"$schema": "https://biomejs.dev/schemas/1.6.4/schema.json",
"organizeImports": {
"enabled": true
"enabled": false
},
"linter": {
"enabled": true,
@ -21,7 +21,8 @@
"useImportType": "warn",
"useShorthandFunctionType": "warn",
"useTemplate": "warn",
"noNonNullAssertion": "off"
"noNonNullAssertion": "off",
"useNodejsImportProtocol": "off"
}
}
}

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,14 @@
Breaking changes are indicated by the :warning: icon.
## Unreleased
- Added `antennaLimit` field to the response of `meta` and `admin/meta`, and the request of `admin/update-meta` (optional).
## v20240413
- :warning: Removed `patrons` endpoint.
## v20240405
- Added `notes/history` endpoint.

View File

@ -5,9 +5,16 @@ 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.
## Unreleased
## [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
- Improve federation and rendering of mathematical expressions
- Remove donor information from the web client
- See also: https://info.firefish.dev/notes/9s1n283sb10rh869
- Fix bugs
## [v20240405](https://firefish.dev/firefish/firefish/-/merge_requests/10733/commits)

View File

@ -1,6 +1,8 @@
BEGIN;
DELETE FROM "migrations" WHERE name IN (
'AddDriveFileUsage1713451569342',
'ConvertCwVarcharToText1713225866247',
'FixChatFileConstraint1712855579316',
'DropTimeZone1712425488543',
'ExpandNoteEdit1711936358554',
@ -22,6 +24,15 @@ DELETE FROM "migrations" WHERE name IN (
'RemoveNativeUtilsMigration1705877093218'
);
-- 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;

View File

@ -154,7 +154,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 +242,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

View File

@ -2,7 +2,7 @@
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).
## Unreleased
## v20240413
### For all users
@ -21,8 +21,8 @@ The number of posts stored on your database can be found at `https://yourserver.
- Please remove `packages/backend-rs/target` before building Firefish.
```sh
rm --recursive --force packages/backend-rs/target
```
rm --recursive --force packages/backend-rs/target
```
- Please do not terminate `pnpm run migrate` even if it appears to be frozen.
### For Docker/Podman users

View File

@ -893,9 +893,6 @@ _aboutFirefish:
source: "الشفرة المصدرية"
translation: "ترجم ميسكي"
donate: "تبرع لميسكي"
morePatrons: "نحن نقدر الدعم الذي قدمه العديد من الأشخاص الذين لم نذكرهم. شكرًا
لكم 🥰"
patrons: "الداعمون"
_nsfw:
respect: "اخف الوسائط ذات المحتوى الحساس"
ignore: "اعرض الوسائط ذات المحتوى الحساس"

View File

@ -975,8 +975,6 @@ _aboutFirefish:
source: "সোর্স কোড"
translation: "Firefish অনুবাদ করুন"
donate: "Firefish তে দান করুন"
morePatrons: "আরও অনেকে আমাদের সাহায্য করছেন। তাদের সবাইকে ধন্যবাদ 🥰"
patrons: "সমর্থনকারী"
_nsfw:
respect: "স্পর্শকাতর মিডিয়া লুকান"
ignore: "স্পর্শকাতর মিডিয়া লুকাবেন না"

View File

@ -1586,18 +1586,12 @@ _aboutFirefish:
translation: Tradueix Firefish
about: Firefish és una bifurcació de Misskey feta per ThatOneCalculator, que està
en desenvolupament des del 2022.
morePatrons: També agraïm el suport de molts altres ajudants que no figuren aquí.
Gràcies! 🥰
patrons: Mecenes de Firefish
patronsList: Llistats cronològicament, no per la quantitat donada. Fes una donació
amb l'enllaç de dalt per veure el teu nom aquí!
donateTitle: T'agrada Firefish?
pleaseDonateToFirefish: Penseu en fer una donació a Firefish per donar suport al
seu desenvolupament.
pleaseDonateToHost: Penseu també en fer una donació a la vostre instància, {host},
per ajudar-lo a suportar els costos de funcionament.
donateHost: Fes una donació a {host}
sponsors: Patrocinadors de Calckey
misskeyContributors: Col·laboradors de Misskey
unknown: Desconegut
pageLikesCount: Nombre de pàgines amb M'agrada
@ -2084,9 +2078,9 @@ _experiments:
release: Publicà
title: Experiments
enablePostImports: Activar l'importació de publicacions
postImportsCaption: Permet els usuaris importar publicacions desde comptes a Firefish,
postImportsCaption: Permet als usuaris importar publicacions des de comptes de Firefish,
Misskey, Mastodon, Akkoma i Pleroma. Pot fer que el servidor vagi més lent durant
la càrrega si tens un coll d'ampolla a la cua.
la importació si la teva cua de feina és saturada.
noGraze: Si us plau, desactiva l'extensió del navegador "Graze for Mastodon", ja que
interfereix amb Firefish.
accessibility: Accessibilitat

View File

@ -1092,17 +1092,11 @@ _aboutFirefish:
source: "Quellcode"
translation: "Firefish übersetzen"
donate: "An Firefish spenden"
morePatrons: "Wir schätzen ebenso die Unterstützung vieler anderer hier nicht gelisteter
Personen sehr. Danke! 🥰"
patrons: "UnterstützerInnen"
patronsList: Auflistung chonologisch, nicht nach Spenden-Größe. Spende über den
Link oben, um hier aufgeführt zu werden!
donateTitle: Gefällt dir Firefish?
pleaseDonateToFirefish: Bitte erwäge eine Spende an Firefish, um dessen Entwicklung
zu unterstützen.
pleaseDonateToHost: Bitte erwäge auch, an deinen Heimatserver {host} zu spenden,
um bei der Deckung der Betriebskosten zu helfen.
sponsors: Firefish-Sponsoren
donateHost: Spende an {host}
misskeyContributors: Misskey-Mitwirkende
_nsfw:

View File

@ -1337,12 +1337,6 @@ _aboutFirefish:
pleaseDonateToHost: "Please also consider donating to your home server, {host},
to help support its operation costs."
donateHost: "Donate to {host}"
morePatrons: "We also appreciate the support of many other helpers not listed here.
Thank you! 🥰"
sponsors: "Firefish sponsors"
patrons: "Firefish patrons"
patronsList: "Listed chronologically, not by donation size. Donate with the link
above to get your name on here!"
_nsfw:
respect: "Hide NSFW media"
ignore: "Don't hide NSFW media"

View File

@ -1073,17 +1073,11 @@ _aboutFirefish:
source: "Código fuente"
translation: "Traducir Firefish"
donate: "Donar a Firefish"
morePatrons: "También apreciamos el apoyo de muchos más que no están enlistados
aquí. ¡Gracias! 🥰"
patrons: "Mecenas de Firefish"
pleaseDonateToFirefish: Por favor considera donar a Firefish para apollar su desarrollo.
donateHost: Dona a {host}
patronsList: Listados cronológicamente no por monto de la donación. ¡Dona con el
vínculo de arriba para que tu nombre aparezca aquí!
donateTitle: ¿Te gusta Firefish?
pleaseDonateToHost: También considera donar a tu propio servidor , {host}, para
ayudar con los costos de operación.
sponsors: Patrocinadores de Firefish
misskeyContributors: Contribuidores de Misskey
_nsfw:
respect: "Ocultar medios NSFW"

View File

@ -996,18 +996,12 @@ _aboutFirefish:
source: "Code source"
translation: "Traduire Firefish"
donate: "Soutenir Firefish"
morePatrons: "Nous apprécions vraiment le soutien de nombreuses autres personnes
non mentionnées ici. Merci à toutes et à tous ! 🥰"
patrons: "Contributeurs"
pleaseDonateToFirefish: Merci de considérer de faire un don pour soutenir le développement
de Firefish.
sponsors: Sponsors Firefish
donateTitle: Firefish vous plaît?
pleaseDonateToHost: Également, veuillez envisager de faire un don à votre serveur
d'accueil, {host}, pour contribuer à couvrir ses frais de fonctionnement.
donateHost: Faire un don à {host}
patronsList: Listé chronologiquement, pas par taille de donation. Faite un don avec
le lien ci-dessus pour avoir votre nom affiché ici !
misskeyContributors: Contributeurs Misskey
_nsfw:
respect: "Cacher les médias marqués comme contenu sensible (NSFW)"
@ -2324,3 +2318,4 @@ 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

View File

@ -985,12 +985,6 @@ _aboutFirefish:
source: "Sumber kode"
translation: "Terjemahkan Firefish"
donate: "Donasi ke Firefish"
morePatrons: "Kami sangat mengapresiasi dukungan dari banyak penolong lain yang
tidak tercantum disini. Terima kasih! 🥰"
patrons: "Pendukung"
patronsList: Diurutkan secara kronologis, bukan berdasarkan jumlah donasi. Berdonasilah
dengan tautan di atas supaya nama kamu ada di sini!
sponsors: Sponsor Firefish
donateTitle: Suka Firefish?
pleaseDonateToFirefish: Silakan pertimbangkan berdonasi ke Firefish untuk mendukung
pengembangannya.

View File

@ -934,18 +934,12 @@ _aboutFirefish:
source: "Codice sorgente"
translation: "Traduzione di Firefish"
donate: "Sostieni Firefish"
morePatrons: "Apprezziamo sinceramente l'aiuto di tante altre persone non elencate
qui. Grazie mille! 🥰"
patrons: "Sostenitori"
sponsors: Gli sponsor di Firefish
misskeyContributors: Contributori di Misskey
donateTitle: Ti piace Firefish?
pleaseDonateToFirefish: Con una donazione puoi supportare lo sviluppo di Firefish.
pleaseDonateToHost: Considera anche una donazione al server che ti ospita, {host},
per contribuire ai costi che sostiene.
donateHost: Dona a {host}
patronsList: Elencati in ordine cronologico, non per importo. Dona con il link sopra
per apparire in questa lista!
_nsfw:
respect: "Nascondi i media sensibli (NSFW)"
ignore: "Mostra i media sensibili (NSFW)"

View File

@ -1109,14 +1109,10 @@ _aboutFirefish:
source: "ソースコード"
translation: "Firefishを翻訳"
donate: "Firefishに寄付"
morePatrons: "他にも多くの方が支援してくれています。ありがとうございます! 🥰"
patrons: "支援者"
patronsList: 寄付額ではなく時系列順に並んでいます。上記のリンクから寄付を行ってここにあなたのIDを載せましょう
pleaseDonateToFirefish: Firefish開発への寄付をご検討ください。
pleaseDonateToHost: また、このサーバー {host} の運営者への寄付もご検討ください。
donateHost: '{host} に寄付する'
donateTitle: Firefishを気に入りましたか
sponsors: Firefish の支援者
_nsfw:
respect: "閲覧注意のメディアは隠す"
ignore: "閲覧注意のメディアを隠さない"

View File

@ -874,8 +874,6 @@ _aboutFirefish:
source: "ソースコード"
translation: "Firefishを翻訳"
donate: "Firefishに寄付"
morePatrons: "他にもぎょうさんの人からサポートしてもろてんねん。ほんまおおきに🥰"
patrons: "支援者"
misskeyContributors: フォーク元のMisskeyを作らはった人ら
_mfm:
cheatSheet: "MFMチートシート"

View File

@ -992,10 +992,6 @@ _aboutFirefish:
source: "소스 코드"
translation: "Firefish를 번역하기"
donate: "Firefish에 기부하기"
morePatrons: "이 외에도 다른 많은 분들이 도움을 주시고 계십니다. 감사합니다🥰"
patrons: "후원자"
patronsList: 기부 금액이 아닌 시간 순서로 정렬합니다. 위 링크를 통해 후원하여 당신의 이름을 새겨 보세요!
sponsors: Firefish 스폰서
pleaseDonateToHost: 또한, 이 서버 {host} 의 운영자에게 기부하는 것도 검토하여 주십시오.
pleaseDonateToFirefish: Firefish의 개발에 후원하는 것을 검토하여 주십시오.
donateHost: '{host} 에게 기부하기'

View File

@ -987,8 +987,6 @@ _aboutFirefish:
pleaseDonateToFirefish: Du kan vurdere å donere en slant til Firefish for å støtte
videre utvikling og feilretting.
donateHost: Donér til {host}
morePatrons: Vi er også takknemlige for bidragene fra mange andre som ikke er listet
her. Takk til dere alle! 🥰
contributors: Hovedutviklere
source: Kildekode
allContributors: Alle bidragsytere
@ -996,10 +994,6 @@ _aboutFirefish:
pleaseDonateToHost: Du kan også vurdere å donere til hjemme-tjeneren din, {host},
for å hjelpe dem med driftskostnadene for tjenesten.
about: Firefish ble opprettet av ThatOneCalculator i 2022, basert på Misskey.
sponsors: Firefishs sponsorer
patrons: Firefishs patroner
patronsList: Listen er kronologisk, ikke etter donert beløp. Doner med lenken over
for å få navnet ditt her!
isBot: Denne kontoen er en bot
_nsfw:
respect: Skjul NSFW-merket media

View File

@ -990,9 +990,6 @@ _aboutFirefish:
source: "Kod źródłowy"
translation: "Tłumacz Firefish"
donate: "Przekaż darowiznę na Firefish"
morePatrons: "Naprawdę doceniam wsparcie ze strony wielu niewymienionych tu osób.
Dziękuję! 🥰"
patrons: "Wspierający"
_nsfw:
respect: "Ukrywaj media NSFW"
ignore: "Nie ukrywaj mediów NSFW"

View File

@ -986,12 +986,6 @@ _aboutFirefish:
source: "Исходный код"
translation: "Перевод Firefish"
donate: "Пожертвование на Firefish"
morePatrons: "Большое спасибо и многим другим, кто принял участие в этом проекте!
🥰"
patrons: "Материальная поддержка"
patronsList: Перечислены в хронологическом порядке, а не по размеру пожертвования.
Сделайте взнос по ссылке выше, чтобы ваше имя было здесь!
sponsors: Спонсоры Firefish
donateTitle: Понравился Firefish?
pleaseDonateToFirefish: Пожалуйста, поддержите разработку Firefish.
pleaseDonateToHost: Также не забудьте поддержать ваш домашний сервер {host}, чтобы

View File

@ -1036,9 +1036,6 @@ _aboutFirefish:
source: "Zdrojový kód"
translation: "Preložiť Firefish"
donate: "Podporiť Firefish"
morePatrons: "Takisto oceňujeme podporu mnoých ďalších, ktorí tu nie sú uvedení.
Ďakujeme! 🥰"
patrons: "Prispievatelia"
_nsfw:
respect: "Skryť NSFW médiá"
ignore: "Neskrývať NSFW médiá"

View File

@ -1022,9 +1022,6 @@ _aboutFirefish:
source: "ซอร์สโค้ด"
translation: "รับแปลภาษา Firefish"
donate: "บริจาคให้กับ Firefish"
morePatrons: "เราขอขอบคุณสำหรับความช่วยเหลือจากผู้ช่วยอื่นๆ ที่ไม่ได้ระบุไว้ที่นี่นะ
ขอขอบคุณ! 🥰"
patrons: "สมาชิกพันธมิตร"
_nsfw:
respect: "ซ่อนสื่อ NSFW"
ignore: "อย่าซ่อนสื่อ NSFW"

View File

@ -1910,14 +1910,9 @@ _preferencesBackups:
updatedAt: 'Güncelleme tarihi: {date} {time}'
cannotLoad: Yüklenemedi
_aboutFirefish:
patronsList: Bağış büyüklüğüne göre değil, kronolojik olarak listelenmiştir. Adınızı
buraya almak için yukarıdaki bağlantıyla bağış yapın!
about: Firefish, 2022'den beri geliştirilmekte olan ThatOneCalculator tarafından
yapılan bir Misskey çatalıdır.
allContributors: Tüm katkıda bulunanlar
patrons: Firefish patronları
morePatrons: Burada listelenmeyen diğer birçok yardımcının desteğini de takdir ediyoruz.
Teşekkür ederim! 🥰
donate: Firefish'e bağışta bulunun
contributors: Ana katkıda bulunanlar
source: Kaynak Kodu
@ -1928,7 +1923,6 @@ _aboutFirefish:
pleaseDonateToHost: İşletme maliyetlerini desteklemek için lütfen ev sunucunuz {host}'a
bağış yapmayı da düşünün.
donateHost: '{ev sahibi} için bağış yapın'
sponsors: Firefish sponsorları
misskeyContributors: Misskey'e katkıda bulunanlar
_weekday:
saturday: Cumartesi

View File

@ -825,17 +825,11 @@ _aboutFirefish:
source: "Вихідний код"
translation: "Перекладати Firefish"
donate: "Пожертвувати Firefish"
morePatrons: "Ми дуже цінуємо підтримку багатьох інших помічників, не перелічених
тут. Дякуємо! 🥰"
patrons: "Підтримали"
patronsList: Перераховані в хронологічному порядку, а не за розміром пожертви. Зробіть
внесок за посиланням вище, щоб ваше ім'я було тут!
donateTitle: Сподобався Firefish?
pleaseDonateToFirefish: Будь ласка, підтримайте розробку Firefish.
pleaseDonateToHost: Також не забудьте підтримати ваш домашній сервер {host}, щоб
допомогти з його операційними витратами.
donateHost: Зробити внесок на рахунок {host}
sponsors: Спонсори Firefish
misskeyContributors: Контрибутори Misskey
_nsfw:
respect: "Приховувати NSFW медіа"

View File

@ -1050,15 +1050,10 @@ _aboutFirefish:
source: "Mã nguồn"
translation: "Dịch Firefish"
donate: "Ủng hộ Firefish"
morePatrons: "Chúng tôi cũng trân trọng sự hỗ trợ của nhiều người đóng góp khác
không được liệt kê ở đây. Cảm ơn! 🥰"
patrons: "Người ủng hộ"
patronsList: Liệt kê theo thứ tự, không theo số tiền ủng hộ. Hãy để tên bạn ở đây!
donateTitle: Thích Firefish?
pleaseDonateToFirefish: Hãy cân nhắc ủng hộ Firefish phát triển.
donateHost: Ủng hộ {host}
pleaseDonateToHost: Cũng như ủng hộ chi phí vận hành máy chủ {host} của bạn.
sponsors: Nhà tài trợ Firefish
misskeyContributors: Người đóng góp Misskey
_nsfw:
respect: "Ẩn nội dung NSFW"

View File

@ -996,10 +996,6 @@ _aboutFirefish:
source: "源代码"
translation: "翻译 Firefish"
donate: "赞助 Firefish"
morePatrons: "还有很多其它的人也在支持我们,非常感谢🥰"
patrons: "Firefish 赞助者"
patronsList: 按时间顺序而不是捐赠金额排列。通过上面的链接捐款,让您的名字出现在这里!
sponsors: Firefish 赞助者们
donateTitle: 喜欢 Firefish 吗?
pleaseDonateToFirefish: 请考虑赞助 Firefish 以支持其开发。
pleaseDonateToHost: 也请考虑赞助您的主服务器 {host},以帮助支持其运营成本。

View File

@ -991,10 +991,6 @@ _aboutFirefish:
source: "原始碼"
translation: "翻譯Firefish"
donate: "贊助Firefish"
morePatrons: "還有許許多多幫助我們的其他人,非常感謝你們。 🥰"
patrons: "贊助者"
patronsList: 按時間順序列出,而不是按贊助規模列出。使用上面的連結贊助,在這裡獲得顯示您名字的機會!
sponsors: Firefish 贊助者們
donateTitle: 覺得 Firefish 棒嗎?
pleaseDonateToFirefish: 請考慮向 Firefish 贊助以支持其發展。
pleaseDonateToHost: 還請考慮捐贈給您在使用的伺服器 {host},以支援龐大的運營成本。

View File

@ -1,11 +1,11 @@
{
"name": "firefish",
"version": "20240405",
"version": "20240421",
"repository": {
"type": "git",
"url": "https://firefish.dev/firefish/firefish.git"
},
"packageManager": "pnpm@8.15.6",
"packageManager": "pnpm@8.15.7",
"private": true,
"scripts": {
"rebuild": "pnpm run clean && pnpm run build",
@ -26,7 +26,9 @@
"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,11 +38,11 @@
"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"
"gulp-terser": "2.1.0",
"js-yaml": "4.1.0"
},
"devDependencies": {
"@biomejs/biome": "1.6.4",
@ -50,7 +52,7 @@
"@biomejs/cli-linux-x64": "^1.6.4",
"@types/node": "20.12.7",
"execa": "8.0.1",
"pnpm": "8.15.6",
"pnpm": "8.15.7",
"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

@ -17,11 +17,14 @@ 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 }
bcrypt = { workspace = true }
cfg-if = { workspace = true }
chrono = { workspace = true }
cuid2 = { workspace = true }
emojis = { workspace = true }
idna = { workspace = true }
jsonschema = { workspace = true }
once_cell = { workspace = true }

View File

@ -7,9 +7,9 @@ SRC += $(call recursive_wildcard, src, *)
.PHONY: regenerate-entities
regenerate-entities:
sea-orm-cli generate entity \
--output-dir='src/model/entity' \
--database-url='postgres://$(POSTGRES_USER):$(POSTGRES_PASSWORD)@localhost:25432/$(POSTGRES_DB)' \
--date-time-crate='chrono' \
--output-dir='src/model/entity' \
--database-url='postgres://$(POSTGRES_USER):$(POSTGRES_PASSWORD)@localhost:25432/$(POSTGRES_DB)' \
--date-time-crate='chrono' \
--model-extra-attributes='NAPI_EXTRA_ATTR_PLACEHOLDER' && \
for file in src/model/entity/*; do \
base=$$(basename -- "$${file}"); \
@ -17,7 +17,7 @@ regenerate-entities:
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}"; \
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)]\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

@ -3,6 +3,16 @@
/* auto-generated by NAPI-RS */
export interface EnvConfig {
onlyQueue: boolean
onlyServer: boolean
noDaemons: boolean
disableClustering: boolean
verbose: boolean
withLogTime: boolean
slow: boolean
}
export function readEnvironmentConfig(): EnvConfig
export interface ServerConfig {
url: string
port: number
@ -118,7 +128,8 @@ export interface Acct {
}
export function stringToAcct(acct: string): Acct
export function acctToString(acct: Acct): string
export interface NoteLike {
/** TODO: handle name collisions better */
export interface NoteLikeForCheckWordMute {
fileIds: Array<string>
userId: string | null
text: string | null
@ -126,15 +137,52 @@ export interface NoteLike {
renoteId: string | null
replyId: string | null
}
export function checkWordMute(note: NoteLike, mutedWordLists: Array<Array<string>>, mutedPatterns: Array<string>): Promise<boolean>
export function checkWordMute(note: NoteLikeForCheckWordMute, mutedWordLists: Array<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
/** TODO: handle name collisions better */
export interface NoteLikeForGetNoteSummary {
fileIds: Array<string>
text: string | null
cw: string | null
hasPoll: boolean
}
export function getNoteSummary(note: NoteLikeForGetNoteSummary): 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>
export interface AbuseUserReport {
id: string
createdAt: Date
@ -300,6 +348,7 @@ export interface DriveFile {
webpublicType: string | null
requestHeaders: Json | null
requestIp: string | null
usageHint: DriveFileUsageHintEnum | null
}
export interface DriveFolder {
id: string
@ -725,81 +774,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

View File

@ -310,8 +310,9 @@ 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 { readEnvironmentConfig, readServerConfig, stringToAcct, acctToString, checkWordMute, getFullApAccount, isSelfHost, isSameOrigin, extractHost, toPuny, isUnicodeEmoji, sqlLikeEscape, safeForSql, formatMilliseconds, getNoteSummary, toMastodonId, fromMastodonId, fetchMeta, metaToPugArgs, nyaify, hashPassword, verifyPassword, isOldPasswordAlgorithm, decodeReaction, countReactions, toDbReaction, AntennaSrcEnum, DriveFileUsageHintEnum, MutedNoteReasonEnum, NoteVisibilityEnum, NotificationTypeEnum, PageVisibilityEnum, PollNotevisibilityEnum, RelayStatusEnum, UserEmojimodpermEnum, UserProfileFfvisibilityEnum, UserProfileMutingnotificationtypesEnum, initIdGenerator, getTimestamp, genId, secureRndstr } = nativeBinding
module.exports.readEnvironmentConfig = readEnvironmentConfig
module.exports.readServerConfig = readServerConfig
module.exports.stringToAcct = stringToAcct
module.exports.acctToString = acctToString
@ -321,10 +322,24 @@ 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.getNoteSummary = getNoteSummary
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.AntennaSrcEnum = AntennaSrcEnum
module.exports.DriveFileUsageHintEnum = DriveFileUsageHintEnum
module.exports.MutedNoteReasonEnum = MutedNoteReasonEnum
module.exports.NoteVisibilityEnum = NoteVisibilityEnum
module.exports.NotificationTypeEnum = NotificationTypeEnum

View File

@ -33,8 +33,8 @@
},
"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",

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 read_environment_config() -> 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,2 @@
pub mod environment;
pub mod server;

View File

@ -4,7 +4,8 @@ use once_cell::sync::Lazy;
use regex::Regex;
use sea_orm::{prelude::*, QuerySelect};
#[crate::export(object)]
/// TODO: handle name collisions better
#[crate::export(object, js_name = "NoteLikeForCheckWordMute")]
pub struct NoteLike {
pub file_ids: Vec<String>,
pub user_id: Option<String>,

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,90 @@
/// TODO: handle name collisions better
#[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,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,12 @@
pub mod acct;
pub mod check_word_mute;
pub mod convert_host;
pub mod emoji;
pub mod escape_sql;
pub mod format_milliseconds;
pub mod get_note_summary;
pub mod mastodon_id;
pub mod meta;
pub mod nyaify;
pub mod password;
pub mod reaction;

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,191 @@
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}:"));
}
} 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}:"));
}
}
};
};
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

@ -1,5 +1,6 @@
//! `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)]
@ -52,6 +53,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

@ -50,8 +50,6 @@ pub struct Model {
pub local_drive_capacity_mb: i32,
#[sea_orm(column_name = "remoteDriveCapacityMb")]
pub remote_drive_capacity_mb: i32,
#[sea_orm(column_name = "antennaLimit")]
pub antenna_limit: i32,
#[sea_orm(column_name = "summalyProxy")]
pub summaly_proxy: Option<String>,
#[sea_orm(column_name = "enableEmail")]
@ -175,6 +173,8 @@ pub struct Model {
pub more_urls: Json,
#[sea_orm(column_name = "markLocalFilesNsfwByDefault")]
pub mark_local_files_nsfw_by_default: bool,
#[sea_orm(column_name = "antennaLimit")]
pub antenna_limit: i32,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]

View File

@ -21,6 +21,7 @@ pub struct Model {
#[sea_orm(column_type = "Text", nullable)]
pub text: Option<String>,
pub name: Option<String>,
#[sea_orm(column_type = "Text", nullable)]
pub cw: Option<String>,
#[sea_orm(column_name = "userId")]
pub user_id: String,

View File

@ -4,7 +4,7 @@ use sea_orm::entity::prelude::*;
#[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum)]
#[cfg_attr(not(feature = "napi"), derive(Clone))]
#[cfg_attr(feature = "napi", napi_derive::napi)]
#[cfg_attr(feature = "napi", napi_derive::napi(string_enum = "camelCase"))]
#[sea_orm(rs_type = "String", db_type = "Enum", enum_name = "antenna_src_enum")]
pub enum AntennaSrcEnum {
#[sea_orm(string_value = "all")]
@ -22,7 +22,21 @@ pub enum AntennaSrcEnum {
}
#[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum)]
#[cfg_attr(not(feature = "napi"), derive(Clone))]
#[cfg_attr(feature = "napi", napi_derive::napi)]
#[cfg_attr(feature = "napi", napi_derive::napi(string_enum = "camelCase"))]
#[sea_orm(
rs_type = "String",
db_type = "Enum",
enum_name = "drive_file_usage_hint_enum"
)]
pub enum DriveFileUsageHintEnum {
#[sea_orm(string_value = "userAvatar")]
UserAvatar,
#[sea_orm(string_value = "userBanner")]
UserBanner,
}
#[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum)]
#[cfg_attr(not(feature = "napi"), derive(Clone))]
#[cfg_attr(feature = "napi", napi_derive::napi(string_enum = "camelCase"))]
#[sea_orm(
rs_type = "String",
db_type = "Enum",
@ -40,7 +54,7 @@ pub enum MutedNoteReasonEnum {
}
#[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum)]
#[cfg_attr(not(feature = "napi"), derive(Clone))]
#[cfg_attr(feature = "napi", napi_derive::napi)]
#[cfg_attr(feature = "napi", napi_derive::napi(string_enum = "camelCase"))]
#[sea_orm(
rs_type = "String",
db_type = "Enum",
@ -60,7 +74,7 @@ pub enum NoteVisibilityEnum {
}
#[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum)]
#[cfg_attr(not(feature = "napi"), derive(Clone))]
#[cfg_attr(feature = "napi", napi_derive::napi)]
#[cfg_attr(feature = "napi", napi_derive::napi(string_enum = "camelCase"))]
#[sea_orm(
rs_type = "String",
db_type = "Enum",
@ -94,7 +108,7 @@ pub enum NotificationTypeEnum {
}
#[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum)]
#[cfg_attr(not(feature = "napi"), derive(Clone))]
#[cfg_attr(feature = "napi", napi_derive::napi)]
#[cfg_attr(feature = "napi", napi_derive::napi(string_enum = "camelCase"))]
#[sea_orm(
rs_type = "String",
db_type = "Enum",
@ -110,7 +124,7 @@ pub enum PageVisibilityEnum {
}
#[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum)]
#[cfg_attr(not(feature = "napi"), derive(Clone))]
#[cfg_attr(feature = "napi", napi_derive::napi)]
#[cfg_attr(feature = "napi", napi_derive::napi(string_enum = "camelCase"))]
#[sea_orm(
rs_type = "String",
db_type = "Enum",
@ -128,7 +142,7 @@ pub enum PollNotevisibilityEnum {
}
#[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum)]
#[cfg_attr(not(feature = "napi"), derive(Clone))]
#[cfg_attr(feature = "napi", napi_derive::napi)]
#[cfg_attr(feature = "napi", napi_derive::napi(string_enum = "camelCase"))]
#[sea_orm(rs_type = "String", db_type = "Enum", enum_name = "relay_status_enum")]
pub enum RelayStatusEnum {
#[sea_orm(string_value = "accepted")]
@ -140,7 +154,7 @@ pub enum RelayStatusEnum {
}
#[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum)]
#[cfg_attr(not(feature = "napi"), derive(Clone))]
#[cfg_attr(feature = "napi", napi_derive::napi)]
#[cfg_attr(feature = "napi", napi_derive::napi(string_enum = "camelCase"))]
#[sea_orm(
rs_type = "String",
db_type = "Enum",
@ -158,7 +172,7 @@ pub enum UserEmojimodpermEnum {
}
#[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum)]
#[cfg_attr(not(feature = "napi"), derive(Clone))]
#[cfg_attr(feature = "napi", napi_derive::napi)]
#[cfg_attr(feature = "napi", napi_derive::napi(string_enum = "camelCase"))]
#[sea_orm(
rs_type = "String",
db_type = "Enum",
@ -174,7 +188,7 @@ pub enum UserProfileFfvisibilityEnum {
}
#[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum)]
#[cfg_attr(not(feature = "napi"), derive(Clone))]
#[cfg_attr(feature = "napi", napi_derive::napi)]
#[cfg_attr(feature = "napi", napi_derive::napi(string_enum = "camelCase"))]
#[sea_orm(
rs_type = "String",
db_type = "Enum",

View File

@ -22,9 +22,9 @@
"@swc/core-android-arm64": "1.3.11"
},
"dependencies": {
"@bull-board/api": "5.15.3",
"@bull-board/koa": "5.15.3",
"@bull-board/ui": "5.15.3",
"@bull-board/api": "5.15.5",
"@bull-board/koa": "5.15.5",
"@bull-board/ui": "5.15.5",
"@discordapp/twemoji": "^15.0.3",
"@koa/cors": "5.0.0",
"@koa/multer": "3.0.2",
@ -33,15 +33,12 @@
"@peertube/http-signature": "1.7.0",
"@redocly/openapi-core": "1.11.0",
"@sinonjs/fake-timers": "11.2.2",
"@twemoji/parser": "^15.1.1",
"adm-zip": "0.5.10",
"ajv": "8.12.0",
"archiver": "7.0.1",
"argon2": "^0.40.1",
"aws-sdk": "2.1597.0",
"aws-sdk": "2.1599.0",
"axios": "^1.6.8",
"backend-rs": "workspace:*",
"bcryptjs": "2.4.3",
"blurhash": "2.0.5",
"bull": "4.12.2",
"cacheable-lookup": "TheEssem/cacheable-lookup",
@ -54,7 +51,7 @@
"date-fns": "3.6.0",
"decompress": "^4.2.1",
"deep-email-validator": "0.1.21",
"deepl-node": "1.12.0",
"deepl-node": "1.13.0",
"escape-regexp": "0.0.1",
"feed": "4.2.2",
"file-type": "19.0.0",
@ -100,7 +97,7 @@
"punycode": "2.3.1",
"pureimage": "0.4.13",
"qrcode": "1.5.3",
"qs": "6.12.0",
"qs": "6.12.1",
"random-seed": "0.3.0",
"ratelimiter": "3.4.1",
"redis-semaphore": "5.5.1",
@ -130,7 +127,6 @@
"@swc/cli": "0.3.12",
"@swc/core": "1.4.13",
"@types/adm-zip": "^0.5.5",
"@types/bcryptjs": "2.4.6",
"@types/color-convert": "^2.0.3",
"@types/content-disposition": "^0.5.8",
"@types/escape-regexp": "0.0.3",

View File

@ -3,7 +3,7 @@ import chalk from "chalk";
import Xev from "xev";
import Logger from "@/services/logger.js";
import { envOption } from "../env.js";
import { envOption } from "@/config/index.js";
import { inspect } from "node:util";
// for typeorm
@ -76,9 +76,7 @@ cluster.on("exit", (worker) => {
});
// Display detail of unhandled promise rejection
if (!envOption.quiet) {
process.on("unhandledRejection", console.dir);
}
process.on("unhandledRejection", console.dir);
// Display detail of uncaught exception
process.on("uncaughtException", (err) => {

View File

@ -10,7 +10,7 @@ import semver from "semver";
import Logger from "@/services/logger.js";
import loadConfig from "@/config/load.js";
import type { Config } from "@/config/types.js";
import { envOption } from "@/env.js";
import { envOption } from "@/config/index.js";
import { showMachineInfo } from "@/misc/show-machine-info.js";
import { db, initDb } from "@/db/postgre.js";
import { inspect } from "node:util";
@ -28,58 +28,56 @@ const bootLogger = logger.createSubLogger("boot", "magenta", false);
const themeColor = chalk.hex("#31748f");
function greet() {
if (!envOption.quiet) {
//#region Firefish logo
console.log(
themeColor(
"██████╗ ██╗██████╗ ███████╗███████╗██╗███████╗██╗ ██╗ ○ ▄ ▄ ",
),
);
console.log(
themeColor(
"██╔════╝██║██╔══██╗██╔════╝██╔════╝██║██╔════╝██║ ██║ ⚬ █▄▄ █▄▄ ",
),
);
console.log(
themeColor(
"█████╗ ██║██████╔╝█████╗ █████╗ ██║███████╗███████║ ▄▄▄▄▄▄ ▄ ",
),
);
console.log(
themeColor(
"██╔══╝ ██║██╔══██╗██╔══╝ ██╔══╝ ██║╚════██║██╔══██║ █ █ █▄▄ ",
),
);
console.log(
themeColor(
"██║ ██║██║ ██║███████╗██║ ██║███████║██║ ██║ █ ● ● █ ",
),
);
console.log(
themeColor(
"╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝ ▀▄▄▄▄▄▄▀ ",
),
);
//#endregion
//#region Firefish logo
console.log(
themeColor(
"██████╗ ██╗██████╗ ███████╗███████╗██╗███████╗██╗ ██╗ ○ ▄ ▄ ",
),
);
console.log(
themeColor(
"██╔════╝██║██╔══██╗██╔════╝██╔════╝██║██╔════╝██║ ██║ ⚬ █▄▄ █▄▄ ",
),
);
console.log(
themeColor(
"█████╗ ██║██████╔╝█████╗ █████╗ ██║███████╗███████║ ▄▄▄▄▄▄ ▄ ",
),
);
console.log(
themeColor(
"██╔══╝ ██║██╔══██╗██╔══╝ ██╔══╝ ██║╚════██║██╔══██║ █ █ █▄▄ ",
),
);
console.log(
themeColor(
"██║ ██║██║ ██║███████╗██║ ██║███████║██║ ██║ █ ● ● █ ",
),
);
console.log(
themeColor(
"╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝ ▀▄▄▄▄▄▄▀ ",
),
);
//#endregion
console.log(
" Firefish is an open-source decentralized microblogging platform.",
);
console.log(
chalk.rgb(
255,
136,
0,
)(
" If you like Firefish, please consider contributing to the repo. https://firefish.dev/firefish/firefish",
),
);
console.log(
" Firefish is an open-source decentralized microblogging platform.",
);
console.log(
chalk.rgb(
255,
136,
0,
)(
" If you like Firefish, please consider contributing to the repo. https://firefish.dev/firefish/firefish",
),
);
console.log("");
console.log(
chalkTemplate`--- ${os.hostname()} {gray (PID: ${process.pid.toString()})} ---`,
);
}
console.log("");
console.log(
chalkTemplate`--- ${os.hostname()} {gray (PID: ${process.pid.toString()})} ---`,
);
bootLogger.info("Welcome to Firefish!");
bootLogger.info(`Firefish v${meta.version}`, null, true);

View File

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

View File

@ -55,6 +55,7 @@ export default function load() {
mixin.userAgent = `Firefish/${meta.version} (${config.url})`;
mixin.clientEntry = clientManifest["src/init.ts"];
if (config.proxyRemoteFiles == null) config.proxyRemoteFiles = true;
if (!config.redis.prefix) config.redis.prefix = mixin.hostname;
if (config.cacheServer && !config.cacheServer.prefix)
config.cacheServer.prefix = mixin.hostname;

View File

@ -1,7 +1,7 @@
import si from "systeminformation";
import Xev from "xev";
import * as osUtils from "os-utils";
import { fetchMeta } from "@/misc/fetch-meta.js";
import { fetchMeta } from "backend-rs";
const ev = new Xev();
@ -13,16 +13,15 @@ const round = (num: number) => Math.round(num * 10) / 10;
/**
* Report server stats regularly
*/
export default function () {
export default async function () {
const log = [] as any[];
ev.on("requestServerStatsLog", (x) => {
ev.emit(`serverStatsLog:${x.id}`, log.slice(0, x.length || 50));
});
fetchMeta().then((meta) => {
if (!meta.enableServerMachineStats) return;
});
const meta = await fetchMeta(true);
if (!meta.enableServerMachineStats) return;
async function tick() {
const cpu = await cpuUsage();

View File

@ -237,31 +237,3 @@ export async function initDb(force = false) {
await db.initialize();
}
}
export async function resetDb() {
const reset = async () => {
await redisClient.flushdb();
const tables = await db.query(`SELECT relname AS "table"
FROM pg_class C LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace)
WHERE nspname NOT IN ('pg_catalog', 'information_schema')
AND C.relkind = 'r'
AND nspname !~ '^pg_toast';`);
for (const table of tables) {
await db.query(`DELETE FROM "${table.table}" CASCADE`);
}
};
for (let i = 1; i <= 3; i++) {
try {
await reset();
} catch (e) {
if (i === 3) {
throw e;
} else {
await new Promise((resolve) => setTimeout(resolve, 1000));
continue;
}
}
break;
}
}

View File

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

View File

@ -0,0 +1,21 @@
import type { MigrationInterface, QueryRunner } from "typeorm";
export class ConvertCwVarcharToText1713225866247 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
queryRunner.query(`DROP INDEX "IDX_8e3bbbeb3df04d1a8105da4c8f"`);
queryRunner.query(`ALTER TABLE "note" ALTER COLUMN "cw" TYPE text`);
queryRunner.query(
`CREATE INDEX "IDX_8e3bbbeb3df04d1a8105da4c8f" ON "note" USING "pgroonga" ("cw")`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
queryRunner.query(`DROP INDEX "IDX_8e3bbbeb3df04d1a8105da4c8f"`);
queryRunner.query(
`ALTER TABLE "note" ALTER COLUMN "cw" TYPE character varying(512)`,
);
queryRunner.query(
`CREATE INDEX "IDX_8e3bbbeb3df04d1a8105da4c8f" ON "note" USING "pgroonga" ("cw" pgroonga_varchar_full_text_search_ops_v2)`,
);
}
}

View File

@ -0,0 +1,17 @@
import type { MigrationInterface, QueryRunner } from "typeorm";
export class AddDriveFileUsage1713451569342 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`CREATE TYPE drive_file_usage_hint_enum AS ENUM ('userAvatar', 'userBanner')`,
);
await queryRunner.query(
`ALTER TABLE "drive_file" ADD "usageHint" drive_file_usage_hint_enum DEFAULT NULL`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "drive_file" DROP COLUMN "usageHint"`);
await queryRunner.query(`DROP TYPE drive_file_usage_hint_enum`);
}
}

View File

@ -1,6 +1,6 @@
import { redisClient } from "@/db/redis.js";
import { encode, decode } from "msgpackr";
import { ChainableCommander } from "ioredis";
import type { ChainableCommander } from "ioredis";
export class Cache<T> {
private ttl: number;

View File

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

View File

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

View File

@ -1,72 +0,0 @@
import { db } from "@/db/postgre.js";
import { Meta } from "@/models/entities/meta.js";
let cache: Meta;
export function metaToPugArgs(meta: Meta): object {
let motd = ["Loading..."];
if (meta.customMotd.length > 0) {
motd = meta.customMotd;
}
let splashIconUrl = meta.iconUrl;
if (meta.customSplashIcons.length > 0) {
splashIconUrl =
meta.customSplashIcons[
Math.floor(Math.random() * meta.customSplashIcons.length)
];
}
return {
img: meta.bannerUrl,
title: meta.name || "Firefish",
instanceName: meta.name || "Firefish",
desc: meta.description,
icon: meta.iconUrl,
splashIcon: splashIconUrl,
themeColor: meta.themeColor,
randomMOTD: motd[Math.floor(Math.random() * motd.length)],
privateMode: meta.privateMode,
};
}
export async function fetchMeta(noCache = false): Promise<Meta> {
if (!noCache && cache) return cache;
return await db.transaction(async (transactionalEntityManager) => {
// New IDs are prioritized because multiple records may have been created due to past bugs.
const metas = await transactionalEntityManager.find(Meta, {
order: {
id: "DESC",
},
});
const meta = metas[0];
if (meta) {
cache = meta;
return meta;
} else {
// If fetchMeta is called at the same time when meta is empty, this part may be called at the same time, so use fail-safe upsert.
const saved = await transactionalEntityManager
.upsert(
Meta,
{
id: "x",
},
["id"],
)
.then((x) =>
transactionalEntityManager.findOneByOrFail(Meta, x.identifiers[0]),
);
cache = saved;
return saved;
}
});
}
setInterval(() => {
fetchMeta(true).then((meta) => {
cache = meta;
});
}, 1000 * 10);

View File

@ -1,9 +1,9 @@
import { fetchMeta } from "./fetch-meta.js";
import { fetchMeta } from "backend-rs";
import type { ILocalUser } from "@/models/entities/user.js";
import { Users } from "@/models/index.js";
export async function fetchProxyAccount(): Promise<ILocalUser | null> {
const meta = await fetchMeta();
const meta = await fetchMeta(true);
if (meta.proxyAccountId == null) return null;
return (await Users.findOneByOrFail({
id: meta.proxyAccountId,

View File

@ -1,53 +0,0 @@
import type { Packed } from "./schema.js";
/**
* 稿
* @param {*} note (packされた)稿
*/
export const getNoteSummary = (note: Packed<"Note">): string => {
if (note.deletedAt) {
return "❌";
}
let summary = "";
// 本文
if (note.cw != null) {
summary += note.cw;
} else {
summary += note.text ? note.text : "";
}
// ファイルが添付されているとき
if ((note.files || []).length !== 0) {
const len = note.files?.length;
summary += ` 📎${len !== 1 ? ` (${len})` : ""}`;
}
// 投票が添付されているとき
if (note.poll) {
summary += " 📊";
}
/*
// 返信のとき
if (note.replyId) {
if (note.reply) {
summary += `\n\nRE: ${getNoteSummary(note.reply)}`;
} else {
summary += '\n\nRE: ...';
}
}
// Renoteのとき
if (note.renoteId) {
if (note.renote) {
summary += `\n\nRN: ${getNoteSummary(note.renote)}`;
} else {
summary += '\n\nRN: ...';
}
}
*/
return summary.trim();
};

View File

@ -1,20 +0,0 @@
import bcrypt from "bcryptjs";
import * as argon2 from "argon2";
export async function hashPassword(password: string): Promise<string> {
return argon2.hash(password);
}
export async function comparePassword(
password: string,
hash: string,
): Promise<boolean> {
if (isOldAlgorithm(hash)) return bcrypt.compare(password, hash);
return argon2.verify(hash, password);
}
export function isOldAlgorithm(hash: string): boolean {
// bcrypt hashes start with $2[ab]$
return hash.startsWith("$2");
}

View File

@ -3,8 +3,7 @@ import { Emojis } from "@/models/index.js";
import type { Emoji } from "@/models/entities/emoji.js";
import type { Note } from "@/models/entities/note.js";
import { Cache } from "./cache.js";
import { isSelfHost, toPuny } from "backend-rs";
import { decodeReaction } from "./reaction-lib.js";
import { decodeReaction, isSelfHost, toPuny } from "backend-rs";
import config from "@/config/index.js";
import { query } from "@/prelude/url.js";
import { redisClient } from "@/db/redis.js";

View File

@ -12,7 +12,7 @@ export function parse(acct: any): Post {
cw: acct.cw,
localOnly: acct.localOnly,
createdAt: new Date(acct.createdAt),
visibility: "hidden" + (acct.visibility || ""),
visibility: `hidden${acct.visibility || ""}`,
};
}

View File

@ -1,87 +0,0 @@
import { emojiRegex } from "./emoji-regex.js";
import { fetchMeta } from "./fetch-meta.js";
import { Emojis } from "@/models/index.js";
import { toPuny } from "backend-rs";
import { IsNull } from "typeorm";
export function convertReactions(reactions: Record<string, number>) {
const result = new Map();
for (const reaction in reactions) {
if (reactions[reaction] <= 0) continue;
const decoded = decodeReaction(reaction).reaction;
result.set(decoded, (result.get(decoded) || 0) + reactions[reaction]);
}
return Object.fromEntries(result);
}
export async function toDbReaction(
reaction?: string | null,
reacterHost?: string | null,
): Promise<string> {
if (!reaction) return (await fetchMeta()).defaultReaction;
reacterHost = reacterHost == null ? null : toPuny(reacterHost);
if (reaction.includes("❤") || reaction.includes("♥️")) return "❤️";
// Allow unicode reactions
const match = emojiRegex.exec(reaction);
if (match) {
const unicode = match[0];
return unicode;
}
const custom = reaction.match(/^:([\w+-]+)(?:@\.)?:$/);
if (custom) {
const name = custom[1];
const emoji = await Emojis.findOneBy({
host: reacterHost || IsNull(),
name,
});
if (emoji) return reacterHost ? `:${name}@${reacterHost}:` : `:${name}:`;
}
return (await fetchMeta()).defaultReaction;
}
type DecodedReaction = {
/**
* (Unicode Emoji or ':name@hostname' or ':name@.')
*/
reaction: string;
/**
* name (name, Emojiクエリに使う)
*/
name?: string;
/**
* host (host, Emojiクエリに使う)
*/
host?: string | null;
};
export function decodeReaction(str: string): DecodedReaction {
const custom = str.match(/^:([\w+-]+)(?:@([\w.-]+))?:$/);
if (custom) {
const name = custom[1];
const host = custom[2] || null;
return {
reaction: `:${name}@${host || "."}:`, // ローカル分は@以降を省略するのではなく.にする
name,
host,
};
}
return {
reaction: str,
name: undefined,
host: undefined,
};
}

View File

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

View File

@ -33,8 +33,10 @@ import { packedGalleryPostSchema } from "@/models/schema/gallery-post.js";
import { packedEmojiSchema } from "@/models/schema/emoji.js";
import { packedNoteEdit } from "@/models/schema/note-edit.js";
import { packedNoteFileSchema } from "@/models/schema/note-file.js";
import { packedAbuseUserReportSchema } from "@/models/schema/abuse-user-report.js";
export const refs = {
AbuseUserReport: packedAbuseUserReportSchema,
UserLite: packedUserLiteSchema,
UserDetailedNotMeOnly: packedUserDetailedNotMeOnlySchema,
MeDetailedOnly: packedMeDetailedOnlySchema,

View File

@ -1,4 +1,4 @@
import { fetchMeta } from "@/misc/fetch-meta.js";
import { fetchMeta } from "backend-rs";
import type { Instance } from "@/models/entities/instance.js";
import type { Meta } from "@/models/entities/meta.js";
@ -13,7 +13,7 @@ export async function shouldBlockInstance(
host: Instance["host"],
meta?: Meta,
): Promise<boolean> {
const { blockedHosts } = meta ?? (await fetchMeta());
const { blockedHosts } = meta ?? (await fetchMeta(true));
return blockedHosts.some(
(blockedHost) => host === blockedHost || host.endsWith(`.${blockedHost}`),
);
@ -30,7 +30,7 @@ export async function shouldSilenceInstance(
host: Instance["host"],
meta?: Meta,
): Promise<boolean> {
const { silencedHosts } = meta ?? (await fetchMeta());
const { silencedHosts } = meta ?? (await fetchMeta(true));
return silencedHosts.some(
(silencedHost) =>
host === silencedHost || host.endsWith(`.${silencedHost}`),

View File

@ -1,5 +1,5 @@
import { Brackets } from "typeorm";
import { fetchMeta } from "@/misc/fetch-meta.js";
import { fetchMeta } from "backend-rs";
import { Instances } from "@/models/index.js";
import type { Instance } from "@/models/entities/instance.js";
import { DAY } from "@/const.js";
@ -19,7 +19,7 @@ export async function skippedInstances(
hosts: Instance["host"][],
): Promise<Instance["host"][]> {
// first check for blocked instances since that info may already be in memory
const meta = await fetchMeta();
const meta = await fetchMeta(true);
const shouldSkip = await Promise.all(
hosts.map((host) => shouldBlockInstance(host, meta)),
);

View File

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

View File

@ -1,7 +1,7 @@
import fetch from "node-fetch";
import { Converter } from "opencc-js";
import { getAgentByUrl } from "@/misc/fetch.js";
import { fetchMeta } from "@/misc/fetch-meta.js";
import { fetchMeta } from "backend-rs";
import type { PostLanguage } from "@/misc/langmap";
import * as deepl from "deepl-node";
@ -26,7 +26,7 @@ export async function translate(
from: PostLanguage | null,
to: PostLanguage,
) {
const instance = await fetchMeta();
const instance = await fetchMeta(true);
if (instance.deeplAuthKey == null && instance.libreTranslateApiUrl == null) {
throw Error("No translator is set up on this server.");

View File

@ -16,6 +16,8 @@ import { DriveFolder } from "./drive-folder.js";
import { DB_MAX_IMAGE_COMMENT_LENGTH } from "@/misc/hard-limits.js";
import { NoteFile } from "./note-file.js";
export type DriveFileUsageHint = "userAvatar" | "userBanner" | null;
@Entity()
@Index(["userId", "folderId", "id"])
export class DriveFile {
@ -177,6 +179,14 @@ export class DriveFile {
})
public isSensitive: boolean;
// Hint for what this file is used for
@Column({
type: "enum",
enum: ["userAvatar", "userBanner"],
nullable: true,
})
public usageHint: DriveFileUsageHint;
/**
* ()URLへの直リンクか否か
*/

View File

@ -72,9 +72,8 @@ export class Note {
})
public name: string | null;
@Index() // USING pgroonga pgroonga_varchar_full_text_search_ops_v2
@Column("varchar", {
length: 512,
@Index() // USING pgroonga
@Column("text", {
nullable: true,
})
public cw: string | null;

View File

@ -2,6 +2,7 @@ import { db } from "@/db/postgre.js";
import { Users } from "../index.js";
import { AbuseUserReport } from "@/models/entities/abuse-user-report.js";
import { awaitAll } from "@/prelude/await-all.js";
import type { Packed } from "@/misc/schema.js";
export const AbuseUserReportRepository = db
.getRepository(AbuseUserReport)
@ -10,7 +11,7 @@ export const AbuseUserReportRepository = db
const report =
typeof src === "object" ? src : await this.findOneByOrFail({ id: src });
return await awaitAll({
const packed: Packed<"AbuseUserReport"> = await awaitAll({
id: report.id,
createdAt: report.createdAt.toISOString(),
comment: report.comment,
@ -31,9 +32,10 @@ export const AbuseUserReportRepository = db
: null,
forwarded: report.forwarded,
});
return packed;
},
packMany(reports: any[]) {
packMany(reports: (AbuseUserReport["id"] | AbuseUserReport)[]) {
return Promise.all(reports.map((x) => this.pack(x)));
},
});

View File

@ -40,6 +40,7 @@ export const ChannelRepository = db.getRepository(Channel).extend({
name: channel.name,
description: channel.description,
userId: channel.userId,
bannerId: channel.bannerId,
bannerUrl: banner ? DriveFiles.getPublicUrl(banner, false) : null,
usersCount: channel.usersCount,
notesCount: channel.notesCount,

View File

@ -152,6 +152,7 @@ export const DriveFileRepository = db.getRepository(DriveFile).extend({
md5: file.md5,
size: file.size,
isSensitive: file.isSensitive,
usageHint: file.usageHint,
blurhash: file.blurhash,
properties: opts.self ? file.properties : this.getPublicProperties(file),
url: opts.self ? file.url : this.getPublicUrl(file, false),
@ -193,6 +194,7 @@ export const DriveFileRepository = db.getRepository(DriveFile).extend({
md5: file.md5,
size: file.size,
isSensitive: file.isSensitive,
usageHint: file.usageHint,
blurhash: file.blurhash,
properties: opts.self ? file.properties : this.getPublicProperties(file),
url: opts.self ? file.url : this.getPublicUrl(file, false),

View File

@ -19,7 +19,9 @@ export const GalleryPostRepository = db.getRepository(GalleryPost).extend({
createdAt: post.createdAt.toISOString(),
updatedAt: post.updatedAt.toISOString(),
userId: post.userId,
user: Users.pack(post.user || post.userId, me),
user: Users.pack(post.user || post.userId, me, {
detail: true,
}),
title: post.title,
description: post.description,
fileIds: post.fileIds,

View File

@ -2,7 +2,7 @@ import { db } from "@/db/postgre.js";
import { NoteReaction } from "@/models/entities/note-reaction.js";
import { Notes, Users } from "../index.js";
import type { Packed } from "@/misc/schema.js";
import { decodeReaction } from "@/misc/reaction-lib.js";
import { decodeReaction } from "backend-rs";
import type { User } from "@/models/entities/user.js";
export const NoteReactionRepository = db.getRepository(NoteReaction).extend({

View File

@ -12,9 +12,8 @@ import {
Channels,
} from "../index.js";
import type { Packed } from "@/misc/schema.js";
import { nyaify } from "backend-rs";
import { countReactions, decodeReaction, nyaify } from "backend-rs";
import { awaitAll } from "@/prelude/await-all.js";
import { convertReactions, decodeReaction } from "@/misc/reaction-lib.js";
import type { NoteReaction } from "@/models/entities/note-reaction.js";
import {
aggregateNoteEmojis,
@ -214,7 +213,7 @@ export const NoteRepository = db.getRepository(Note).extend({
note.visibility === "specified" ? note.visibleUserIds : undefined,
renoteCount: note.renoteCount,
repliesCount: note.repliesCount,
reactions: convertReactions(note.reactions),
reactions: countReactions(note.reactions),
reactionEmojis: reactionEmoji,
emojis: noteEmoji,
tags: note.tags.length > 0 ? note.tags : undefined,
@ -233,6 +232,7 @@ export const NoteRepository = db.getRepository(Note).extend({
uri: note.uri || undefined,
url: note.url || undefined,
updatedAt: note.updatedAt?.toISOString() || undefined,
hasPoll: note.hasPoll,
poll: note.hasPoll ? populatePoll(note, meId) : undefined,
...(meId
? {

View File

@ -0,0 +1,69 @@
export const packedAbuseUserReportSchema = {
type: "object",
properties: {
id: {
type: "string",
optional: false,
nullable: false,
format: "id",
example: "xxxxxxxxxx",
},
createdAt: {
type: "string",
optional: false,
nullable: false,
format: "date-time",
},
comment: {
type: "string",
optional: false,
nullable: false,
},
resolved: {
type: "boolean",
optional: false,
nullable: false,
},
reporterId: {
type: "string",
optional: false,
nullable: false,
format: "id",
},
targetUserId: {
type: "string",
optional: false,
nullable: false,
format: "id",
},
assigneeId: {
type: "string",
optional: false,
nullable: true,
format: "id",
},
reporter: {
type: "object",
optional: false,
nullable: false,
ref: "UserDetailed",
},
targetUser: {
type: "object",
optional: false,
nullable: false,
ref: "UserDetailed",
},
assignee: {
type: "object",
optional: true,
nullable: true,
ref: "UserDetailed",
},
forwarded: {
type: "boolean",
optional: false,
nullable: false,
},
},
} as const;

View File

@ -36,6 +36,13 @@ export const packedChannelSchema = {
nullable: true,
optional: false,
},
bannerId: {
type: "string",
optional: false,
nullable: true,
format: "id",
example: "xxxxxxxxxx",
},
notesCount: {
type: "number",
nullable: false,
@ -57,5 +64,10 @@ export const packedChannelSchema = {
optional: false,
format: "id",
},
hasUnreadNote: {
type: "boolean",
optional: true,
nullable: false,
},
},
} as const;

View File

@ -44,6 +44,12 @@ export const packedDriveFileSchema = {
optional: false,
nullable: false,
},
usageHint: {
type: "string",
optional: false,
nullable: true,
enum: ["userAvatar", "userBanner"],
},
blurhash: {
type: "string",
optional: false,

View File

@ -38,7 +38,7 @@ export const packedGalleryPostSchema = {
},
user: {
type: "object",
ref: "UserLite",
ref: "UserDetailed",
optional: false,
nullable: false,
},
@ -79,5 +79,15 @@ export const packedGalleryPostSchema = {
optional: false,
nullable: false,
},
isLiked: {
type: "boolean",
optional: true,
nullable: false,
},
likedCount: {
type: "number",
optional: false,
nullable: false,
},
},
} as const;

View File

@ -28,7 +28,7 @@ export const packedNoteSchema = {
},
cw: {
type: "string",
optional: true,
optional: false,
nullable: true,
},
userId: {
@ -98,7 +98,7 @@ export const packedNoteSchema = {
},
fileIds: {
type: "array",
optional: true,
optional: false,
nullable: false,
items: {
type: "string",
@ -128,6 +128,11 @@ export const packedNoteSchema = {
nullable: false,
},
},
hasPoll: {
type: "boolean",
optional: false,
nullable: false,
},
poll: {
type: "object",
optional: true,

View File

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

View File

@ -34,7 +34,7 @@ export function initialize<T>(name: string, limitPerSec = -1) {
function apBackoff(attemptsMade: number, err: Error) {
const baseDelay = 60 * 1000; // 1min
const maxBackoff = 8 * 60 * 60 * 1000; // 8hours
let backoff = (Math.pow(2, attemptsMade) - 1) * baseDelay;
let backoff = (2 ** attemptsMade - 1) * baseDelay;
backoff = Math.min(backoff, maxBackoff);
backoff += Math.round(backoff * Math.random() * 0.2);
return backoff;

View File

@ -5,7 +5,7 @@ import perform from "@/remote/activitypub/perform.js";
import Logger from "@/services/logger.js";
import { registerOrFetchInstanceDoc } from "@/services/register-or-fetch-instance-doc.js";
import { Instances } from "@/models/index.js";
import { fetchMeta } from "@/misc/fetch-meta.js";
import { fetchMeta } from "backend-rs";
import { toPuny, extractHost } from "backend-rs";
import { getApId } from "@/remote/activitypub/type.js";
import { fetchInstanceMetadata } from "@/services/fetch-instance-metadata.js";
@ -41,7 +41,7 @@ export default async (job: Bull.Job<InboxJobData>): Promise<string> => {
const host = toPuny(new URL(signature.keyId).hostname);
// interrupt if blocked
const meta = await fetchMeta();
const meta = await fetchMeta(true);
if (await shouldBlockInstance(host, meta)) {
return `Blocked request: ${host}`;
}

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