Compare commits

...

222 commits

Author SHA1 Message Date
Johann150 17324e1e94
server: add unique constraint for registry items
fixes FoundKeyGang/FoundKey#335
2023-02-03 00:27:33 +01:00
Johann150 8b98c9f2f4
server: remove unused 'domain' column 2023-02-02 23:29:24 +01:00
Johann150 be30e70344
server: add more OpenGraph data, remove custom misskey meta tags
Changelog: Changed
2023-02-01 23:18:10 +01:00
Johann150 39fb7e5946
server: improve OpenGraph data for note attachments
With this change, not all files will be proclaimed to be image files. Only
images, videos and audio files will be represented with OpenGraph data.

More properties for these files will also be represented, e.g. image alt text.

However, if the note has a CW or any of the files are marked sensitive, none
of the files will be used.

The users profile picture will not be used any more.

Changelog: Changed
2023-02-01 22:53:32 +01:00
Johann150 75b14124f2
server: improve variable naming 2023-02-01 11:30:53 +01:00
Johann150 7480e27c0c
server: remove twitter links from HTML templates
Since the twitter integration has been removed, this will never be true
and can therefore be removed.
2023-02-01 11:27:27 +01:00
Johann150 953de3e4b2
adjust mailmap 2023-01-30 19:36:22 +01:00
Johann150 2d32bc33d7
server: fix error for invalid URLs in profile fields
Co-authored-by: Chloe Kudryavtsev <code@code.bunkerlabs.net>
2023-01-30 19:24:15 +01:00
Chloe Kudryavtsev bb3ec8bafe Revert "server: fix user deletion race condition"
This reverts commit cc83cbe523, reversing
changes made to 8abd3ebec7.

This changeset contains:
* multiple type errors
* a foreign key incompatibility
* breaks outgoing note federation (in at least two ways)
2023-01-30 14:59:24 +01:00
Johann150 6fd80816fa
client: remove unused property from MFM component 2023-01-29 14:29:58 +01:00
Johann150 cc83cbe523
server: fix user deletion race condition
Changelog: Fixed
Ref: https://github.com/misskey-dev/misskey/issues/7506
2023-01-29 12:53:29 +01:00
Johann150 8abd3ebec7
client: remove notification forwarding to service worker
This was an interim measure, but now that push notifications are always enabled,
this should not be necessary any more and the service worker should receive
all notifications automatically.
2023-01-29 12:39:26 +01:00
Johann150 36031c083a
docs: adjust parameters for v2 methods other than POST 2023-01-26 13:34:13 +01:00
Johann150 05f8172ce9
docs: describe /ap/ endpoints 2023-01-26 13:25:50 +01:00
Johann150 151053897d
server: lower rate limit for deletion activities
Changelog: Changed
2023-01-26 13:25:50 +01:00
Johann150 95a9027a66
docs: show rate limit information
Changelog: Added
2023-01-26 13:25:49 +01:00
Johann150 57cf6c7163
server: indicate Retry-After when rate limiting
This refactors the rate limiting code to throw an ApiError directly.

Changelog: Added
2023-01-26 08:37:07 +01:00
Johann150 9b76c805ec
fix: DriveFile folder & user undefined instead of null when unrequested 2023-01-25 22:14:53 +01:00
Johann150 21b20920c2
docs: use endpoint stability to mark endpoints deprecated 2023-01-23 20:13:17 +01:00
Johann150 e7644eb757
server: add index to human readable URL 2023-01-23 19:58:07 +01:00
Johann150 66ec875624
server: also search human readable URL
Changelog: Fixed
2023-01-23 18:09:04 +01:00
Johann150 78f5ca3792
server: fix empty array in quote detection 2023-01-22 21:47:02 +01:00
Johann150 c792e4199c
server: add missing return in extractQuoteUrl 2023-01-22 21:42:49 +01:00
Johann150 afa4094050
BREAKING: Remove galleries
Existing gallery posts will be made into normal notes.
If a user has gallery posts, a clip with all gallery posts will be created.

Changelog: Removed
2023-01-22 20:18:57 +01:00
Johann150 c4b5952788
migrate galleries to notes/clips 2023-01-22 19:44:39 +01:00
Johann150 e3fd371f4a
Implement FEP-e232 qoutes
Changelog: Added
2023-01-17 21:49:27 +01:00
Johann150 5893a44ff5
server: parse quote tag syntax
Ref: FEP-e232
2023-01-17 21:45:57 +01:00
Johann150 9bdf24d3a5
enhance: add tag for quotes
Ref: FEP-e232
2023-01-17 21:45:49 +01:00
Johann150 2bbb85b472
backend: remove galleries 2023-01-16 18:53:57 +01:00
Johann150 70fb1e9a5c
foundkey-js: remove galleries 2023-01-16 18:47:29 +01:00
Johann150 48163872ed
client: remove galleries 2023-01-16 18:39:50 +01:00
Johann150 7170b86724
fixup: websocket data parsing + logger 2023-01-14 13:22:09 +01:00
Johann150 96e6187e83
docs: admin password reset 2023-01-14 12:03:50 +01:00
Johann150 3d2cfc075a
fixup: actually check whether the group joining is from the user 2023-01-13 21:55:14 +01:00
Norm 83373e0c51
split build task into parallel and non-parallel versions
This allows FoundKey to be built on systems with 2GB or less RAM without swapping or running out of memory.
2023-01-12 17:08:50 -05:00
Johann150 11518d2f26
add documentation about moderation 2023-01-12 20:53:39 +01:00
Johann150 f9c360d59f
client: remove duplicated check
Since the enclosing block already checks for moderator, this check is unnecessary.
2023-01-12 20:09:24 +01:00
Johann150 2a8792fe07
client: add button to delete all files for moderators
The functionality was already there, there was just no button to access it
for some reason.

closes FoundKeyGang/FoundKey#243

Changelog: Added
2023-01-12 19:42:57 +01:00
Johann150 b8963a0119
update yarn.lock 2023-01-12 19:27:16 +01:00
Johann150 1319dc93d9
server: switch websocket to ws 2023-01-11 23:57:37 +01:00
Johann150 80e2851378
fixup: remove trailing comma in JSON 2023-01-11 20:58:38 +01:00
Johann150 14b48fb07c
client: remove unused dependencies 2023-01-11 20:32:51 +01:00
Johann150 5e2a9224f3
add "fungus" as alias to emoji list 2023-01-11 19:50:29 +01:00
Johann150 624628d582
client: remove unused websocket libraries
The websocket functionality is provided by foundkey-js so there is no need to import
any websocket libraries.
2023-01-11 19:45:18 +01:00
Johann150 e68eeba7a6
fixup: remove admin/delete-account endpoint from foundkey-js
This is a fixup for commit c7ab8839dc.
2023-01-11 19:12:58 +01:00
Johann150 ee2fa2e0be
fixup: import 2023-01-10 20:35:03 +01:00
Johann150 57d1af1117
remove default export in streaming API 2023-01-10 20:30:47 +01:00
Johann150 8c2b7e20b2
translating comments, cleanup 2023-01-09 20:44:01 +01:00
Johann150 fdf30f60e6
server: remove SQL boolean comparisons 2023-01-09 20:43:12 +01:00
Johann150 b245d39b6e
server: delete records of fully deleted users 2023-01-08 21:22:03 +01:00
Johann150 80f72e21cd
server: track deletion completion 2023-01-08 21:22:03 +01:00
Johann150 85e985d13f
server: change data structure to track deletion completion 2023-01-08 21:21:54 +01:00
Johann150 4fe288f17c server: rewrite user status queries in SQL 2023-01-08 20:02:21 +00:00
Johann150 cd26e3a35c
fixup: missing parenthesis 2023-01-08 19:34:03 +01:00
Johann150 c7ab8839dc
BREAKING: remove admin/delete-account, change admin/accounts/delete
You should use the API endpoint admin/accounts/delete.
It has the same parameter and the same behaviour.

The admin/accounts/delete endpoint now requries administrator privileges
instead of just moderator privileges.

Changelog: Removed
2023-01-07 23:53:48 +01:00
Johann150 1eda1760d1
server: refactor to always use deleteAccount service
This should reduce code duplication around how deletion of an actor is
handled.
2023-01-07 19:46:05 +01:00
Johann150 8772181b6f
server: refactor remote host check to validateActor
Instead of checking that an actor is not from the local host separately,
it seems like a good idea to do it in the central place that is supposed
to validate an actor.
2023-01-07 19:46:05 +01:00
Norm 8f21275dd5 roadmap: Update links to reference labels instead of projects 2023-01-07 01:50:25 +00:00
Norm 5102d0bc2e
chore: remove unused user_group_invite table
Based on `1558257926829-UserGroupInvite.js` but switched `up` and `down`
migrations around.

Closes #314
2023-01-06 02:51:44 -05:00
Puniko cdba5447e6
server: remove joins to avatar and banners in children endpoint
Reviewed-on: FoundKeyGang/FoundKey#303
2023-01-05 21:05:22 +01:00
Johann150 4bb814adfc
client: add space between endpoint and code in error message 2023-01-05 20:55:03 +01:00
Johann150 35e9d7f958
client: fix null i18n interpolation values
Fixed the occurence that was reported in
<FoundKeyGang/FoundKey#317> along with a similar one.

Fixes <FoundKeyGang/FoundKey#317>

Also changed the i18n code so this should not happen any more in the general case.
2023-01-05 20:50:25 +01:00
Johann150 a0c2cf328e
server: fix redirected fetch
Don't throw a StatusError on an intended redirect.
2023-01-05 20:03:38 +01:00
Johann150 334368f6e2
fix: allow to pick higher visibility than chosen before
If you selected a lower visibility that one would then be used as
the parent visibility. Instead it is necessary to use two separate
variables, one for parent and one for the preselected visibility.
2023-01-04 21:39:33 +01:00
Johann150 3efa7046bd
meta: don't type check dependencies 2023-01-04 20:59:31 +01:00
Johann150 48f8fb97df
activitypub: use quoteUri instead of quoteUrl
It's not quite Mastodon, but still, I said they'd use a different approach...

Changelog: Changed
2023-01-04 20:56:06 +01:00
Johann150 0230f819e2
fixup: wrong negation
This is a fixup for commit 417d252e9d.
2023-01-04 19:09:03 +01:00
Norm e58c940d6f
meta: Bump copyright year 2023-01-03 21:53:02 -05:00
Johann150 08af6fda37
fix some type errors 2023-01-03 22:18:01 +01:00
Johann150 0c8a3cfeec
server: fix lints 2023-01-03 03:51:38 +01:00
Johann150 8bc366fde0
server: fix comma-dangle lint 2023-01-03 02:47:58 +01:00
Johann150 417d252e9d
server: fix custom lint typeorm-prefer-count 2023-01-03 02:42:42 +01:00
Johann150 b54e07caec
enhance typeorm-prefer-count lint rule 2023-01-03 02:41:53 +01:00
Johann150 e0560dbe9e
client: dont display tooltip if software name unknown
Changelog: Fixed
2023-01-02 21:51:13 +01:00
Johann150 5b898c6c82
chore: update yarn files 2023-01-02 21:40:12 +01:00
Johann150 6010884e62
cleanup: translate japanese, use SECOND constant 2023-01-02 21:07:56 +01:00
Johann150 b423d23cf6
server: fix custom lint typeorm-prefer-count 2023-01-02 21:07:02 +01:00
Johann150 29714d1ae0
add custom eslint rule to prefer countBy over findBy 2023-01-02 20:58:33 +01:00
Johann150 7bf4d4426a
use count instead of find to check existence 2023-01-02 14:43:27 +01:00
Johann150 d28931bf00
server: remove dateUTC function 2023-01-02 12:45:30 +01:00
Johann150 2a46719f31
server: set file permissions after copy
This explicitly sets the file permissions to allow everyone to read files
since apparently multer sometimes doesn't set the permissions we expect.

Ref: FoundKeyGang/FoundKey#202
Changelog: Fixed
2023-01-02 12:44:09 +01:00
Johann150 7f564431be
server: fixup sql
Fixup to 0b7c9095bf.
2023-01-02 00:11:35 +01:00
Johann150 0fbd7fa492
client: fix 500 error in notifications
closes FoundKeyGang/FoundKey#73

Changelog: Fixed
2023-01-01 23:32:01 +01:00
Johann150 3aaa9facc6
translate japanese to english 2023-01-01 23:30:43 +01:00
Johann150 8f09b05e7c
chore: remove reversi database tables
Changelog: Fixed
2023-01-01 22:27:34 +01:00
Johann150 8b0b7ff525
server: change default value for api/admin/show-users origin param
Changed from "local" to "combined" to fix a bug when the hostname is set
but origin is not.

Changelog: Changed
2023-01-01 22:11:19 +01:00
Johann150 0b7c9095bf
server: don't return users twice in search 2023-01-01 21:22:53 +01:00
Johann150 338e898f56
client: disable unavailable visibilities 2023-01-01 21:02:55 +01:00
Puniko 3a9d283630
client: make mod patterns display when seeking on stopped track 2022-12-29 21:43:34 +01:00
Johann150 ed27f61a4d
client: add mod tracker
Squashed commit of the following:

commit 54f0b67b25bc6064b5c0ab3982e20943859aff76
Author: Puniko <me@absturztaube.ch>
Date:   Thu Dec 29 21:27:15 2022 +0100

    use nextTick instead of setTimeout

commit 6998cae7e3a706b00c1b63320750dab279f13e3d
Author: Puniko <me@absturztaube.ch>
Date:   Thu Dec 29 21:14:55 2022 +0100

    my absolute terrible fix to the unhide issue

commit 79f546d1509185c315a5db7e12985e01ed430b08
Author: Puniko <me@absturztaube.ch>
Date:   Thu Dec 29 21:01:35 2022 +0100

    stop player on hide/unhide

commit 6b7f13e8ef48d74edb92441144051e4225b27f71
Author: Puniko <me@absturztaube.ch>
Date:   Thu Dec 29 10:36:59 2022 +0100

    make webkit style range slider the same

commit 8a267c5cdc5c1e98d6dd1e038571d92d01985a98
Author: Puniko <me@absturztaube.ch>
Date:   Thu Dec 29 01:16:18 2022 +0100

    restyling range inputs

commit c39e1671b2957326ff91746da44091d728f58e82
Author: Puniko <me@absturztaube.ch>
Date:   Thu Dec 29 00:57:47 2022 +0100

    make module seekable

commit c1762f27ae2e4d342ede88018f777c75b8b31d4a
Author: Puniko <me@absturztaube.ch>
Date:   Thu Dec 29 00:14:35 2022 +0100

    remove accesskey attribs

commit 08f75a01f1c2359799e4c2ec734a391d3abfc07c
Author: Puniko <me@absturztaube.ch>
Date:   Thu Dec 29 00:12:23 2022 +0100

    v-else on play button

commit 9302a9faaa5a75c33314befb3f8e0f414e9d886b
Author: Puniko <me@absturztaube.ch>
Date:   Thu Dec 29 00:08:19 2022 +0100

    replace filter with some

commit bffd15daedf55f7623d6ebd777a12384274b89d6
Author: Puniko <me@absturztaube.ch>
Date:   Wed Dec 28 09:13:20 2022 +0100

    add chiptune2 and libopenmpt into COPYING

commit 794298c21c6fe09909136d141fd2a6b83eb64a89
Author: Puniko <me@absturztaube.ch>
Date:   Tue Dec 27 15:32:43 2022 +0100

    little cleanup

commit f383aec1cd5a9f8a44539d9f802dee2d1332f64c
Author: Puniko <me@absturztaube.ch>
Date:   Tue Dec 27 15:23:25 2022 +0100

    repeat only once and proper handling of track ending

commit fdaa9614c993de306789ecafc38d781d350fe1ab
Author: Puniko <me@absturztaube.ch>
Date:   Tue Dec 27 14:52:20 2022 +0100

    prevent losing connection when downloading module

commit 6c5723c795a3610558111f5d0cd6f39dcfd06965
Author: Puniko <me@absturztaube.ch>
Date:   Tue Dec 27 14:45:59 2022 +0100

    colours!!! 🌈

commit dba4f0a4a909b956d928bfb7e4f0321291a096e0
Author: Puniko <me@absturztaube.ch>
Date:   Tue Dec 27 13:01:06 2022 +0100

    replace  with i18n

commit 4234dfbdbc7f9b73fe07348af1fe2590db4e811d
Author: Puniko <me@absturztaube.ch>
Date:   Mon Dec 26 15:47:10 2022 +0100

    retab

commit 0cc1ea8c3ec14fde81936ec3c9eed4d06ad52d95
Author: Puniko <me@absturztaube.ch>
Date:   Mon Dec 26 15:19:28 2022 +0100

    include libopenmpt tracker to foundkey

commit c2437c696a5dabb8581fd81f7852cdd6091fe00f
Author: Puniko <me@absturztaube.ch>
Date:   Mon Dec 26 12:08:49 2022 +0100

    add libopenmpt

Reviewed-on: FoundKeyGang/FoundKey#306
Changelog: Added
2022-12-29 21:36:44 +01:00
Chloe Kudryavtsev ed9d4023d4 backend: add argon2 support
Passwords will be automatically re-hashed on sign-in.
All new password hashes will be argon2 by default.

This uses argon2id and is not configurable.
In the very unlikely case someone has more specific needs,
a fork is recommended.

ChangeLog: Added

Co-authored-by: Chloe Kudryavtsev <code@toast.bunkerlabs.net>
Reviewed-on: FoundKeyGang/FoundKey#308
2022-12-29 20:13:47 +00:00
Johann150 76a8e000a3
client: only catch erroneous key errors in i18n.ts 2022-12-27 21:47:34 +01:00
Johann150 a673647fba
server: remove avatarColor and bannerColor properties
According to comments next to those properties, they were kept for backward compatibility.
However they were always being set to null.

Changelog: Removed
2022-12-26 18:52:16 +01:00
Johann150 eea2eb4919
use Promise.all instead of separate promises 2022-12-25 19:04:00 +01:00
Johann150 114d416de0
server: refactor password hashing & comparison to module
For easier replacement should the hash algorithm ever be changed.
2022-12-25 19:03:51 +01:00
Johann150 c2372315f7
server: improve error messages
Refactor Error's to ApiError's.

Changelog: Changed
2022-12-25 16:07:48 +01:00
Norm 09bc3cf95a
activitypub: Do block checks more globally
Changelog: Fixed
Reviewed-on: FoundKeyGang/FoundKey#299
2022-12-24 18:40:44 -05:00
Johann150 de3cdb5833
activitypub: block check for resolving collections 2022-12-24 18:39:44 -05:00
Norm a732cdc1ad
activitypub: perform block check in performOneActivity 2022-12-24 18:39:44 -05:00
Norm a8f82050c8
activitypub: perform resolver block check on objects as well 2022-12-24 18:39:44 -05:00
Norm 8e12b9a33e
server: restore original comment for skippedInstances 2022-12-24 15:01:32 -05:00
Norm 6583d0c43d
server: pass in resolved meta table to shouldBlockInstance
This should make it more friendly to use in places where the meta table
has already been resolved for other reasons.
2022-12-24 14:56:48 -05:00
Johann150 c02a03168d
fixup tooltip: use this instead of self 2022-12-24 13:48:58 +01:00
Johann150 85419326f8
server: use prelude function instead of separate function 2022-12-23 13:55:15 +01:00
Johann150 eaa11647f0
server: rewrite drive usage queries in raw SQL 2022-12-23 13:54:12 +01:00
Johann150 61a2db49df
server: always use user id for calcDriveUsageOf 2022-12-23 13:38:29 +01:00
Johann150 79ddbafd0d
client: fix tooltips not closing
closes FoundKeyGang/FoundKey#120

Changelog: Fixed
2022-12-23 11:06:07 +01:00
Norm 0e1459e5cf Merge pull request 'server: refactor follow request functions to be named exports' (#296) from refactor/follow-requests into main
Reviewed-on: FoundKeyGang/FoundKey#296
2022-12-23 02:06:31 +00:00
Johann150 e8e82dac82
fixup: confusing commas 2022-12-23 02:30:57 +01:00
Norm 9690244848
server: add return type for all follow reject funcs 2022-12-22 17:52:30 -05:00
Norm 4db25e4b1f
server: add doc for cancelFollowRequest 2022-12-22 16:55:08 -05:00
Norm 549302e9c0
server: add doc for createFollowRequest 2022-12-22 16:55:07 -05:00
Norm a3354904af
server: use named export for createFollowRequest 2022-12-22 16:52:52 -05:00
Norm 28f65bebfc
server: use named export for cancelFollowRequest 2022-12-22 16:52:52 -05:00
Norm 2204adc657
server: use named export for acceptAllFollowRequests 2022-12-22 16:52:52 -05:00
Norm b11e4053db
server: use named export for acceptFollowRequest 2022-12-22 16:52:52 -05:00
Johann150 e2ef800708
server: dont use replace for file types
No point in using replace if we already know which character we want to replace.
2022-12-22 14:46:21 +01:00
Johann150 a7048f17f7
server: simplify duplicated code 2022-12-22 14:45:20 +01:00
Johann150 ddf3e2c3db
client: refactor tooltip directive
Using the beforeUnmount hook should hopefully improve issues with
tooltips being left behind.
2022-12-22 14:12:25 +01:00
Johann150 52afff800a
server: start adding /api/v2 routes
empty changelog commit

Changelog: Added
2022-12-22 11:03:38 +01:00
Johann150 33f0b24c56
server: add v2 routes to notes endpoints 2022-12-22 11:02:04 +01:00
Andy 7685b92511
improve fetching of endpoint arguments
including support for route parameters (e.g. '/v2/note/:noteId' giving us a 'noteId' value)

Co-authored-by: Johann150 <johann.galle@protonmail.com>
2022-12-22 11:02:04 +01:00
Andy 8276bd3bdc
generate OpenAPI spec for v2 endpoints 2022-12-22 11:02:04 +01:00
Andy aed2752470
server: make v2 meta endpoint support GET 2022-12-22 11:01:56 +01:00
Andy 4a3b91d658
server: add additional API v2 options to endpoints
* improve type definitions for v2 method
The method has to be lowercase because it is used as an index to get
the respective method of the router.

Co-authored-by: Johann150 <johann.galle@protonmail.com>
2022-12-22 11:00:46 +01:00
Johann150 9317d25078
server: expire notifications after 3 months
closes FoundKeyGang/FoundKey#292

Changelog: Added
2022-12-21 21:46:45 +01:00
Johann150 fc36bb8880
server: reduce code duplication in check-expired queue job 2022-12-21 21:46:27 +01:00
Johann150 711bb8be7d
fixup: add missing redirect argument 2022-12-21 21:23:23 +01:00
Johann150 275136cf8b
allow redirects in API ap/* endpoints 2022-12-21 20:45:55 +01:00
Johann150 aa33708b90
server: handle redirects in signed get
part of FoundKeyGang/FoundKey#288

Changelog: Fixed
2022-12-20 22:07:24 +01:00
Norm d75e295ee8
remove "your" from "read:reactions"
Makes it consistent wiht the rest of the _permissions strings not using
pronouns.
2022-12-20 00:10:17 -05:00
Norm 766ab1c4c4
docs: readd missing "read:reactions" string 2022-12-20 00:07:35 -05:00
Johann150 99c459a21a
server: better upload limit error
Ref: FoundKeyGang/FoundKey#293
2022-12-19 21:29:29 +01:00
Johann150 bd68096ea9
server: refactor API error 2022-12-19 21:24:39 +01:00
Johann150 c411669133
client: fix token-generate-window component 2022-12-18 20:42:05 +01:00
Johann150 639fa74d43
client: restyle app token view 2022-12-18 19:37:52 +01:00
Johann150 3bf7deb233
client: remove unused styling classes 2022-12-18 00:50:02 +01:00
Johann150 2520633210
client: move MFM animations to MFM component 2022-12-18 00:49:28 +01:00
Johann150 4574db523a
client: add default margin to FormSwitch and MkButton 2022-12-18 00:11:32 +01:00
Johann150 263fb94f3f
client: unify import names of form ui components 2022-12-18 00:04:53 +01:00
Johann150 0e3321c106
client: unify import names of MkButton component 2022-12-17 23:32:25 +01:00
Johann150 677ee537d1
client: remove unused component 2022-12-17 22:39:18 +01:00
Johann150 1e539e6af5
client: add missing space
see FoundKeyGang/FoundKey#291
2022-12-17 22:34:49 +01:00
Johann150 0e5f744560
client: 2fa is not its own settings page 2022-12-17 22:34:49 +01:00
Norm 8f782f8ce5 scripts: convert to ESM and deduplicate (#290)
This makes it a bit easier to add any new files that may need to be
cleaned up in the future.

Also allows us to use top-level await for the `yarn dev` task.

Co-authored-by: Francis Dinh <normandy@biribiri.dev>
Reviewed-on: FoundKeyGang/FoundKey#290
2022-12-16 17:15:25 +00:00
Johann150 6c7f1774e3
server: fix thread mutes not applying to renotes
Changelog: Fixed
2022-12-15 21:20:24 +01:00
Johann150 af43df15ca
reduce duplication in secureRndstr 2022-12-15 20:46:17 +01:00
Johann150 5f83383ab8
fix import error in tests 2022-12-15 20:45:55 +01:00
Johann150 8c759dde6c
server: fix error about duplicate resolve 2022-12-15 19:44:55 +01:00
Johann150 84d83d908a
client: add button to unrenote
Changelog: Added
2022-12-15 17:52:19 +01:00
Johann150 16d091497a
server: use extractDbHost instead of toPuny, translate comments
Also swapped logical or for nullish coalescing operator in some places.
2022-12-15 00:32:15 +01:00
Johann150 ef53ec276a
activitypub: simplify some URI/id related checks
followup on previous commit
2022-12-15 00:31:23 +01:00
Johann150 3582fd8260
activitypub: centrally check id matches URL in resolver
This makes some duplicated checks in models/note and models/person
unnecessary.
2022-12-15 00:29:39 +01:00
Johann150 6256ddbd30
client: remove unused variables 2022-12-14 22:09:29 +01:00
Johann150 00fcc238f7
client: remove broken instance ticker from landing page 2022-12-14 22:08:27 +01:00
Johann150 9f1670d5fd
server: fix default not found error image 2022-12-14 19:05:41 +01:00
Norm ff31b8b06d server: remove bios and cli
The BIOS and CLI functionality were mainly for debugging purposes.
If a user has to use those to resolve an issue with the server, that
really should be fixed at the source instead.

Closes: FoundKeyGang/FoundKey#283
Changelog: Removed
2022-12-14 17:59:25 +00:00
Johann150 e317a771b3
remove vscode config files 2022-12-14 18:52:36 +01:00
Johann150 398ee6435b
client: replace repo link with foundkey link 2022-12-14 18:21:24 +01:00
Johann150 ffff2ae5ef
server: fix missing import
closes FoundKeyGang/FoundKey#286
2022-12-14 18:08:44 +01:00
Johann150 ccc8bf0289
chore: fix more miscellaneous lints 2022-12-13 23:09:32 +01:00
Johann150 a231b36d59
chore: fix lint about unused variables in entities 2022-12-13 23:09:32 +01:00
Johann150 8e9c65fab0
chore: fix some import related lints 2022-12-13 23:09:31 +01:00
Norm 78a3051313
remove unneeded TODO 2022-12-13 16:46:29 -05:00
Norm 78717e85d3
server: change JSON.parse/stringify to structuredClone
structuredClone is more typesafe than using JSON.parse and
JSON.stringify.

Now that Node 18.x is the new baseline, this should be safe to use now.

See https://developer.mozilla.org/en-US/docs/Web/API/structuredClone
for details.
2022-12-13 16:45:38 -05:00
Norm a9d3cae511
server: add return type to extractApMentions 2022-12-13 16:31:15 -05:00
Norm bd27b7ca3a
server: add typing for renderFollowRelay 2022-12-13 16:06:18 -05:00
Norm e28a9eb8e8
use tsc --noEmit for backend and client
See https://github.com/misskey-dev/misskey/pull/9316
2022-12-13 16:02:06 -05:00
Norm e5a4c5d2d0
chore: update @typescript-eslint packages 2022-12-13 15:57:26 -05:00
Norm 6bba55c196
sw: add TypeScript type checking
This implements the upstream changes from
https://github.com/misskey-dev/misskey/pull/9314 but updated to our
version of ESLint.

Also updates TypeScript to 4.9.4 for all packages.
2022-12-13 15:42:08 -05:00
Norm 1d469f3c34
fix import typo 2022-12-13 15:12:29 -05:00
Norm 3f0228e14c
server: use color-convert KEYWORD instead of extracting parameter type 2022-12-13 15:11:29 -05:00
Johann150 73f81177b4
foundkey-js: adjust type definition 2022-12-13 20:54:50 +01:00
Johann150 6a26da3516
client: use configurable images 2022-12-13 20:54:49 +01:00
Johann150 5ea744b1b2
server: use configurable images 2022-12-13 20:54:49 +01:00
Johann150 ae6ba05306
add config for error images
Changelog: Added
2022-12-13 20:54:49 +01:00
Sam Smucny 21069223e3
client: add tooltips to visibility icons
Changelog: Changed
2022-12-13 20:49:17 +01:00
Johann150 d4d1e03479
server: fix errors for replies and state when note doesnt exist 2022-12-13 20:35:46 +01:00
Norm 5513a3eb3a chore: update .gitattributes to reflect removed assets
Since there's no longer any .afdesign/.psd files or the like, it's not necessary to have those in .gitattributes any longer.
2022-12-11 23:16:05 +00:00
Johann150 d5dd7c1ef5
chore: remove more unused assets 2022-12-11 20:52:52 +01:00
Johann150 a80521b6a8
chore: remove unused assets 2022-12-11 20:44:38 +01:00
Norm 030394b30d
refactor: remove default export for boot 2022-12-11 14:42:55 -05:00
Johann150 768d9bbdfb
refactor: remove default export for perform 2022-12-11 18:23:19 +01:00
Johann150 3ef1a4b0f9
refactor: remove default export for Resolver 2022-12-11 18:23:07 +01:00
Johann150 ae59ce51b0
refactor: remove default export for DbResolver 2022-12-11 18:16:48 +01:00
Johann150 14a9b9bedd
refactor: remove default export for request 2022-12-11 18:16:45 +01:00
Johann150 985a13f47f
refactor: remove default export for DeliverManager 2022-12-11 17:56:25 +01:00
Norm 3e46433ede docker: Ignore .woodpecker 2022-12-11 01:35:33 +00:00
Johann150 507b328fdf
activitypub: also forward resolver to resolveNote 2022-12-10 11:23:10 +01:00
Norm cf7449509f docs: add rfc links to oauth documentation 2022-12-10 05:23:22 +00:00
Norm f46ba3f700 Merge pull request 'server: misc services code cleanup' (#275) from refactor/services into main
Reviewed-on: FoundKeyGang/FoundKey#275
2022-12-10 04:10:44 +00:00
Norm 3cf673960b server: Fix typing for user token
Also fix a comment in the User model that wrongly states that the token
is null if the user is local, when it's the opposite.
2022-12-08 23:20:41 -05:00
Norm cbfd866122 server: make fetcher key non-null 2022-12-08 23:19:39 -05:00
Norm b23a8dbaed server: translate comments 2022-12-08 23:18:45 -05:00
Norm 80a73a7510 server: remove unused imports from suspend-user.ts 2022-12-08 23:18:45 -05:00
Norm 3dec9a47f0 server: fix various type errors in services 2022-12-08 23:18:45 -05:00
Norm b8fb7a38cc server: improve Logger typing information and docs 2022-12-08 23:18:45 -05:00
Norm fdc682e810 server: remove sendEmailNotification
The functions have their bodies completely comented out,
which means they are doing nothing.
2022-12-08 23:18:45 -05:00
Johann150 fde751df8f
fix: properly supply resolver (2) 2022-12-08 19:06:55 +01:00
Johann150 1faf1035f9
server: handle users getting deleted somewhere else
I don't know why but several jobs got stuck in my inbox queue because
of errors like 'Could not find any entity of type "User" matching...'.
2022-12-08 18:12:24 +01:00
Johann150 e2ce599aca
fix: properly supply resolver 2022-12-08 18:12:05 +01:00
Johann150 73870e85cd
client: make headlines in queue widget links
The headlines "inbox queue" and "deliver queue" are now links to the
admin panel page about the queue.

Changelog: Changed
2022-12-07 23:23:16 +01:00
Norm 350f21d955
server: fix typing for skippedInstances query 2022-12-07 16:41:34 -05:00
Norm 873e21f090
chore: update eslint 2022-12-07 16:27:53 -05:00
Norm 2afe54c121
eslint: allow backticks to avoid escaping single/double quotes 2022-12-07 16:27:39 -05:00
Johann150 501cf834c8
client: fix issue of search only working once
closes FoundKeyGang/FoundKey#274

Changelog: Fixed
2022-12-07 21:56:27 +01:00
Norm b66f7550ab
server: auto-fix lints 2022-12-07 13:39:21 -05:00
Johann150 18664dbca3
server: add missing paren
How did this not break yet?
2022-12-07 18:29:04 +01:00
Johann150 0f3f42eb39
remove rndstr dependency
This dependency was unused in the client.

The use of it in the server can be replaced entirely by the
secureRndstr function, with some slight modifications.

That function could probably be refactored a bit more as well.
2022-12-07 18:08:09 +01:00
Johann150 71b976ec96
BREAKING: remove integrations
The Discord, Github and Twitter integrations have been removed to reduce
complexity and because they were only used on very few instances.

Server admins that did disable this may want to revoke the OAuth client
registrations for their instance that they made on the respective service.

Changelog: Removed
2022-12-07 17:16:14 +01:00
Andy d3f1ad9a88 chore: remove unused packages 2022-12-06 23:18:27 +01:00
Andy 1aa3898db5 server: remove unused import 2022-12-06 23:12:45 +01:00
Andy 96c3744555 client: remove integration settings menu entry 2022-12-06 23:00:32 +01:00
Andy b023741f50 server: remove integrations field from user 2022-12-06 23:00:08 +01:00
Andy 87e1e658f2 locales: remove integration-related locales 2022-12-06 22:03:34 +01:00
Andy 7e8d5c3b79 foundkey-js: remove integration fields from instance type 2022-12-06 21:52:16 +01:00
Andy c785fbab6e client: remove integration signin options 2022-12-06 21:51:01 +01:00
Andy 547a1f81d4 client: remove integration settings 2022-12-06 21:50:34 +01:00
Andy 95384d0bb2 client: remove integration admin settings 2022-12-06 21:50:20 +01:00
Andy 4cc5b734e7 activitypub: remove integration fields from person and nodeinfo 2022-12-06 21:49:19 +01:00
Andy 5d32872999 server: remove integration API routes 2022-12-06 21:48:31 +01:00
Andy b4b1204f77 server: remove integration-related fields from meta 2022-12-06 21:47:59 +01:00
531 changed files with 3875 additions and 6001 deletions

View file

@ -133,3 +133,10 @@ redis:
#allowedPrivateNetworks: [
# '127.0.0.1/32'
#]
# images used on error screens. You can use absolute or relative URLs.
# If you use relative URLs, be aware that the URL may be used on different pages/paths, so the path component should be absolute.
#images:
# info: /twemoji/1f440.svg
# notFound: /twemoji/2049.svg
# error: /twemoji/1f480.svg

View file

@ -1,6 +1,6 @@
.autogen
.vscode
.config
.woodpecker
Dockerfile
build/
built/

View file

@ -2,9 +2,10 @@ root = true
[*]
indent_style = tab
indent_size = 2
indent_size = 4
charset = utf-8
insert_final_newline = true
[*.yml]
indent_style = space
indent_size = 2

6
.gitattributes vendored
View file

@ -1,7 +1 @@
*.svg -diff -text
*.psd -diff -text
*.ai -diff -text
*.mqo -diff -text
*.glb -diff -text
*.blend -diff -text
*.afdesign -diff -text

5
.gitignore vendored
View file

@ -1,6 +1,6 @@
# Visual Studio Code
/.vscode
!/.vscode/extensions.json
/.vsls.json
# Intelij-IDEA
/.idea
@ -11,6 +11,9 @@
# nano
.swp
# vimlocal
.vimlocal
# Node.js
node_modules
report.*.json

View file

@ -1,9 +1,9 @@
Andreas Nedbal <git@pixelde.su> <andreas.nedbal@in2code.de>
Andreas Nedbal <git@pixelde.su> <github-bf215181b5140522137b3d4f6b73544a@desu.email>
Balazs Nadasdi <balazs@weave.works> <yitsushi@gmail.com>
Chloe Kudryavtsev <code@code.bunkerlabs.net> <code@toast.bunkerlabs.net>
Chloe Kudryavtsev <code@code.bunkerlabs.net> <toast+git@toast.cafe>
Chloe Kudryavtsev <code@code.bunkerlabs.net> <toast@toast.cafe>
Chloe Kudryavtsev <code@toast.bunkerlabs.net> <code@code.bunkerlabs.net>
Chloe Kudryavtsev <code@toast.bunkerlabs.net> <toast+git@toast.cafe>
Chloe Kudryavtsev <code@toast.bunkerlabs.net> <toast@toast.cafe>
Dr. Gutfuck LLC <40531868+gutfuckllc@users.noreply.github.com>
Ehsan Javadynia <31900907+ehsanjavadynia@users.noreply.github.com> <ehsan.javadynia@gmail.com>
Francis Dinh <normandy@biribiri.dev>

View file

@ -1,8 +0,0 @@
{
"recommendations": [
"editorconfig.editorconfig",
"dbaeumer.vscode-eslint",
"Vue.volar",
"Vue.vscode-typescript-vue-plugin"
]
}

View file

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

22
.woodpecker/lint-sw.yml Normal file
View file

@ -0,0 +1,22 @@
clone:
git:
image: woodpeckerci/plugin-git
settings:
depth: 1 # CI does not need commit history
recursive: true
pipeline:
install:
when:
event:
- pull_request
image: node:18.6.0
commands:
- yarn install
lint:
when:
event:
- pull_request
image: node:18.6.0
commands:
- yarn workspace sw run lint

View file

@ -316,8 +316,8 @@ This does not apply when using the Composition API since reactivation is manual.
If you import json in TypeScript, the json file will be spit out together with the TypeScript file into the dist directory when compiling with tsc. This behavior may cause unintentional rewriting of files, so when importing json files, be sure to check whether the files are allowed to be rewritten or not. If you do not want the file to be rewritten, you should make sure that the file can be rewritten by importing the json file. If you do not want the file to be rewritten, use functions such as `fs.readFileSync` to read the file instead of importing it.
### Component style definitions do not have a `margin`
Setting the `margin` of a component may be confusing.
Instead, it should always be the user of a component that sets a `margin`.
~~Setting the `margin` of a component may be confusing. Instead, it should always be the user of a component that sets a `margin`.~~
This was a philosophy used previously. Hoever it now seems a better idea to add a default margin to the top level element of a component which can be easily overwritten on the usage of that component with a `style` attribute.
### Do not use the word "follow" in HTML class names
This has caused things to be blocked by an ad blocker in the past.

10
COPYING
View file

@ -1,6 +1,6 @@
Unless otherwise stated this repository is
Copyright © 2014-2022 syuilo and contributors
Copyright © 2022 FoundKey contributors
Copyright © 2022-2023 FoundKey contributors
And is distributed under The GNU Affero General Public License Version 3, you should have received a copy of the license file as LICENSE.
(You may be able to run `git shortlog -se` to see a full list of authors.)
@ -13,3 +13,11 @@ https://github.com/muan/emojilib/blob/master/LICENSE
RsaSignature2017 implementation by Transmute Industries Inc
License: MIT
https://github.com/transmute-industries/RsaSignature2017/blob/master/LICENSE
Chiptune2.js by Simon Gündling
License: MIT
https://github.com/deskjet/chiptune2.js#license
libopenmpt (as part of openmpt) by OpenMPT
License: BSD 3-Clause
https://github.com/OpenMPT/openmpt/blob/master/LICENSE

View file

@ -3,9 +3,9 @@ Note: this document is historical.
Everything starting with the next section is the original "idea" document that led to the foundation of FoundKey.
For the current status you should see the following:
* The Behavioral Fixes [project](https://akkoma.dev/FoundKeyGang/FoundKey/projects/3)
* The Technological Upkeep [project](https://akkoma.dev/FoundKeyGang/FoundKey/projects/4)
* The Features [project](https://akkoma.dev/FoundKeyGang/FoundKey/projects/5)
* Issues labeled with [behaviour-fix](https://akkoma.dev/FoundKeyGang/FoundKey/issues?labels=44)
* Issues labeled with [upkeep](https://akkoma.dev/FoundKeyGang/FoundKey/issues?labels=43)
* Issues labeled with [feature](https://akkoma.dev/FoundKeyGang/FoundKey/issues?labels=42)
## Misskey Goals
Ive been thinking about a community misskey fork for a while now. To some of you, this is not a surprise. Lets talk about that.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 317 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 200 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 235 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 238 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 148 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

View file

@ -70,6 +70,8 @@ Build foundkey with the following:
`NODE_ENV=production yarn build`
If your system has at least 4GB of RAM, run `NODE_ENV=production yarn build-parallel` to speed up build times.
If you're still encountering errors about some modules, use node-gyp:
1. `npx node-gyp configure`
@ -182,6 +184,7 @@ Use git to pull in the latest changes and rerun the build and migration commands
git pull
git submodule update --init
yarn install
# Use build-parallel if your system has 4GB or more RAM and want faster builds
NODE_ENV=production yarn build
yarn migrate
```

103
docs/moderation.md Normal file
View file

@ -0,0 +1,103 @@
# User moderation
A lot of the user moderation activities can be found on the `user-info` page. You can reach this page by going to a users profile page, open the three dot menu, select "About" and navigating to the "Moderation" section of the page that opens.
With the necessary privileges, this page will allow you to:
- Toggle whether a user is a moderator (administrators on local users only)
- Reset the users password (local users only)
- Delete a user (administrators only)
- Delete all files of a user
For remote users, cached files (if any) will be deleted.
- Silence a user
This disallows a user from making a note with `public` visibility.
If necessary the visibility of incoming notes or locally created notes will be lowered.
- Suspend a user
This will drop any incoming activities of this actor and hide them from public view on this instance.
# Administrator
When an instance is first set up, the initial user to be created will be made an administrator by default.
This means that typically the instance owner is the administrator.
It is also possible to have multiple administrators, however making a user an administrator is not implemented in the client.
To make a user an administrator, you will need access to the database.
This is intended for security reasons of
1. not exposing this very dangerous functionality via the API
2. making sure someone that has shell access to the server anyway "approves" this.
To make a user an administrator, you will first need the user's ID.
To get it you can go to the user's profile page, open the three dot menu, select "About" and copy the ID displayed there.
Then, go to the database and run the following query, replacing `<ID>` with the ID gotten above.
```sql
UPDATE "user" SET "isAdmin" = true WHERE "id" = '<ID>';
```
The user that was made administrator may need to reload their client to see the changes take effect.
To demote a user, you can do a similar operation, but instead with `... SET "isAdmin" = false ...`.
## Immunity
- Cannot be reported by local users.
- Cannot have their password reset.
To see how you can reset an administrator password, see below.
- Cannot have their account deleted.
- Cannot be suspended.
- Cannot be silenced.
- Cannot have their account details viewed by moderators.
- Cannot be made moderators.
## Abilities
- Create or delete user accounts.
- Add or remove moderators.
- View and change instance configuration (e.g. Translation API keys).
- View all followers and followees.
Administrators also have the same ability as moderators.
Note of course that people with access to the server and/or database access can do basically anything without restrictions (including breaking the instance).
## Resetting an administrators password
Administrators are blocked from the paths of resetting the password by moderators or administrators.
However, if your server has email configured you should be able to use the "Forgot password" link on the normal signin dialog.
If you did not set up email, you will need to kick of this process instead through modifying the database yourself.
You will need the user ID whose password should be reset, indicated in the following as `<USERID>`;
as well as a random string (a UUID would be recommended) indicated as `<TOKEN>`.
Replacing the two terms above, run the following SQL query:
```sql
INSERT INTO "password_reset_request" VALUES ('0000000000', now(), '<TOKEN>', '<USERID>');
```
After that, navigate to `/reset-password/<TOKEN>` on your instance to finish the password reset process.
After that you should be able to sign in with the new password you just set.
# Moderator
A moderator has fewer privileges than an administrator.
They can also be more easily added or removed by an adminstrator.
Having moderators may be a good idea to help with user moderation.
## Immunity
- Cannot be reported by local users.
- Cannot be suspended.
## Abilities
- Suspend users.
- Add, list and remove relays.
- View queue, database and server information.
- Create, edit, delete, export and import local custom emoji.
- View global, social and local timelines even if disabled by administrators.
- Show, update and delete any users files and file metadata.
Managing emoji is described in [a separate file](emoji.md).
- Delete any users notes.
- Create an invitation.
This allows users to register an account even if (public) registrations are closed using an invite code.
- View users' account details.
- Suspend and unsuspend users.
- Silence and unsilence users.
- Handle reports.
- Create, update and delete announcements.
- View the moderation log.

View file

@ -1,9 +1,9 @@
# 3rd party access
Foundkey supports:
- OAuth 2.0 Authorization Code grant per RFC 6749.
- OAuth Bearer Token Usage per RFC 6750.
- Proof Key for Code Exchange (PKCE) per RFC 7636.
- OAuth 2.0 Authorization Server Metadata per RFC 8414.
- OAuth 2.0 Authorization Code grant per [RFC 6749](https://www.rfc-editor.org/rfc/rfc6749).
- OAuth Bearer Token Usage per [RFC 6750](https://www.rfc-editor.org/rfc/rfc6750).
- Proof Key for Code Exchange (PKCE) per [RFC 7636](https://www.rfc-editor.org/rfc/rfc7636).
- OAuth 2.0 Authorization Server Metadata per [RFC 8414](https://www.rfc-editor.org/rfc/rfc8414.html).
# Discovery
Because the implementation may change in the future, it is recommended that you use OAuth 2.0 Authorization Server Metadata a.k.a. OpenID Connect Discovery.

View file

@ -36,7 +36,7 @@ gulp.task('copy:client:locales', cb => {
});
gulp.task('build:backend:script', () => {
return gulp.src(['./packages/backend/src/server/web/boot.js', './packages/backend/src/server/web/bios.js', './packages/backend/src/server/web/cli.js'])
return gulp.src(['./packages/backend/src/server/web/boot.js'])
.pipe(replace('LANGS', JSON.stringify(Object.keys(locales))))
.pipe(terser({
toplevel: true
@ -45,7 +45,7 @@ gulp.task('build:backend:script', () => {
});
gulp.task('build:backend:style', () => {
return gulp.src(['./packages/backend/src/server/web/style.css', './packages/backend/src/server/web/bios.css', './packages/backend/src/server/web/cli.css'])
return gulp.src(['./packages/backend/src/server/web/style.css'])
.pipe(cssnano({
zindex: false
}))

View file

@ -293,9 +293,6 @@ dayX: "{day}"
monthX: "{month}"
yearX: "{year}"
pages: "الصفحات"
integration: "التكامل"
connectService: "اتصل"
disconnectService: "اقطع الاتصال"
enableLocalTimeline: "تفعيل الخيط المحلي"
enableGlobalTimeline: "تفعيل الخيط الزمني الشامل"
disablingTimelinesInfo: "سيتمكن المديرون والمشرفون من الوصول إلى كل الخيوط الزمنية\
@ -402,7 +399,6 @@ normalPassword: "الكلمة السرية جيدة"
strongPassword: "الكلمة السرية قوية"
passwordMatched: "التطابق صحيح!"
passwordNotMatched: "غير متطابقتان"
signinWith: "الولوج عبر {x}"
signinFailed: "فشل الولوج، خطأ في اسم المستخدم أو كلمة المرور."
tapSecurityKey: "أنقر مفتاح الأمان"
or: "أو"

View file

@ -308,9 +308,6 @@ dayX: "{day}"
monthX: "{month}"
yearX: "{year}"
pages: "পৃষ্ঠা"
integration: "ইন্টিগ্রেশন"
connectService: "সংযুক্ত করুন"
disconnectService: "সংযোগ বিচ্ছিন্ন করুন"
enableLocalTimeline: "স্থানীয় টাইমলাইন চালু করুন"
enableGlobalTimeline: "গ্লোবাল টাইমলাইন চালু করুন"
disablingTimelinesInfo: "আপনি এই টাইমলাইনগুলি বন্ধ করলেও প্রশাসক এবং মডারেটররা এই\
@ -417,7 +414,6 @@ normalPassword: "সাধারণ পাসওয়ার্ড"
strongPassword: "শক্তিশালী পাসওয়ার্ড"
passwordMatched: "মিলেছে"
passwordNotMatched: "মিলেনি"
signinWith: "{x} এর সাহায্যে সাইন ইন করুন"
signinFailed: "লগ ইন করা যায়নি। আপনার ব্যবহারকারীর নাম এবং পাসওয়ার্ড চেক করুন."
tapSecurityKey: "সিকিউরিটি কী স্পর্শ করুন"
or: "অথবা"

View file

@ -277,9 +277,6 @@ dayX: "{day}"
monthX: "{month}"
yearX: "{year}"
pages: "Stránky"
integration: "Integrace"
connectService: "Připojit"
disconnectService: "Odpojit"
enableLocalTimeline: "Povolit lokální čas"
enableGlobalTimeline: "Povolit globální čas"
enableRegistration: "Povolit registraci novým uživatelům"
@ -353,7 +350,6 @@ normalPassword: "Dobré heslo"
strongPassword: "Silné heslo"
passwordMatched: "Hesla se schodují"
passwordNotMatched: "Hesla se neschodují"
signinWith: "Přihlásit se s {x}"
signinFailed: "Nelze se přihlásit. Zkontrolujte prosím své uživatelské jméno a heslo."
or: "Nebo"
language: "Jazyk"

View file

@ -321,9 +321,6 @@ dayX: "{day}"
monthX: "{month}"
yearX: "{year}"
pages: "Seiten"
integration: "Integration"
connectService: "Verbinden"
disconnectService: "Trennen"
enableLocalTimeline: "Lokale Chronik aktivieren"
enableGlobalTimeline: "Globale Chronik aktivieren"
disablingTimelinesInfo: "Administratoren und Moderatoren haben immer Zugriff auf alle\
@ -433,7 +430,6 @@ normalPassword: "Durchschnittliches Passwort"
strongPassword: "Starkes Passwort"
passwordMatched: "Stimmt überein"
passwordNotMatched: "Stimmt nicht überein"
signinWith: "Mit {x} anmelden"
signinFailed: "Anmeldung fehlgeschlagen. Überprüfe Benutzername und Passswort."
tapSecurityKey: "Tippe deinen Sicherheitsschlüssel an"
or: "Oder"
@ -1373,18 +1369,6 @@ recommended: "Empfehlung"
check: "Check"
maxCustomEmojiPicker: Maximale Anzahl vorgeschlagener benutzerdefinierter Emoji
maxUnicodeEmojiPicker: Maximale Anzahl vorgeschlagener Unicode-Emoji
_services:
_discord:
connected: 'Discord: @{username}#{discriminator} wurde mit Foundkey-Account @{mkUsername}
verknüpft!'
disconnected: Discord-Verknüpfung wurde entfernt.
_twitter:
connected: Twitter-Account @{twitterUserName} wurde mit Foundkey-Account @{userName}
verknüpft!
disconnected: Twitter-Verknüpfung wurde entfernt.
_github:
connected: GitHub-Account @{login} wurde mit Foundkey-Account @{userName} verknüpft!
disconnected: GitHub-Verknüpfung wurde entfernt.
documentation: Dokumentation
signinHistoryExpires: Frühere Login-Versuche werden aus Datenschutzgründen nach 60
Tagen automatisch gelöscht.

View file

@ -96,6 +96,8 @@ unfollow: "Unfollow"
followRequestPending: "Follow request pending"
renote: "Renote"
unrenote: "Take back renote"
unrenoteAll: "Take back all renotes"
unrenoteAllConfirm: "Are you sure that you want to take back all renotes of this note?"
quote: "Quote"
pinnedNote: "Pinned note"
you: "You"
@ -311,9 +313,6 @@ dayX: "{day}"
monthX: "{month}"
yearX: "{year}"
pages: "Pages"
integration: "Integration"
connectService: "Connect"
disconnectService: "Disconnect"
enableLocalTimeline: "Enable local timeline"
enableGlobalTimeline: "Enable global timeline"
disablingTimelinesInfo: "Adminstrators and Moderators will always have access to all\
@ -420,7 +419,6 @@ normalPassword: "Average password"
strongPassword: "Strong password"
passwordMatched: "Matches"
passwordNotMatched: "Does not match"
signinWith: "Sign in with {x}"
signinFailed: "Unable to sign in. The entered username or password is incorrect."
tapSecurityKey: "Tap your security key"
or: "Or"
@ -503,6 +501,7 @@ scratchpadDescription: "The Scratchpad provides an environment for AiScript expe
\ in it."
output: "Output"
updateRemoteUser: "Update remote user information"
deleteAllFiles: "Delete all files"
deleteAllFilesConfirm: "Are you sure that you want to delete all files?"
removeAllFollowing: "Unfollow all followed users"
removeAllFollowingDescription: "Executing this unfollows all accounts from {host}.\
@ -680,7 +679,6 @@ editCode: "Edit code"
apply: "Apply"
receiveAnnouncementFromInstance: "Receive notifications from this instance"
emailNotification: "Email notifications"
publish: "Publish"
useReactionPickerForContextMenu: "Open reaction picker on right-click"
typingUsers: "{users} is/are typing..."
jumpToSpecifiedDate: "Jump to specific date"
@ -721,11 +719,7 @@ switch: "Switch"
noMaintainerInformationWarning: "Maintainer information is not configured."
noBotProtectionWarning: "Bot protection is not configured."
configure: "Configure"
postToGallery: "Create new gallery post"
attachmentRequired: "At least 1 attachment is required."
gallery: "Gallery"
recentPosts: "Recent posts"
popularPosts: "Popular posts"
shareWithNote: "Share with note"
emailNotConfiguredWarning: "Email address not set."
ratio: "Ratio"
@ -864,11 +858,6 @@ _forgotPassword:
\ instance administrator instead."
contactAdmin: "This instance does not support using email addresses, please contact\
\ the instance administrator to reset your password instead."
_gallery:
my: "My Gallery"
liked: "Liked Posts"
like: "Like"
unlike: "Remove like"
_email:
_follow:
title: "You've got a new follower"
@ -1099,6 +1088,7 @@ _permissions:
"write:notes": "Create and delete notes"
"read:notifications": "Read notifications"
"write:notifications": "Mark notifications as read and create custom notifications"
"read:reactions": "View reactions"
"write:reactions": "Create and delete reactions"
"write:votes": "Vote in polls"
"read:pages": "List and read pages"
@ -1109,10 +1099,6 @@ _permissions:
"write:user-groups": "Create, modify, delete, transfer, join and leave groups. Invite and ban others from groups. Accept and reject group invitations."
"read:channels": "List and read followed and joined channels"
"write:channels": "Create, modify, follow and unfollow channels"
"read:gallery": "List and read gallery posts"
"write:gallery": "Create, modify and delete gallery posts"
"read:gallery-likes": "List and read gallery post likes"
"write:gallery-likes": "Like and unlike gallery posts"
_auth:
shareAccess: "Would you like to authorize \"{name}\" to access this account?"
shareAccessAsk: "Are you sure you want to authorize this application to access your\
@ -1344,16 +1330,6 @@ _deck:
list: "List"
mentions: "Mentions"
direct: "Direct notes"
_services:
_discord:
connected: "Discord: @{username}#{discriminator} connected to FoundKey: @{mkUsername}!"
disconnected: "Discord linkage has been removed."
_twitter:
connected: "Twitter: @{twitterUserName} connected to FoundKey: @{userName}!"
disconnected: "Twitter linkage has been removed."
_github:
connected: "GitHub: @{login} connected to FoundKey: @{userName}!"
disconnected: "GitHub linkage has been removed."
_translationService:
_deepl:
authKey: "DeepL Auth Key"

View file

@ -311,9 +311,6 @@ dayX: "Día {day}"
monthX: "Mes {month}"
yearX: "Año {year}"
pages: "Páginas"
integration: "Integración"
connectService: "Conectar"
disconnectService: "Desconectar"
enableLocalTimeline: "Habilitar linea de tiempo local"
enableGlobalTimeline: "Habilitar linea de tiempo global"
disablingTimelinesInfo: "Aunque se desactiven estas lineas de tiempo, por conveniencia\
@ -420,7 +417,6 @@ normalPassword: "Buena contraseña"
strongPassword: "Muy buena contraseña"
passwordMatched: "Correcto"
passwordNotMatched: "Las contraseñas no son las mismas"
signinWith: "Inicie sesión con {x}"
signinFailed: "Autenticación fallida. Asegúrate de haber usado el nombre de usuario\
\ y contraseña correctos."
tapSecurityKey: "Toque la clave de seguridad"

View file

@ -311,9 +311,6 @@ dayX: "{day}"
monthX: "{month}"
yearX: "{year}"
pages: "Pages"
integration: "Intégrations"
connectService: "Connexion"
disconnectService: "Déconnexion"
enableLocalTimeline: "Activer le fil local"
enableGlobalTimeline: "Activer le fil global"
disablingTimelinesInfo: "Même si vous désactivez ces fils, les administrateur·rice·s\
@ -423,7 +420,6 @@ normalPassword: "Mot de passe acceptable"
strongPassword: "Mot de passe fort"
passwordMatched: "Les mots de passe correspondent"
passwordNotMatched: "Les mots de passe ne correspondent pas"
signinWith: "Se connecter avec {x}"
signinFailed: "Échec dauthentification. Veuillez vérifier que votre nom dutilisateur\
\ et mot de passe sont corrects."
tapSecurityKey: "Appuyez sur votre clé de sécurité"
@ -1331,18 +1327,6 @@ _deck:
list: "Listes"
mentions: "Mentions"
direct: "Direct"
_services:
_discord:
connected: '@{username}#{discriminator} sur Discord est connecté à @{mkUsername}
sur FoundKey !'
disconnected: La liaison avec Discord à été supprimée.
_twitter:
connected: '@{twitterUserName} sur Twitter est connecté à @{userName} sur FoundKey
!'
disconnected: La liaison avec Twitter à été supprimée.
_github:
disconnected: La liaison avec Github à été supprimée.
connected: '@{login} sur Github est connecté à @{userName} sur FoundKey !'
exportAll: Tout exporter
stopActivityDeliveryDescription: L'activité locale ne sera pas envoyé à cette instance.
La réception de l'activité continuera de fonctionner comme avant.

View file

@ -310,9 +310,6 @@ dayX: "{day}"
monthX: "{month}"
yearX: "{year}"
pages: "Halaman"
integration: "Integrasi"
connectService: "Sambungkan"
disconnectService: "Putuskan"
enableLocalTimeline: "Nyalakan linimasa lokal"
enableGlobalTimeline: "Nyalakan linimasa global"
disablingTimelinesInfo: "Admin dan Moderator akan selalu memiliki akses ke semua linimasa\
@ -419,7 +416,6 @@ normalPassword: "Kata sandi baik"
strongPassword: "Kata sandi kuat"
passwordMatched: "Kata sandi sama"
passwordNotMatched: "Kata sandi tidak sama"
signinWith: "Masuk dengan {x}"
signinFailed: "Tidak dapat masuk. Nama pengguna atau kata sandi yang kamu masukkan\
\ salah."
tapSecurityKey: "Ketuk kunci keamanan kamu"

View file

@ -304,9 +304,6 @@ dayX: "{day}"
monthX: "{month}"
yearX: "{year}"
pages: "Pagine"
integration: "App collegate"
connectService: "Connessione"
disconnectService: "Disconnessione "
enableLocalTimeline: "Abilita Timeline locale"
enableGlobalTimeline: "Abilita Timeline federata"
disablingTimelinesInfo: "Anche se disabiliti queste timeline, gli amministratori e\
@ -413,7 +410,6 @@ normalPassword: "Password buona"
strongPassword: "Password forte"
passwordMatched: "Corretta"
passwordNotMatched: "Le password non corrispondono."
signinWith: "Accedi con {x}"
signinFailed: "Autenticazione non riuscita. Controlla la tua password e nome utente."
tapSecurityKey: "Premi la chiave di sicurezza"
or: "oppure"

View file

@ -286,9 +286,6 @@ dayX: "{day}日"
monthX: "{month}月"
yearX: "{year}年"
pages: "ページ"
integration: "連携"
connectService: "接続する"
disconnectService: "切断する"
enableLocalTimeline: "ローカルタイムラインを有効にする"
enableGlobalTimeline: "グローバルタイムラインを有効にする"
disablingTimelinesInfo: "これらのタイムラインを無効化しても、利便性のため管理者およびモデレーターは引き続き利用することができます。"
@ -392,7 +389,6 @@ normalPassword: "普通のパスワード"
strongPassword: "強いパスワード"
passwordMatched: "一致しました"
passwordNotMatched: "一致していません"
signinWith: "{x}でログイン"
signinFailed: "ログインできませんでした。ユーザー名とパスワードを確認してください。"
tapSecurityKey: "セキュリティキーにタッチ"
or: "もしくは"
@ -1273,13 +1269,3 @@ _deck:
list: "リスト"
mentions: "あなた宛て"
direct: "ダイレクト"
_services:
_discord:
connected: "Discord: @{username}#{discriminator} を、FoundKey: @{mkUsername} に接続しました!"
disconnected: "Discordの連携を解除しました :v:"
_twitter:
connected: "Twitter: @{twitterUserName} を、FoundKey: @{userName} に接続しました!"
disconnected: "Twitterの連携を解除しました :v:"
_github:
connected: "GitHub: @{login} を、FoundKey: @{userName} に接続しました!"
disconnected: "GitHubの連携を解除しました :v:"

View file

@ -288,7 +288,6 @@ dayX: "{day}日"
monthX: "{month}月"
yearX: "{year}年"
pages: "ページ"
integration: "連携"
enableLocalTimeline: "ローカルタイムラインを使えるようにする"
enableGlobalTimeline: "グローバルタイムラインを使えるようにする"
disablingTimelinesInfo: "ここらへんのタイムラインを使えんようにしてしもても、管理者とモデレーターは使えるままになってるで、そうやなかったら不便やからな。"
@ -391,7 +390,6 @@ normalPassword: "普通のパスワード"
strongPassword: "ええ感じのパスワード"
passwordMatched: "よし!一致や!"
passwordNotMatched: "一致しとらんで?"
signinWith: "{x}でログイン"
or: "それか"
language: "言語"
uiLanguage: "UIの表示言語"

View file

@ -39,7 +39,6 @@ userList: "Tibdarin"
securityKey: "Tasarutt n tɣellist"
securityKeyName: "Isem n tsarutt"
signinRequired: "Ttxil jerred"
signinWith: "Tuqqna s {x}"
tapSecurityKey: "Sekcem tasarutt-ik·im n tɣellist"
uiLanguage: "Tutlayt n wegrudem"
plugins: "Izegrar"

View file

@ -315,9 +315,6 @@ dayX: "{day}일"
monthX: "{month}월"
yearX: "{year}년"
pages: "페이지"
integration: "연동"
connectService: "계정 연동"
disconnectService: "계정 연동 해제"
enableLocalTimeline: "로컬 타임라인 활성화"
enableGlobalTimeline: "글로벌 타임라인 활성화"
disablingTimelinesInfo: "특정 타임라인을 비활성화하더라도 관리자 및 모더레이터는 계속 사용할 수 있습니다."
@ -438,7 +435,6 @@ normalPassword: "좋은 비밀번호"
strongPassword: "강한 비밀번호"
passwordMatched: "일치합니다"
passwordNotMatched: "일치하지 않습니다"
signinWith: "{x}로 로그인"
signinFailed: "로그인할 수 없습니다. 사용자명과 비밀번호를 확인하여 주십시오."
tapSecurityKey: "보안 키를 터치"
or: "혹은"

View file

@ -298,9 +298,6 @@ dayX: "{day}"
monthX: "{month}"
yearX: "{year}"
pages: "Strony"
integration: "Integracja"
connectService: "Połącz"
disconnectService: "Rozłącz"
enableLocalTimeline: "Włącz lokalną oś czasu"
enableGlobalTimeline: "Włącz globalną oś czasu"
disablingTimelinesInfo: "Administratorzy i moderatorzy będą zawsze mieć dostęp do\
@ -407,7 +404,6 @@ normalPassword: "Dobre hasło"
strongPassword: "Silne hasło"
passwordMatched: "Pasuje"
passwordNotMatched: "Hasła nie pasują do siebie"
signinWith: "Zaloguj się z {x}"
signinFailed: "Nie udało się zalogować. Wprowadzona nazwa użytkownika lub hasło są\
\ nieprawidłowe."
tapSecurityKey: "Wybierz swój klucz bezpieczeństwa"

View file

@ -311,9 +311,6 @@ dayX: "{day}"
monthX: "{month}"
yearX: "{year}"
pages: "Pagini"
integration: "Integrare"
connectService: "Conectează"
disconnectService: "Deconectează"
enableLocalTimeline: "Activează cronologia locală"
enableGlobalTimeline: "Activeaza cronologia globală"
disablingTimelinesInfo: "Administratorii și Moderatorii vor avea mereu access la toate\
@ -420,7 +417,6 @@ normalPassword: "Parolă medie"
strongPassword: "Parolă puternică"
passwordMatched: "Se potrivește!"
passwordNotMatched: "Nu se potrivește"
signinWith: "Autentifică-te cu {x}"
signinFailed: "Nu se poate autentifica. Numele de utilizator sau parola introduse\
\ sunt incorecte."
tapSecurityKey: "Apasă pe cheia ta de securitate."

View file

@ -304,9 +304,6 @@ dayX: "{day} день"
monthX: "{month} месяц"
yearX: "{year} год"
pages: "Страницы"
integration: "Интеграция"
connectService: "Подключиться"
disconnectService: "Отключиться"
enableLocalTimeline: "Включить локальную ленту"
enableGlobalTimeline: "Включить глобальную ленту"
disablingTimelinesInfo: "У администраторов и модераторов есть доступ ко всем лентам,\
@ -415,7 +412,6 @@ normalPassword: "Годный пароль"
strongPassword: "Надёжный пароль"
passwordMatched: "Совпали"
passwordNotMatched: "Не совпадают"
signinWith: "Использовать {x} для входа"
signinFailed: "Невозможно войти в систему. Введенное вами имя пользователя или пароль\
\ неверны."
tapSecurityKey: "Нажмите на свой электронный ключ"

View file

@ -305,9 +305,6 @@ dayX: "{day}"
monthX: "{month}"
yearX: "{year}"
pages: "Stránky"
integration: "Integrácia"
connectService: "Pripojiť"
disconnectService: "Odpojiť"
enableLocalTimeline: "Povoliť lokálnu časovú os"
enableGlobalTimeline: "Povoliť globálnu časovú os"
disablingTimelinesInfo: "Administrátori a moderátori majú vždy prístup ku všetkým\
@ -414,7 +411,6 @@ normalPassword: "Dobré heslo"
strongPassword: "Silné heslo"
passwordMatched: "Heslá sú rovnaké"
passwordNotMatched: "Heslá nie sú rovnaké"
signinWith: "Prihlásiť sa použitím {x}"
signinFailed: "Nedá sa prihlásiť. Skontrolujte prosím meno používateľa a heslo."
tapSecurityKey: "Ťuknite na bezpečnostný kľúč"
or: "Alebo"

View file

@ -305,9 +305,6 @@ dayX: "{day}"
monthX: "{month}"
yearX: "{year}"
pages: "Сторінки"
integration: "Інтеграція"
connectService: "Під’єднати"
disconnectService: "Відключитися"
enableLocalTimeline: "Увімкнути локальну стрічку"
enableGlobalTimeline: "Увімкнути глобальну стрічку"
disablingTimelinesInfo: "Адміністратори та модератори завжди мають доступ до всіх\
@ -414,7 +411,6 @@ normalPassword: "Достатній пароль"
strongPassword: "Міцний пароль"
passwordMatched: "Все вірно"
passwordNotMatched: "Паролі не співпадають"
signinWith: "Увійти за допомогою {x}"
signinFailed: "Не вдалося увійти. Введені ім’я користувача або пароль неправильнi."
tapSecurityKey: "Торкніться ключа безпеки"
or: "або"

View file

@ -305,9 +305,6 @@ dayX: "{day}"
monthX: "{month}"
yearX: "{year}"
pages: "Trang"
integration: "Tương tác"
connectService: "Kết nối"
disconnectService: "Ngắt kết nối"
enableLocalTimeline: "Bật bảng tin máy chủ"
enableGlobalTimeline: "Bật bảng tin liên hợp"
disablingTimelinesInfo: "Quản trị viên và Kiểm duyệt viên luôn có quyền truy cập mọi\
@ -415,7 +412,6 @@ normalPassword: "Mật khẩu tạm được"
strongPassword: "Mật khẩu mạnh"
passwordMatched: "Trùng khớp"
passwordNotMatched: "Không trùng khớp"
signinWith: "Đăng nhập bằng {x}"
signinFailed: "Không thể đăng nhập. Vui lòng kiểm tra tên người dùng và mật khẩu của\
\ bạn."
tapSecurityKey: "Nhấn mã bảo mật của bạn"

View file

@ -284,9 +284,6 @@ dayX: "{day}日"
monthX: "{month}月"
yearX: "{year}年"
pages: "页面"
integration: "关联"
connectService: "连接"
disconnectService: "断开连接"
enableLocalTimeline: "启用本地时间线功能"
enableGlobalTimeline: "启用全局时间线"
disablingTimelinesInfo: "即使时间线功能被禁用,出于方便,管理员和数据图表也可以继续使用。"
@ -390,7 +387,6 @@ normalPassword: "密码强度:中等"
strongPassword: "密码强度:强"
passwordMatched: "密码一致"
passwordNotMatched: "密码不一致"
signinWith: "以{x}登录"
signinFailed: "无法登录,请检查您的用户名和密码是否正确。"
tapSecurityKey: "轻触硬件安全密钥"
or: "或者"

View file

@ -284,9 +284,6 @@ dayX: "{day}日"
monthX: "{month}月"
yearX: "{year}年"
pages: "頁面"
integration: "整合"
connectService: "己連結"
disconnectService: "己斷開 "
enableLocalTimeline: "開啟本地時間軸"
enableGlobalTimeline: "啟用公開時間軸"
disablingTimelinesInfo: "即使您關閉了時間線功能,管理員和協調人仍可以繼續使用,以方便您。"
@ -390,7 +387,6 @@ normalPassword: "密碼強度普通"
strongPassword: "密碼強度高"
passwordMatched: "密碼一致"
passwordNotMatched: "密碼不一致"
signinWith: "以{x}登錄"
signinFailed: "登入失敗。 請檢查使用者名稱和密碼。"
tapSecurityKey: "點擊安全密鑰"
or: "或者"

View file

@ -10,7 +10,8 @@
"packages/*"
],
"scripts": {
"build": "yarn workspaces foreach --parallel --topological run build && yarn run gulp",
"build": "yarn workspaces foreach --topological run build && yarn run gulp",
"build-parallel": "yarn workspaces foreach --parallel --topological run build && yarn run gulp",
"start": "yarn workspace backend run start",
"start:test": "yarn workspace backend run start:test",
"init": "yarn migrate",
@ -18,7 +19,7 @@
"migrateandstart": "yarn migrate && yarn start",
"gulp": "gulp build",
"watch": "yarn dev",
"dev": "node ./scripts/dev.js",
"dev": "node ./scripts/dev.mjs",
"lint": "yarn workspaces foreach run lint",
"cy:open": "cypress open --browser --e2e --config-file=cypress.config.ts",
"cy:run": "cypress run",
@ -26,8 +27,8 @@
"mocha": "yarn workspace backend run mocha",
"test": "yarn mocha",
"format": "gulp format",
"clean": "node ./scripts/clean.js",
"clean-all": "node ./scripts/clean-all.js",
"clean": "node ./scripts/clean.mjs",
"clean-all": "node ./scripts/clean-all.mjs",
"cleanall": "yarn clean-all"
},
"resolutions": {
@ -35,6 +36,7 @@
"lodash": "^4.17.21"
},
"dependencies": {
"argon2": "^0.30.2",
"execa": "5.1.1",
"gulp": "4.0.2",
"gulp-cssnano": "2.1.3",
@ -46,11 +48,11 @@
"devDependencies": {
"@types/gulp": "4.0.9",
"@types/gulp-rename": "2.0.1",
"@typescript-eslint/parser": "^5.44.0",
"@typescript-eslint/parser": "^5.46.1",
"cross-env": "7.0.3",
"cypress": "10.3.0",
"start-server-and-test": "1.14.0",
"typescript": "^4.9.3"
"typescript": "^4.9.4"
},
"packageManager": "yarn@3.3.0"
}

View file

@ -6,7 +6,11 @@ module.exports = {
extends: [
'../shared/.eslintrc.js',
],
plugins: [
'foundkey-custom-rules',
],
rules: {
'foundkey-custom-rules/typeorm-prefer-count': 'error',
'import/order': ['warn', {
'groups': ['builtin', 'external', 'internal', 'parent', 'sibling', 'index', 'object', 'type'],
'pathGroups': [

View file

@ -0,0 +1,29 @@
export class removeIntegrations1670359028055 {
name = 'removeIntegrations1670359028055'
async up(queryRunner) {
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "enableTwitterIntegration"`);
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "twitterConsumerKey"`);
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "twitterConsumerSecret"`);
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "enableGithubIntegration"`);
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "githubClientId"`);
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "githubClientSecret"`);
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "enableDiscordIntegration"`);
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "discordClientId"`);
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "discordClientSecret"`);
await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "integrations"`);
}
async down(queryRunner) {
await queryRunner.query(`ALTER TABLE "user_profile" ADD "integrations" jsonb NOT NULL DEFAULT '{}'`);
await queryRunner.query(`ALTER TABLE "meta" ADD "discordClientSecret" character varying(128)`);
await queryRunner.query(`ALTER TABLE "meta" ADD "discordClientId" character varying(128)`);
await queryRunner.query(`ALTER TABLE "meta" ADD "enableDiscordIntegration" boolean NOT NULL DEFAULT false`);
await queryRunner.query(`ALTER TABLE "meta" ADD "githubClientSecret" character varying(128)`);
await queryRunner.query(`ALTER TABLE "meta" ADD "githubClientId" character varying(128)`);
await queryRunner.query(`ALTER TABLE "meta" ADD "enableGithubIntegration" boolean NOT NULL DEFAULT false`);
await queryRunner.query(`ALTER TABLE "meta" ADD "twitterConsumerSecret" character varying(128)`);
await queryRunner.query(`ALTER TABLE "meta" ADD "twitterConsumerKey" character varying(128)`);
await queryRunner.query(`ALTER TABLE "meta" ADD "enableTwitterIntegration" boolean NOT NULL DEFAULT false`);
}
}

View file

@ -0,0 +1,21 @@
export class removeReversi1672607891750 {
name = 'removeReversi1672607891750';
async up(queryRunner) {
await queryRunner.query(`DROP TABLE "reversi_matching"`);
await queryRunner.query(`DROP TABLE "reversi_game"`);
}
async down(queryRunner) {
await queryRunner.query(`CREATE TABLE "reversi_game" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "startedAt" TIMESTAMP WITH TIME ZONE, "user1Id" character varying(32) NOT NULL, "user2Id" character varying(32) NOT NULL, "user1Accepted" boolean NOT NULL DEFAULT false, "user2Accepted" boolean NOT NULL DEFAULT false, "black" integer, "isStarted" boolean NOT NULL DEFAULT false, "isEnded" boolean NOT NULL DEFAULT false, "winnerId" character varying(32), "surrendered" character varying(32), "logs" jsonb NOT NULL DEFAULT '[]', "map" character varying(64) array NOT NULL, "bw" character varying(32) NOT NULL, "isLlotheo" boolean NOT NULL DEFAULT false, "canPutEverywhere" boolean NOT NULL DEFAULT false, "loopedBoard" boolean NOT NULL DEFAULT false, "form1" jsonb DEFAULT null, "form2" jsonb DEFAULT null, "crc32" character varying(32), CONSTRAINT "PK_76b30eeba71b1193ad7c5311c3f" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE INDEX "IDX_b46ec40746efceac604142be1c" ON "reversi_game" ("createdAt")`);
await queryRunner.query(`CREATE TABLE "reversi_matching" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "parentId" character varying(32) NOT NULL, "childId" character varying(32) NOT NULL, CONSTRAINT "PK_880bd0afbab232f21c8b9d146cf" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE INDEX "IDX_b604d92d6c7aec38627f6eaf16" ON "reversi_matching" ("createdAt")`);
await queryRunner.query(`CREATE INDEX "IDX_3b25402709dd9882048c2bbade" ON "reversi_matching" ("parentId")`);
await queryRunner.query(`CREATE INDEX "IDX_e247b23a3c9b45f89ec1299d06" ON "reversi_matching" ("childId")`);
await queryRunner.query(`ALTER TABLE "reversi_game" ADD CONSTRAINT "FK_f7467510c60a45ce5aca6292743" FOREIGN KEY ("user1Id") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "reversi_game" ADD CONSTRAINT "FK_6649a4e8c5d5cf32fb03b5da9f6" FOREIGN KEY ("user2Id") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "reversi_matching" ADD CONSTRAINT "FK_3b25402709dd9882048c2bbade0" FOREIGN KEY ("parentId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "reversi_matching" ADD CONSTRAINT "FK_e247b23a3c9b45f89ec1299d066" FOREIGN KEY ("childId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
}
}

View file

@ -0,0 +1,23 @@
export class removeUserGroupInvite1672991292018 {
async up(queryRunner) {
await queryRunner.query(`ALTER TABLE "user_group_invite" DROP CONSTRAINT "FK_e10924607d058004304611a436a"`);
await queryRunner.query(`ALTER TABLE "user_group_invite" DROP CONSTRAINT "FK_1039988afa3bf991185b277fe03"`);
await queryRunner.query(`DROP INDEX "IDX_d9ecaed8c6dc43f3592c229282"`);
await queryRunner.query(`DROP INDEX "IDX_78787741f9010886796f2320a4"`);
await queryRunner.query(`DROP INDEX "IDX_e10924607d058004304611a436"`);
await queryRunner.query(`DROP INDEX "IDX_1039988afa3bf991185b277fe0"`);
await queryRunner.query(`DROP TABLE "user_group_invite"`);
}
async down(queryRunner) {
await queryRunner.query(`CREATE TABLE "user_group_invite" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "userId" character varying(32) NOT NULL, "userGroupId" character varying(32) NOT NULL, CONSTRAINT "PK_3893884af0d3a5f4d01e7921a97" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE INDEX "IDX_1039988afa3bf991185b277fe0" ON "user_group_invite" ("userId") `);
await queryRunner.query(`CREATE INDEX "IDX_e10924607d058004304611a436" ON "user_group_invite" ("userGroupId") `);
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_78787741f9010886796f2320a4" ON "user_group_invite" ("userId", "userGroupId") `);
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_d9ecaed8c6dc43f3592c229282" ON "user_group_joining" ("userId", "userGroupId") `);
await queryRunner.query(`ALTER TABLE "user_group_invite" ADD CONSTRAINT "FK_1039988afa3bf991185b277fe03" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "user_group_invite" ADD CONSTRAINT "FK_e10924607d058004304611a436a" FOREIGN KEY ("userGroupId") REFERENCES "user_group"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
}
}

View file

@ -0,0 +1,65 @@
import { genId } from '../built/misc/gen-id.js';
export class removeGroups1673892262930 {
name = 'removeGroups1673892262930';
async up(queryRunner) {
// migrate gallery posts into notes, keeping the ids
await queryRunner.query(`
INSERT INTO "note" (
"id", "createdAt", "text", "cw", "userId", "visibility", "fileIds", "attachedFileTypes", "tags"
)
WITH "file_types" ("id", "types") AS (
SELECT "gallery_post"."id", ARRAY_AGG("drive_file"."type")
FROM "gallery_post"
JOIN "drive_file" ON "drive_file"."id" = ANY("gallery_post"."fileIds")
GROUP BY "gallery_post"."id"
)
SELECT "gallery_post"."id", "gallery_post"."createdAt",
CASE
WHEN "gallery_post"."title" IS NULL THEN "gallery_post"."description"
ELSE '<b>' || "gallery_post"."title" || E'</b>\\n\\n' || "gallery_post"."description"
END,
CASE
WHEN "gallery_post"."isSensitive" THEN 'NSFW'
ELSE NULL
END,
"gallery_post"."userId", 'home', "gallery_post"."fileIds", "file_types"."types", "gallery_post"."tags"
FROM "gallery_post"
JOIN "file_types" ON "gallery_post"."id" = "file_types"."id"
`);
// make a clip for each users gallery
await queryRunner.query(`SELECT DISTINCT "userId" FROM "gallery_post"`).then(userIds =>
Promise.all(userIds.map(({ userId }) => {
const clipId = genId();
// generate the clip itself
return queryRunner.query(`INSERT INTO "clip" ("id", "createdAt", "userId", "name", "isPublic") VALUES ($1, now(), $2, 'Gallery', true)`, [clipId, userId])
// and add all the previous gallery posts to it
// to not have to use genId for each gallery post, we just prepend a zero, something that could never be generated by genId
.then(() => queryRunner.query(`INSERT INTO "clip_note" ("id", "noteId", "clipId") SELECT '0' || "id", "id", $1 FROM "gallery_post" WHERE "userId" = $2`, [clipId, userId]));
}))
);
await queryRunner.query(`DROP TABLE "gallery_like"`);
await queryRunner.query(`DROP TABLE "gallery_post"`);
}
async down(queryRunner) {
// can only restore the table structure
await queryRunner.query(`CREATE TABLE "gallery_post" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL, "title" character varying(256) NOT NULL, "description" character varying(2048), "userId" character varying(32) NOT NULL, "fileIds" character varying(32) array NOT NULL DEFAULT '{}'::varchar[], "isSensitive" boolean NOT NULL DEFAULT false, "likedCount" integer NOT NULL DEFAULT '0', "tags" character varying(128) array NOT NULL DEFAULT '{}'::varchar[], CONSTRAINT "PK_8e90d7b6015f2c4518881b14753" PRIMARY KEY ("id")); COMMENT ON COLUMN "gallery_post"."createdAt" IS 'The created date of the GalleryPost.'; COMMENT ON COLUMN "gallery_post"."updatedAt" IS 'The updated date of the GalleryPost.'; COMMENT ON COLUMN "gallery_post"."userId" IS 'The ID of author.'; COMMENT ON COLUMN "gallery_post"."isSensitive" IS 'Whether the post is sensitive.'`);
await queryRunner.query(`CREATE INDEX "IDX_8f1a239bd077c8864a20c62c2c" ON "gallery_post" ("createdAt") `);
await queryRunner.query(`CREATE INDEX "IDX_f631d37835adb04792e361807c" ON "gallery_post" ("updatedAt") `);
await queryRunner.query(`CREATE INDEX "IDX_985b836dddd8615e432d7043dd" ON "gallery_post" ("userId") `);
await queryRunner.query(`CREATE INDEX "IDX_3ca50563facd913c425e7a89ee" ON "gallery_post" ("fileIds") `);
await queryRunner.query(`CREATE INDEX "IDX_f2d744d9a14d0dfb8b96cb7fc5" ON "gallery_post" ("isSensitive") `);
await queryRunner.query(`CREATE INDEX "IDX_1a165c68a49d08f11caffbd206" ON "gallery_post" ("likedCount") `);
await queryRunner.query(`CREATE INDEX "IDX_05cca34b985d1b8edc1d1e28df" ON "gallery_post" ("tags") `);
await queryRunner.query(`CREATE TABLE "gallery_like" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "userId" character varying(32) NOT NULL, "postId" character varying(32) NOT NULL, CONSTRAINT "PK_853ab02be39b8de45cd720cc15f" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE INDEX "IDX_8fd5215095473061855ceb948c" ON "gallery_like" ("userId") `);
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_df1b5f4099e99fb0bc5eae53b6" ON "gallery_like" ("userId", "postId") `);
await queryRunner.query(`ALTER TABLE "gallery_post" ADD CONSTRAINT "FK_985b836dddd8615e432d7043ddb" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "gallery_like" ADD CONSTRAINT "FK_8fd5215095473061855ceb948cf" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "gallery_like" ADD CONSTRAINT "FK_b1cb568bfe569e47b7051699fc8" FOREIGN KEY ("postId") REFERENCES "gallery_post"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
}
}

View file

@ -0,0 +1,17 @@
export class syncOrm1674499888924 {
name = 'syncOrm1674499888924'
async up(queryRunner) {
await queryRunner.query(`COMMENT ON COLUMN "user"."token" IS 'The native access token of local users, or null.'`);
await queryRunner.query(`ALTER TABLE "auth_session" DROP CONSTRAINT "UQ_8e001e5a101c6dca37df1a76d66"`);
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_71d35fceee0d0fa62b2fa8f3b2" ON "note" ("url") `);
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_d9ecaed8c6dc43f3592c229282" ON "user_group_joining" ("userId", "userGroupId") `);
}
async down(queryRunner) {
await queryRunner.query(`DROP INDEX "public"."IDX_d9ecaed8c6dc43f3592c229282"`);
await queryRunner.query(`DROP INDEX "public"."IDX_71d35fceee0d0fa62b2fa8f3b2"`);
await queryRunner.query(`ALTER TABLE "auth_session" ADD CONSTRAINT "UQ_8e001e5a101c6dca37df1a76d66" UNIQUE ("accessTokenId")`);
await queryRunner.query(`COMMENT ON COLUMN "user"."token" IS 'The native access token of the User. It will be null if the origin of the user is local.'`);
}
}

View file

@ -0,0 +1,19 @@
export class registryRemoveDomain1675375940759 {
name = 'registryRemoveDomain1675375940759'
async up(queryRunner) {
await queryRunner.query(`DROP INDEX "public"."IDX_0a72bdfcdb97c0eca11fe7ecad"`);
await queryRunner.query(`ALTER TABLE "registry_item" DROP COLUMN "domain"`);
await queryRunner.query(`ALTER TABLE "registry_item" ALTER COLUMN "key" TYPE text USING "key"::text`);
// delete existing duplicated entries, keeping the latest updated one
await queryRunner.query(`DELETE FROM "registry_item" AS "a" WHERE "updatedAt" != (SELECT MAX("updatedAt") OVER (PARTITION BY "userId", "key", "scope") FROM "registry_item" AS "b" WHERE "a"."userId" = "b"."userId" AND "a"."key" = "b"."key" AND "a"."scope" = "b"."scope")`);
await queryRunner.query(`ALTER TABLE "registry_item" ADD CONSTRAINT "UQ_b8d6509f847331273ab99daccc7" UNIQUE ("userId", "key", "scope")`);
}
async down(queryRunner) {
await queryRunner.query(`ALTER TABLE "registry_item" DROP CONSTRAINT "UQ_b8d6509f847331273ab99daccc7"`);
await queryRunner.query(`ALTER TABLE "registry_item" ALTER COLUMN "key" TYPE character varying(1024) USING "key"::varchar(1024)`);
await queryRunner.query(`ALTER TABLE "registry_item" ADD "domain" character varying(512)`);
await queryRunner.query(`CREATE INDEX "IDX_0a72bdfcdb97c0eca11fe7ecad" ON "registry_item" ("domain") `);
}
}

View file

@ -7,7 +7,7 @@
"scripts": {
"build": "tsc -p tsconfig.json || echo done. && tsc-alias -p tsconfig.json",
"watch": "node watch.mjs",
"lint": "eslint src --ext .ts",
"lint": "tsc --noEmit --skipLibCheck && eslint src --ext .ts",
"mocha": "cross-env NODE_ENV=test TS_NODE_FILES=true TS_NODE_TRANSPILE_ONLY=true TS_NODE_PROJECT=\"./test/tsconfig.json\" mocha",
"migrate": "npx typeorm migration:run -d ormconfig.js",
"start": "node --experimental-json-modules ./built/index.js",
@ -29,7 +29,6 @@
"ajv": "8.11.0",
"archiver": "5.3.1",
"autobind-decorator": "2.4.0",
"autwh": "0.1.0",
"aws-sdk": "2.1165.0",
"bcryptjs": "2.4.3",
"blurhash": "1.1.5",
@ -92,7 +91,6 @@
"reflect-metadata": "0.1.13",
"rename": "1.0.4",
"require-all": "3.0.0",
"rndstr": "1.0.0",
"rss-parser": "3.12.0",
"sanitize-html": "2.7.0",
"semver": "7.3.7",
@ -115,7 +113,6 @@
"unzipper": "0.10.11",
"uuid": "8.3.2",
"web-push": "3.5.0",
"websocket": "1.0.34",
"ws": "8.8.0",
"xev": "3.0.2"
},
@ -124,6 +121,7 @@
"@types/bcryptjs": "2.4.2",
"@types/bull": "3.15.8",
"@types/cbor": "6.0.0",
"@types/color-convert": "^2.0.0",
"@types/escape-regexp": "0.0.1",
"@types/fluent-ffmpeg": "2.1.20",
"@types/is-url": "1.2.30",
@ -146,7 +144,6 @@
"@types/node": "18.7.16",
"@types/node-fetch": "3.0.3",
"@types/nodemailer": "6.4.5",
"@types/oauth": "^0.9.1",
"@types/pg": "^8.6.5",
"@types/pug": "2.0.6",
"@types/punycode": "2.1.0",
@ -161,20 +158,21 @@
"@types/sinon": "^10.0.13",
"@types/sinonjs__fake-timers": "8.1.2",
"@types/speakeasy": "2.0.7",
"@types/syslog-pro": "^1.0.0",
"@types/tinycolor2": "1.4.3",
"@types/tmp": "0.2.3",
"@types/uuid": "8.3.4",
"@types/web-push": "3.3.2",
"@types/websocket": "1.0.5",
"@types/ws": "8.5.3",
"@typescript-eslint/eslint-plugin": "^5.44.0",
"@typescript-eslint/parser": "^5.44.0",
"@typescript-eslint/eslint-plugin": "^5.46.1",
"@typescript-eslint/parser": "^5.46.1",
"cross-env": "7.0.3",
"eslint": "^8.28.0",
"eslint": "^8.29.0",
"eslint-plugin-foundkey-custom-rules": "file:../shared/custom-rules",
"eslint-plugin-import": "^2.26.0",
"execa": "6.1.0",
"form-data": "^4.0.0",
"sinon": "^14.0.2",
"typescript": "^4.9.3"
"typescript": "^4.9.4"
}
}

View file

@ -17,7 +17,7 @@ const ev = new Xev();
/**
* Init process
*/
export default async function(): Promise<void> {
export async function boot(): Promise<void> {
process.title = `FoundKey (${cluster.isPrimary ? 'master' : 'worker'})`;
if (cluster.isPrimary || envOption.disableClustering) {

View file

@ -140,7 +140,7 @@ async function connectDb(): Promise<void> {
}
async function spawnWorkers(clusterLimits: Required<Config['clusterLimits']>): Promise<void> {
const modes = ['web', 'queue'];
const modes = ['web' as const, 'queue' as const];
const cpus = os.cpus().length;
for (const mode of modes.filter(mode => clusterLimits[mode] > cpus)) {
bootLogger.warn(`configuration warning: cluster limit for ${mode} exceeds number of cores (${cpus})`);
@ -153,7 +153,7 @@ async function spawnWorkers(clusterLimits: Required<Config['clusterLimits']>): P
bootLogger.info(`Starting ${total} workers...`);
await Promise.all(workers.map(mode => spawnWorker(mode)));
bootLogger.succ(`All workers started`);
bootLogger.succ('All workers started');
}
function spawnWorker(mode: 'web' | 'queue'): Promise<void> {

View file

@ -38,6 +38,12 @@ export default function load(): Config {
config.port = config.port || parseInt(process.env.PORT || '', 10);
config.images = Object.assign({
info: '/twemoji/1f440.svg',
notFound: '/twemoji/2049.svg',
error: '/twemoji/1f480.svg',
}, config.images ?? {});
if (!config.maxNoteTextLength) config.maxNoteTextLength = 3000;
mixin.version = meta.version;

View file

@ -59,7 +59,7 @@ export type Source = {
deliverJobMaxAttempts?: number;
inboxJobMaxAttempts?: number;
syslog: {
syslog?: {
host: string;
port: number;
};
@ -67,6 +67,12 @@ export type Source = {
mediaProxy?: string;
proxyRemoteFiles?: boolean;
internalStoragePath?: string;
images?: {
info?: string;
notFound?: string;
error?: string;
};
};
/**

View file

@ -1,6 +1,5 @@
// https://github.com/typeorm/typeorm/issues/2400
import pg from 'pg';
import { SECOND } from '@/const.js';
pg.types.setTypeParser(20, Number);
@ -8,6 +7,7 @@ import { Logger, DataSource } from 'typeorm';
import * as highlight from 'cli-highlight';
import config from '@/config/index.js';
import { SECOND } from '@/const.js';
import { User } from '@/models/entities/user.js';
import { DriveFile } from '@/models/entities/drive-file.js';
import { DriveFolder } from '@/models/entities/drive-folder.js';
@ -50,8 +50,6 @@ import { UserSecurityKey } from '@/models/entities/user-security-key.js';
import { AttestationChallenge } from '@/models/entities/attestation-challenge.js';
import { Page } from '@/models/entities/page.js';
import { PageLike } from '@/models/entities/page-like.js';
import { GalleryPost } from '@/models/entities/gallery-post.js';
import { GalleryLike } from '@/models/entities/gallery-like.js';
import { ModerationLog } from '@/models/entities/moderation-log.js';
import { UsedUsername } from '@/models/entities/used-username.js';
import { Announcement } from '@/models/entities/announcement.js';
@ -78,33 +76,33 @@ import { redisClient } from './redis.js';
const sqlLogger = dbLogger.createSubLogger('sql', 'gray', false);
class MyCustomLogger implements Logger {
private highlight(sql: string) {
private highlight(sql: string): string {
return highlight.highlight(sql, {
language: 'sql', ignoreIllegals: true,
});
}
public logQuery(query: string, parameters?: any[]) {
public logQuery(query: string): void {
sqlLogger.info(this.highlight(query).substring(0, 100));
}
public logQueryError(error: string, query: string, parameters?: any[]) {
public logQueryError(error: string, query: string): void {
sqlLogger.error(this.highlight(query));
}
public logQuerySlow(time: number, query: string, parameters?: any[]) {
public logQuerySlow(time: number, query: string): void {
sqlLogger.warn(this.highlight(query));
}
public logSchemaBuild(message: string) {
public logSchemaBuild(message: string): void {
sqlLogger.info(message);
}
public log(message: string) {
public log(message: string): void {
sqlLogger.info(message);
}
public logMigration(message: string) {
public logMigration(message: string): void {
sqlLogger.info(message);
}
}
@ -143,8 +141,6 @@ export const entities = [
NoteUnread,
Page,
PageLike,
GalleryPost,
GalleryLike,
DriveFile,
DriveFolder,
Poll,

View file

@ -3,7 +3,7 @@
*/
import { EventEmitter } from 'node:events';
import boot from '@/boot/index.js';
import { boot } from '@/boot/index.js';
Error.stackTraceLimit = Infinity;
EventEmitter.defaultMaxListeners = 128;

View file

@ -72,7 +72,7 @@ export function fromHtml(html: string, quoteUri?: string | null): string {
const href = getAttr(node, 'href');
// hashtags
if (txt.startsWith('#') && href && (attrHas(node, 'rel', 'tag') || attrHas(node, 'class', 'hashtag')) {
if (txt.startsWith('#') && href && (attrHas(node, 'rel', 'tag') || attrHas(node, 'class', 'hashtag'))) {
text += txt;
// mentions
} else if (txt.startsWith('@') && !attrHas(node, 'rel', 'me')) {

View file

@ -27,9 +27,5 @@ export const kinds = [
'write:user-groups',
'read:channels',
'write:channels',
'read:gallery',
'write:gallery',
'read:gallery-likes',
'write:gallery-likes',
];
// IF YOU ADD KINDS(PERMISSIONS), YOU MUST ADD TRANSLATIONS (under _permissions).

View file

@ -8,10 +8,7 @@ import { SECOND } from '@/const.js';
*/
const retryDelay = 100;
const lock: (key: string, timeout?: number) => Promise<() => void>
= redisClient
? promisify(redisLock(redisClient, retryDelay))
: async () => () => { };
const lock: (key: string, timeout?: number) => Promise<() => void> = promisify(redisLock(redisClient, retryDelay));
/**
* Get AP Object lock

View file

@ -1,7 +1,7 @@
export class Cache<T> {
public cache: Map<string | null, { date: number; value: T; }>;
public cache: Map<string, { date: number; value: T; }>;
private lifetime: number;
public fetcher: (key: string | null) => Promise<T | undefined>;
public fetcher: (key: string) => Promise<T | undefined>;
constructor(lifetime: number, fetcher: Cache<T>['fetcher']) {
this.cache = new Map();
@ -9,14 +9,14 @@ export class Cache<T> {
this.fetcher = fetcher;
}
public set(key: string | null, value: T): void {
public set(key: string, value: T): void {
this.cache.set(key, {
date: Date.now(),
value,
});
}
public get(key: string | null): T | undefined {
public get(key: string): T | undefined {
const cached = this.cache.get(key);
if (cached == null) return undefined;
@ -29,7 +29,7 @@ export class Cache<T> {
return cached.value;
}
public delete(key: string | null): void {
public delete(key: string): void {
this.cache.delete(key);
}
@ -38,7 +38,7 @@ export class Cache<T> {
* run to get the value. If the fetcher returns undefined, it is
* returned but not cached.
*/
public async fetch(key: string | null): Promise<T | undefined> {
public async fetch(key: string): Promise<T | undefined> {
const cached = this.get(key);
if (cached !== undefined) {
return cached;

View file

@ -22,7 +22,7 @@ export async function checkHitAntenna(antenna: Antenna, note: (Note | Packed<'No
if (note.visibility === 'specified') return false;
// skip if the antenna creator is blocked by the note author
const blockings = await blockingCache.fetch(noteUser.id);
const blockings = (await blockingCache.fetch(noteUser.id)) ?? [];
if (blockings.some(blocking => blocking === antenna.userId)) return false;
if (note.visibility === 'followers') {

View file

@ -3,7 +3,7 @@ import { db } from '@/db/postgre.js';
import { Meta } from '@/models/entities/meta.js';
import { getFetchInstanceMetadataLock } from '@/misc/app-lock.js';
let cache: Meta;
let cache: Meta | undefined;
/**
* Performs the primitive database operation to set the server configuration
@ -57,5 +57,5 @@ export async function fetchMeta(noCache = false): Promise<Meta> {
await getMeta();
return cache;
return cache!;
}

View file

@ -35,7 +35,7 @@ export async function getHtml(url: string, accept = 'text/html, */*', timeout =
return await res.text();
}
export async function getResponse(args: { url: string, method: string, body?: string, headers: Record<string, string>, timeout?: number, size?: number }) {
export async function getResponse(args: { url: string, method: string, body?: string, headers: Record<string, string>, timeout?: number, size?: number, redirect: 'follow' | 'manual' | 'error' = 'follow' }) {
const timeout = args.timeout || 10 * SECOND;
const controller = new AbortController();
@ -47,13 +47,18 @@ export async function getResponse(args: { url: string, method: string, body?: st
method: args.method,
headers: args.headers,
body: args.body,
redirect: args.redirect,
timeout,
size: args.size || 10 * 1024 * 1024,
size: args.size || 10 * 1024 * 1024, // 10 MiB
agent: getAgentByUrl,
signal: controller.signal,
});
if (!res.ok) {
if (
!res.ok
&&
// intended redirect is not an error
!(args.redirect != 'follow' && res.status >= 300 && res.status < 400)) {
throw new StatusError(`${res.status} ${res.statusText}`, res.status, res.statusText);
}

View file

@ -0,0 +1,17 @@
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

@ -75,7 +75,7 @@ export async function toDbReaction(reaction?: string | null, idnReacterHost?: st
const custom = reaction.match(/^:([\w+-]+)(?:@\.)?:$/);
if (custom) {
const name = custom[1];
const emoji = await Emojis.findOneBy({
const emoji = await Emojis.countBy({
host: reacterHost ?? IsNull(),
name,
});

View file

@ -1,5 +1,11 @@
import { Note } from '@/models/entities/note.js';
export function isPureRenote(note: Note): note is Note & { renoteId: string, text: null, fileIds: null | never[], hasPoll: false } {
return note.renoteId != null && note.text == null && (note.fileIds == null || note.fileIds.length === 0) && !note.hasPoll;
return note.renoteId != null
&& note.text == null
&& (
note.fileIds == null
|| note.fileIds.length === 0
)
&& !note.hasPoll;
}

View file

@ -28,7 +28,6 @@ import { packedAntennaSchema } from '@/models/schema/antenna.js';
import { packedClipSchema } from '@/models/schema/clip.js';
import { packedFederationInstanceSchema } from '@/models/schema/federation-instance.js';
import { packedQueueCountSchema } from '@/models/schema/queue.js';
import { packedGalleryPostSchema } from '@/models/schema/gallery-post.js';
import { packedEmojiSchema } from '@/models/schema/emoji.js';
export const refs = {
@ -61,7 +60,6 @@ export const refs = {
Antenna: packedAntennaSchema,
Clip: packedClipSchema,
FederationInstance: packedFederationInstanceSchema,
GalleryPost: packedGalleryPostSchema,
Emoji: packedEmojiSchema,
};

View file

@ -1,10 +1,9 @@
import * as crypto from 'node:crypto';
const L_CHARS = '0123456789abcdefghijklmnopqrstuvwxyz';
const LU_CHARS = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
const LU_CHARS = L_CHARS + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
export function secureRndstr(length = 32, useLU = true): string {
const chars = useLU ? LU_CHARS : L_CHARS;
export function secureRndstrCustom(length = 32, chars: string): string {
const chars_len = chars.length;
let str = '';
@ -19,3 +18,8 @@ export function secureRndstr(length = 32, useLU = true): string {
return str;
}
export function secureRndstr(length = 32, useLU = true): string {
const chars = useLU ? LU_CHARS : L_CHARS;
return secureRndstrCustom(length, chars);
}

View file

@ -6,11 +6,10 @@ import { Meta } from '@/models/entities/meta.js';
* Returns whether a specific host (punycoded) should be blocked.
*
* @param host punycoded instance host
* @param meta a Promise contatining the information from the meta table (optional)
* @param meta a resolved Meta table
* @returns whether the given host should be blocked
*/
export async function shouldBlockInstance(host: Instance['host'], meta: Promise<Meta> = fetchMeta()): Promise<boolean> {
const { blockedHosts } = await meta;
export async function shouldBlockInstance(host: Instance['host'], meta?: Meta): Promise<boolean> {
const { blockedHosts } = meta ?? await fetchMeta();
return blockedHosts.some(blockedHost => host === blockedHost || host.endsWith('.' + blockedHost));
}

View file

@ -15,8 +15,8 @@ const deadThreshold = 7 * DAY;
* @returns array of punycoded instance hosts that should be skipped (subset of hosts parameter)
*/
export async function skippedInstances(hosts: Array<Instance['host']>): Promise<Array<Instance['host']>> {
// Resolve the boolean promises before filtering
const meta = fetchMeta();
// first check for blocked instances since that info may already be in memory
const meta = await fetchMeta();
const shouldSkip = await Promise.all(hosts.map(host => shouldBlockInstance(host, meta)));
const skipped = hosts.filter((_, i) => shouldSkip[i]);
@ -35,7 +35,7 @@ export async function skippedInstances(hosts: Array<Instance['host']>): Promise<
hosts.filter(host => !skipped.includes(host) && !host.includes(',')).join(','),
],
)
.then(res => res.map(row => row.host)),
.then((res: Instance[]) => res.map(row => row.host)),
);
}

View file

@ -17,7 +17,7 @@ export class AbuseUserReport {
@Column(id())
public targetUserId: User['id'];
@ManyToOne(type => User, {
@ManyToOne(() => User, {
onDelete: 'CASCADE',
})
@JoinColumn()
@ -27,7 +27,7 @@ export class AbuseUserReport {
@Column(id())
public reporterId: User['id'];
@ManyToOne(type => User, {
@ManyToOne(() => User, {
onDelete: 'CASCADE',
})
@JoinColumn()
@ -39,7 +39,7 @@ export class AbuseUserReport {
})
public assigneeId: User['id'] | null;
@ManyToOne(type => User, {
@ManyToOne(() => User, {
onDelete: 'SET NULL',
})
@JoinColumn()

View file

@ -41,7 +41,7 @@ export class AccessToken {
@Column(id())
public userId: User['id'];
@ManyToOne(type => User, {
@ManyToOne(() => User, {
onDelete: 'CASCADE',
})
@JoinColumn()
@ -53,7 +53,7 @@ export class AccessToken {
})
public appId: App['id'] | null;
@ManyToOne(type => App, {
@ManyToOne(() => App, {
onDelete: 'CASCADE',
})
@JoinColumn()
@ -79,7 +79,6 @@ export class AccessToken {
@Column('varchar', {
length: 64, array: true,
default: '{}',
})
public permission: string[];

View file

@ -18,7 +18,7 @@ export class AnnouncementRead {
@Column(id())
public userId: User['id'];
@ManyToOne(type => User, {
@ManyToOne(() => User, {
onDelete: 'CASCADE',
})
@JoinColumn()
@ -28,7 +28,7 @@ export class AnnouncementRead {
@Column(id())
public announcementId: Announcement['id'];
@ManyToOne(type => Announcement, {
@ManyToOne(() => Announcement, {
onDelete: 'CASCADE',
})
@JoinColumn()

View file

@ -32,12 +32,4 @@ export class Announcement {
length: 1024, nullable: true,
})
public imageUrl: string | null;
constructor(data: Partial<Announcement>) {
if (data == null) return;
for (const [k, v] of Object.entries(data)) {
(this as any)[k] = v;
}
}
}

View file

@ -16,7 +16,7 @@ export class AntennaNote {
})
public noteId: Note['id'];
@ManyToOne(type => Note, {
@ManyToOne(() => Note, {
onDelete: 'CASCADE',
})
@JoinColumn()
@ -29,7 +29,7 @@ export class AntennaNote {
})
public antennaId: Antenna['id'];
@ManyToOne(type => Antenna, {
@ManyToOne(() => Antenna, {
onDelete: 'CASCADE',
})
@JoinColumn()

View file

@ -21,7 +21,7 @@ export class Antenna {
})
public userId: User['id'];
@ManyToOne(type => User, {
@ManyToOne(() => User, {
onDelete: 'CASCADE',
})
@JoinColumn()
@ -42,7 +42,7 @@ export class Antenna {
})
public userListId: UserList['id'] | null;
@ManyToOne(type => UserList, {
@ManyToOne(() => UserList, {
onDelete: 'CASCADE',
})
@JoinColumn()
@ -54,7 +54,7 @@ export class Antenna {
})
public userGroupJoiningId: UserGroupJoining['id'] | null;
@ManyToOne(type => UserGroupJoining, {
@ManyToOne(() => UserGroupJoining, {
onDelete: 'CASCADE',
})
@JoinColumn()

View file

@ -21,7 +21,7 @@ export class App {
})
public userId: User['id'] | null;
@ManyToOne(type => User, {
@ManyToOne(() => User, {
onDelete: 'SET NULL',
nullable: true,
})

View file

@ -11,7 +11,7 @@ export class AttestationChallenge {
@PrimaryColumn(id())
public userId: User['id'];
@ManyToOne(type => User, {
@ManyToOne(() => User, {
onDelete: 'CASCADE',
})
@JoinColumn()
@ -35,12 +35,4 @@ export class AttestationChallenge {
default: false,
})
public registrationChallenge: boolean;
constructor(data: Partial<AttestationChallenge>) {
if (data == null) return;
for (const [k, v] of Object.entries(data)) {
(this as any)[k] = v;
}
}
}

View file

@ -1,4 +1,4 @@
import { Entity, PrimaryColumn, Index, Column, ManyToOne, OneToOne, JoinColumn } from 'typeorm';
import { Entity, PrimaryColumn, Index, Column, ManyToOne, JoinColumn } from 'typeorm';
import { id } from '../id.js';
import { AccessToken } from './access-token.js';
import { App } from './app.js';
@ -45,5 +45,5 @@ export class AuthSession {
nullable: true,
comment: 'PKCE code_challenge value, if provided (OAuth only)',
})
pkceChallenge: string | null;
pkceChallenge: string | null;
}

View file

@ -21,7 +21,7 @@ export class Blocking {
})
public blockeeId: User['id'];
@ManyToOne(type => User, {
@ManyToOne(() => User, {
onDelete: 'CASCADE',
})
@JoinColumn()
@ -34,7 +34,7 @@ export class Blocking {
})
public blockerId: User['id'];
@ManyToOne(type => User, {
@ManyToOne(() => User, {
onDelete: 'CASCADE',
})
@JoinColumn()

View file

@ -22,7 +22,7 @@ export class ChannelFollowing {
})
public followeeId: Channel['id'];
@ManyToOne(type => Channel, {
@ManyToOne(() => Channel, {
onDelete: 'CASCADE',
})
@JoinColumn()
@ -35,7 +35,7 @@ export class ChannelFollowing {
})
public followerId: User['id'];
@ManyToOne(type => User, {
@ManyToOne(() => User, {
onDelete: 'CASCADE',
})
@JoinColumn()

View file

@ -18,7 +18,7 @@ export class ChannelNotePining {
@Column(id())
public channelId: Channel['id'];
@ManyToOne(type => Channel, {
@ManyToOne(() => Channel, {
onDelete: 'CASCADE',
})
@JoinColumn()
@ -27,7 +27,7 @@ export class ChannelNotePining {
@Column(id())
public noteId: Note['id'];
@ManyToOne(type => Note, {
@ManyToOne(() => Note, {
onDelete: 'CASCADE',
})
@JoinColumn()

View file

@ -28,7 +28,7 @@ export class Channel {
})
public userId: User['id'] | null;
@ManyToOne(type => User, {
@ManyToOne(() => User, {
onDelete: 'SET NULL',
})
@JoinColumn()
@ -53,7 +53,7 @@ export class Channel {
})
public bannerId: DriveFile['id'] | null;
@ManyToOne(type => DriveFile, {
@ManyToOne(() => DriveFile, {
onDelete: 'SET NULL',
})
@JoinColumn()

View file

@ -16,7 +16,7 @@ export class ClipNote {
})
public noteId: Note['id'];
@ManyToOne(type => Note, {
@ManyToOne(() => Note, {
onDelete: 'CASCADE',
})
@JoinColumn()
@ -29,7 +29,7 @@ export class ClipNote {
})
public clipId: Clip['id'];
@ManyToOne(type => Clip, {
@ManyToOne(() => Clip, {
onDelete: 'CASCADE',
})
@JoinColumn()

View file

@ -19,7 +19,7 @@ export class Clip {
})
public userId: User['id'];
@ManyToOne(type => User, {
@ManyToOne(() => User, {
onDelete: 'CASCADE',
})
@JoinColumn()

View file

@ -23,7 +23,7 @@ export class DriveFile {
})
public userId: User['id'] | null;
@ManyToOne(type => User, {
@ManyToOne(() => User, {
onDelete: 'RESTRICT',
})
@JoinColumn()
@ -144,7 +144,7 @@ export class DriveFile {
})
public folderId: DriveFolder['id'] | null;
@ManyToOne(type => DriveFolder, {
@ManyToOne(() => DriveFolder, {
onDelete: 'SET NULL',
})
@JoinColumn()

View file

@ -27,7 +27,7 @@ export class DriveFolder {
})
public userId: User['id'] | null;
@ManyToOne(type => User, {
@ManyToOne(() => User, {
onDelete: 'CASCADE',
})
@JoinColumn()
@ -41,7 +41,7 @@ export class DriveFolder {
})
public parentId: DriveFolder['id'] | null;
@ManyToOne(type => DriveFolder, {
@ManyToOne(() => DriveFolder, {
onDelete: 'SET NULL',
})
@JoinColumn()

View file

@ -20,7 +20,7 @@ export class FollowRequest {
})
public followeeId: User['id'];
@ManyToOne(type => User, {
@ManyToOne(() => User, {
onDelete: 'CASCADE',
})
@JoinColumn()
@ -33,7 +33,7 @@ export class FollowRequest {
})
public followerId: User['id'];
@ManyToOne(type => User, {
@ManyToOne(() => User, {
onDelete: 'CASCADE',
})
@JoinColumn()

View file

@ -21,7 +21,7 @@ export class Following {
})
public followeeId: User['id'];
@ManyToOne(type => User, {
@ManyToOne(() => User, {
onDelete: 'CASCADE',
})
@JoinColumn()
@ -34,7 +34,7 @@ export class Following {
})
public followerId: User['id'];
@ManyToOne(type => User, {
@ManyToOne(() => User, {
onDelete: 'CASCADE',
})
@JoinColumn()

View file

@ -1,33 +0,0 @@
import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm';
import { id } from '../id.js';
import { User } from './user.js';
import { GalleryPost } from './gallery-post.js';
@Entity()
@Index(['userId', 'postId'], { unique: true })
export class GalleryLike {
@PrimaryColumn(id())
public id: string;
@Column('timestamp with time zone')
public createdAt: Date;
@Index()
@Column(id())
public userId: User['id'];
@ManyToOne(type => User, {
onDelete: 'CASCADE',
})
@JoinColumn()
public user: User | null;
@Column(id())
public postId: GalleryPost['id'];
@ManyToOne(type => GalleryPost, {
onDelete: 'CASCADE',
})
@JoinColumn()
public post: GalleryPost | null;
}

View file

@ -1,79 +0,0 @@
import { Entity, Index, JoinColumn, Column, PrimaryColumn, ManyToOne } from 'typeorm';
import { id } from '../id.js';
import { User } from './user.js';
import { DriveFile } from './drive-file.js';
@Entity()
export class GalleryPost {
@PrimaryColumn(id())
public id: string;
@Index()
@Column('timestamp with time zone', {
comment: 'The created date of the GalleryPost.',
})
public createdAt: Date;
@Index()
@Column('timestamp with time zone', {
comment: 'The updated date of the GalleryPost.',
})
public updatedAt: Date;
@Column('varchar', {
length: 256,
})
public title: string;
@Column('varchar', {
length: 2048, nullable: true,
})
public description: string | null;
@Index()
@Column({
...id(),
comment: 'The ID of author.',
})
public userId: User['id'];
@ManyToOne(type => User, {
onDelete: 'CASCADE',
})
@JoinColumn()
public user: User | null;
@Index()
@Column({
...id(),
array: true, default: '{}',
})
public fileIds: DriveFile['id'][];
@Index()
@Column('boolean', {
default: false,
comment: 'Whether the post is sensitive.',
})
public isSensitive: boolean;
@Index()
@Column('integer', {
default: 0,
})
public likedCount: number;
@Index()
@Column('varchar', {
length: 128, array: true, default: '{}',
})
public tags: string[];
constructor(data: Partial<GalleryPost>) {
if (data == null) return;
for (const [k, v] of Object.entries(data)) {
(this as any)[k] = v;
}
}
}

View file

@ -22,7 +22,7 @@ export class MessagingMessage {
})
public userId: User['id'];
@ManyToOne(type => User, {
@ManyToOne(() => User, {
onDelete: 'CASCADE',
})
@JoinColumn()
@ -35,7 +35,7 @@ export class MessagingMessage {
})
public recipientId: User['id'] | null;
@ManyToOne(type => User, {
@ManyToOne(() => User, {
onDelete: 'CASCADE',
})
@JoinColumn()
@ -48,7 +48,7 @@ export class MessagingMessage {
})
public groupId: UserGroup['id'] | null;
@ManyToOne(type => UserGroup, {
@ManyToOne(() => UserGroup, {
onDelete: 'CASCADE',
})
@JoinColumn()
@ -81,7 +81,7 @@ export class MessagingMessage {
})
public fileId: DriveFile['id'] | null;
@ManyToOne(type => DriveFile, {
@ManyToOne(() => DriveFile, {
onDelete: 'CASCADE',
})
@JoinColumn()

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