Compare commits

...

960 commits
v0.5.0 ... main

Author SHA1 Message Date
Jesse Wierzbinski 6462669e9e
chore(packages/client): 🔖 Release 0.2.1
Some checks failed
CodeQL Scan / Analyze (javascript-typescript) (push) Failing after 0s
Build Docker Images / lint (push) Failing after 7s
Build Docker Images / check (push) Failing after 7s
Build Docker Images / tests (push) Failing after 6s
Build Docker Images / detect-circular (push) Failing after 6s
Deploy Docs to GitHub Pages / build (push) Failing after 0s
Build Docker Images / build (server, Dockerfile, ${{ github.repository_owner }}/server) (push) Has been skipped
Build Docker Images / build (worker, Worker.Dockerfile, ${{ github.repository_owner }}/worker) (push) Has been skipped
Deploy Docs to GitHub Pages / Deploy (push) Has been skipped
Mirror to Codeberg / Mirror (push) Failing after 0s
Nix Build / check (push) Failing after 0s
Test Publish / build (client) (push) Failing after 0s
Test Publish / build (sdk) (push) Failing after 0s
2025-11-21 12:17:15 +01:00
Jesse Wierzbinski d18f135fbd
refactor(packages/client): 🔥 Remove broken SSO login code 2025-11-21 12:17:02 +01:00
Jesse Wierzbinski 59ad71964b
feat(api): Add FormData support in SSO login 2025-11-21 12:16:47 +01:00
Jesse Wierzbinski bf890aec15
fix(packages/client): 🚨 Remove useless default value 2025-11-21 11:03:16 +01:00
Jesse Wierzbinski 9cf85e951e
test(api): Remove bad streaming timeline tests 2025-11-21 11:02:22 +01:00
Jesse Wierzbinski 814d63554f
chore(packages/client): 🔖 Release 0.2.0 2025-11-21 11:00:40 +01:00
Jesse Wierzbinski ce650a69d4
feat(packages/client): Add SSO login helper to client 2025-11-21 10:59:28 +01:00
Jesse Wierzbinski 5e84fb66f9
chore: ⬆️ Upgrade dependencies
Some checks failed
CodeQL Scan / Analyze (javascript-typescript) (push) Failing after 0s
Build Docker Images / lint (push) Failing after 7s
Build Docker Images / check (push) Failing after 7s
Build Docker Images / tests (push) Failing after 6s
Build Docker Images / detect-circular (push) Failing after 6s
Deploy Docs to GitHub Pages / build (push) Failing after 0s
Build Docker Images / build (server, Dockerfile, ${{ github.repository_owner }}/server) (push) Has been skipped
Build Docker Images / build (worker, Worker.Dockerfile, ${{ github.repository_owner }}/worker) (push) Has been skipped
Deploy Docs to GitHub Pages / Deploy (push) Has been skipped
Mirror to Codeberg / Mirror (push) Failing after 1s
Nix Build / check (push) Failing after 0s
Test Publish / build (client) (push) Failing after 0s
Test Publish / build (sdk) (push) Failing after 0s
2025-11-21 09:00:37 +01:00
Jesse Wierzbinski 1430d6f7e7
chore: ⬆️ Upgrade to Bun 1.3.2 2025-11-21 08:49:19 +01:00
Jesse Wierzbinski f00ac1a590
chore: ⬆️ Upgrade dependencies 2025-11-21 08:31:02 +01:00
Jesse Wierzbinski f260064083
fix: 🚨 Remove useless function overloading 2025-11-21 07:26:35 +01:00
Gaspard Wierzbinski f2e9c862a6
Merge pull request #42 from versia-pub/refactor/openid
Rewrite old authentication code and go OpenID-only
2025-11-21 07:21:01 +01:00
Jesse Wierzbinski 82bb92768c
fix: 🐛 Set test pattern in DeepSource code analysis config 2025-11-21 06:53:50 +01:00
Jesse Wierzbinski c63b2b320b
fix(config): 💚 Fix incorrect CI config 2025-11-21 06:50:41 +01:00
Jesse Wierzbinski a9dbd2cc4e
fix: 🔥 Remove old tests and docs related to old auth endpoints 2025-11-21 06:45:12 +01:00
Jesse Wierzbinski ae207c10b6
fix: 💚 Update Nix hash 2025-11-03 00:17:57 +01:00
Jesse Wierzbinski 955a933fe9
refactor(api): 🔥 Remove old forced OpenID auth code 2025-10-24 19:12:40 +02:00
Jesse Wierzbinski 45c3f6ae3f
fix(database): 🐛 Cascade application ID deletion 2025-10-24 18:45:07 +02:00
Jesse Wierzbinski bfa7a06958
fix(database): 🐛 Fix applications table not getting deleted correctly during migration 2025-10-24 18:19:22 +02:00
Jesse Wierzbinski c93071666a
fix(packages/client): 🐛 Remove nonexistent options from client's media API
Some checks failed
CodeQL Scan / Analyze (javascript-typescript) (push) Failing after 0s
Build Docker Images / lint (push) Failing after 6s
Build Docker Images / check (push) Failing after 6s
Build Docker Images / tests (push) Failing after 6s
Build Docker Images / detect-circular (push) Failing after 7s
Deploy Docs to GitHub Pages / build (push) Failing after 0s
Build Docker Images / build (server, Dockerfile, ${{ github.repository_owner }}/server) (push) Has been skipped
Build Docker Images / build (worker, Worker.Dockerfile, ${{ github.repository_owner }}/worker) (push) Has been skipped
Deploy Docs to GitHub Pages / Deploy (push) Has been skipped
Mirror to Codeberg / Mirror (push) Failing after 0s
Nix Build / check (push) Failing after 0s
Test Publish / build (client) (push) Failing after 0s
Test Publish / build (sdk) (push) Failing after 0s
2025-08-28 07:07:33 +02:00
Jesse Wierzbinski 0d53436f7e
ci: 💚 Fix CI docs
Some checks failed
CodeQL Scan / Analyze (javascript-typescript) (push) Failing after 1s
Build Docker Images / lint (push) Failing after 8s
Build Docker Images / check (push) Failing after 8s
Build Docker Images / tests (push) Failing after 8s
Build Docker Images / detect-circular (push) Failing after 8s
Build Docker Images / build (server, Dockerfile, ${{ github.repository_owner }}/server) (push) Has been skipped
Deploy Docs to GitHub Pages / build (push) Failing after 1s
Build Docker Images / build (worker, Worker.Dockerfile, ${{ github.repository_owner }}/worker) (push) Has been skipped
Deploy Docs to GitHub Pages / Deploy (push) Has been skipped
Mirror to Codeberg / Mirror (push) Failing after 1s
Nix Build / check (push) Failing after 1s
Test Publish / build (client) (push) Failing after 1s
Test Publish / build (sdk) (push) Failing after 1s
2025-08-22 20:46:38 +02:00
Jesse Wierzbinski d8f9f47814
ci: 💚 Add detect-circular as dependency for docker build 2025-08-22 20:45:11 +02:00
Jesse Wierzbinski b46f7828a5
feat: 🔒 Harden Systemd unit config 2025-08-22 20:44:26 +02:00
Jesse Wierzbinski 1a0a27bee1
refactor(database): 🚚 Rename Application to Client everywhere 2025-08-21 01:21:32 +02:00
Jesse Wierzbinski 6f97903f3b
fix(api): Fix all failing tests 2025-08-21 01:15:38 +02:00
Jesse Wierzbinski 1bfc5fb013
refactor(api): ♻️ Rewrite full authentication code to go OpenID-only 2025-08-21 00:45:58 +02:00
Jesse Wierzbinski 4eae4cd062
feat: 🔒 Harden Systemd unit config 2025-08-09 17:15:05 +02:00
Jesse Wierzbinski a6c9d6cd4f
chore: 🐛 Update Nix hashes
Some checks failed
CodeQL Scan / Analyze (javascript-typescript) (push) Failing after 0s
Build Docker Images / lint (push) Failing after 7s
Build Docker Images / check (push) Failing after 6s
Build Docker Images / tests (push) Failing after 6s
Build Docker Images / build (server, Dockerfile, ${{ github.repository_owner }}/server) (push) Has been skipped
Build Docker Images / build (worker, Worker.Dockerfile, ${{ github.repository_owner }}/worker) (push) Has been skipped
Build Docker Images / detect-circular (push) Failing after 6s
Deploy Docs to GitHub Pages / build (push) Failing after 0s
Deploy Docs to GitHub Pages / Deploy (push) Has been skipped
Mirror to Codeberg / Mirror (push) Failing after 0s
Nix Build / check (push) Failing after 0s
Test Publish / build (client) (push) Failing after 0s
Test Publish / build (sdk) (push) Failing after 0s
2025-07-07 06:11:21 +02:00
Jesse Wierzbinski b5e9e35427
refactor: 🔥 Remove plugin functionality, move OpenID plugin to core 2025-07-07 05:52:11 +02:00
Gaspard Wierzbinski 278bf960cb
Merge pull request #41 from versia-pub/refactor/packages
Refactor/packages
2025-07-07 05:16:09 +02:00
Jesse Wierzbinski 0bf5f7c983
fix: 🚨 Fix further DeepSource issues 2025-07-07 05:12:23 +02:00
Jesse Wierzbinski 870b6dbe85
fix: 🚨 Fix Deepsource warnings 2025-07-07 05:08:34 +02:00
Jesse Wierzbinski 2fffbcbede
fix: 🐛 Fix weird imports failing build 2025-07-07 04:52:46 +02:00
Jesse Wierzbinski 551b9a94fe
ci: 💚 Use correct name in CI 2025-07-07 04:39:36 +02:00
Jesse Wierzbinski 24d4150da4
refactor: ⬆️ Upgrade to Zod v4 and hono-openapi 0.5.0 2025-07-07 03:42:35 +02:00
Jesse Wierzbinski add2429606
docs: 📝 Improve Copilot instructions file
Some checks failed
Mirror to Codeberg / Mirror (push) Failing after 0s
Test Publish / build (client) (push) Failing after 1s
Test Publish / build (sdk) (push) Failing after 0s
2025-07-06 05:06:05 +02:00
Jesse Wierzbinski eb096c5991
fix: 💚 Fix the Nix build, remove dependency on PNPM 2025-07-06 02:25:06 +02:00
Jesse Wierzbinski 30bb801f9f
fix: 💚 Fix Docker image builds 2025-07-06 02:10:44 +02:00
Jesse Wierzbinski 6d7c545c88
chore: ⬆️ Upgrade Bun to 1.2.18 2025-07-06 02:03:27 +02:00
Jesse Wierzbinski a1300466f4
chore: ⬆️ Upgrade dependencies 2025-07-06 02:02:48 +02:00
Jesse Wierzbinski 90b6399407
refactor: ♻️ Rewrite build system to fit the monorepo architecture
Some checks failed
Mirror to Codeberg / Mirror (push) Failing after 0s
Test Publish / build (client) (push) Failing after 1s
Test Publish / build (sdk) (push) Failing after 0s
2025-07-04 06:29:43 +02:00
Jesse Wierzbinski 7de4b573e3
refactor(worker): 🚚 Move queue code to plugin-kit package
Some checks failed
Mirror to Codeberg / Mirror (push) Failing after 1s
Test Publish / build (client) (push) Failing after 1s
Test Publish / build (sdk) (push) Failing after 1s
2025-06-29 22:56:52 +02:00
Jesse Wierzbinski dc802ff5f6
feat(api): Begin work on Streaming API 2025-06-29 22:23:03 +02:00
Jesse Wierzbinski 59cd519337
fix(api): 🐛 Fix error when masto-fe stupidly sends empty spoiler_text
Some checks failed
Mirror to Codeberg / Mirror (push) Failing after 0s
Test Publish / build (client) (push) Failing after 0s
Test Publish / build (sdk) (push) Failing after 0s
2025-06-23 18:53:40 +02:00
Jesse Wierzbinski aff51b651c
refactor: ♻️ Rewrite logging logic into a unified package
Some checks failed
Mirror to Codeberg / Mirror (push) Failing after 0s
Test Publish / build (client) (push) Failing after 0s
Test Publish / build (sdk) (push) Failing after 0s
2025-06-22 18:43:03 +02:00
Jesse Wierzbinski e1bd389bf1
refactor: 🔥 Remove Devcontainer
It was useless and nobody has ever used it once.
2025-06-22 17:56:54 +02:00
Jesse Wierzbinski 2310e8b33d
chore: ⬆️ Upgrade Bun to 1.2.17 2025-06-22 17:55:50 +02:00
Jesse Wierzbinski 129bc97b09
chore: ⬆️ Upgrade dependencies 2025-06-22 17:53:53 +02:00
Jesse Wierzbinski 1a666e8371
refactor: ♻️ [BROKEN] Refactor Nix build to use fetchBunDeps PR 2025-06-22 17:46:29 +02:00
Jesse Wierzbinski 03940cd8fd
fix: 📄 Add licenses to both JSR packages
Some checks failed
Mirror to Codeberg / Mirror (push) Failing after 0s
Test Publish / build (client) (push) Failing after 0s
Test Publish / build (sdk) (push) Failing after 0s
2025-06-17 19:59:37 +02:00
Jesse Wierzbinski 1f03017327
refactor: 🚚 Rename @versia/kit to @versia-server/kit
Some checks failed
Mirror to Codeberg / Mirror (push) Failing after 0s
Test Publish / build (client) (push) Failing after 0s
Test Publish / build (sdk) (push) Failing after 0s
2025-06-15 23:50:34 +02:00
Jesse Wierzbinski 3798e170d0
refactor: 🚚 Move more utilities into packages 2025-06-15 23:43:27 +02:00
Jesse Wierzbinski 5cae547f8d
chore: 💚 Update Nix hashes 2025-06-15 22:26:43 +02:00
Jesse Wierzbinski fde70fa61a
refactor: 🚚 Move testing to its own sub-package 2025-06-15 22:17:33 +02:00
Jesse Wierzbinski a211772309
fix: 🐛 Fix Nix build
Some checks failed
Mirror to Codeberg / Mirror (push) Failing after 0s
Test Publish / build (client) (push) Failing after 0s
Test Publish / build (sdk) (push) Failing after 0s
2025-06-15 19:23:11 +02:00
Jesse Wierzbinski a6d3ebbeef
refactor: 🚚 Organize code into sub-packages, instead of a single large package 2025-06-15 04:38:20 +02:00
Jesse Wierzbinski 79742f47dc
chore(api): 🔥 Remove clustering ability from API worker
Some checks failed
CodeQL Scan / Analyze (javascript-typescript) (push) Failing after 1s
Build Docker Images / lint (push) Failing after 7s
Build Docker Images / check (push) Failing after 6s
Build Docker Images / tests (push) Failing after 6s
Deploy Docs to GitHub Pages / build (push) Failing after 0s
Build Docker Images / build (server, Dockerfile, ${{ github.repository_owner }}/server) (push) Has been skipped
Build Docker Images / build (worker, Worker.Dockerfile, ${{ github.repository_owner }}/worker) (push) Has been skipped
Deploy Docs to GitHub Pages / Deploy (push) Has been skipped
Mirror to Codeberg / Mirror (push) Failing after 0s
Nix Build / check (push) Failing after 1s
Test Publish / build (client) (push) Failing after 0s
Test Publish / build (sdk) (push) Failing after 0s
2025-06-15 02:28:02 +02:00
Jesse Wierzbinski 4cc6284eb4
chore: ⬆️ Upgrade dependencies 2025-06-15 02:24:56 +02:00
Jesse Wierzbinski 13d43e8e71
fix: 🐛 Fix Nix build
Some checks failed
CodeQL Scan / Analyze (javascript-typescript) (push) Failing after 0s
Build Docker Images / lint (push) Failing after 6s
Build Docker Images / check (push) Failing after 7s
Build Docker Images / tests (push) Failing after 6s
Deploy Docs to GitHub Pages / build (push) Failing after 0s
Build Docker Images / build (server, Dockerfile, ${{ github.repository_owner }}/server) (push) Has been skipped
Build Docker Images / build (worker, Worker.Dockerfile, ${{ github.repository_owner }}/worker) (push) Has been skipped
Deploy Docs to GitHub Pages / Deploy (push) Has been skipped
Mirror to Codeberg / Mirror (push) Failing after 0s
Nix Build / check (push) Failing after 0s
Test Publish / build (client) (push) Failing after 0s
Test Publish / build (sdk) (push) Failing after 0s
2025-06-04 16:24:09 +02:00
Jesse Wierzbinski 0ae8f632b5
chore: ⬆️ Upgrade to Bun 1.2.15
Some checks failed
CodeQL Scan / Analyze (javascript-typescript) (push) Failing after 0s
Build Docker Images / lint (push) Failing after 6s
Build Docker Images / check (push) Failing after 6s
Build Docker Images / tests (push) Failing after 7s
Deploy Docs to GitHub Pages / build (push) Failing after 1s
Build Docker Images / build (server, Dockerfile, ${{ github.repository_owner }}/server) (push) Has been skipped
Build Docker Images / build (worker, Worker.Dockerfile, ${{ github.repository_owner }}/worker) (push) Has been skipped
Deploy Docs to GitHub Pages / Deploy (push) Has been skipped
Mirror to Codeberg / Mirror (push) Failing after 1s
Nix Build / check (push) Failing after 1s
Test Publish / build (client) (push) Failing after 1s
Test Publish / build (sdk) (push) Failing after 1s
2025-06-04 16:20:24 +02:00
Jesse Wierzbinski 85aceb2e48
chore: ⬆️ Upgrade dependencies 2025-06-04 16:18:49 +02:00
Jesse Wierzbinski 0692aa6efa
fix(federation): 👽 Add Reactions to list of supported extensions
Some checks failed
CodeQL Scan / Analyze (javascript-typescript) (push) Failing after 1s
Build Docker Images / lint (push) Failing after 8s
Build Docker Images / check (push) Failing after 7s
Build Docker Images / tests (push) Failing after 7s
Deploy Docs to GitHub Pages / build (push) Failing after 0s
Build Docker Images / build (server, Dockerfile, ${{ github.repository_owner }}/server) (push) Has been skipped
Build Docker Images / build (worker, Worker.Dockerfile, ${{ github.repository_owner }}/worker) (push) Has been skipped
Deploy Docs to GitHub Pages / Deploy (push) Has been skipped
Mirror to Codeberg / Mirror (push) Failing after 0s
Nix Build / check (push) Failing after 0s
Test Publish / build (client) (push) Failing after 0s
Test Publish / build (sdk) (push) Failing after 0s
2025-05-28 20:42:29 +02:00
Jesse Wierzbinski 15e291b487
chore(packages/client): 🔖 Release 0.2.0-alpha.4 2025-05-28 17:19:04 +02:00
Jesse Wierzbinski 343a507ecc
feat(federation): Federate Reactions 2025-05-28 17:17:03 +02:00
Jesse Wierzbinski fa1dd69e2d
feat(api): Make Reactions API correctly output whether a reaction is remote 2025-05-28 17:07:24 +02:00
Jesse Wierzbinski e0adaca2a2
feat(federation): Add inbound Reaction processing 2025-05-28 16:50:59 +02:00
Jesse Wierzbinski 1fba91f772
chore(config): 👽 Update JSON schema files
Some checks failed
CodeQL Scan / Analyze (javascript-typescript) (push) Failing after 0s
Build Docker Images / lint (push) Failing after 8s
Build Docker Images / check (push) Failing after 7s
Build Docker Images / tests (push) Failing after 8s
Deploy Docs to GitHub Pages / build (push) Failing after 0s
Build Docker Images / build (server, Dockerfile, ${{ github.repository_owner }}/server) (push) Has been skipped
Build Docker Images / build (worker, Worker.Dockerfile, ${{ github.repository_owner }}/worker) (push) Has been skipped
Deploy Docs to GitHub Pages / Deploy (push) Has been skipped
Mirror to Codeberg / Mirror (push) Failing after 0s
Nix Build / check (push) Failing after 0s
Test Publish / build (client) (push) Failing after 0s
Test Publish / build (sdk) (push) Failing after 0s
2025-05-28 03:06:49 +02:00
Jesse Wierzbinski 710f965144
fix(federation): 🔒 Enforce content filters for remote content as well 2025-05-28 02:59:26 +02:00
Jesse Wierzbinski c737aeba8e
fix(api): 🐛 Enforce emoji shortcode filters 2025-05-28 02:45:53 +02:00
Jesse Wierzbinski 9eac364e01
refactor(database): 🔥 Always import SQL operators directly from drizzle
Some checks failed
CodeQL Scan / Analyze (javascript-typescript) (push) Failing after 0s
Build Docker Images / lint (push) Failing after 7s
Build Docker Images / tests (push) Failing after 7s
Deploy Docs to GitHub Pages / build (push) Failing after 0s
Build Docker Images / build (server, Dockerfile, ${{ github.repository_owner }}/server) (push) Has been skipped
Build Docker Images / build (worker, Worker.Dockerfile, ${{ github.repository_owner }}/worker) (push) Has been skipped
Deploy Docs to GitHub Pages / Deploy (push) Has been skipped
Mirror to Codeberg / Mirror (push) Failing after 0s
Nix Build / check (push) Failing after 0s
Test Publish / build (client) (push) Failing after 0s
Test Publish / build (sdk) (push) Failing after 0s
Build Docker Images / check (push) Failing after 7s
2025-05-26 19:00:24 +02:00
Jesse Wierzbinski d3f411915f
chore(packages/client): 🔖 Release 0.2.0-alpha.3 2025-05-26 18:42:20 +02:00
Jesse Wierzbinski 7bd07801f2
feat(api): Add support for batch account data API 2025-05-26 18:41:45 +02:00
Jesse Wierzbinski 287f428a83
fix(api): 🐛 Add Reaction custom emoji data in statuses 2025-05-26 15:13:56 +02:00
Jesse Wierzbinski 8c0a20a743
chore(packages/client): 🔖 Release 0.2.0-alpha.2
Some checks failed
Build Docker Images / lint (push) Failing after 7s
Build Docker Images / check (push) Failing after 7s
Build Docker Images / tests (push) Failing after 7s
Deploy Docs to GitHub Pages / build (push) Failing after 0s
CodeQL Scan / Analyze (javascript-typescript) (push) Failing after 0s
Build Docker Images / build (server, Dockerfile, ${{ github.repository_owner }}/server) (push) Has been skipped
Build Docker Images / build (worker, Worker.Dockerfile, ${{ github.repository_owner }}/worker) (push) Has been skipped
Deploy Docs to GitHub Pages / Deploy (push) Has been skipped
Mirror to Codeberg / Mirror (push) Failing after 0s
Nix Build / check (push) Failing after 0s
Test Publish / build (client) (push) Failing after 0s
Test Publish / build (sdk) (push) Failing after 0s
2025-05-26 11:05:02 +02:00
Jesse Wierzbinski 6d85dbdfcb
fix(packages/client): 🐛 Fix client types, and add missing banner schema for Instance 2025-05-26 11:04:09 +02:00
Jesse Wierzbinski 77cd27a458
ci: 👷 Add CI workflow to publish packages 2025-05-26 09:08:14 +02:00
Jesse Wierzbinski e5e688a154
fix: 🐛 Add type: json specifier to all JSON imports 2025-05-26 08:55:06 +02:00
Jesse Wierzbinski fa5be6bd6a
chore: 🐛 Add jsr registry file to SDK package 2025-05-26 08:52:30 +02:00
Jesse Wierzbinski bf9840bd14
ci: 💚 Fix test publish CI 2025-05-26 08:47:27 +02:00
Jesse Wierzbinski 0551b8e12d
chore: 🔖 Set version to 0.9.0-alpha.0
Some checks failed
CodeQL Scan / Analyze (javascript-typescript) (push) Failing after 0s
Build Docker Images / lint (push) Failing after 7s
Build Docker Images / check (push) Failing after 6s
Build Docker Images / tests (push) Failing after 6s
Deploy Docs to GitHub Pages / build (push) Failing after 0s
Build Docker Images / build (server, Dockerfile, ${{ github.repository_owner }}/server) (push) Has been skipped
Build Docker Images / build (worker, Worker.Dockerfile, ${{ github.repository_owner }}/worker) (push) Has been skipped
Deploy Docs to GitHub Pages / Deploy (push) Has been skipped
Mirror to Codeberg / Mirror (push) Failing after 0s
Nix Build / check (push) Failing after 0s
Test Publish / build (client) (push) Failing after 0s
Test Publish / build (sdk) (push) Failing after 0s
2025-05-25 16:14:24 +02:00
Jesse Wierzbinski 9722b94eae
feat(api): Add Emoji Reactions 2025-05-25 16:11:56 +02:00
Jesse Wierzbinski 70974d3c35
chore: ⬆️ Upgrade dependencies
Some checks failed
Build Docker Images / check (push) Failing after 6s
CodeQL Scan / Analyze (javascript-typescript) (push) Failing after 0s
Build Docker Images / tests (push) Failing after 6s
Deploy Docs to GitHub Pages / build (push) Failing after 0s
Build Docker Images / build (server, Dockerfile, ${{ github.repository_owner }}/server) (push) Has been skipped
Build Docker Images / build (worker, Worker.Dockerfile, ${{ github.repository_owner }}/worker) (push) Has been skipped
Deploy Docs to GitHub Pages / Deploy (push) Has been skipped
Mirror to Codeberg / Mirror (push) Failing after 0s
Nix Build / check (push) Failing after 0s
Test Publish / build (client) (push) Failing after 0s
Test Publish / build (sdk) (push) Failing after 0s
Build Docker Images / lint (push) Failing after 6s
2025-05-23 19:21:49 +02:00
Jesse Wierzbinski 99a7658956
refactor: 🏷️ Update tsconfig.json and fix resulting errors
Some checks failed
Build Docker Images / build (server, Dockerfile, ${{ github.repository_owner }}/server) (push) Has been skipped
Build Docker Images / build (worker, Worker.Dockerfile, ${{ github.repository_owner }}/worker) (push) Has been skipped
Deploy Docs to GitHub Pages / build (push) Failing after 1s
Deploy Docs to GitHub Pages / Deploy (push) Has been skipped
Mirror to Codeberg / Mirror (push) Failing after 0s
Nix Build / check (push) Failing after 0s
CodeQL Scan / Analyze (javascript-typescript) (push) Failing after 0s
Build Docker Images / lint (push) Failing after 8s
Build Docker Images / check (push) Failing after 8s
Build Docker Images / tests (push) Failing after 8s
Test Publish / build (client) (push) Failing after 0s
Test Publish / build (sdk) (push) Failing after 0s
2025-05-23 17:29:27 +02:00
Jesse Wierzbinski 64068f9d23
docs: 📝 Update changelog
Some checks failed
CodeQL Scan / Analyze (javascript-typescript) (push) Failing after 0s
Build Docker Images / lint (push) Failing after 7s
Build Docker Images / check (push) Failing after 7s
Build Docker Images / tests (push) Failing after 6s
Deploy Docs to GitHub Pages / build (push) Failing after 0s
Build Docker Images / build (server, Dockerfile, ${{ github.repository_owner }}/server) (push) Has been skipped
Build Docker Images / build (worker, Worker.Dockerfile, ${{ github.repository_owner }}/worker) (push) Has been skipped
Deploy Docs to GitHub Pages / Deploy (push) Has been skipped
Mirror to Codeberg / Mirror (push) Failing after 0s
Nix Build / check (push) Failing after 0s
Test Publish / build (client) (push) Failing after 0s
Test Publish / build (sdk) (push) Failing after 0s
2025-05-19 15:16:00 +02:00
Jesse Wierzbinski 1d96db5313
Merge branch 'chore/v0.8' of https://github.com/versia-pub/server into chore/v0.8 2025-05-19 15:07:50 +02:00
Jesse Wierzbinski e64457c7e2
chore: 🔖 Set all versions to v0.8 2025-05-19 15:07:08 +02:00
Jesse Wierzbinski 6bb4f90c18
chore: 🔖 Set all versions to v0.8 2025-05-19 15:06:48 +02:00
Jesse Wierzbinski e50c8b6a5b
docs: 📝 Update docs to reflect latest changes 2025-05-19 15:05:22 +02:00
Jesse Wierzbinski d12bbb2a1b
ci: 👷 Correctly populate NPM token 2025-05-19 14:44:02 +02:00
Jesse Wierzbinski 0fc94cab3b
ci(api): 🐛 Fix public test workflow 2025-05-19 14:37:48 +02:00
Jesse Wierzbinski affe456fb8
chore: ⬆️ Upgrade dependencies 2025-05-19 14:35:27 +02:00
Jesse Wierzbinski 980b902927
chore: ⬆️ Upgrade Bun to 1.2.13
Some checks failed
Build Docker Images / lint (push) Failing after 6s
CodeQL Scan / Analyze (javascript-typescript) (push) Failing after 0s
Build Docker Images / check (push) Failing after 6s
Build Docker Images / tests (push) Failing after 7s
Deploy Docs to GitHub Pages / build (push) Failing after 0s
Build Docker Images / build (server, Dockerfile, ${{ github.repository_owner }}/server) (push) Has been skipped
Build Docker Images / build (worker, Worker.Dockerfile, ${{ github.repository_owner }}/worker) (push) Has been skipped
Deploy Docs to GitHub Pages / Deploy (push) Has been skipped
Mirror to Codeberg / Mirror (push) Failing after 0s
Nix Build / check (push) Failing after 0s
Test Publish / build (client) (push) Failing after 0s
Test Publish / build (sdk) (push) Failing after 0s
2025-05-16 01:51:06 +02:00
Jesse Wierzbinski 7f23aa893b
chore: ⬆️ Upgrade dependencies 2025-05-16 01:50:19 +02:00
Jesse Wierzbinski 5dfcfc548f
refactor(api): ♻️ Make SDK and client package only use resources in their own package
Some checks failed
CodeQL Scan / Analyze (javascript-typescript) (push) Failing after 1s
Build Docker Images / lint (push) Failing after 7s
Build Docker Images / check (push) Failing after 6s
Build Docker Images / tests (push) Failing after 6s
Deploy Docs to GitHub Pages / build (push) Failing after 0s
Build Docker Images / build (server, Dockerfile, ${{ github.repository_owner }}/server) (push) Has been skipped
Build Docker Images / build (worker, Worker.Dockerfile, ${{ github.repository_owner }}/worker) (push) Has been skipped
Deploy Docs to GitHub Pages / Deploy (push) Has been skipped
Mirror to Codeberg / Mirror (push) Failing after 0s
Nix Build / check (push) Failing after 1s
Test Publish / build (client) (push) Failing after 0s
Test Publish / build (sdk) (push) Failing after 0s
2025-05-13 11:51:59 +02:00
Jesse Wierzbinski c0060f1baf
test(federation): Add test for Note deletion 2025-05-13 11:06:50 +02:00
Jesse Wierzbinski 58bcbc4da7
fix(federation): 🐛 Properly handle manually_approves_followers
Some checks failed
CodeQL Scan / Analyze (javascript-typescript) (push) Failing after 1s
Build Docker Images / lint (push) Failing after 7s
Build Docker Images / check (push) Failing after 6s
Build Docker Images / tests (push) Failing after 6s
Deploy Docs to GitHub Pages / build (push) Failing after 0s
Build Docker Images / build (server, Dockerfile, ${{ github.repository_owner }}/server) (push) Has been skipped
Build Docker Images / build (worker, Worker.Dockerfile, ${{ github.repository_owner }}/worker) (push) Has been skipped
Deploy Docs to GitHub Pages / Deploy (push) Has been skipped
Mirror to Codeberg / Mirror (push) Failing after 0s
Nix Build / check (push) Failing after 0s
2025-05-09 18:47:28 +02:00
Jesse Wierzbinski 4d8fe93188
chore: ⬆️ Upgrade dependencies
Some checks failed
CodeQL Scan / Analyze (javascript-typescript) (push) Failing after 0s
Build Docker Images / lint (push) Failing after 6s
Build Docker Images / check (push) Failing after 6s
Build Docker Images / tests (push) Failing after 6s
Deploy Docs to GitHub Pages / build (push) Failing after 0s
Build Docker Images / build (server, Dockerfile, ${{ github.repository_owner }}/server) (push) Has been skipped
Build Docker Images / build (worker, Worker.Dockerfile, ${{ github.repository_owner }}/worker) (push) Has been skipped
Deploy Docs to GitHub Pages / Deploy (push) Has been skipped
Mirror to Codeberg / Mirror (push) Failing after 0s
Nix Build / check (push) Failing after 0s
2025-05-08 12:57:24 +02:00
Jesse Wierzbinski cf08479c48
chore: ⬆️ Upgrade dependencies
Some checks failed
CodeQL Scan / Analyze (javascript-typescript) (push) Failing after 0s
Build Docker Images / lint (push) Failing after 6s
Build Docker Images / check (push) Failing after 6s
Build Docker Images / tests (push) Failing after 6s
Build Docker Images / build (server, Dockerfile, ${{ github.repository_owner }}/server) (push) Has been skipped
Build Docker Images / build (worker, Worker.Dockerfile, ${{ github.repository_owner }}/worker) (push) Has been skipped
Deploy Docs to GitHub Pages / build (push) Failing after 0s
Deploy Docs to GitHub Pages / Deploy (push) Has been skipped
Mirror to Codeberg / Mirror (push) Failing after 0s
Nix Build / check (push) Failing after 0s
2025-05-05 18:03:57 +02:00
Jesse Wierzbinski ddb3cfc978
perf(api): Store user and post metrics directly in database instead of recalculating them on-the-fly
Some checks failed
CodeQL Scan / Analyze (javascript-typescript) (push) Failing after 0s
Build Docker Images / lint (push) Failing after 6s
Build Docker Images / check (push) Failing after 6s
Build Docker Images / tests (push) Failing after 6s
Deploy Docs to GitHub Pages / build (push) Failing after 1s
Build Docker Images / build (server, Dockerfile, ${{ github.repository_owner }}/server) (push) Has been skipped
Build Docker Images / build (worker, Worker.Dockerfile, ${{ github.repository_owner }}/worker) (push) Has been skipped
Deploy Docs to GitHub Pages / Deploy (push) Has been skipped
Mirror to Codeberg / Mirror (push) Failing after 1s
Nix Build / check (push) Failing after 1s
2025-05-04 16:38:37 +02:00
Jesse Wierzbinski cd12ccd6c1
feat(federation): Implement Share federation support
Some checks failed
CodeQL Scan / Analyze (javascript-typescript) (push) Failing after 0s
Build Docker Images / lint (push) Failing after 6s
Build Docker Images / check (push) Failing after 7s
Build Docker Images / tests (push) Failing after 6s
Build Docker Images / build (server, Dockerfile, ${{ github.repository_owner }}/server) (push) Has been skipped
Build Docker Images / build (worker, Worker.Dockerfile, ${{ github.repository_owner }}/worker) (push) Has been skipped
Deploy Docs to GitHub Pages / build (push) Failing after 0s
Deploy Docs to GitHub Pages / Deploy (push) Has been skipped
Mirror to Codeberg / Mirror (push) Failing after 0s
Nix Build / check (push) Failing after 0s
2025-05-02 12:48:47 +02:00
Jesse Wierzbinski ec69fc2ac0
feat(api): Add media attachments to RSS and Atom feeds
Some checks failed
CodeQL Scan / Analyze (javascript-typescript) (push) Failing after 0s
Build Docker Images / lint (push) Failing after 8s
Build Docker Images / check (push) Failing after 6s
Build Docker Images / tests (push) Failing after 7s
Deploy Docs to GitHub Pages / build (push) Failing after 0s
Build Docker Images / build (server, Dockerfile, ${{ github.repository_owner }}/server) (push) Has been skipped
Build Docker Images / build (worker, Worker.Dockerfile, ${{ github.repository_owner }}/worker) (push) Has been skipped
Deploy Docs to GitHub Pages / Deploy (push) Has been skipped
Mirror to Codeberg / Mirror (push) Failing after 0s
Nix Build / check (push) Failing after 0s
2025-05-01 22:47:29 +02:00
Jesse Wierzbinski 3832328aaf
feat(api): Add RSS and Atom feed functionality 2025-05-01 22:35:32 +02:00
Jesse Wierzbinski 70aff2df68
fix: 🐛 Re-add mathjax dependency
Required for building docs
2025-05-01 22:02:45 +02:00
Jesse Wierzbinski 55329eaae0
chore: ⬆️ Upgrade Bun to 1.2.11 2025-05-01 16:30:58 +02:00
Jesse Wierzbinski 4a4f72fd66
chore: ⬆️ Upgrade dependencies 2025-05-01 16:27:34 +02:00
Jesse Wierzbinski 441c7714d9
fix(api): 🐛 Fix source not being correctly saved when creating notes 2025-05-01 15:52:00 +02:00
Jesse Wierzbinski 294924fc49
fix(api): 🐛 Don't allow replying to reblogs
Some checks failed
CodeQL Scan / Analyze (javascript-typescript) (push) Failing after 0s
Build Docker Images / lint (push) Failing after 6s
Build Docker Images / check (push) Failing after 6s
Build Docker Images / tests (push) Failing after 6s
Deploy Docs to GitHub Pages / build (push) Failing after 0s
Build Docker Images / build (server, Dockerfile, ${{ github.repository_owner }}/server) (push) Has been skipped
Build Docker Images / build (worker, Worker.Dockerfile, ${{ github.repository_owner }}/worker) (push) Has been skipped
Deploy Docs to GitHub Pages / Deploy (push) Has been skipped
Mirror to Codeberg / Mirror (push) Failing after 0s
Nix Build / check (push) Failing after 0s
2025-05-01 04:36:08 +02:00
Jesse Wierzbinski 2155ca12be
fix(federation): 🐛 Fix remote interactions not sending out notifications 2025-05-01 03:19:38 +02:00
Jesse Wierzbinski 8874688054
fix(api): 🐛 Massively increase lookup ratelimit 2025-05-01 03:03:14 +02:00
Jesse Wierzbinski cf75679d7f
fix: 🐛 Remove usage of old cli start command 2025-05-01 01:58:22 +02:00
Jesse Wierzbinski 37cbe12c4d
fix: 🐛 Copy detect-libc inside node_modules
Some checks failed
CodeQL Scan / Analyze (javascript-typescript) (push) Failing after 0s
Build Docker Images / lint (push) Failing after 6s
Build Docker Images / check (push) Failing after 6s
Build Docker Images / tests (push) Failing after 6s
Deploy Docs to GitHub Pages / build (push) Failing after 0s
Build Docker Images / build (worker, Worker.Dockerfile, ${{ github.repository_owner }}/worker) (push) Has been skipped
Build Docker Images / build (server, Dockerfile, ${{ github.repository_owner }}/server) (push) Has been skipped
Deploy Docs to GitHub Pages / Deploy (push) Has been skipped
Mirror to Codeberg / Mirror (push) Failing after 0s
Nix Build / check (push) Failing after 0s
2025-04-30 21:26:35 +02:00
Jesse Wierzbinski d2531e8ace
fix: 🐛 Fix incorrect worker Dockerfile executable
Some checks failed
CodeQL Scan / Analyze (javascript-typescript) (push) Failing after 1s
Build Docker Images / lint (push) Failing after 7s
Build Docker Images / check (push) Failing after 6s
Build Docker Images / tests (push) Failing after 7s
Deploy Docs to GitHub Pages / build (push) Failing after 0s
Build Docker Images / build (worker, Worker.Dockerfile, ${{ github.repository_owner }}/worker) (push) Has been skipped
Build Docker Images / build (server, Dockerfile, ${{ github.repository_owner }}/server) (push) Has been skipped
Deploy Docs to GitHub Pages / Deploy (push) Has been skipped
Mirror to Codeberg / Mirror (push) Failing after 0s
Nix Build / check (push) Failing after 0s
2025-04-30 02:53:58 +02:00
Jesse Wierzbinski 9e08248f0c
refactor: 🎨 De-clutter flake
Some checks failed
CodeQL Scan / Analyze (javascript-typescript) (push) Failing after 0s
Build Docker Images / lint (push) Failing after 6s
Build Docker Images / check (push) Failing after 6s
Build Docker Images / tests (push) Failing after 6s
Deploy Docs to GitHub Pages / build (push) Failing after 0s
Build Docker Images / build (server, Dockerfile, ${{ github.repository_owner }}/server) (push) Has been skipped
Build Docker Images / build (worker, Worker.Dockerfile, ${{ github.repository_owner }}/worker) (push) Has been skipped
Deploy Docs to GitHub Pages / Deploy (push) Has been skipped
Mirror to Codeberg / Mirror (push) Failing after 0s
Nix Build / check (push) Failing after 0s
2025-04-20 17:08:46 +02:00
Jesse Wierzbinski 138f4fade3
refactor(database): ♻️ Use Bun.SQL instead of pg
Some checks failed
CodeQL Scan / Analyze (javascript-typescript) (push) Failing after 1s
Build Docker Images / lint (push) Failing after 7s
Build Docker Images / check (push) Failing after 7s
Build Docker Images / tests (push) Failing after 7s
Deploy Docs to GitHub Pages / build (push) Failing after 0s
Build Docker Images / build (server, Dockerfile, ${{ github.repository_owner }}/server) (push) Has been skipped
Build Docker Images / build (worker, Worker.Dockerfile, ${{ github.repository_owner }}/worker) (push) Has been skipped
Deploy Docs to GitHub Pages / Deploy (push) Has been skipped
Mirror to Codeberg / Mirror (push) Failing after 0s
Nix Build / check (push) Failing after 0s
2025-04-19 14:15:08 +02:00
Jesse Wierzbinski f95f57c4d4
chore: ⬆️ Upgrade to Bun 1.2.10 2025-04-19 13:29:35 +02:00
Jesse Wierzbinski fd9145b7a8
chore: 🔥 Remove useless file 2025-04-19 13:27:25 +02:00
Jesse Wierzbinski 8ae4f3815a
fix(federation): 🚑 Fix broken inbound federation and add end-to-end testing for federation 2025-04-19 13:16:53 +02:00
Jesse Wierzbinski 85ef96fc7f
fix(federation): 🐛 Use explicit header object destructuring in inbox processing
Some checks failed
CodeQL Scan / Analyze (javascript-typescript) (push) Failing after 0s
Build Docker Images / lint (push) Failing after 6s
Build Docker Images / check (push) Failing after 6s
Build Docker Images / tests (push) Failing after 6s
Deploy Docs to GitHub Pages / build (push) Failing after 0s
Build Docker Images / build (server, Dockerfile, ${{ github.repository_owner }}/server) (push) Has been skipped
Build Docker Images / build (worker, Worker.Dockerfile, ${{ github.repository_owner }}/worker) (push) Has been skipped
Deploy Docs to GitHub Pages / Deploy (push) Has been skipped
Mirror to Codeberg / Mirror (push) Failing after 1s
Nix Build / check (push) Failing after 1s
2025-04-18 14:58:04 +02:00
Jesse Wierzbinski 6edb0310d8
fix(api): 🐛 Don't require JWT cookie for static content in bull-board UI 2025-04-18 14:38:00 +02:00
Jesse Wierzbinski 054b8bc5cb
fix(federation): 🐛 Fix incorrect destructuring causing federation issues 2025-04-18 14:27:53 +02:00
Jesse Wierzbinski 1d17831454
chore: Remove unused dependencies
Some checks failed
CodeQL Scan / Analyze (javascript-typescript) (push) Failing after 0s
Build Docker Images / lint (push) Failing after 6s
Build Docker Images / check (push) Failing after 6s
Build Docker Images / tests (push) Failing after 5s
Deploy Docs to GitHub Pages / build (push) Failing after 0s
Build Docker Images / build (server, Dockerfile, ${{ github.repository_owner }}/server) (push) Has been skipped
Build Docker Images / build (worker, Worker.Dockerfile, ${{ github.repository_owner }}/worker) (push) Has been skipped
Deploy Docs to GitHub Pages / Deploy (push) Has been skipped
Mirror to Codeberg / Mirror (push) Failing after 0s
Nix Build / check (push) Failing after 0s
2025-04-16 17:31:34 +02:00
Jesse Wierzbinski 98616ceefb
chore: ⬆️ Upgrade dependencies 2025-04-16 17:03:28 +02:00
Jesse Wierzbinski ffa0c209b6
fix(api): 👽 Use new Scalar API
Some checks failed
CodeQL Scan / Analyze (javascript-typescript) (push) Failing after 0s
Build Docker Images / lint (push) Failing after 6s
Build Docker Images / check (push) Failing after 6s
Build Docker Images / tests (push) Failing after 6s
Deploy Docs to GitHub Pages / build (push) Failing after 0s
Build Docker Images / build (server, Dockerfile, ${{ github.repository_owner }}/server) (push) Has been skipped
Build Docker Images / build (worker, Worker.Dockerfile, ${{ github.repository_owner }}/worker) (push) Has been skipped
Deploy Docs to GitHub Pages / Deploy (push) Has been skipped
Mirror to Codeberg / Mirror (push) Failing after 0s
Nix Build / check (push) Failing after 0s
2025-04-16 16:40:47 +02:00
Jesse Wierzbinski a2e907390f
fix(api): 🐛 Don't use URL in Versia entity schemas, fixes OpenAPI 2025-04-16 16:35:17 +02:00
Jesse Wierzbinski 0a712128a5
fix(api): 🐛 Fix OpenID provider logos not showing up
Some checks failed
CodeQL Scan / Analyze (javascript-typescript) (push) Failing after 1s
Build Docker Images / lint (push) Failing after 7s
Build Docker Images / check (push) Failing after 6s
Build Docker Images / tests (push) Failing after 6s
Deploy Docs to GitHub Pages / build (push) Failing after 0s
Build Docker Images / build (server, Dockerfile, ${{ github.repository_owner }}/server) (push) Has been skipped
Build Docker Images / build (worker, Worker.Dockerfile, ${{ github.repository_owner }}/worker) (push) Has been skipped
Deploy Docs to GitHub Pages / Deploy (push) Has been skipped
Mirror to Codeberg / Mirror (push) Failing after 0s
Nix Build / check (push) Failing after 0s
2025-04-15 22:59:05 +02:00
Jesse Wierzbinski d54527454f
build: 🚑 Remove old workaround that isn't needed anymore 2025-04-15 21:45:21 +02:00
Jesse Wierzbinski f35aae6c44
chore: 💚 Update dependency hash 2025-04-15 21:39:16 +02:00
Jesse Wierzbinski fb5c3fcd12
refactor(config): 🔥 Remove dependency on c12, use confbox instead 2025-04-15 21:37:36 +02:00
Jesse Wierzbinski da1e209f9e
fix: 🐛 Replace dataDir with module path 2025-04-15 21:07:47 +02:00
Jesse Wierzbinski d6b15b1b85
fix: 🐛 Disable msgpackr native acceleration in Nix deployment
It keeps causing issues with the module loading and being annoying to work with in general
2025-04-15 21:04:08 +02:00
Jesse Wierzbinski 26f2dca5d6
fix(config): 🐛 Make vapid key schema more consistent with other key schemas 2025-04-15 20:46:29 +02:00
Jesse Wierzbinski 88944712fe
fix(config): 🐛 Trigger vapid key autogeneration even when block is not commented out 2025-04-15 20:43:32 +02:00
Jesse Wierzbinski b67d86dc57
fix: 🐛 Fix NixOS module passing incorrect environment variable 2025-04-15 20:31:25 +02:00
Jesse Wierzbinski dad99e854d
fix: 🐛 Fix NixOS module incorrect systemd definitions 2025-04-15 20:27:50 +02:00
Jesse Wierzbinski 2d4465617b
fix: 🐛 Fix more errors in NixOS module definition 2025-04-15 20:19:24 +02:00
Jesse Wierzbinski d46befbd1d
fix: 🐛 Fix NixOS module definitions 2025-04-15 18:11:14 +02:00
Jesse Wierzbinski 765348c440
fix: 🐛 Fix Nix module errors when importing
Some checks failed
CodeQL Scan / Analyze (javascript-typescript) (push) Failing after 0s
Build Docker Images / lint (push) Failing after 6s
Build Docker Images / check (push) Failing after 6s
Build Docker Images / tests (push) Failing after 6s
Deploy Docs to GitHub Pages / build (push) Failing after 1s
Build Docker Images / build (server, Dockerfile, ${{ github.repository_owner }}/server) (push) Has been skipped
Build Docker Images / build (worker, Worker.Dockerfile, ${{ github.repository_owner }}/worker) (push) Has been skipped
Deploy Docs to GitHub Pages / Deploy (push) Has been skipped
Mirror to Codeberg / Mirror (push) Failing after 1s
Nix Build / check (push) Failing after 1s
2025-04-15 14:15:36 +02:00
Jesse Wierzbinski 404d63f6d0
feat(media): Add path_style and path configuration to S3 2025-04-15 13:28:12 +02:00
Jesse Wierzbinski 5bb4e967a7
fix: 📝 Fix typo in Nix docs 2025-04-15 13:05:00 +02:00
Jesse Wierzbinski c26e896afe
docs: 📝 Document Nix installation 2025-04-15 13:03:52 +02:00
Jesse Wierzbinski 2d921438a9
feat: Add DevShell to flake 2025-04-15 11:32:11 +02:00
Jesse Wierzbinski 385997cdcc
feat: Add NixOS module 2025-04-15 11:15:17 +02:00
Jesse Wierzbinski 37bc4458e5
test: 🧪 Fix failing tests due to incorrect cwd resolving
Some checks failed
CodeQL Scan / Analyze (javascript-typescript) (push) Failing after 0s
Build Docker Images / lint (push) Failing after 6s
Build Docker Images / check (push) Failing after 6s
Build Docker Images / tests (push) Failing after 5s
Deploy Docs to GitHub Pages / build (push) Failing after 0s
Build Docker Images / build (server, Dockerfile, ${{ github.repository_owner }}/server) (push) Has been skipped
Build Docker Images / build (worker, Worker.Dockerfile, ${{ github.repository_owner }}/worker) (push) Has been skipped
Deploy Docs to GitHub Pages / Deploy (push) Has been skipped
Mirror to Codeberg / Mirror (push) Failing after 0s
Nix Build / check (push) Failing after 0s
2025-04-14 17:30:01 +02:00
Jesse Wierzbinski 1beb18e321
build: 🏗️ Package Worker 2025-04-14 17:13:36 +02:00
Jesse Wierzbinski 5a4ce29206
build: 🏗️ Make Nix build great again 2025-04-14 16:51:00 +02:00
Jesse Wierzbinski 1679585c4c
fix: 🚨 Enable more Biome 2.0 rules
Some checks failed
CodeQL Scan / Analyze (javascript-typescript) (push) Failing after 4s
Build Docker Images / lint (push) Failing after 10s
Build Docker Images / check (push) Failing after 10s
Build Docker Images / tests (push) Failing after 6s
Deploy Docs to GitHub Pages / build (push) Failing after 0s
Build Docker Images / build (server, Dockerfile, ${{ github.repository_owner }}/server) (push) Has been skipped
Build Docker Images / build (worker, Worker.Dockerfile, ${{ github.repository_owner }}/worker) (push) Has been skipped
Deploy Docs to GitHub Pages / Deploy (push) Has been skipped
Mirror to Codeberg / Mirror (push) Failing after 0s
Nix Build / check (push) Failing after 0s
2025-04-10 19:56:42 +02:00
Jesse Wierzbinski 963173cdae
chore: ⬆️ Upgrade to Biome 2.0 2025-04-10 19:15:31 +02:00
Jesse Wierzbinski e7aec8752c
refactor(database): 🔥 Remove unnecessary Redis connections 2025-04-10 18:58:44 +02:00
Jesse Wierzbinski dc1b58a791
chore: ⬆️ Upgrade Bun to 1.2.9 2025-04-10 18:53:01 +02:00
Jesse Wierzbinski dbde49b9bd
chore: ⬆️ Upgrade dependencies 2025-04-10 18:50:41 +02:00
Gaspard Wierzbinski 7e44e55b3f
Merge pull request #36 from versia-pub/refactor/federation
Some checks failed
CodeQL Scan / Analyze (javascript-typescript) (push) Failing after 54s
Build Docker Images / lint (push) Successful in 28s
Build Docker Images / check (push) Successful in 53s
Build Docker Images / tests (push) Failing after 6s
Build Docker Images / build (server, Dockerfile, ${{ github.repository_owner }}/server) (push) Has been skipped
Build Docker Images / build (worker, Worker.Dockerfile, ${{ github.repository_owner }}/worker) (push) Has been skipped
Deploy Docs to GitHub Pages / build (push) Failing after 12s
Mirror to Codeberg / Mirror (push) Failing after 0s
Deploy Docs to GitHub Pages / Deploy (push) Has been skipped
Nix Build / check (push) Failing after 32m28s
Refactor/federation
2025-04-09 02:18:14 +02:00
Jesse Wierzbinski 1d301d72ae
fix: 🚨 Fix DeepSource linter warnings 2025-04-09 02:15:00 +02:00
Jesse Wierzbinski 45e5460975
docs(federation): 📝 Update SDK documentation 2025-04-08 21:54:55 +02:00
Jesse Wierzbinski f79b0bc999
refactor(federation): 🔥 Refactor Note federation and creation code 2025-04-08 18:13:30 +02:00
Jesse Wierzbinski 54b2dfb78d
refactor(federation): 🔥 Remove confusing User federation methods 2025-04-08 17:27:08 +02:00
Jesse Wierzbinski 9ff9b90f6b
refactor(federation): ♻️ Refactor User federation code 2025-04-08 16:59:18 +02:00
Jesse Wierzbinski d638610361
refactor(federation): ♻️ Rewrite federation SDK 2025-04-08 16:01:10 +02:00
Jesse Wierzbinski ad1dc13a51
chore: ⬆️ Upgrade dependencies
Some checks failed
CodeQL Scan / Analyze (javascript-typescript) (push) Failing after 1m1s
Build Docker Images / lint (push) Successful in 38s
Build Docker Images / check (push) Successful in 51s
Build Docker Images / tests (push) Failing after 6s
Build Docker Images / build (server, Dockerfile, ${{ github.repository_owner }}/server) (push) Has been skipped
Build Docker Images / build (worker, Worker.Dockerfile, ${{ github.repository_owner }}/worker) (push) Has been skipped
Deploy Docs to GitHub Pages / build (push) Failing after 6s
Mirror to Codeberg / Mirror (push) Failing after 0s
Deploy Docs to GitHub Pages / Deploy (push) Has been skipped
Nix Build / check (push) Failing after 5s
2025-04-07 21:51:59 +02:00
Jesse Wierzbinski 2908fcc9e8
fix(worker): 🐛 Remove old bull-board patch, use official fix instead
Explicitely specifying the path prevents the module from using `eval`
2025-04-07 21:50:43 +02:00
Jesse Wierzbinski 512e0295a2
fix(config): 🐛 Fix bundling errors related to config
Some checks failed
CodeQL Scan / Analyze (javascript-typescript) (push) Failing after 52s
Build Docker Images / check (push) Failing after 5m20s
Build Docker Images / tests (push) Failing after 6s
Build Docker Images / build (server, Dockerfile, ${{ github.repository_owner }}/server) (push) Has been skipped
Build Docker Images / build (worker, Worker.Dockerfile, ${{ github.repository_owner }}/worker) (push) Has been skipped
Deploy Docs to GitHub Pages / build (push) Failing after 13s
Mirror to Codeberg / Mirror (push) Failing after 0s
Deploy Docs to GitHub Pages / Deploy (push) Has been skipped
Build Docker Images / lint (push) Successful in 26s
Nix Build / check (push) Failing after 32m35s
2025-04-06 22:11:18 +02:00
Jesse Wierzbinski 12740a2d06
feat(config): Allow specifying config path via env variable 2025-04-06 21:40:00 +02:00
Jesse Wierzbinski 838f2fd4cf
fix: 🐛 Fix cyclical imports causing crashes 2025-04-06 21:18:24 +02:00
Jesse Wierzbinski 52630e7042
chore: ⬆️ Upgrade dependencies 2025-04-06 20:58:00 +02:00
Jesse Wierzbinski 40b34e4855
fix: 🐛 Fix build error because of missing library
Some checks failed
CodeQL Scan / Analyze (javascript-typescript) (push) Failing after 5s
Build Docker Images / lint (push) Failing after 10s
Build Docker Images / check (push) Failing after 10s
Build Docker Images / tests (push) Failing after 6s
Build Docker Images / build (server, Dockerfile, ${{ github.repository_owner }}/server) (push) Has been skipped
Build Docker Images / build (worker, Worker.Dockerfile, ${{ github.repository_owner }}/worker) (push) Has been skipped
Deploy Docs to GitHub Pages / build (push) Failing after 4s
Mirror to Codeberg / Mirror (push) Failing after 0s
Deploy Docs to GitHub Pages / Deploy (push) Has been skipped
Nix Build / check (push) Failing after 5s
2025-04-01 21:07:56 +02:00
Jesse Wierzbinski 9840b5e10f
fix(config): 🐛 Expand contexts in which the config will autogenerate keys 2025-04-01 20:32:43 +02:00
Jesse Wierzbinski 14855b9dfe
fix: 🐛 Update example docker-compose.yml
Some checks failed
CodeQL Scan / Analyze (javascript-typescript) (push) Failing after 45s
Build Docker Images / lint (push) Successful in 28s
Build Docker Images / check (push) Successful in 50s
Build Docker Images / tests (push) Failing after 6s
Build Docker Images / build (server, Dockerfile, ${{ github.repository_owner }}/server) (push) Has been skipped
Build Docker Images / build (worker, Worker.Dockerfile, ${{ github.repository_owner }}/worker) (push) Has been skipped
Deploy Docs to GitHub Pages / build (push) Failing after 12s
Mirror to Codeberg / Mirror (push) Failing after 0s
Deploy Docs to GitHub Pages / Deploy (push) Has been skipped
Nix Build / check (push) Failing after 32m30s
2025-04-01 16:05:51 +02:00
Jesse Wierzbinski 844fbf7c9e
docs: 📝 Update installation documentation to match latest guidelines 2025-04-01 13:52:47 +02:00
Jesse Wierzbinski 7a6b93a36c
chore: ⬆️ Upgrade Bun to 1.2.8 2025-04-01 13:18:14 +02:00
Jesse Wierzbinski dc1ddb758d
fix(media): 🐛 Don't proxy media from trusted origins, use new ProxiedUrl class
Some checks failed
CodeQL Scan / Analyze (javascript-typescript) (push) Failing after 6s
Build Docker Images / lint (push) Failing after 10s
Build Docker Images / check (push) Failing after 11s
Build Docker Images / tests (push) Failing after 27s
Build Docker Images / build (server, Dockerfile, ${{ github.repository_owner }}/server) (push) Has been skipped
Build Docker Images / build (worker, Worker.Dockerfile, ${{ github.repository_owner }}/worker) (push) Has been skipped
Deploy Docs to GitHub Pages / build (push) Failing after 6s
Mirror to Codeberg / Mirror (push) Failing after 0s
Deploy Docs to GitHub Pages / Deploy (push) Has been skipped
Nix Build / check (push) Failing after 5s
2025-03-30 23:44:50 +02:00
Jesse Wierzbinski 411fcd8af5
refactor(api): ♻️ Reduce complexity of a few functions 2025-03-30 23:17:11 +02:00
Jesse Wierzbinski 25ea870f71
refactor: ♻️ Don't use Bun global 2025-03-30 23:06:34 +02:00
Jesse Wierzbinski d55668d529
fix(database): 💩 Replace uuid_generate_v7 in migrations to a dummy PostgreSQL function
Prevents a fatal error when running migrations on vanilla PostgreSQL instance without the pg_uuidv7 extension
2025-03-30 22:32:04 +02:00
Jesse Wierzbinski dde085464c
fix(database): 🐛 Remove some leftover parts of pg_uuidv7 2025-03-30 22:21:01 +02:00
Jesse Wierzbinski 9d79543951
ci: 👷 Use vanilla PostgreSQL 17 in CI tests 2025-03-30 22:12:40 +02:00
Jesse Wierzbinski 37f68bbffd
refactor(database): Remove dependency on pg_uuidv7 extension 2025-03-30 22:10:33 +02:00
Jesse Wierzbinski 2bb3731187
fix: 🐛 Correctly set youch to beta version 2025-03-30 21:45:51 +02:00
Jesse Wierzbinski ed06d0b54c
chore: ⬆️ Upgrade dependencies 2025-03-30 21:15:02 +02:00
Jesse Wierzbinski c68bfdf6e1
refactor(federation): ♻️ Simplify inbox processing by using ApiError 2025-03-30 21:13:47 +02:00
Jesse Wierzbinski 757c227f00
fix(federation): 🐛 Update user processing to not refetch user when its data is already available 2025-03-30 21:02:36 +02:00
Jesse Wierzbinski c9a1581932
feat(api): Implement duration controls on mutes 2025-03-30 20:54:47 +02:00
Jesse Wierzbinski 9d1d56bd08
feat(api): Implement indexing toggle and followers/following privacy settings 2025-03-30 20:32:42 +02:00
Jesse Wierzbinski 666eef063c
chore: 📝 Update changelog 2025-03-30 19:56:46 +02:00
Jesse Wierzbinski 1b983f9334
fix(api): 🐛 Fix routes using incorrect path parameter notation
Some checks failed
CodeQL Scan / Analyze (javascript-typescript) (push) Failing after 44s
Build Docker Images / lint (push) Successful in 29s
Build Docker Images / check (push) Failing after 5m42s
Build Docker Images / tests (push) Failing after 6s
Build Docker Images / build (server, Dockerfile, ${{ github.repository_owner }}/server) (push) Has been skipped
Build Docker Images / build (worker, Worker.Dockerfile, ${{ github.repository_owner }}/worker) (push) Has been skipped
Deploy Docs to GitHub Pages / build (push) Failing after 13s
Mirror to Codeberg / Mirror (push) Failing after 0s
Deploy Docs to GitHub Pages / Deploy (push) Has been skipped
Nix Build / check (push) Failing after 32m30s
2025-03-29 03:59:06 +01:00
Jesse Wierzbinski e5b7325379
fix: 🐛 Remove broken import from dependency 2025-03-29 03:39:55 +01:00
Jesse Wierzbinski 58342e86e1
refactor(api): ♻️ Move from @hono/zod-openapi to hono-openapi
hono-openapi is easier to work with and generates better OpenAPI definitions
2025-03-29 03:30:06 +01:00
Jesse Wierzbinski 0576aff972
chore: ⬆️ Upgrade dependencies
Some checks failed
CodeQL Scan / Analyze (javascript-typescript) (push) Failing after 46s
Build Docker Images / lint (push) Successful in 33s
Build Docker Images / check (push) Successful in 1m6s
Build Docker Images / tests (push) Failing after 6s
Build Docker Images / build (server, Dockerfile, ${{ github.repository_owner }}/server) (push) Has been skipped
Build Docker Images / build (worker, Worker.Dockerfile, ${{ github.repository_owner }}/worker) (push) Has been skipped
Deploy Docs to GitHub Pages / build (push) Failing after 12s
Mirror to Codeberg / Mirror (push) Failing after 0s
Deploy Docs to GitHub Pages / Deploy (push) Has been skipped
Nix Build / check (push) Failing after 32m37s
2025-03-28 22:26:20 +01:00
Jesse Wierzbinski cd4cfa6a70
fix(api): 🐛 Add tags to all API routes that were missing one 2025-03-28 22:12:07 +01:00
Jesse Wierzbinski d75254fc71
refactor(federation): 🚚 Change Like path from /objects/{id} to /likes/{id} 2025-03-28 22:06:42 +01:00
Jesse Wierzbinski 3d3e64edab
feat(api): Implement rate limiting
Some checks failed
CodeQL Scan / Analyze (javascript-typescript) (push) Failing after 42s
Build Docker Images / lint (push) Successful in 31s
Build Docker Images / check (push) Successful in 1m3s
Build Docker Images / tests (push) Failing after 6s
Build Docker Images / build (server, Dockerfile, ${{ github.repository_owner }}/server) (push) Has been skipped
Build Docker Images / build (worker, Worker.Dockerfile, ${{ github.repository_owner }}/worker) (push) Has been skipped
Deploy Docs to GitHub Pages / build (push) Failing after 13s
Mirror to Codeberg / Mirror (push) Failing after 0s
Deploy Docs to GitHub Pages / Deploy (push) Has been skipped
Nix Build / check (push) Failing after 33m18s
2025-03-27 20:12:00 +01:00
Jesse Wierzbinski 1993231663
feat(api): 🧑‍💻 Improve error quality with Youch 2025-03-27 19:08:38 +01:00
Jesse Wierzbinski 58b4d7454f
refactor(api): ♻️ Serve frontend from static files instead of proxying another process 2025-03-27 18:51:22 +01:00
Jesse Wierzbinski 5f8c57b3e1
chore: ⬆️ Upgrade Bun to 1.2.7 2025-03-27 14:41:43 +01:00
Jesse Wierzbinski ebb0f52f1e
chore: ⬆️ Upgrade dependencies 2025-03-27 14:16:22 +01:00
Jesse Wierzbinski c674a1309c
feat(api): Add OpenAPI visualizer
Some checks failed
CodeQL Scan / Analyze (javascript-typescript) (push) Failing after 53s
Build Docker Images / lint (push) Failing after 10s
Build Docker Images / check (push) Failing after 10s
Build Docker Images / tests (push) Failing after 6s
Build Docker Images / build (server, Dockerfile, ${{ github.repository_owner }}/server) (push) Has been skipped
Build Docker Images / build (worker, Worker.Dockerfile, ${{ github.repository_owner }}/worker) (push) Has been skipped
Deploy Docs to GitHub Pages / build (push) Failing after 5s
Mirror to Codeberg / Mirror (push) Failing after 0s
Deploy Docs to GitHub Pages / Deploy (push) Has been skipped
Nix Build / check (push) Failing after 5s
2025-03-24 15:25:40 +01:00
Jesse Wierzbinski 65e2e19ff1
refactor(api): ♻️ Properly reuse error messages and schemas 2025-03-24 14:42:09 +01:00
Jesse Wierzbinski 7112a66e4c
refactor: Refactor tests to not use module mocks, so bun test can be used
Some checks failed
CodeQL Scan / Analyze (javascript-typescript) (push) Failing after 45s
Build Docker Images / lint (push) Successful in 27s
Build Docker Images / check (push) Successful in 1m7s
Build Docker Images / tests (push) Failing after 6s
Build Docker Images / build (server, Dockerfile, ${{ github.repository_owner }}/server) (push) Has been skipped
Build Docker Images / build (worker, Worker.Dockerfile, ${{ github.repository_owner }}/worker) (push) Has been skipped
Deploy Docs to GitHub Pages / build (push) Failing after 12s
Mirror to Codeberg / Mirror (push) Failing after 0s
Deploy Docs to GitHub Pages / Deploy (push) Has been skipped
Nix Build / check (push) Failing after 32m31s
2025-03-23 04:12:28 +01:00
Jesse Wierzbinski ec506241f0
test(api): Remove old tests and introduce new, better ones
Some checks failed
CodeQL Scan / Analyze (javascript-typescript) (push) Failing after 6s
Build Docker Images / lint (push) Successful in 50s
Build Docker Images / check (push) Successful in 1m24s
Build Docker Images / tests (push) Failing after 8s
Build Docker Images / build (server, Dockerfile, ${{ github.repository_owner }}/server) (push) Has been skipped
Build Docker Images / build (worker, Worker.Dockerfile, ${{ github.repository_owner }}/worker) (push) Has been skipped
Deploy Docs to GitHub Pages / build (push) Failing after 15s
Mirror to Codeberg / Mirror (push) Failing after 0s
Deploy Docs to GitHub Pages / Deploy (push) Has been skipped
Nix Build / check (push) Failing after 33m5s
2025-03-23 03:34:17 +01:00
Jesse Wierzbinski f1ef85b314
fix: 🏷️ Remove unnecessary ts-expect-error
Some checks failed
CodeQL Scan / Analyze (javascript-typescript) (push) Failing after 5s
Build Docker Images / lint (push) Failing after 10s
Build Docker Images / check (push) Failing after 10s
Build Docker Images / tests (push) Failing after 6s
Build Docker Images / build (server, Dockerfile, ${{ github.repository_owner }}/server) (push) Has been skipped
Build Docker Images / build (worker, Worker.Dockerfile, ${{ github.repository_owner }}/worker) (push) Has been skipped
Deploy Docs to GitHub Pages / build (push) Failing after 5s
Mirror to Codeberg / Mirror (push) Failing after 1s
Deploy Docs to GitHub Pages / Deploy (push) Has been skipped
Nix Build / check (push) Failing after 5s
2025-03-22 18:17:53 +01:00
Jesse Wierzbinski 2a1a164d59
fix: 🐛 Recreate broken lockfile 2025-03-22 18:10:48 +01:00
Jesse Wierzbinski 8d1af1b0cd
refactor(api): 🔥 Remove old @versia/client version 2025-03-22 18:04:47 +01:00
Jesse Wierzbinski 54e282b03c
refactor(api): ♻️ Refactor all tests to use new client 2025-03-22 17:32:46 +01:00
Jesse Wierzbinski b6373dc185
fix: 🚨 Use ts-expect-error over ts-ignore
Some checks failed
CodeQL Scan / Analyze (javascript-typescript) (push) Failing after 45s
Build Docker Images / lint (push) Successful in 30s
Build Docker Images / check (push) Successful in 1m11s
Build Docker Images / tests (push) Failing after 6s
Build Docker Images / build (server, Dockerfile, ${{ github.repository_owner }}/server) (push) Has been skipped
Build Docker Images / build (worker, Worker.Dockerfile, ${{ github.repository_owner }}/worker) (push) Has been skipped
Deploy Docs to GitHub Pages / build (push) Failing after 5s
Mirror to Codeberg / Mirror (push) Failing after 1s
Deploy Docs to GitHub Pages / Deploy (push) Has been skipped
Nix Build / check (push) Failing after 5s
2025-03-22 04:06:37 +01:00
Jesse Wierzbinski 84b9fc3719
refactor(api): ♻️ Refactor test code to use new client 2025-03-22 04:04:06 +01:00
Jesse Wierzbinski 232ce83e4d
refactor(api): 🔥 Remove old ID lookup API 2025-03-22 03:34:59 +01:00
Jesse Wierzbinski dd38a3900c
chore: ⬆️ Upgrade dependencies
Some checks failed
CodeQL Scan / Analyze (javascript-typescript) (push) Failing after 58s
Build Docker Images / lint (push) Successful in 30s
Build Docker Images / check (push) Failing after 1m11s
Build Docker Images / tests (push) Failing after 6s
Build Docker Images / build (server, Dockerfile, ${{ github.repository_owner }}/server) (push) Has been skipped
Build Docker Images / build (worker, Worker.Dockerfile, ${{ github.repository_owner }}/worker) (push) Has been skipped
Deploy Docs to GitHub Pages / build (push) Failing after 16s
Mirror to Codeberg / Mirror (push) Failing after 0s
Deploy Docs to GitHub Pages / Deploy (push) Has been skipped
Nix Build / check (push) Failing after 33m10s
2025-03-22 02:38:01 +01:00
Jesse Wierzbinski c2d270e4e3
chore: ⬆️ Upgrade Bun to 1.2.5 2025-03-22 02:36:18 +01:00
Jesse Wierzbinski 3fe07a79b8
refactor(api): ♻️ Move all client schema code to new package 2025-03-22 02:34:03 +01:00
Jesse Wierzbinski 52602c3da7
chore: ⬆️ Upgrade dependencies
Some checks failed
CodeQL Scan / Analyze (javascript-typescript) (push) Failing after 56s
Build Docker Images / lint (push) Failing after 10s
Build Docker Images / check (push) Failing after 11s
Build Docker Images / tests (push) Failing after 6s
Build Docker Images / build (server, Dockerfile, ${{ github.repository_owner }}/server) (push) Has been skipped
Build Docker Images / build (worker, Worker.Dockerfile, ${{ github.repository_owner }}/worker) (push) Has been skipped
Deploy Docs to GitHub Pages / build (push) Failing after 5s
Mirror to Codeberg / Mirror (push) Failing after 1s
Deploy Docs to GitHub Pages / Deploy (push) Has been skipped
Nix Build / check (push) Failing after 5s
2025-03-16 17:01:07 +01:00
Jesse Wierzbinski 956a5fd2b3
chore: ⚰️ Remove unused dependencies
Some checks failed
CodeQL Scan / Analyze (javascript-typescript) (push) Failing after 5s
Build Docker Images / lint (push) Failing after 10s
Build Docker Images / check (push) Failing after 10s
Build Docker Images / tests (push) Failing after 6s
Build Docker Images / build (server, Dockerfile, ${{ github.repository_owner }}/server) (push) Has been skipped
Build Docker Images / build (worker, Worker.Dockerfile, ${{ github.repository_owner }}/worker) (push) Has been skipped
Deploy Docs to GitHub Pages / build (push) Failing after 5s
Mirror to Codeberg / Mirror (push) Failing after 0s
Deploy Docs to GitHub Pages / Deploy (push) Has been skipped
Nix Build / check (push) Failing after 5s
2025-02-26 00:37:54 +01:00
Jesse Wierzbinski 764061b4be
fix: 🔊 Automatically create logs folder if it doesn't exist 2025-02-26 00:28:05 +01:00
Gaspard Wierzbinski 457a4054b7
Merge pull request #35 from versia-pub/refactor/cli
Rewrite CLI
2025-02-26 00:14:47 +01:00
Jesse Wierzbinski ce64afe283
fix(cli): 🚨 Use RegExp literal instead of .match() 2025-02-26 00:12:31 +01:00
Jesse Wierzbinski f98d7ec560
fix: 📌 Recalculate lockfile 2025-02-26 00:09:22 +01:00
Jesse Wierzbinski 21b4f8a024
Merge branch 'main' into refactor/cli 2025-02-26 00:07:44 +01:00
Jesse Wierzbinski 5b756ea2dd
refactor(cli): ♻️ Rewrite CLI with Clerk. Removes a bunch of commands now covered by API. 2025-02-26 00:00:21 +01:00
Jesse Wierzbinski fc1877c6cc
chore: ⬆️ Upgrade Bun to 1.2.3
Some checks failed
CodeQL Scan / Analyze (javascript-typescript) (push) Failing after 58s
Build Docker Images / lint (push) Successful in 33s
Build Docker Images / check (push) Failing after 5m31s
Build Docker Images / tests (push) Failing after 8s
Build Docker Images / build (server, Dockerfile, ${{ github.repository_owner }}/server) (push) Has been skipped
Build Docker Images / build (worker, Worker.Dockerfile, ${{ github.repository_owner }}/worker) (push) Has been skipped
Deploy Docs to GitHub Pages / build (push) Failing after 17s
Mirror to Codeberg / Mirror (push) Failing after 0s
Deploy Docs to GitHub Pages / Deploy (push) Has been skipped
Nix Build / check (push) Failing after 32m49s
2025-02-25 23:33:22 +01:00
Jesse Wierzbinski f114f9a51a
chore: ⬆️ Upgrade dependencies 2025-02-25 23:32:05 +01:00
Jesse Wierzbinski 066220ffbd
feat: Add Copilot Instructions file 2025-02-25 23:18:39 +01:00
Jesse Wierzbinski e19a1b061a
chore(federation): ⬆️ Upgrade dependencies
Some checks failed
CodeQL Scan / Analyze (javascript-typescript) (push) Failing after 41s
Build Docker Images / lint (push) Successful in 31s
Build Docker Images / check (push) Successful in 1m1s
Build Docker Images / tests (push) Failing after 6s
Build Docker Images / build (server, Dockerfile, ${{ github.repository_owner }}/server) (push) Has been skipped
Build Docker Images / build (worker, Worker.Dockerfile, ${{ github.repository_owner }}/worker) (push) Has been skipped
Deploy Docs to GitHub Pages / build (push) Failing after 12s
Mirror to Codeberg / Mirror (push) Failing after 1s
Deploy Docs to GitHub Pages / Deploy (push) Has been skipped
Nix Build / check (push) Failing after 32m23s
2025-02-18 12:18:52 +01:00
Jesse Wierzbinski 28577d017a
docs: 📝 Update changelog
Some checks failed
CodeQL Scan / Analyze (javascript-typescript) (push) Failing after 5s
Build Docker Images / lint (push) Failing after 10s
Build Docker Images / check (push) Failing after 10s
Build Docker Images / tests (push) Failing after 6s
Build Docker Images / build (server, Dockerfile, ${{ github.repository_owner }}/server) (push) Has been skipped
Build Docker Images / build (worker, Worker.Dockerfile, ${{ github.repository_owner }}/worker) (push) Has been skipped
Deploy Docs to GitHub Pages / build (push) Failing after 5s
Mirror to Codeberg / Mirror (push) Failing after 0s
Deploy Docs to GitHub Pages / Deploy (push) Has been skipped
Nix Build / check (push) Failing after 4s
2025-02-18 00:59:55 +01:00
Jesse Wierzbinski 7fc7959712
chore: ⬆️ Upgrade dependencies 2025-02-17 23:33:12 +01:00
Jesse Wierzbinski 6622ee9020
refactor(federation): ♻️ Move Versia Note URIs to /notes, instead of /objects 2025-02-17 23:31:39 +01:00
Jesse Wierzbinski 4063d58d79
chore: ⬆️ Upgrade dependencies
Some checks failed
CodeQL Scan / Analyze (javascript-typescript) (push) Failing after 1m1s
Build Docker Images / lint (push) Failing after 10s
Build Docker Images / check (push) Failing after 11s
Build Docker Images / tests (push) Failing after 5s
Build Docker Images / build (server, Dockerfile, ${{ github.repository_owner }}/server) (push) Has been skipped
Build Docker Images / build (worker, Worker.Dockerfile, ${{ github.repository_owner }}/worker) (push) Has been skipped
Deploy Docs to GitHub Pages / build (push) Failing after 4s
Mirror to Codeberg / Mirror (push) Failing after 0s
Deploy Docs to GitHub Pages / Deploy (push) Has been skipped
Nix Build / check (push) Failing after 5s
2025-02-17 13:09:26 +01:00
Jesse Wierzbinski ed9ffe34f4
feat(federation): ⬆️ Upgrade to Versia 0.5 2025-02-17 13:07:43 +01:00
Jesse Wierzbinski e6c7e8a597
docs: 📝 Remove support for from-source installs
Some checks failed
CodeQL Scan / Analyze (javascript-typescript) (push) Failing after 5s
Build Docker Images / lint (push) Failing after 10s
Build Docker Images / check (push) Failing after 10s
Build Docker Images / tests (push) Failing after 6s
Build Docker Images / build (server, Dockerfile, ${{ github.repository_owner }}/server) (push) Has been skipped
Build Docker Images / build (worker, Worker.Dockerfile, ${{ github.repository_owner }}/worker) (push) Has been skipped
Deploy Docs to GitHub Pages / build (push) Failing after 5s
Deploy Docs to GitHub Pages / Deploy (push) Has been skipped
Mirror to Codeberg / Mirror (push) Failing after 0s
Nix Build / check (push) Failing after 5s
2025-02-15 18:49:31 +01:00
Jesse Wierzbinski e5b44cb946
docs: 💸 Add Fastly sponsorship note 2025-02-15 14:43:17 +01:00
Gaspard Wierzbinski 131fd1c6e9
Merge pull request #34 from versia-pub/refactor/config
Some checks failed
CodeQL Scan / Analyze (javascript-typescript) (push) Failing after 47s
Build Docker Images / lint (push) Successful in 30s
Build Docker Images / check (push) Successful in 1m1s
Build Docker Images / tests (push) Failing after 6s
Build Docker Images / build (server, Dockerfile, ${{ github.repository_owner }}/server) (push) Has been skipped
Build Docker Images / build (worker, Worker.Dockerfile, ${{ github.repository_owner }}/worker) (push) Has been skipped
Deploy Docs to GitHub Pages / build (push) Failing after 12s
Mirror to Codeberg / Mirror (push) Failing after 1s
Deploy Docs to GitHub Pages / Deploy (push) Has been skipped
Nix Build / check (push) Failing after 32m28s
Overhaul config system
2025-02-15 02:59:06 +01:00
Jesse Wierzbinski ef57198220
fix(config): 💚 Enable Challenges in CI tests 2025-02-15 02:54:22 +01:00
Jesse Wierzbinski 935ad72936
fix: 🚨 Throw new error instead of using process.exit() 2025-02-15 02:53:08 +01:00
Jesse Wierzbinski bf42f3d677
fix(config): 💚 Enable Sonic in the CI tests 2025-02-15 02:51:57 +01:00
Jesse Wierzbinski 045b7d6083
fix(config): 🐛 Update JSON schema for config 2025-02-15 02:48:39 +01:00
Jesse Wierzbinski 54fd81f076
refactor(config): ♻️ Redo config structure from scratch, simplify validation code, improve checks, add support for loading sensitive data from paths 2025-02-15 02:47:29 +01:00
Jesse Wierzbinski d4afd84019
refactor(media): 🐛 Use hono/proxy in media proxy
Some checks failed
CodeQL Scan / Analyze (javascript-typescript) (push) Failing after 38s
Build Docker Images / lint (push) Successful in 28s
Build Docker Images / check (push) Failing after 5m26s
Build Docker Images / tests (push) Failing after 6s
Build Docker Images / build (server, Dockerfile, ${{ github.repository_owner }}/server) (push) Has been skipped
Build Docker Images / build (worker, Worker.Dockerfile, ${{ github.repository_owner }}/worker) (push) Has been skipped
Deploy Docs to GitHub Pages / build (push) Failing after 14s
Mirror to Codeberg / Mirror (push) Failing after 0s
Deploy Docs to GitHub Pages / Deploy (push) Has been skipped
Nix Build / check (push) Failing after 32m28s
2025-02-14 18:16:07 +01:00
Jesse Wierzbinski 3fe9926fcf
chore: ⬆️ Upgrade dependencies 2025-02-14 18:09:15 +01:00
Gaspard Wierzbinski 416e3009a0
Merge pull request #33 from versia-pub/refactor/types
Overhaul OpenAPI schemas and validation
2025-02-14 18:04:41 +01:00
Gaspard Wierzbinski 276f82882f
Merge branch 'main' into refactor/types 2025-02-14 17:59:18 +01:00
Jesse Wierzbinski 59a3463c72
fix(api): 🚨 Use shorthand property syntax everywhere, remove useless template literals 2025-02-14 17:55:54 +01:00
Jesse Wierzbinski 6a810529bc
refactor(api): 🏷️ Finish OpenAPI documentation refactor 2025-02-14 17:49:34 +01:00
Jesse Wierzbinski 1856176de5
refactor(api): 🏷️ Port almost all remaining v1 endpoints to OpenAPI 2025-02-14 16:44:32 +01:00
Jesse Wierzbinski 247a8fbce3
refactor(api): 🏷️ Port more misc endpoints to use new schemas
Some checks failed
Mirror to Codeberg / Mirror (push) Failing after 0s
2025-02-13 02:34:44 +01:00
Jesse Wierzbinski e3e285571e
refactor(api): 🏷️ Port all /api/v1/accounts to use new schemas
Some checks failed
Mirror to Codeberg / Mirror (push) Failing after 0s
2025-02-13 01:31:15 +01:00
Jesse Wierzbinski a0ce18337a
refactor(api): 🏷️ Use more new schemas 2025-02-12 23:33:07 +01:00
Jesse Wierzbinski bff1c5f734
refactor(api): 🏷️ Begin porting all code over to new schemas 2025-02-12 23:25:22 +01:00
Jesse Wierzbinski fda1167234
feat(api): 🏷️ Finish porting full Mastodon API to OpenAPI 2025-02-12 23:04:44 +01:00
Jesse Wierzbinski 6ff27ede73
feat(cli): Add token generation command to CLI
Some checks failed
CodeQL Scan / Analyze (javascript-typescript) (push) Failing after 51s
Build Docker Images / lint (push) Successful in 28s
Build Docker Images / check (push) Successful in 54s
Build Docker Images / tests (push) Failing after 6s
Build Docker Images / build (server, Dockerfile, ${{ github.repository_owner }}/server) (push) Has been skipped
Build Docker Images / build (worker, Worker.Dockerfile, ${{ github.repository_owner }}/worker) (push) Has been skipped
Deploy Docs to GitHub Pages / build (push) Failing after 12s
Deploy Docs to GitHub Pages / Deploy (push) Has been skipped
Mirror to Codeberg / Mirror (push) Failing after 0s
Nix Build / check (push) Failing after 32m24s
2025-02-11 18:51:28 +01:00
Jesse Wierzbinski 03d3a2d3d4
feat(cli): Implement note recalculation command 2025-02-11 18:39:38 +01:00
Jesse Wierzbinski 264e2fe8ac
feat(api): 🏷️ Port Role and CustomEmoji OpenAPI schemas
Some checks failed
Mirror to Codeberg / Mirror (push) Failing after 1s
2025-02-11 18:22:39 +01:00
Jesse Wierzbinski e5f222c529
chore: Remove unused dependencies
Some checks failed
CodeQL Scan / Analyze (javascript-typescript) (push) Failing after 2m19s
Build Docker Images / lint (push) Successful in 51s
Build Docker Images / check (push) Successful in 1m9s
Build Docker Images / tests (push) Failing after 10s
Build Docker Images / build (server, Dockerfile, ${{ github.repository_owner }}/server) (push) Has been skipped
Build Docker Images / build (worker, Worker.Dockerfile, ${{ github.repository_owner }}/worker) (push) Has been skipped
Deploy Docs to GitHub Pages / build (push) Failing after 22s
Deploy Docs to GitHub Pages / Deploy (push) Has been skipped
Mirror to Codeberg / Mirror (push) Failing after 1s
Nix Build / check (push) Failing after 3m32s
2025-02-05 22:57:05 +01:00
Jesse Wierzbinski 546b7446b9
chore: ⬆️ Upgrade dependencies
Some checks failed
CodeQL Scan / Analyze (javascript-typescript) (push) Failing after 5m26s
Build Docker Images / lint (push) Successful in 50s
Build Docker Images / check (push) Successful in 1m13s
Build Docker Images / tests (push) Failing after 40s
Build Docker Images / build (server, Dockerfile, ${{ github.repository_owner }}/server) (push) Has been skipped
Build Docker Images / build (worker, Worker.Dockerfile, ${{ github.repository_owner }}/worker) (push) Has been skipped
Deploy Docs to GitHub Pages / build (push) Failing after 25s
Deploy Docs to GitHub Pages / Deploy (push) Has been skipped
Mirror to Codeberg / Mirror (push) Failing after 0s
Nix Build / check (push) Failing after 3m49s
2025-02-05 22:50:47 +01:00
Jesse Wierzbinski 7c622730dc
feat(api): 🏷️ Port Status OpenAPI schemas from Mastodon API docs 2025-02-05 22:49:07 +01:00
Jesse Wierzbinski 2aeada4904
feat(api): 🏷️ Port Account OpenAPI schemas from Mastodon API docs 2025-02-05 21:49:39 +01:00
Jesse Wierzbinski 76d1ccc859
refactor(api): ♻️ Use URL literal instead of strings 2025-02-01 16:32:18 +01:00
Jesse Wierzbinski 99fac323c8
chore: ⬆️ Upgrade Bun to 1.2.2 2025-02-01 11:10:57 +01:00
Jesse Wierzbinski ff7b11440d
chore: ⬆️ Upgrade dependencies 2025-02-01 11:07:36 +01:00
Gaspard Wierzbinski 450058213d
Merge pull request #32 from versia-pub/refactor/media
Refactor and simplify the media pipeline
2025-02-01 11:02:28 +01:00
Jesse Wierzbinski 1d2ea36fac
refactor: 🔥 Remove old pre Bun 1.2 compile error check
Bun 1.2 now default to actually throwing an error instead of always succeeding
2025-01-29 17:25:04 +01:00
Jesse Wierzbinski bf071c1b27
chore: 🔥 Remove old S3 client dependency 2025-01-29 17:23:32 +01:00
Jesse Wierzbinski 9ba6237f13
refactor(media): ♻️ Massively simplify media pipeline with Bun.S3 2025-01-29 17:21:40 +01:00
Jesse Wierzbinski f60663506a
fix(api): 🐛 Add missing attributes to /api/v1/instance 2025-01-29 16:05:04 +01:00
Jesse Wierzbinski 29cbe7d293
chore: ⬆️ Upgrade Bun to 1.2.1 2025-01-28 19:32:03 +01:00
Jesse Wierzbinski ba431e2b11
refactor(database): ♻️ Make user avatar and header into a Media instead of plaintext 2025-01-28 19:07:55 +01:00
Jesse Wierzbinski bc961b70bb
refactor(database): 🔥 Simplify media management code 2025-01-28 18:06:33 +01:00
Jesse Wierzbinski cf1104d762
refactor(database): ♻️ Make emojis use a Media instead of just rawdogging the URI 2025-01-28 17:43:43 +01:00
Jesse Wierzbinski c7aae24d42
style(database): 🚚 Reorder declarations of db tables
Putting relations next to the tables makes it easier to not break everything
2025-01-28 17:06:28 +01:00
Jesse Wierzbinski 8ac476fe66
chore: ⬆️ Upgrade dependencies 2025-01-28 16:55:43 +01:00
Jesse Wierzbinski 3216fc339a
refactor(database): ♻️ Move Note <-> Media relations to a many-to-many model instead of one-to-many 2025-01-23 20:36:09 +01:00
Jesse Wierzbinski 9c30dacda7
refactor(database): ♻️ Use ContentFormat to store media data 2025-01-23 19:37:17 +01:00
Jesse Wierzbinski 2f61cd8f0a
refactor(database): 🚚 Rename "Attachment" to "Media" 2025-01-23 16:08:42 +01:00
Jesse Wierzbinski bbd56b600d
refactor: 👷 Switch to Bun's text lockfile 2025-01-23 15:48:12 +01:00
Jesse Wierzbinski c4339e64bd
chore: ⬆️ Upgrade Bun to 1.2.0 2025-01-23 15:28:16 +01:00
Jesse Wierzbinski e32b6f9f8e
chore: ⬆️ Upgrade dependencies 2025-01-23 15:15:34 +01:00
Jesse Wierzbinski 88bb724ae0
chore: ⬆️ Upgrade dependencies 2025-01-17 14:18:31 +01:00
Jesse Wierzbinski 24efc77770
fix: 🚑 Put Sharp back into the API worker
The Attachment processor needs it for metadata
2025-01-07 15:16:51 +01:00
Jesse Wierzbinski 11ba1ab5c8
fix(api): 🐛 Fix failing build
The node_modules directory didn't exist, so cp threw an error
2025-01-07 14:10:54 +01:00
Jesse Wierzbinski b086e65404
refactor(worker): Move blurhash processing to worker 2025-01-06 19:45:32 +01:00
Jesse Wierzbinski 8188a6ffc7
refactor: 🔥 Remove sharp from api worker output 2025-01-06 19:31:42 +01:00
Jesse Wierzbinski ded8799a9c
fix: 🚑 Bundle Sharp into worker 2025-01-06 19:30:51 +01:00
Jesse Wierzbinski 80b874e5fb
refactor(api): Move media processing to background job 2025-01-06 19:21:57 +01:00
Jesse Wierzbinski dcdc8c7365
fix(api): 🐛 Fix user registration incorrectly counting remote users as local 2025-01-06 18:14:09 +01:00
Jesse Wierzbinski 0e9db83279
style: 🚨 Run Biome 2025-01-02 04:52:30 +01:00
Jesse Wierzbinski 3484b1e1a1
fix(api): 🐛 Improve notification text 2025-01-02 04:49:36 +01:00
Jesse Wierzbinski 1c543723fb
fix(api): 🐛 Use aesgcm, not aes128gcm during push notifications
Mastodon's server hates aes128gcm
2025-01-02 04:36:28 +01:00
Jesse Wierzbinski bedc25bacf
fix(api): 🐛 Properly await notification result 2025-01-02 04:27:52 +01:00
Jesse Wierzbinski cde2836982
fix(api): 👽 Ignore what the Mastodon docs say, they're wrong 2025-01-02 04:13:12 +01:00
Jesse Wierzbinski 5d64ecd04f
fix(api): 🐛 Switch base64 version to base64url in subscriptions 2025-01-02 04:02:29 +01:00
Jesse Wierzbinski ea0afdaf22
fix(api): 🐛 Make validation on push subscription more lax 2025-01-02 03:53:38 +01:00
Jesse Wierzbinski 59cf4e384a
fix(api): 🐛 Correctly calculate user based on token 2025-01-02 03:36:54 +01:00
Jesse Wierzbinski 8706c7b405
feat(federation): Add Push Queue to Bull Board 2025-01-02 03:27:26 +01:00
Jesse Wierzbinski 85de7b8ddc
ci: 🐛 Fix bug causing worker Docker image to be a copy of Server 2025-01-02 03:22:18 +01:00
Jesse Wierzbinski c58c8c6cc8
fix(api): 🐛 Correctly start push worker 2025-01-02 03:12:59 +01:00
Jesse Wierzbinski 7b3158c102
fix(api): 🚑 Fix incorrect builds
Everything under api/ should be a route, or it messes up bundling
2025-01-02 02:55:56 +01:00
Jesse Wierzbinski d839c274b1
feat(api): Finish push notification delivery 2025-01-02 02:45:40 +01:00
Jesse Wierzbinski d096ab830c
feat(api): Add initial Push Notifications support 2025-01-02 01:29:33 +01:00
Jesse Wierzbinski acd2bcb469
feat: 🧑‍💻 Add VSCode launch json 2025-01-01 23:42:14 +01:00
Jesse Wierzbinski 1137782f2a
ci: 🐛 Remove unnecessary permissions from tests 2024-12-31 17:38:13 +01:00
Jesse Wierzbinski 9d88fdbe53
ci: ♻️ Make tests, linting and checks pass before build is run 2024-12-31 17:35:27 +01:00
Jesse Wierzbinski deada6cbd9
ci: ♻️ Merge Worker & Server build into one CI file 2024-12-31 17:21:49 +01:00
Jesse Wierzbinski fbd352e23c
fix: 🚑 Correctly apply patches to bull-board 2024-12-31 17:16:16 +01:00
Jesse Wierzbinski 82da70bcac
refactor(api): ♻️ Group note/account fetching code in some routes 2024-12-30 21:30:10 +01:00
Jesse Wierzbinski 16f302c2dc
refactor(api): ♻️ Simplify route schema definitions 2024-12-30 20:26:56 +01:00
Jesse Wierzbinski 4926d6ff5d
refactor(api): 🔥 Remove all useless route metadata objects 2024-12-30 20:18:48 +01:00
Jesse Wierzbinski a9ea5eb672
chore: ⬆️ Upgrade TypeScript peer dependency to latest 2024-12-30 19:41:36 +01:00
Jesse Wierzbinski 09f30db83a
refactor(api): ♻️ Remove useless authorization checks 2024-12-30 19:38:41 +01:00
Jesse Wierzbinski dc12b269f5
refactor(api): ♻️ Improve authentication checker API 2024-12-30 19:18:31 +01:00
Jesse Wierzbinski 621dd7e9d9
refactor(api): ♻️ Upgrade zod-openapi to 0.18.3
Needed to add "as const" to all middleware handlers :)
2024-12-30 18:20:22 +01:00
Jesse Wierzbinski fbfd237f27
refactor(api): ♻️ Throw ApiError instead of returning error JSON 2024-12-30 18:00:23 +01:00
Jesse Wierzbinski c14621ee06
refactor(api): 🔥 Simplify oauth authorize handler 2024-12-30 16:47:48 +01:00
Jesse Wierzbinski 44d7264b79
refactor: 🔥 Remove deprecated ioredis type package 2024-12-30 16:20:47 +01:00
Jesse Wierzbinski a7b29d563e
fix(api): 🏷️ Use context.body for 204 responses 2024-12-30 16:18:28 +01:00
Jesse Wierzbinski 6af6bde12a
chore: ⬆️ Upgrade dependencies 2024-12-30 16:07:10 +01:00
Jesse Wierzbinski 8d2451cafc
chore: 🔖 Bump version to 0.8.0-alpha 2024-12-30 16:05:45 +01:00
Jesse Wierzbinski 20970a76fd
chore: ⬆️ Upgrade Bun to 1.1.42 2024-12-22 11:57:32 +01:00
Jesse Wierzbinski c621d9251e
chore: ⬆️ Upgrade dependencies 2024-12-22 11:56:52 +01:00
Jesse Wierzbinski 7268bd74f7
fix(api): ✏️ Remove extra attribute on NoteReaction
Was left there by mistake
2024-12-19 15:45:06 +01:00
Jesse Wierzbinski 98d63d85d4
docs(api): 📝 Document Reactions API 2024-12-19 15:41:56 +01:00
Jesse Wierzbinski 6f97f9f8f1
refactor(database): ♻️ Clean up database schema 2024-12-18 21:52:53 +01:00
Jesse Wierzbinski c334cd9cc8
chore: ⬆️ Upgrade Bun to 1.1.40 2024-12-18 20:46:26 +01:00
Jesse Wierzbinski 1509786090
chore: ⬆️ Upgrade dependencies 2024-12-18 20:42:40 +01:00
Jesse Wierzbinski f67fed12e0
feat(database): Add Reaction database class 2024-12-18 20:01:26 +01:00
Jesse Wierzbinski e00182cf54
feat(database): Add reactions table to database schema 2024-12-18 19:25:45 +01:00
Jesse Wierzbinski 4fdb96930f
fix(api): 🐛 Fetch media content-type from data, instead of doing naive guesses 2024-12-16 23:57:21 +01:00
Jesse Wierzbinski 6f67881d96
feat(api): Add ability to set URL as avatar or banner 2024-12-16 15:46:11 +01:00
Jesse Wierzbinski 41341cf252
docs: 📝 Improve README 2024-12-16 14:47:51 +01:00
Gaspard Wierzbinski 43b87dbfd3
docs: 📖 Add license for Versia assets 2024-12-15 15:09:01 +01:00
Jesse Wierzbinski e293bd280d
fix(api): 🐛 Fix duplicated mentions, general refactorings 2024-12-09 15:30:18 +01:00
Jesse Wierzbinski 84a0a07ea6
ci: 👷 Add the short commit SHA as a docker container tag 2024-12-09 15:08:32 +01:00
Jesse Wierzbinski 0ae9cfe26c
refactor(federation): ♻️ More federation logic cleanup 2024-12-09 15:01:19 +01:00
Jesse Wierzbinski 83399ba5f1
refactor(database): ♻️ Simplify User and Note logic further 2024-12-09 13:50:46 +01:00
Jesse Wierzbinski a8541bdc44
refactor(database): ♻️ Simplify Note and User federation logic 2024-12-09 13:36:15 +01:00
Jesse Wierzbinski cbbf49905b
fix(federation): 🐛 Fix remote emojis being incorrectly marked as local 2024-12-09 13:11:23 +01:00
Jesse Wierzbinski c94dd7c59d
fix(api): 🐛 Don't transform raw URLs as if they were attachments 2024-12-09 12:43:53 +01:00
Jesse Wierzbinski 8796f694bc
feat(api): 🚩 Add emoji shortcode size controls 2024-12-09 11:09:40 +01:00
Jesse Wierzbinski cfefd56a55
feat(api): 👽 Expose emoji limits in /api/v2/instance 2024-12-09 11:02:15 +01:00
Jesse Wierzbinski c8b909db08
chore: ⬆️ Upgrade Bun to 1.1.38 2024-12-09 10:57:24 +01:00
Jesse Wierzbinski 0708b3c45d
chore: ⬆️ Upgrade dependencies 2024-12-09 10:56:56 +01:00
Jesse Wierzbinski b14fa17e1a
feat(config): 🚩 Add emoji size/description size controls 2024-12-09 10:55:04 +01:00
Jesse Wierzbinski 5074ac788f
fix(api): 🐛 Force text content-type header on all empty responses
Fixes a problem where the content-type would default to application/json
2024-12-07 13:24:24 +01:00
Jesse Wierzbinski 06376cf58a
fix(api): 🐛 Correctly return empty body without content-type headers, when returning empty responses 2024-12-07 12:20:06 +01:00
Jesse Wierzbinski 2743528727
fix(api): 🐛 Fix emoji editing always making the emoji non-global 2024-12-07 11:12:17 +01:00
Jesse Wierzbinski 57e17e7607
fix(cli): 🏷️ Handle possible undefined values on some variables 2024-12-02 15:43:56 +01:00
Jesse Wierzbinski e4768620e2
fix(api): 🐛 Fix account lookup address parsing (again) 2024-12-02 15:40:20 +01:00
Jesse Wierzbinski 91da99c934
feat(federation): Handle instances not existing 2024-12-02 15:07:05 +01:00
Jesse Wierzbinski deee65ad6d
refactor(api): ♻️ Refactor user lookup endpoint 2024-12-02 15:00:37 +01:00
Jesse Wierzbinski ca42df1dfd
fix(api): 🐛 Properly include global field in API emojis 2024-11-29 21:49:41 +01:00
Jesse Wierzbinski 46933c1bef
fix(api): Fix tests expecting null instead of undefined in Roles API 2024-11-28 11:24:56 +01:00
Jesse Wierzbinski d1d7ca25a4
chore(api): ⬆️ Upgrade @versia/client to 0.1.1 2024-11-28 11:21:11 +01:00
Jesse Wierzbinski caa071d353
docs(api): 🐛 Fix incorrect Role API docs 2024-11-28 11:04:22 +01:00
Jesse Wierzbinski 594e8ca4e6
docs(api): 🐛 Add proper parameters to Roles API docs 2024-11-28 10:59:14 +01:00
Jesse Wierzbinski eb405d33cd
fix(api): 🐛 Don't use null in Role properties 2024-11-28 10:54:44 +01:00
Jesse Wierzbinski 8f339669b5
chore: ⬆️ Upgrade Bun to 1.1.37 2024-11-28 10:27:17 +01:00
Jesse Wierzbinski cd4b021aec
chore: ⬆️ Upgrade dependencies 2024-11-28 10:26:28 +01:00
Jesse Wierzbinski 4e38749ccb
docs(api): 📝 Update Roles API docs and changelog 2024-11-26 15:41:08 +01:00
Jesse Wierzbinski 49c53de99e
feat(api): Overhaul Role API, add ability to edit roles and assign/unassign them from any user 2024-11-26 15:27:39 +01:00
Jesse Wierzbinski 7431c1e21d
fix: 🏗️ Update file to patch's location in builder 2024-11-25 23:25:37 +01:00
Jesse Wierzbinski 49a301663a
feat(federation): Use instance messaging to send errors to remote instance 2024-11-25 23:14:42 +01:00
Jesse Wierzbinski a037448ebb
refactor(federation): ♻️ Remove Response return semantics from inbox worker 2024-11-25 23:11:17 +01:00
Jesse Wierzbinski 025d5bea94
docs: 📝 Restrict reference docker-compose permissions 2024-11-25 22:21:00 +01:00
Jesse Wierzbinski ece36f6adc
fix: 💚 Also include repo owner in worker image name 2024-11-25 21:58:41 +01:00
Jesse Wierzbinski 87bb0b6bcb
fix: 💚 Make worker CI build use correct image name 2024-11-25 21:56:57 +01:00
Jesse Wierzbinski 1b98381242
feat: Split off queue workers into a separate worker process 2024-11-25 21:54:31 +01:00
Jesse Wierzbinski 0b3e74107e
refactor(federation): ♻️ Make user inbox use the delayed processing 2024-11-25 21:17:52 +01:00
Jesse Wierzbinski a6574249df
docs: 📝 Update Changelog 2024-11-25 21:00:18 +01:00
Jesse Wierzbinski 55256e3568
refactor(config): 🚩 Remove enable flag on Redis queue config 2024-11-25 20:55:55 +01:00
Jesse Wierzbinski fb9a0feac8
fix: 💚 Add Redis to GitHub tester 2024-11-25 20:54:06 +01:00
Jesse Wierzbinski c899f12893
feat(federation): Prioritize delivery to instance inbox, and use delivery queue in more places 2024-11-25 20:50:55 +01:00
Jesse Wierzbinski 7a73a1a24e
feat(federation): Store remote instance shared inbox and extensions as well 2024-11-25 20:37:00 +01:00
Jesse Wierzbinski 5fc6c4dcfa
feat(federation): Implement queue for fetches 2024-11-25 20:29:59 +01:00
Jesse Wierzbinski 79cf43d752
refactor(api): 🛂 Increase JWT cookie lifetime to 2 weeks 2024-11-25 20:25:42 +01:00
Jesse Wierzbinski eb466a0cc7
feat(federation): Implement Shared Inboxes 2024-11-25 17:05:53 +01:00
Jesse Wierzbinski 756f67c0f3
feat(federation): Implement Instance Messaging Extension 2024-11-25 16:54:46 +01:00
Jesse Wierzbinski 4594c69808
docs: 📝 Update changelog 2024-11-25 14:27:34 +01:00
Jesse Wierzbinski 61b773ed11
feat(federation): Add config option to control automatic queue purge time 2024-11-25 13:53:14 +01:00
Jesse Wierzbinski 048dd6b0ab
fix: 🐛 Use CommonJS resolve instead of ESM one in Bull-Board UI 2024-11-25 13:37:58 +01:00
Jesse Wierzbinski fb84db3ea7
fix: 🐛 Correctly include bull-board UI package in dist 2024-11-25 13:24:14 +01:00
Jesse Wierzbinski ecc7d1eee7
feat(federation): Add UI to view BullMQ metadata 2024-11-25 13:09:28 +01:00
Jesse Wierzbinski 8a920218ea
feat(federation): Add queue to note delivery 2024-11-25 11:29:48 +01:00
Jesse Wierzbinski 3ef361f521
fix(federation): 🐛 Correctly remove listeners after job processing finishes 2024-11-25 08:59:48 +01:00
Jesse Wierzbinski 3e19b11609
refactor: 🔊 Fix duplicate logs 2024-11-24 23:13:29 +01:00
Jesse Wierzbinski 005a3a2721
fix(federation): 🚑 Don't always try to use instance key when an instance is not the request signer 2024-11-24 23:01:47 +01:00
Jesse Wierzbinski 34370a082a
refactor(federation): ♻️ Make Instance updateFromRemote non-static 2024-11-24 22:48:34 +01:00
Jesse Wierzbinski 8b23eb888d
refactor(cli): ♻️ Rewrite instance fetch command to refetch instances instead 2024-11-24 22:45:41 +01:00
Jesse Wierzbinski 50ebc12783
fix(federation): 🐛 Show error for inbox failures 2024-11-24 22:33:51 +01:00
Jesse Wierzbinski d527947182
fix(federation): 🐛 Correctly handle job failures in inboxes 2024-11-24 22:28:29 +01:00
Jesse Wierzbinski c59ebef851
feat(federation): Add more debugging to inbox processing 2024-11-24 22:17:45 +01:00
Jesse Wierzbinski be69407c01
refactor(federation): 🔊 Logging color tweaks 2024-11-24 22:10:23 +01:00
Jesse Wierzbinski 40e7903d90
feat(federation): 🔊 Add processing finished log 2024-11-24 22:06:32 +01:00
Jesse Wierzbinski b333ecc816
refactor(federation): 🔊 Add more logging to some federation logic 2024-11-24 22:01:14 +01:00
Jesse Wierzbinski ef0cca671a
feat(federation): Add Redis online check 2024-11-24 22:01:01 +01:00
Jesse Wierzbinski b320ddf3ae
refactor(federation): ♻️ Queue all incoming inbox processing events 2024-11-24 21:35:59 +01:00
Jesse Wierzbinski 26f1407efe
chore: 📝 Update changelog 2024-11-24 20:20:56 +01:00
Jesse Wierzbinski 8d968fa98c
chore: ⬆️ Upgrade dependencies 2024-11-24 17:54:42 +01:00
Jesse Wierzbinski 340ed7b258
fix(federation): 🐛 Correctly handle non-lowercase acct queries in account lookups 2024-11-24 17:42:30 +01:00
Jesse Wierzbinski 259fba17a7
fix(federation): 🐛 Make an empty allowed_ip list for bridge correctly allow any IP 2024-11-24 16:54:24 +01:00
Jesse Wierzbinski b55237cdc8
refactor(federation): ♻️ Allow ActivityPub bridge requests to omit all signature headers, including x-signed-by 2024-11-24 16:40:23 +01:00
Jesse Wierzbinski 80b5184d6a
test(api): 🐛 Fix S3 tests 2024-11-24 16:32:05 +01:00
Jesse Wierzbinski 59b069ce2c
fix(api): 🐛 Specify content-type when uploading to S3 2024-11-24 13:16:52 +01:00
Jesse Wierzbinski 6301121900
refactor(cli): 🔥 Removed unused variables 2024-11-24 00:48:41 +01:00
Jesse Wierzbinski 083b77bbb9
feat(cli): Add automatic setup script 2024-11-24 00:46:40 +01:00
Jesse Wierzbinski 36b25e0307
feat(cli): Add direct password option to CLI 2024-11-24 00:37:26 +01:00
Jesse Wierzbinski da369e604c
fix(api): 🐛 Fix lookup endpoints thinking local user is remote 2024-11-23 23:09:59 +01:00
Jesse Wierzbinski ace6921447
refactor(federation): ♻️ Correctly handle bridge requests and instance signatures in user inboxes 2024-11-23 23:02:18 +01:00
Jesse Wierzbinski afc5a74a40
fix: 🚑 Correctly use Musl version of Sharp in the production build 2024-11-23 14:35:04 +01:00
Jesse Wierzbinski 5b6924810e
chore: ⬆️ Upgrade Bun to 1.1.36 2024-11-23 14:27:26 +01:00
Jesse Wierzbinski fb9dbcdff0
chore: ⬆️ Upgrade dependencies 2024-11-23 14:26:58 +01:00
Jesse Wierzbinski 8444ff5741
fix(federation): 🐛 Use correct URL for bridge queries 2024-11-22 22:17:25 +01:00
Jesse Wierzbinski 217d3c286d
feat(api): Allow divs and spans in HTML 2024-11-22 19:12:52 +01:00
Jesse Wierzbinski fa0d48b88d
fix(api): 🐛 Use consistent user address matching patterns 2024-11-22 16:53:36 +01:00
Jesse Wierzbinski 569ba8bf2d
fix(api): 🔒 Correctly put all URIs in profiles through proxy 2024-11-22 15:06:46 +01:00
Jesse Wierzbinski bd1f09837b
fix(api): 🐛 Correctly handle underscores when parsing usernames 2024-11-22 14:51:11 +01:00
Jesse Wierzbinski bbfd26bb64
docs: 📝 Point to v0.7.0 installation info in docs instead of main branch 2024-11-21 20:34:21 +01:00
Jesse Wierzbinski 66c5c6e62d
fix(api): 🐛 Also validate other username characters in username changes 2024-11-21 09:31:37 +01:00
Jesse Wierzbinski 2fea17fdaa
fix(api): 🐛 Make uppercase usernames send errors during registration again 2024-11-21 09:26:03 +01:00
Jesse Wierzbinski a3b745358b
fix(api): 🐛 Automatically make all usernames lowercase 2024-11-21 09:23:25 +01:00
Jesse Wierzbinski 5dd8b872d9
chore: 📝 Update changelog for 0.7.0 release 2024-11-21 08:05:47 +01:00
Jesse Wierzbinski 9682cd0f99
fix(api): 🔒 Correctly check for note ownership when editing 2024-11-19 17:26:14 +01:00
Jesse Wierzbinski 653cf712ea
fix(api): 🚑 Correctly check visibility in timelines 2024-11-19 17:07:11 +01:00
Jesse Wierzbinski c20e6eb3b8
feat(federation): Add registration info to nodeinfo 2024-11-19 15:29:21 +01:00
Jesse Wierzbinski 055ee417cb
feat(federation): Implement user statistics and node data in nodeinfo 2024-11-19 15:24:15 +01:00
Jesse Wierzbinski 1837a6feb4
fix(federation): 🐛 Make nodeinfo correctly return JSON instead of redirect 2024-11-19 14:41:12 +01:00
Jesse Wierzbinski bfbaa7ce2c
fix(api): 🐛 Correctly sanitize checkbox inputs 2024-11-19 11:32:16 +01:00
Jesse Wierzbinski dc8a64355a
feat(api): Allow disabled checkbox inputs in rich text 2024-11-19 11:20:24 +01:00
Jesse Wierzbinski 32f71b3adf
fix(api): 🐛 Allow for forcing registrations via OpenID and disabling regular signups 2024-11-18 19:53:52 +01:00
Jesse Wierzbinski 8d2d9bd7fa
fix(config): 🚑 Use correct key in example config 2024-11-18 19:29:24 +01:00
Jesse Wierzbinski aac94e578f
chore: ⬆️ Upgrade dependencies 2024-11-18 13:46:06 +01:00
Jesse Wierzbinski d6fe6d2068
docs: Add favicon to docs 2024-11-10 15:41:00 +01:00
Jesse Wierzbinski bfa44e3f34
fix(api): 🐛 Fix for changed HTTP response code in Emoji API 2024-11-10 15:38:08 +01:00
Gaspard Wierzbinski b0645855ec
Merge pull request #30 from versia-pub/feat/vitepress-docs
Rewrite documentation to use VitePress
2024-11-10 15:35:02 +01:00
Jesse Wierzbinski 83f573c14f
refactor: 🚚 Point to new docs site everywhere 2024-11-10 15:33:21 +01:00
Jesse Wierzbinski cbcfe51362
feat: Enable clean URLs for docs 2024-11-10 15:29:43 +01:00
Jesse Wierzbinski 9796280a55
ci: 👷 Add automatic docs building and deployment 2024-11-10 15:28:27 +01:00
Jesse Wierzbinski 06a8dd1c0a
refactor: 📝 Move documentation to a custom VitePress site 2024-11-10 15:24:34 +01:00
Jesse Wierzbinski 19d8680289
feat(api): Add timeline benchmark 2024-11-10 13:08:43 +01:00
Jesse Wierzbinski 3ec5118771
refactor(plugin): ♻️ Move plugin loading to PluginLoader class 2024-11-10 13:08:26 +01:00
Jesse Wierzbinski 95b8eb6e20
chore: ⬆️ Upgrade dependencies 2024-11-07 13:24:11 +01:00
Jesse Wierzbinski b2405bd118
docs: 📝 Clarify wording in contribution guide 2024-11-07 13:15:58 +01:00
Jesse Wierzbinski 93ebeba368
Revert "refactor(database): ♻️ Correctly use @versia/kit imports everywhere"
This reverts commit 2f94884d37.
2024-11-04 15:20:53 +01:00
Jesse Wierzbinski 2f94884d37
refactor(database): ♻️ Correctly use @versia/kit imports everywhere 2024-11-04 15:06:23 +01:00
Jesse Wierzbinski 02c3c9d0bf
refactor: 🏷️ Move all types that represent ORM abstractions to ORM class static properties 2024-11-04 14:58:17 +01:00
Jesse Wierzbinski ca31830fb3
revert(api): 🔥 Use Notification's schema declaration instead of declaring it again every time 2024-11-04 14:35:27 +01:00
Jesse Wierzbinski 8765a45240
ci: 💚 Correctly name CI mirror workflow 2024-11-04 11:40:08 +01:00
Jesse Wierzbinski 2860323294
ci: 💚 Correctly name the mirror workflow 2024-11-04 11:38:02 +01:00
Jesse Wierzbinski 4552d3712b
ci: 💚 Inherit secrets in mirror workflow 2024-11-04 11:36:52 +01:00
Jesse Wierzbinski cd5ef61ce5
ci: 💚 Correctly add CI secret 2024-11-04 11:35:59 +01:00
Jesse Wierzbinski ad3a417b03
ci: 👷 Add Codeberg mirror CI 2024-11-04 11:29:26 +01:00
Jesse Wierzbinski e732a3df03
refactor(database): ♻️ Move Notifications to their own ORM abstractions 2024-11-04 10:43:30 +01:00
Jesse Wierzbinski 14ace17ad4
chore: ⬆️ Upgrade Bun to 1.1.34 2024-11-04 10:10:32 +01:00
Jesse Wierzbinski 9f7850a9b1
chore: ⬆️ Upgrade dependencies 2024-11-04 10:09:55 +01:00
Jesse Wierzbinski 845041e4db
refactor(database): ♻️ Move Token to its own ORM abstraction, optimize familiar_followers route 2024-11-03 17:45:21 +01:00
Jesse Wierzbinski 962c159ddd
chore: ⬆️ Upgrade dependencies 2024-11-02 00:46:24 +01:00
Jesse Wierzbinski 2eb0509fd3
refactor: 🚨 Turn on a few more linter rules 2024-11-02 00:45:48 +01:00
Jesse Wierzbinski c1dcdc78ae
refactor: ♻️ Always use explicit types in every function 2024-11-02 00:43:33 +01:00
Jesse Wierzbinski 54cea29ce9
refactor: 🚨 Always explicitely state member accessibility 2024-11-01 21:20:12 +01:00
Jesse Wierzbinski 7a73b8db91
refactor(database): 🚚 Only import ORM table data from @versia/kit 2024-11-01 21:05:54 +01:00
Jesse Wierzbinski 2f8b85a299
refactor(database): 🚚 Only import ORM abstractions from @versia/kit 2024-11-01 20:57:16 +01:00
Jesse Wierzbinski f26493140f
refactor(federation): ♻️ Move incoming federation handling to custom class 2024-11-01 20:42:32 +01:00
Jesse Wierzbinski d570e8c200
chore: ⬆️ Upgrade dependencies 2024-11-01 00:27:47 +01:00
Jesse Wierzbinski 1298b3732e
fix(api): 🐛 Fix false values not correctly falling back to generic mime type 2024-10-31 23:19:19 +01:00
Jesse Wierzbinski d06301ed72
refactor(federation): ♻️ Refactor user inbox API to reduce complexity 2024-10-28 13:13:50 +01:00
Jesse Wierzbinski 7638a094f4
refactor(api): 🔥 Remove unused function 2024-10-28 12:31:46 +01:00
Jesse Wierzbinski 074d0e3dcc
chore: ⬆️ Upgrade dependencies 2024-10-26 15:05:45 +02:00
Jesse Wierzbinski 64b263a1c1
fix(federation): 🐛 Update old version of federation SDK 2024-10-24 19:18:21 +02:00
Jesse Wierzbinski 0a31b7a8f6
feat(federation): Implement inbound federation of likes and like deletion 2024-10-24 19:08:28 +02:00
Jesse Wierzbinski df84572148
fix(api): 🐛 Fix missing FormData acceptance for registration route 2024-10-24 18:48:11 +02:00
Jesse Wierzbinski 3b704b4c8c
fix(plugin): 🐛 Fix misleading error message related to plugin initialization 2024-10-24 18:41:11 +02:00
Jesse Wierzbinski 11bb0a6f49
feat(cli): Add generate-keys CLI command 2024-10-24 18:18:39 +02:00
Jesse Wierzbinski 33f16bb9b1
docs: 📝 Update installation docs 2024-10-24 18:00:26 +02:00
Jesse Wierzbinski f494f76f82
feat(federation): Federate likes and unlikes 2024-10-24 17:31:39 +02:00
Jesse Wierzbinski 5a26bdf2f8
refactor(database): 🚚 Move Likes to our custom ORM 2024-10-24 17:20:00 +02:00
Jesse Wierzbinski e52e230ce3
refactor(database): 🚚 Move database ORM code to classes/database
The old directory, packages/database-interface, was confusingly named so it was better to move it here
2024-10-24 16:28:38 +02:00
Jesse Wierzbinski 120ba0fb81
chore: ⬆️ Upgrade Bun to 1.1.33 2024-10-24 16:21:53 +02:00
Jesse Wierzbinski 807aa986b0
chore: ⬆️ Upgrade Bun to 1.1.32 2024-10-23 18:02:32 +02:00
Jesse Wierzbinski 6338f711ad
chore: ⬆️ Upgrade dependencies 2024-10-23 18:01:40 +02:00
Jesse Wierzbinski 9e96eca032
refactor(database): ♻️ Move Applications to our custom ORM 2024-10-23 17:56:47 +02:00
Jesse Wierzbinski e8827bccfa
chore: ⬆️ Upgrade dependencies 2024-10-14 18:08:23 +02:00
Jesse Wierzbinski d000914f61
fix(plugin): 🐛 Don't incorrectly call errorSearchParams before initialization in callback 2024-10-11 17:23:51 +02:00
Jesse Wierzbinski a265e9df41
fix(plugin): 🐛 Add missing plugin middleware to some OIDC plugin routes 2024-10-11 17:16:03 +02:00
Jesse Wierzbinski d2dcdce763
fix(api): 🐛 Fix incorrect order of function parameters 2024-10-11 17:09:51 +02:00
Jesse Wierzbinski d84ae38573
fix(api): 🐛 Fix missing nullish coalescing 2024-10-11 17:07:17 +02:00
Jesse Wierzbinski a1aa49e089
ci(config): 🔥 Remove old oidc key from CI config 2024-10-11 17:04:03 +02:00
Jesse Wierzbinski 9f1e89b592
refactor(config): 🔥 Remove old oidc section in config 2024-10-11 17:03:33 +02:00
Jesse Wierzbinski ce781f3336
refactor(database): ♻️ Use new Drizzle count API 2024-10-11 15:46:05 +02:00
Jesse Wierzbinski 7f17074d16
chore: ⬆️ Upgrade dependencies 2024-10-11 15:40:54 +02:00
Jesse Wierzbinski 7cdbb8ba6f
chore: ⬆️ Upgrade Bun to 1.1.30 2024-10-11 15:20:46 +02:00
Jesse Wierzbinski 04651746bb
refactor(plugin): 🚚 Move SSO login callback route to OpenID plugin 2024-10-11 15:15:06 +02:00
Jesse Wierzbinski 777a39faf5
refactor(plugin): 🚚 Move SSO login route to OpenID plugin 2024-10-11 14:39:25 +02:00
Jesse Wierzbinski 6cf97e5dd7
chore: ⬆️ Upgrade dependencies 2024-10-07 12:59:54 +02:00
Jesse Wierzbinski 0557d52afe
refactor(plugin): 🚚 Move JWKS well-known endpoint to OpenID plugin 2024-10-07 12:52:22 +02:00
Jesse Wierzbinski 2e827814de
ci(config): 🐛 Fix CI config to pass new config checks 2024-10-06 16:03:40 +02:00
Jesse Wierzbinski 33b375f3ae
chore: 📝 Update changelog 2024-10-06 15:56:28 +02:00
Jesse Wierzbinski f26ab0f0e6
feat(plugin): Add override settings to plugin loading 2024-10-06 15:55:15 +02:00
Jesse Wierzbinski c0805ff125
fix(config): 🐛 Make some default config values more sensible 2024-10-04 18:36:32 +02:00
Jesse Wierzbinski f9dcbb1be8
refactor: 🚨 Enable noUndeclaredDependencies for Biome 2024-10-04 15:38:02 +02:00
Jesse Wierzbinski 728ccc9002
chore: ⬆️ Upgrade dependencies 2024-10-04 15:35:43 +02:00
Jesse Wierzbinski df29091c44
refactor: 🔥 Remove package version pinning from Dockerfile 2024-10-04 15:31:46 +02:00
Jesse Wierzbinski c4ff5aa2fb
fix: 💚 Add missing symbols 2024-10-04 15:30:05 +02:00
Jesse Wierzbinski d61e366a29
ci: 📌 Set libstc++ version to ^13.0.0 2024-10-04 15:28:36 +02:00
Jesse Wierzbinski 40f9b46392
fix: 💚 Fix incorrect Dockerfile syntax 2024-10-04 15:25:54 +02:00
Jesse Wierzbinski b53307c824
refactor: 🚚 Explicitely add extensions to all imports 2024-10-04 15:22:48 +02:00
Jesse Wierzbinski b5b7014c00
refactor: 🚨 Add empty default case to OpenID grant_type handler 2024-10-03 19:06:24 +02:00
Jesse Wierzbinski 2537e3cd48
refactor: 🚨 Make more class methods static 2024-10-03 19:02:13 +02:00
Jesse Wierzbinski 5ec19f037a
refactor: 🚨 Remove unnecessary function overloads 2024-10-03 13:54:09 +02:00
Jesse Wierzbinski 835cdc3f18
refactor: 🚨 Make class methods that don't use this static 2024-10-03 13:51:19 +02:00
Jesse Wierzbinski 53688095cc
refactor: 🚨 Don't use wildcard imports 2024-10-03 13:44:55 +02:00
Jesse Wierzbinski bec3e4ea70
refactor: 🚨 Consolidate consecutive RUN calls 2024-10-03 13:43:19 +02:00
Jesse Wierzbinski 3fade63567
refactor: 🚨 Use shortand property syntax for object literals 2024-10-03 13:41:58 +02:00
Jesse Wierzbinski 48ffe97849
refactor: 🚨 Pin all package versions in Docker images 2024-10-03 13:30:49 +02:00
Jesse Wierzbinski 360ec4817c
fix(api): 🚨 Correct duplicate assignments 2024-10-03 13:26:53 +02:00
Jesse Wierzbinski 8da9567ca2
refactor(api): 🚨 Don't export a mutable value 2024-10-03 13:26:00 +02:00
Jesse Wierzbinski 076e930369
refactor: 🚨 Remove process.exit usage 2024-10-03 11:59:26 +02:00
Jesse Wierzbinski b1d8595a7c
refactor: 🚨 Remove unnecessary async keywords 2024-10-03 11:43:16 +02:00
Jesse Wierzbinski 132bec4d5b
fix(api): 🐛 Fix incorrect ?? placement
This would not set a default value of 20
2024-10-03 10:31:57 +02:00
Jesse Wierzbinski 5ed3f04d48
refactor: 🚨 Simplify boolean return 2024-10-03 10:30:22 +02:00
Jesse Wierzbinski a4aafc202c
refactor(cli): 🔥 Remove useless bin files in CLI 2024-10-03 10:29:25 +02:00
Jesse Wierzbinski 5e1ec8778c
fix: 🚨 Correct useless template literal 2024-10-03 10:27:41 +02:00
Jesse Wierzbinski 06315e8a81
fix: 🚨 Replace ts-ignore with ts-expect-error 2024-10-03 10:26:58 +02:00
Jesse Wierzbinski f523e5d355
feat: Add Deepsource config file 2024-10-03 10:24:39 +02:00
Jesse Wierzbinski 3879763971
refactor(plugin): ♻️ Add more exports to @versia/kit for database behaviour 2024-09-30 14:34:43 +02:00
Jesse Wierzbinski 23300ae93e
chore: ⬆️ Upgrade dependencies 2024-09-30 13:53:14 +02:00
Jesse Wierzbinski 3f3cf8ec39
perf: 🔥 Remove useless @hono/zod-validator dependency (replaced by @hono/zod-openapi) 2024-09-30 13:50:25 +02:00
Jesse Wierzbinski 1e84fa6e41
docs: 📝 Update changelog and README 2024-09-30 13:49:04 +02:00
Jesse Wierzbinski 19213ec29e
refactor(api): ♻️ Move token endpoint to OpenID plugin, add revoke endpoint 2024-09-30 13:42:12 +02:00
Jesse Wierzbinski 2254c3d39c
refactor(api): 🔥 Remove useless allowedMethods from route meta info 2024-09-27 13:08:47 +02:00
Jesse Wierzbinski b040c88445
refactor(api): ♻️ Finish first pass of OpenAPI refactor 2024-09-27 13:00:12 +02:00
Jesse Wierzbinski 5e80122e81
chore: ⬆️ Upgrade dependencies 2024-09-25 12:36:19 +02:00
Jesse Wierzbinski 74ec563ba5
refactor(api): ♻️ Move all SSO account linking endpoint logic to OpenID plugin 2024-09-25 12:31:35 +02:00
Jesse Wierzbinski 6d4b4eb13b
fix(api): 🐛 Fix incorrect test case 2024-09-24 17:03:27 +02:00
Jesse Wierzbinski 96d1805925
refactor(api): ♻️ Move /api/v1/sso to OpenID plugin 2024-09-24 14:42:39 +02:00
Jesse Wierzbinski c7ec678a3e
fix: 💚 Run every test file separately instead of using the global bun test command 2024-09-23 13:20:30 +02:00
Jesse Wierzbinski de8b8e2cc0
ci: 💚 Add debug for CI 2024-09-23 13:06:22 +02:00
Jesse Wierzbinski c7ae7f3042
refactor: 🔥 Remove superflous logger call 2024-09-23 12:03:10 +02:00
Jesse Wierzbinski 08ce64e9b9
fix: 🔊 Fix logging code to not run in tests 2024-09-23 12:01:02 +02:00
Jesse Wierzbinski c993b7207e
fix(config): 💚 Fix incorrect CI config 2024-09-23 11:56:18 +02:00
Jesse Wierzbinski c7221ae9d1
refactor(plugin): ♻️ Remove mandatory manifest inside Plugin constructor 2024-09-23 11:54:42 +02:00
Jesse Wierzbinski d224d7b9b8
feat(plugin): Add dynamic plugin and manifest loader 2024-09-23 11:51:15 +02:00
Jesse Wierzbinski f623f2c1a0
refactor(plugin): ♻️ Move plugin manifests to json file, add JSON schema 2024-09-23 10:34:14 +02:00
Jesse Wierzbinski 3bcb7225bf
chore: ⬆️ Upgrade Bun to 1.1.29 2024-09-23 10:02:30 +02:00
Jesse Wierzbinski ea248c96c4
chore: ⬆️ Upgrade dependencies 2024-09-23 10:01:40 +02:00
Jesse Wierzbinski 24172b5138
fix: 🚑 Patch federation library to work around a bug in Bun's bundling
Fixes build failures
2024-09-23 10:00:06 +02:00
Jesse Wierzbinski 5aa1c4e625
refactor(api): ♻️ More OpenAPI refactoring work 2024-09-16 15:29:09 +02:00
Jesse Wierzbinski 6d9e385a04
chore: ⬆️ Upgrade dependencies 2024-09-16 12:34:33 +02:00
Jesse Wierzbinski 9e3311e29f
refactor(api): ♻️ Refactor roles, SSO and timelines to new OpenAPI route format 2024-09-16 12:30:05 +02:00
Jesse Wierzbinski 739bbe935b
refactor(api): ♻️ More OpenAPI refactoring 2024-09-15 14:59:21 +02:00
Jesse Wierzbinski b755fc5d62
refactor(api): ♻️ Move more API routes to new OpenAPI format 2024-09-15 14:28:47 +02:00
Jesse Wierzbinski 166d1c59a5
refactor: 🔥 Remove some code already provided by LogTape 2024-09-15 13:47:26 +02:00
Jesse Wierzbinski 12f7fa4047
fix(api): 🐛 Remove useless null check 2024-09-14 17:34:12 +02:00
Jesse Wierzbinski ad2d47d174
chore: ⬆️ Upgrade Bun to 1.1.27 2024-09-14 17:33:02 +02:00
Jesse Wierzbinski 2e41bfeee4
chore: ⬆️ Upgrade dependencies 2024-09-14 17:32:32 +02:00
Jesse Wierzbinski a05a0b313f
fix(api): 🐛 Fix logging causing crashes when parsing FormData 2024-09-14 17:30:02 +02:00
Jesse Wierzbinski cf149b737a
chore: ⬆️ Upgrade dependencies 2024-09-05 15:16:08 +02:00
Jesse Wierzbinski d335965b2e
chore: 📝 Update changelog 2024-09-04 23:40:22 +02:00
Jesse Wierzbinski d63196b5ee
fix(api): 🐛 Only decode URI, not full URI component, in application's redirect_url 2024-09-04 23:31:58 +02:00
Jesse Wierzbinski 53184bbe99
fix: 💚 Make CI config valid again 2024-09-04 23:22:48 +02:00
Jesse Wierzbinski bfd4c7884e
fix(api): 🐛 Also encode OAuth authorize endpoint response 2024-09-04 23:21:52 +02:00
Jesse Wierzbinski ac906acbe2
fix(api): 🐛 Encode redirect URI 2024-09-04 23:18:08 +02:00
Jesse Wierzbinski e68832683f
fix(api): 🐛 Correctly use Hono primitives in OpenID redirect 2024-09-04 23:15:33 +02:00
Jesse Wierzbinski 7f8ade5fc1
fix(api): 🐛 Correctly decode URI component when obtaining token.
Prevents redirect_uri mismatch by normalizing URL encoding
2024-09-04 23:11:16 +02:00
Jesse Wierzbinski 9dc143060f
refactor: 🔊 Don't use debugRequest for logging middleware (doesn't output a body) 2024-09-04 23:08:11 +02:00
Jesse Wierzbinski 128a21cd47
fix: 💚 Fix incorrect CI config 2024-09-04 23:04:07 +02:00
Jesse Wierzbinski 45c131dfed
fix(api): 🐛 Don't clone body twice 2024-09-04 22:59:39 +02:00
Jesse Wierzbinski 5d2aa82247
feat(api): Add response logging 2024-09-04 22:52:43 +02:00
Jesse Wierzbinski b5411c01e4
fix: ✏️ Fix typos 2024-09-04 21:55:23 +02:00
Jesse Wierzbinski 6c56b582b3
chore: ⬆️ Upgrade Bun to 1.1.26 2024-09-04 21:51:53 +02:00
Jesse Wierzbinski c0fafcdfda
chore: ⬆️ Upgrade dependencies 2024-09-04 21:44:49 +02:00
Jesse Wierzbinski d51bae52c6
refactor(plugin): ♻️ Move parts of OpenID logic to plugin 2024-08-29 20:32:04 +02:00
Jesse Wierzbinski 69d7d50239
refactor(api): ♻️ Remove old redirect() and response() in favour of Hono's builtins 2024-08-28 17:01:56 +02:00
Jesse Wierzbinski 691716f7eb
chore: 🔥 Move special GitHub files to another repository 2024-08-28 03:28:03 +02:00
Jesse Wierzbinski 878abd1c77
chore: ⬆️ Upgrade dependencies 2024-08-28 00:28:35 +02:00
Jesse Wierzbinski 5f090c3259
chore: ♻️ Use new branding 2024-08-28 00:06:49 +02:00
Jesse Wierzbinski f9023893af
refactor: 🚚 Point everything to the new GitHub org 2024-08-27 21:40:42 +02:00
Jesse Wierzbinski 47c666894c
refactor: ♻️ Refactor tests to use a simpler syntax 2024-08-27 21:25:26 +02:00
Jesse Wierzbinski 6ed1bd747f
refactor(api): ♻️ Refactor more routes to use OpenAPI 2024-08-27 20:14:10 +02:00
Jesse Wierzbinski 5554038f44
style: 🚨 Run Biome 2024-08-27 18:56:20 +02:00
Jesse Wierzbinski bcbc9e6bf1
refactor(api): ♻️ Refactor more routes into OpenAPI-compatible formats 2024-08-27 18:55:02 +02:00
Jesse Wierzbinski 02cb8bcd4f
feat(api): Add Swagger UI and OpenAPI endpoint 2024-08-27 18:09:15 +02:00
Jesse Wierzbinski f03542b37e
fix(api): 🚑 Make server start again when NUM_CPUS is undefined 2024-08-27 18:00:39 +02:00
Jesse Wierzbinski b0b750c05d
refactor(api): ♻️ Convery more routes to use OpenAPI 2024-08-27 17:40:58 +02:00
Jesse Wierzbinski 1ab1c68d36
refactor(api): 🚚 Refactor authentication middleware and implement some OpenAPI routes 2024-08-27 17:20:36 +02:00
Jesse Wierzbinski edf5edca9f
refactor(api): 🔥 Remove old mastodon-compatible login endpoints 2024-08-27 16:47:58 +02:00
Jesse Wierzbinski 184dae75ba
chore: 📝 Update Changelog 2024-08-27 16:46:10 +02:00
Jesse Wierzbinski bec60fbf96
refactor(api): 🔥 Remove Glitch-FE explicit support 2024-08-27 16:45:05 +02:00
Jesse Wierzbinski df466ecaa0
refactor(api): ♻️ Use OpenAPIHono instead of Hono in preparation for future changes 2024-08-27 16:40:11 +02:00
Jesse Wierzbinski 3c1b330d4b
refactor(api): 🚚 Use api/ for API routes instead of server/api/ 2024-08-27 16:37:23 +02:00
Jesse Wierzbinski dfc0bf4595
chore: ⬆️ Upgrade dependencies 2024-08-27 16:01:10 +02:00
Jesse Wierzbinski cea0544686
fix(federation): 🏷️ Correctly make InstanceMetadata description a string 2024-08-27 15:51:41 +02:00
Jesse Wierzbinski fbb845f7f8
refactor(federation): 🚚 Move old function to User 2024-08-27 15:50:14 +02:00
Jesse Wierzbinski 3b2c0d3b5a
chore(federation): ⬆️ Upgrade dependencies 2024-08-27 02:40:09 +02:00
Jesse Wierzbinski 4bf3c44959
fix(federation): 🐛 Change incorrect X-Date to X-Nonce 2024-08-27 02:26:00 +02:00
Jesse Wierzbinski 9cd53ce58a
fix(federation): 🐛 Correctly parse instance hostname (not a URL) 2024-08-27 02:21:09 +02:00
Jesse Wierzbinski df5e06ca8a
fix(federation): 🐛 Make HTTP header validation lowercase 2024-08-27 02:17:07 +02:00
Jesse Wierzbinski 9a917e2801
refactor(federation): 🔥 Remove 0.3.1 from list of supported versions 2024-08-27 01:59:39 +02:00
Jesse Wierzbinski 60ca66395c
fix(cli): 🐛 Fix incorrect path after build 2024-08-27 01:52:29 +02:00
Jesse Wierzbinski 0da6d508f3
fix: 🐛 Add correct Nix output hash 2024-08-26 20:23:47 +02:00
Jesse Wierzbinski 0ac540132a
chore: 💚 Update Nix hashes 2024-08-26 19:53:43 +02:00
Jesse Wierzbinski fbe86043b7
refactor(api): ♻️ Replace old client library with new version (@versia/client) 2024-08-26 19:40:15 +02:00
Jesse Wierzbinski 7708bff31f
chore(federation): Remove old federation SDK 2024-08-26 19:34:54 +02:00
Jesse Wierzbinski 334c429bfa
fix(federation): 🐛 Correctly validate outbound Collections 2024-08-26 19:34:21 +02:00
Jesse Wierzbinski 42e198ca0e
chore(federation): 👽 Finish initial Versia Working Draft 4 update 2024-08-26 19:27:40 +02:00
Jesse Wierzbinski c3fa867e74
chore(federation): 👽 Initial Versia Working Draft 4.0 support 2024-08-26 19:06:49 +02:00
Jesse Wierzbinski 9c71c3fe51
chore: ⬆️ Upgrade dependencies 2024-08-26 18:15:14 +02:00
Jesse Wierzbinski bc0943c569
feat(database): Implement read replicas for database 2024-08-26 18:04:22 +02:00
Jesse Wierzbinski c75306c58b
chore: 🚚 Rename Nix package to use new site 2024-08-26 17:45:14 +02:00
Gaspard Wierzbinski 5c817fdb57
Merge pull request #27 from snaakey/nix
ci: Add nix workflow
2024-08-26 13:52:06 +02:00
emily 082df183d3
ci: Add nix workflow 2024-08-25 22:36:53 +02:00
Gaspard Wierzbinski a7e8b2d405
Merge pull request #26 from snaakey/nix
feat: nix flake overlay
2024-08-25 17:57:55 +02:00
emily 9aad2d0b27
feat: nix flake overlay 2024-08-25 17:52:04 +02:00
Gaspard Wierzbinski a3817564f7
Merge pull request #25 from snaakey/nix
fix nix package and add nix binary cache
2024-08-25 13:11:24 +02:00
emily a88af8cb18
feat: Add nix binary cache 2024-08-24 18:46:19 +02:00
emily c95296b82c
fix: nix package 2024-08-24 18:37:18 +02:00
emily 877b216eae
refactor: nix flake 2024-08-24 18:23:57 +02:00
Jesse Wierzbinski 832f72160f
refactor: 🚚 Use more Versia branding and assets 2024-08-24 15:34:49 +02:00
Jesse Wierzbinski 3d5a693d71
refactor: ♻️ Use node:cluster instead of Web Workers 2024-08-23 18:43:13 +02:00
Jesse Wierzbinski 6617413222
chore: ⬆️ Upgrade Bun to 1.1.25 2024-08-23 17:22:18 +02:00
Jesse Wierzbinski cfd9d0ceb1
chore: ⬆️ Upgrade dependencies 2024-08-23 17:21:04 +02:00
Jesse Wierzbinski 3912314a83
fix: 🐛 Allow all headers during CORS 2024-08-19 21:56:14 +02:00
Jesse Wierzbinski 4d98034a79
fix: 🔥 Remove Prometheus integration as it is causing issues 2024-08-19 21:53:39 +02:00
Jesse Wierzbinski 5f0ef971f4
fix: 🐛 Only apply security headers to /api/* 2024-08-19 21:26:13 +02:00
Jesse Wierzbinski f3dd229dcb
feat: Implement Prometheus support 2024-08-19 21:23:47 +02:00
Jesse Wierzbinski 26749e576a
feat: Add more utility middleware 2024-08-19 21:17:25 +02:00
Jesse Wierzbinski 866692c1dc
refactor: ♻️ Use native Hono return functions instead of custom ones 2024-08-19 21:03:59 +02:00
Jesse Wierzbinski 7e2f333945
refactor: ♻️ Use a typed wrapper for all API endpoints 2024-08-19 20:06:38 +02:00
Jesse Wierzbinski b0e49855f5
fix: 🏷️ Temporarily suppress type error until packages get rebranding 2024-08-19 15:21:00 +02:00
Jesse Wierzbinski 771097d037
refactor: 🚚 Begin rebranding to Versia Server 2024-08-19 15:16:01 +02:00
Jesse Wierzbinski 64cef5c6d6
fix: 🐛 Fix incorrect path in import 2024-08-19 14:49:36 +02:00
Jesse Wierzbinski 82daa0c74f
chore: ⬆️ Upgrade Bun to 1.1.24 2024-08-19 14:47:12 +02:00
Jesse Wierzbinski eeafabe4dd
chore: ⬆️ Upgrade dependencies 2024-08-19 14:46:20 +02:00
Jesse Wierzbinski 526ae6cfdd
Merge branch 'main' of github.com:lysand-org/lysand 2024-08-19 14:43:57 +02:00
Jesse Wierzbinski 4a1ad9dd96
refactor: 🔥 Remove old config-manager symlink 2024-08-19 14:43:54 +02:00
emily f480036454 feat: Add nix package 2024-08-16 20:29:57 +02:00
emily f678d51542 refactor: flake 2024-08-16 20:29:57 +02:00
April John 4e6e3425ce fix: staging bundle 2024-08-13 14:59:03 +02:00
April John 2f46e75659 fix: staging bundle 2024-08-13 14:03:58 +02:00
April John 78eaa5478c fix: staging bundle 2024-08-13 13:49:17 +02:00
April John a17c634c16 fix: staging bundle 2024-08-13 13:27:10 +02:00
April John 9c1ca570b6 fix: staging bundle 2024-08-13 13:24:48 +02:00
April John 5b6a7557bb feat: staging builds 2024-08-13 13:19:13 +02:00
April John 92db74acf3 rm: nix build 2024-08-13 12:04:46 +02:00
Jesse Wierzbinski 26dc389010
fix: 🔥 Remove old useless post-build patches 2024-08-09 18:49:23 +02:00
Jesse Wierzbinski a80234c445
chore: 🔥 Remove stuff @cutestnekoaqua added by accident 2024-08-09 13:14:38 +02:00
Jesse Wierzbinski 2d7792e936
refactor: 🔥 Remove Husky 2024-08-09 13:10:39 +02:00
Jesse Wierzbinski d43c96e591
chore: ⬆️ Upgrade Bun to 1.1.22 2024-08-09 13:10:02 +02:00
Jesse Wierzbinski 5a159226db
chore: ⬆️ Upgrade dependencies 2024-08-09 13:09:27 +02:00
aprilthepink e588e98f4e feat: flake 2024-08-08 23:42:51 +02:00
Jesse Wierzbinski 8b96401c71
chore: ⬆️ Upgrade dependencies 2024-08-07 01:24:51 +02:00
Gaspard Wierzbinski 838debec25
fix(federation): Send Undos to the followee when unfollowing 2024-08-02 18:28:47 +02:00
DevMiner d2113e349f fix(federation): unfollows don't send Undos to the followee 2024-08-02 17:53:29 +02:00
Jesse Wierzbinski 1368dac77e
chore: ⬆️ Upgrade dependencies (patch Hono until my PR merges) 2024-07-28 01:22:59 +02:00
Jesse Wierzbinski 6445ceedc8
chore: ⬆️ Upgrade Bun to 1.1.21 2024-07-28 00:38:42 +02:00
Jesse Wierzbinski 0194b471a8
feat: Enable Sentry's extra error data integration 2024-07-27 22:44:21 +02:00
Jesse Wierzbinski 3baac85cf7
refactor: ♻️ Rewrite relationship system 2024-07-27 20:46:19 +02:00
Jesse Wierzbinski 62b68a64ac
fix: 🐛 Explicitely set log severity to "info" 2024-07-27 15:43:30 +02:00
Jesse Wierzbinski 7563315750
fix(federation): 🚚 Rename statuses to correct name, Notes 2024-07-26 21:37:09 +02:00
Jesse Wierzbinski 627afffdb2
fix(federation): ✏️ Correct typo causing incorrect URIs 2024-07-26 21:29:37 +02:00
Jesse Wierzbinski 903415161e
fix(federation): 🐛 Fix issues with note federation URIs 2024-07-26 21:19:41 +02:00
Jesse Wierzbinski 92a80d97c2
fix(federation): 🐛 Force add content-type http header 2024-07-26 20:35:26 +02:00
Jesse Wierzbinski 5162000a1f
chore: ⬆️ Upgrade SDK version 2024-07-26 20:22:52 +02:00
Jesse Wierzbinski 385bdc13da
fix(federation): 🐛 Send correct headers in inbox requests 2024-07-26 19:51:08 +02:00
Jesse Wierzbinski 5826acbf24
docs: 📝 Update CHANGELOG 2024-07-26 19:33:54 +02:00
Jesse Wierzbinski eb96544e68
fix(federation): 🐛 Remove usage of Origin header during federation 2024-07-26 19:26:35 +02:00
Jesse Wierzbinski 558ae72c82
fix(federation): 🐛 Fix new notes being federated to ALL remote users, regardless of visibility 2024-07-26 19:21:03 +02:00
Jesse Wierzbinski 2f823317c2
refactor(federation): 🔥 Remove old code and simplify federation requests 2024-07-26 18:51:39 +02:00
Jesse Wierzbinski ad9ed2598c
refactor(federation): ⬆️ Refactor code to use v2.2 of federation SDK 2024-07-26 18:07:11 +02:00
Jesse Wierzbinski aca837cb16
feat(federation): Log signatures generated when serving entites via HTTP 2024-07-26 17:32:03 +02:00
Jesse Wierzbinski 1216e278e8
fix(federation): 🐛 Fix some errors not being ignored properly 2024-07-26 17:20:37 +02:00
Jesse Wierzbinski db2d582295
chore: ⬆️ Upgrade dependencies 2024-07-26 00:58:56 +02:00
Jesse Wierzbinski 505f7712d6
fix(config): 🐛 Add trace propagation targets to Sentry example config 2024-07-26 00:50:03 +02:00
Jesse Wierzbinski 6ae13265fa
feat(federation): Make server actor available on /users/actor 2024-07-26 00:32:33 +02:00
Jesse Wierzbinski 420a0d05dc
feat: Add git commit SHAs to Sentry release 2024-07-26 00:20:58 +02:00
Jesse Wierzbinski 152e42fd30
fix: 💚 Move git commit env var in last step of Dockerfile to avoid it being removed 2024-07-26 00:17:54 +02:00
Jesse Wierzbinski 39d9b4c031
ci: 💚 Correctly input Git SHA into docker build 2024-07-26 00:10:42 +02:00
Jesse Wierzbinski bc25896ed8
feat(config): 🧑‍💻 Add custom tracing URL support to Sentry config 2024-07-26 00:02:48 +02:00
Jesse Wierzbinski 7d1522cc1e
feat(config): Add JSON schema for config 2024-07-25 23:51:00 +02:00
Jesse Wierzbinski d20988afa1
feat(federation): Add signatures to all users and objects served 2024-07-24 23:42:00 +02:00
Jesse Wierzbinski 5a52ac005b
fix(federation): 🚑 Don't re-parse response as JSON when FederationRequester has done so already 2024-07-24 19:25:14 +02:00
Jesse Wierzbinski 0bc6a89706
feat(api): Add more Sentry logging 2024-07-24 19:04:00 +02:00
Jesse Wierzbinski daba8e8178
refactor(federation): ♻️ Refactor code to use less fetch calls and instead use FederationRequester 2024-07-24 18:52:30 +02:00
Jesse Wierzbinski 833f261392
fix(config): 🚑 Add default value for Sentry config 2024-07-24 18:15:33 +02:00
Jesse Wierzbinski 59be7cb55f
chore: 📝 Update changelog for 0.7.0 2024-07-24 18:13:45 +02:00
Jesse Wierzbinski 5061735da7
feat: Add Sentry support 2024-07-24 18:10:29 +02:00
Jesse Wierzbinski 0679971cc0
feat(api): Log all server errors in logs 2024-07-24 17:19:23 +02:00
Jesse Wierzbinski 98a2549a3d
chore: ⬆️ Upgrade dependencies 2024-07-24 16:39:12 +02:00
Jesse Wierzbinski 8213ca62e0
chore: ⬆️ Upgrade @lysand-org/federation to latest 2024-07-23 00:13:46 +02:00
Jesse Wierzbinski 8a6d71d958
feat(api): 🏷️ Only allow JSON values in JSON HTTP responses 2024-07-22 22:02:17 +02:00
Jesse Wierzbinski d4894c362e
refactor(api): ♻️ Remove password2 from password resets (done on client) 2024-07-22 21:29:02 +02:00
Jesse Wierzbinski 0645203d97
docs: 📝 Clarify installation pitfalls 2024-07-22 21:02:41 +02:00
Jesse Wierzbinski f3902f8c7b
fix(api): 🛂 Default grant_type to authorization_code in token requests 2024-07-22 15:54:53 +02:00
Jesse Wierzbinski 757eb835e9
feat(api): Redirect browsers to frontend when accessing raw Lysand objects 2024-07-21 22:37:12 +02:00
Jesse Wierzbinski cf5684cf26
feat(api): Add debug query parameter when viewing raw Lysand objects 2024-07-21 22:33:15 +02:00
Jesse Wierzbinski 7f48c990e7
feat(api): Add global server error handler 2024-07-20 00:30:13 +02:00
Jesse Wierzbinski 23d091f7ce
fix(federation): 🐛 Make WebFinger always search for local users 2024-07-20 00:18:44 +02:00
Jesse Wierzbinski b5b8831073
fix(federation): 🐛 Output error when requesting remote users through /users 2024-07-20 00:17:35 +02:00
Jesse Wierzbinski 42ff591e48
fix(federation): 🐛 Fix fetching of ActivityPub users not working anymore 2024-07-17 15:37:36 +02:00
Jesse Wierzbinski 0e054e7cba
fix(federation): 🐛 Put the ActivityPub link first in WebFinger to work around Misskey bug 2024-07-17 15:08:21 +02:00
Jesse Wierzbinski 896d22616d
chore: ⬆️ Upgrade dependencies 2024-07-17 14:49:36 +02:00
Jesse Wierzbinski 42144a578b
chore: ⬆️ Upgrade Bun to 1.1.20 2024-07-17 14:48:45 +02:00
Jesse Wierzbinski fea19eeb2e
feat(federation): Add WebFinger forwarding to bridge for ActivityPub requests 2024-07-17 14:46:43 +02:00
Jesse Wierzbinski be881f18cd
feat(api): Add new endpoint to get a user by its username 2024-07-17 14:02:29 +02:00
Jesse Wierzbinski 407eb5e205
feat(api): Make account searches case-insensitive 2024-07-17 01:26:24 +02:00
Jesse Wierzbinski 7c285ee14d
feat(api): Add refetching API 2024-07-17 01:20:18 +02:00
Jesse Wierzbinski f081941474
test: 🐛 Delete test users after all tests 2024-07-17 00:50:59 +02:00
Jesse Wierzbinski cc8a97ae79
fix(config): 🐛 Make bridge url default to undefined, not "" 2024-07-16 23:36:36 +02:00
Jesse Wierzbinski f2c9814171
fix(config): 🐛 Ensure bridge config is optional 2024-07-16 23:33:11 +02:00
Jesse Wierzbinski aae99c804a
fix(federation): 🐛 Don't try to fetch from ActivityPub instances if no bridge is configured 2024-07-16 23:30:52 +02:00
Jesse Wierzbinski ff315af230
feat(federation): Add ActivityPub bridge support with CLI command 2024-07-16 23:29:20 +02:00
Jesse Wierzbinski 153aa061f0
ci: 👷 Add new typecheck CI action 2024-07-16 20:17:35 +02:00
Jesse Wierzbinski ba56c98e35
chore(federation): ⬆️ Upgrade @lysand-org/federation to 2.1.1 2024-07-16 20:17:18 +02:00
Jesse Wierzbinski da16a5d4c2
chore: ⬆️ Upgrade dependencies 2024-07-16 20:10:04 +02:00
Jesse Wierzbinski 74b194b1f4
fix: 👷 Correctly add Node during Docker build 2024-07-11 13:34:24 +02:00
Jesse Wierzbinski 65abaa9c7b
feat: 👷 Add linting workflow, add Node to Docker building step 2024-07-11 13:25:31 +02:00
Jesse Wierzbinski be3bced531
chore: ⬆️ Upgrade Bun to 1.1.18 2024-07-11 13:21:16 +02:00
Jesse Wierzbinski 939815510c
refactor: ⬆️ Upgrade dependencies, use JSR for Hono 2024-07-11 12:56:28 +02:00
Jesse Wierzbinski 57b295ccf2
fix(cli): 🐛 Don't federate changes to remote users, initialize search indexer on all CLI commands 2024-06-29 22:55:50 -10:00
Jesse Wierzbinski 49a2552e96
docs: 📝 Fix for slightly incorrect docs 2024-06-29 22:36:00 -10:00
Jesse Wierzbinski b111a41f01
chore: ⬆️ Upgrade dependencies 2024-06-29 22:25:15 -10:00
Jesse Wierzbinski cea9452127
refactor(federation): ♻️ Replace WebFinger code with @lysand-org/federation logic, add new debug command 2024-06-29 22:24:10 -10:00
Jesse Wierzbinski 38c8ea24a9
refactor(federation): 🔥 Remove some old SQL queries for Instances 2024-06-29 21:08:03 -10:00
Jesse Wierzbinski f2b0de779b
refactor: ♻️ Store instance federation protocol in database, refactor fetcher 2024-06-29 20:58:39 -10:00
Jesse Wierzbinski 6dc51ab323
fix: 🐛 Externalize acorn because it builds weirdly and leads to errors 2024-06-29 01:27:03 -10:00
Jesse Wierzbinski 03f5965755
fix: 🐛 Hack a fix for incorrect bundler output 2024-06-29 00:06:09 -10:00
Jesse Wierzbinski 84bdb75d77
fix: 🐛 Fix v2 search API not working at all 2024-06-28 23:59:01 -10:00
Jesse Wierzbinski 93b8609411
feat: Add CLI command to rebuild index 2024-06-28 23:58:43 -10:00
Jesse Wierzbinski 19c15f7e96
refactor: ♻️ Replace Meilisearch with Sonic 2024-06-28 23:40:44 -10:00
Jesse Wierzbinski 2cf1537a7e
chore: ♻️ Replace all @ts-ignore with @ts-expect-error 2024-06-28 21:33:19 -10:00
Jesse Wierzbinski a8132e8d53
docs: 📝 Improve contribution docs 2024-06-28 21:31:17 -10:00
Jesse Wierzbinski 5f7c77a3d8
fix: 🐛 Fix files without a filename crashing the media manager 2024-06-28 21:00:02 -10:00
Jesse Wierzbinski e95cabb304
fix: 🐛 Properly install and import the S3 client 2024-06-28 20:49:17 -10:00
Jesse Wierzbinski 106e34848a
refactor(api): 🏷️ Replace API types with those from @lysand-org/client 2024-06-28 20:36:15 -10:00
Jesse Wierzbinski 99b8c35f7b
chore: ⬆️ Upgrade dependencies 2024-06-28 20:15:07 -10:00
Jesse Wierzbinski faf829437d
refactor: ♻️ Rewrite media management code 2024-06-28 20:10:02 -10:00
Jesse Wierzbinski d09f74e58a
refactor: 🚚 Rename functions, move getUrl to Attachment 2024-06-28 17:50:56 -10:00
Jesse Wierzbinski 11c3931007
docs: 📝 Add changelog for 0.7, update README with new features 2024-06-28 17:44:42 -10:00
Jesse Wierzbinski e1555e6fe7
test(api): Add test to check state parameter during oauth 2024-06-26 17:00:09 -10:00
Jesse Wierzbinski a6c5f320e3
fix(api): 🚑 Remove hack to get text 2024-06-26 16:14:12 -10:00
Jesse Wierzbinski a93085ae1d
fix(api): 🐛 Allow empty website in app creation to fix issue with Traewelling 2024-06-26 15:39:48 -10:00
Jesse Wierzbinski e59c3aa625
fix(api): 🐛 Fix body not being logged in request debugging 2024-06-26 15:31:51 -10:00
Jesse Wierzbinski de75310b61
fix: 🐛 Fix request logs not showing up when starting server via CLI 2024-06-26 15:16:50 -10:00
Jesse Wierzbinski 556ef83ecf
fix: 🐛 Fix LogTape expecting a Deno global to exist (patch) 2024-06-26 15:06:57 -10:00
Jesse Wierzbinski 3004ec2350
chore: ⬆️ Upgrade Bun to 1.1.17 2024-06-26 14:46:22 -10:00
Jesse Wierzbinski d29603275a
refactor(api): ♻️ Use Web Workers instead of spawning the same process once for each thread 2024-06-26 14:44:08 -10:00
Jesse Wierzbinski bc8220c8f9
refactor: ♻️ Replace logging system with @logtape/logtape 2024-06-26 13:11:39 -10:00
Jesse Wierzbinski 75992dfe62
fix(api): 🐛 Disable CSP upgrade-insecure-requests when using Tor 2024-06-25 18:46:16 -10:00
Jesse Wierzbinski ae3d5813cf
fix(api): 🐛 Fix frontend proxy breaking when host is different than base_url 2024-06-25 18:31:57 -10:00
Jesse Wierzbinski 51cbb22eb0
fix(config): 🐛 Allow empty proxy address (in case config value isn't set) 2024-06-25 18:00:03 -10:00
Jesse Wierzbinski b8b822e553
feat(config): Add support for HTTP proxies on outgoing requests 2024-06-25 17:13:40 -10:00
Jesse Wierzbinski 0ecb65de29
refactor: ♻️ Move config checker code into its own file 2024-06-25 16:30:51 -10:00
Jesse Wierzbinski 8a774fa05d
refactor(plugin): ♻️ Use enum instead of strings 2024-06-21 23:44:21 -10:00
Jesse Wierzbinski 98f8ec071c
feat(plugin): Initialize new plugin system 2024-06-21 18:22:53 -10:00
Jesse Wierzbinski 1b427cf225
chore: ⬆️ Upgrade dependencies 2024-06-20 19:15:18 -10:00
Jesse Wierzbinski 70cd00cfa8
refactor(federation): ⬆️ Use @lysand-org/federation v2.0.0 2024-06-19 13:21:02 -10:00
Jesse Wierzbinski 47ce60494a
chore: ⬆️ Upgrade Bun to 1.1.14 2024-06-19 13:04:01 -10:00
Jesse Wierzbinski 84f2312508
chore: ⬆️ Upgrade dependencies 2024-06-19 13:01:54 -10:00
Jesse Wierzbinski f5330b6134
feat: Add more metadata to v2 instance API endpoint 2024-06-19 12:02:47 -10:00
Jesse Wierzbinski e9f504aa0c
fix(api): 🐛 Proxy all OIDC provider icons 2024-06-18 20:56:29 -10:00
Jesse Wierzbinski 6e7d16864a
chore: 🧑‍💻 Add list of recommended vscode extensions 2024-06-18 20:56:12 -10:00
Jesse Wierzbinski f341f58a73
fix(api): 🐛 Properly edit username when asked via API 2024-06-18 18:39:34 -10:00
Jesse Wierzbinski e013362ac4
fix(api): 🛂 Fix two variables with the same name causing incorrect data to be returned 2024-06-18 18:01:13 -10:00
Jesse Wierzbinski 32538586dc
docs: 🐛 Fix incorrect path in docs 2024-06-17 19:47:15 -10:00
Jesse Wierzbinski 925179211a
docs: 🐛 Fix incorrect filename 2024-06-17 11:13:16 -10:00
Jesse Wierzbinski 65498e7bd7
fix(api): 🐛 Fix Cloudflare R2 incorrectly serving SVG images as XML 2024-06-16 01:50:32 -10:00
Jesse Wierzbinski de9dca5735
feat(config): Add option to never convert vector images 2024-06-16 01:39:16 -10:00
Jesse Wierzbinski 6ef3a854d9
feat(api): Allow animated media to be uploaded 2024-06-16 01:15:49 -10:00
Jesse Wierzbinski d33a61e713
fix(api): 🐛 Properly disable challenges when config option is set 2024-06-15 20:27:31 -10:00
Jesse Wierzbinski a0d56c044b
docs: 📝 Update some outdated documentation info 2024-06-14 00:02:33 -10:00
Jesse Wierzbinski 731fc9847c
fix(api): 🚚 Rename "id" to "hash" 2024-06-13 23:55:39 -10:00
Jesse Wierzbinski 2ec7e512e0
fix(api): 🐛 Fix local media endpoint not being correctly registered
Expected only a name, forgot about the file hash
2024-06-13 23:54:47 -10:00
Jesse Wierzbinski c764cc044d
fix: 🚑 Sleep process instead of exiting it on error
Avoids Docker's auto-restart policy from causing infinite reboots and hanging the system
2024-06-13 23:44:46 -10:00
Jesse Wierzbinski 7ba0eb82f1
docs: 📝 Add info about possible errors during installation 2024-06-13 23:42:24 -10:00
Jesse Wierzbinski afeffdbd13
fix(config): 🔥 Remove tos_url from example config (not used anymore) 2024-06-13 23:30:55 -10:00
Jesse Wierzbinski b7f8f6689e
fix(config): 🐛 Fix crashes with default config values 2024-06-13 23:29:41 -10:00
Jesse Wierzbinski edbe6e45b2
fix: 🐛 Fix incorrect docs, make exit code 0 instead of 1 2024-06-13 23:26:20 -10:00
Jesse Wierzbinski 641e712272
fix: 📝 Update docker-compose file 2024-06-13 23:11:01 -10:00
Jesse Wierzbinski 99f14ba114
feat(api): Automatically register user when connecting with OIDC profile not already existing 2024-06-13 23:05:04 -10:00
Jesse Wierzbinski 70a669a29c
docs(api): 📝 Document API parameter to change username 2024-06-13 22:19:15 -10:00
Jesse Wierzbinski cfa0ab4ac9
chore: ⬆️ Upgrade dependencies 2024-06-13 22:06:50 -10:00
Jesse Wierzbinski 279ccf078f
chore: 🚨 Disable linter cognitive complexity by default 2024-06-13 22:04:22 -10:00
Jesse Wierzbinski 8f9472b221
feat(api): Implement Challenges API 2024-06-13 22:03:51 -10:00
Jesse Wierzbinski 924ff9b2d4
refactor(api): 🚚 Change ApiRouteMetadata structure to be more consistent 2024-06-13 18:03:17 -10:00
Jesse Wierzbinski 00fd751c2a
fix(federation): 🐛 Fix mention URIs in Lysand Notes being empty string for local users 2024-06-13 11:53:41 -10:00
Jesse Wierzbinski 0359ba13c4
fix(api): 🛂 Don't automatically remove trailing slashes from OIDC issuer URLs 2024-06-13 11:33:07 -10:00
Jesse Wierzbinski c3271ba264
docs: 📝 More work on JSDoc 2024-06-12 22:52:03 -10:00
Jesse Wierzbinski 527137f279
chore: ⬆️ Upgrade dependencies 2024-06-12 20:39:53 -10:00
Jesse Wierzbinski 83275be536
refactor: ⚰️ Remove dead code and useless files 2024-06-12 20:34:17 -10:00
Jesse Wierzbinski 98f3ab23d8
refactor(api): 🎨 Simplify expressions 2024-06-12 20:20:49 -10:00
Jesse Wierzbinski 36d70fb612
refactor(api): 🎨 Simplify bait middleware 2024-06-12 19:38:57 -10:00
Jesse Wierzbinski d301d4da09
refactor(api): 🎨 Don't use node:crypto for random strings 2024-06-12 19:38:26 -10:00
Jesse Wierzbinski d8cb1d475b
refactor(api): 🎨 Refactor emojis into their own class 2024-06-12 18:52:01 -10:00
Jesse Wierzbinski c61f519a34
refactor(api): 🎨 Refactor complex functions into smaller ones 2024-06-12 18:16:59 -10:00
Jesse Wierzbinski a1e02d0d78
refactor: 🚨 Turn every linter rule on and fix issues (there were a LOT :3) 2024-06-12 16:26:43 -10:00
Jesse Wierzbinski 2e98859153
refactor(database): ♻️ Move Attachment into its own class 2024-06-12 15:03:57 -10:00
Jesse Wierzbinski 5565bf00de
refactor(database): 🎨 Improve database handlers to have more consistent naming and methods 2024-06-12 14:45:07 -10:00
Jesse Wierzbinski a6159b9d55
feat(api): Preliminary captcha support database tables 2024-06-12 13:48:58 -10:00
Jesse Wierzbinski 9d8c2e81e9
refactor(database): 🚚 Move drizzle migrations to their own folder 2024-06-12 13:10:40 -10:00
Jesse Wierzbinski b17b2be683
fix(api): 🔒 Properly proxy role icons through media proxy 2024-06-11 18:57:04 -10:00
Jesse Wierzbinski 3d1cc52d14
docs: ✏️ Fix typos in docs 2024-06-11 14:42:29 -10:00
Jesse Wierzbinski ddaaa38fce
chore: ⬆️ Update Bun to 1.1.13 2024-06-11 14:40:55 -10:00
Jesse Wierzbinski efe202ea27
feat(api): Add Lysand roles to user accounts 2024-06-11 14:29:59 -10:00
Jesse Wierzbinski 4f2c98390c
chore: 📦 Update dependencies 2024-06-11 13:43:33 -10:00
Jesse Wierzbinski c4da7e1484
fix(api): 🐛 Fix incorrect relationships being returned (small rewrite) 2024-06-11 13:42:36 -10:00
Jesse Wierzbinski 20d1a5f39e
feat(api): Add requested_by to relationships 2024-06-11 12:32:38 -10:00
Jesse Wierzbinski ffcf01e3cd
feat(api): Add TOS and Privacy Policy support 2024-06-11 09:55:40 -10:00
Jesse Wierzbinski e9e33432c2
fix: 🐛 Make sure reverse proxies don't tamper with URLs 2024-06-10 16:24:32 -10:00
Jesse Wierzbinski 9f262c12d6
fix: 🛂 Remove code parameter from redirect URI 2024-06-10 16:15:29 -10:00
Jesse Wierzbinski 876b0dcde8
fix: 🛂 Don't remove iss from current URL 2024-06-10 16:11:42 -10:00
Jesse Wierzbinski 5a7b3d0f25
fix: 🛂 Remove iss parameter from OIDC redirect URL 2024-06-10 16:08:57 -10:00
Jesse Wierzbinski 80c9b10c36
fix: 🛂 Remove uninitialized variable access 2024-06-10 16:04:47 -10:00
Jesse Wierzbinski f0c69cfb33
chore: 📦 Update packages 2024-06-10 16:00:52 -10:00
Jesse Wierzbinski b3bace4d53
fix(api): 🛂 Fix incorrect redirect URI passed to external OIDC providers, normalize issuer URL 2024-06-10 15:58:51 -10:00
Jesse Wierzbinski dae37d47a3
fix(api): Don't use markdown-it-anchor when rendering Markdown 2024-06-09 18:04:20 -10:00
Jesse Wierzbinski 8da4b07642
chore: 📦 Update packages 2024-06-09 16:16:21 -10:00
Jesse Wierzbinski d2f5aaf114
fix(api): 🐛 Add default role with default permissions in roles API 2024-06-09 16:14:36 -10:00
Jesse Wierzbinski 11369649c0
fix(api): 🐛 Give correct URI/URL values for notes in API 2024-06-09 15:17:03 -10:00
Jesse Wierzbinski c6c71bebb7
chore: 📦 Update packages 2024-06-08 15:36:50 -10:00
Jesse Wierzbinski 1163dacbd6
chore: 🐛 Update lockfile 2024-06-07 23:08:38 -10:00
Jesse Wierzbinski deb532c970
chore: 📦 Update packages 2024-06-07 23:08:15 -10:00
Jesse Wierzbinski 3f90625429
chore: 🐛 Change --apply to --write in precommit linting 2024-06-07 19:32:47 -10:00
Jesse Wierzbinski 4902f078a8
feat(api): Add permissions to every route and permission config 2024-06-07 18:57:29 -10:00
Jesse Wierzbinski 19823d8eca
feat(api): Add Roles API 2024-06-07 17:31:17 -10:00
Jesse Wierzbinski 46f41199ac
chore: 📦 Update packages 2024-06-07 15:36:17 -10:00
Jesse Wierzbinski e229c30a9f
chore: 📦 Use NPM version of @lysand-org/federation 2024-06-07 15:33:00 -10:00
Jesse Wierzbinski 43544a44da
fix(api): 🐛 Fix some redirect URIs breaking the oauth redirection code 2024-06-07 14:08:27 -10:00
Jesse Wierzbinski 06e97bbf0a
fix(api): 🐛 Set proxy redirect to manual 2024-06-07 13:58:49 -10:00
Jesse Wierzbinski 43b41b793f
Merge branch 'main' of github.com:lysand-org/lysand 2024-06-07 13:52:22 -10:00
Jesse Wierzbinski 56e32e2c20
fix(api): 🐛 Add debug statement to proxy 2024-06-07 13:52:11 -10:00
Gaspard Wierzbinski 71d4c82573
Merge pull request #15 from skyeforeverblue/patch-1
Pedantic bullshit :>
2024-06-06 12:59:01 -10:00
Skye Chappelle 5c02477c52
Pedantic readme update 2024-06-06 18:55:10 -04:00
Jesse Wierzbinski 8f09ea4c60
feat(federation): Add support for federation of note editing 2024-06-05 21:04:52 -10:00
Jesse Wierzbinski 3e94a9d491
fix(api): 🐛 Fix incorrect editing of note attachments (when attachments are removed) 2024-06-05 20:35:56 -10:00
Jesse Wierzbinski 88ad7178bf
feat(federation): Federate note deletions 2024-06-05 19:25:49 -10:00
Jesse Wierzbinski 431bc9c715
fix(federation): 🐛 Add missing SQL AND 2024-06-05 19:15:10 -10:00
Jesse Wierzbinski 0eee4a1f20
fix(federation): 🐛 Don't try to federate to local users 2024-06-05 19:12:23 -10:00
Jesse Wierzbinski 32cb0ea733
fix(federation): 🐛 Fix profile edits not being federated 2024-06-05 19:08:34 -10:00
Jesse Wierzbinski 4c22b0edcc
feat(federation): Federate user profile changes to other instances 2024-06-05 18:58:28 -10:00
Jesse Wierzbinski f8196f72f9
feat(federation): Add user refetching, support for Undo in federation 2024-06-05 18:49:06 -10:00
Jesse Wierzbinski 908fdcaa79
feat(api): Add real filename to proxy 2024-06-05 18:07:22 -10:00
Jesse Wierzbinski 7f4e39e08b
fix(api): 🐛 Explicitely set Content-Encoding to an empty value in proxy to avoid Traefik v3.0 errors 2024-06-05 16:35:25 -10:00
Jesse Wierzbinski 5efd832e64
fix(api): 🐛 Another attempt at a fix 2024-06-05 16:26:19 -10:00
Jesse Wierzbinski a319d1e628
fix(api): 🐛 Attempt 2 at fixing proxy bug 2024-06-05 16:07:56 -10:00
Jesse Wierzbinski 29b98fd1d1
fix(api): 🐛 (hopefully) fix some media not being decoded well on proxy 2024-06-05 15:56:40 -10:00
Jesse Wierzbinski f4af0e2407
fix(api): 🐛 Prevent non-images uploaded from not being saved to object storage 2024-06-05 15:40:37 -10:00
Jesse Wierzbinski 268ced27ef
test(api): Add more tests for favourite and unfavourite 2024-06-03 21:23:00 -10:00
Jesse Wierzbinski 1d55570abd
fix(api): 🐛 Fix bugs where favourite/unfavourite could return negative values (+ add tests) 2024-06-03 21:12:55 -10:00
Jesse Wierzbinski d2767b0862
chore: 📦 Update packages in package.json too 2024-06-03 21:11:56 -10:00
Jesse Wierzbinski e6a4800bd1
chore: 📦 Update Bun to 1.1.12 2024-06-03 16:11:47 -10:00
Jesse Wierzbinski 381094c12d
chore: 📦 Update all dependencies to latest version 2024-06-03 16:08:42 -10:00
Jesse Wierzbinski f904ad33ba
refactor: ♻️ Clean up Dockerfile to not require Node to build 2024-05-28 22:14:20 -10:00
Jesse Wierzbinski ade9bd08fa
ci: 💚 Checkout with recursive submodules correctly 2024-05-28 15:19:39 -10:00
Jesse Wierzbinski f87bcbd0da
chore: ⚰️ Remove dead code and unused imports 2024-05-28 15:14:24 -10:00
Jesse Wierzbinski 3a37790315
chore: 🔖 Set version to 0.7.0 2024-05-28 15:03:20 -10:00
Jesse Wierzbinski 0706541546
refactor: 🚚 Add slash to typescript path shortcuts 2024-05-28 14:59:49 -10:00
Jesse Wierzbinski 5b658984a5
refactor: Only delete old test users once at preload 2024-05-28 14:51:55 -10:00
Jesse Wierzbinski f5a0f52b93
fix: 💚 Remove --frozen-lockfile during CI, as the build and dev Bun version differ and cause issues 2024-05-28 14:43:15 -10:00
Jesse Wierzbinski fbe0e35587
refactor(federation): ♻️ Refactor inbox code to use new package builtins 2024-05-28 14:36:15 -10:00
Jesse Wierzbinski fbc0c2c586
chore: 📦 Replace bun-types with @types/bun 2024-05-28 13:58:38 -10:00
Jesse Wierzbinski a87a474a62
chore: 📦 Update Bun to 1.1.10 2024-05-28 13:50:38 -10:00
Jesse Wierzbinski ddaa7269ba
chore: 📦 Add submodule to lysand-api, for feature testing 2024-05-28 13:49:24 -10:00
Jesse Wierzbinski 241ad8232d
fix(federation): 🐛 Hopefully fix the Great Signature Bug 2024-05-23 20:08:30 -10:00
Jesse Wierzbinski 75043bae15
feat(federation): Log signed string to debug as well 2024-05-23 19:46:22 -10:00
Jesse Wierzbinski fd59d9ebae
chore: 📦 Update all packages 2024-05-23 19:05:26 -10:00
Jesse Wierzbinski fc98c95892
chore(federation): Add sender public key to federation debugging 2024-05-23 19:04:05 -10:00
Jesse Wierzbinski f5605e6814
build: 📦 Update Bun to 1.1.9 2024-05-21 15:41:48 -10:00
Jesse Wierzbinski 14851fa93e
chore: 📦 Update all packages to their latest version 2024-05-21 15:33:36 -10:00
Jesse Wierzbinski fd38161769
fix(federation): 🐛 Fix debug logger not correctly outputting 2024-05-21 15:23:48 -10:00
Jesse Wierzbinski eab61b38f1
feat(federation): Add bridge tokens, federation request debugging 2024-05-21 14:59:03 -10:00
Jesse Wierzbinski 673b7d0bae
fix(api): 🐛 Automatically rewrite http to https in federation 2024-05-17 11:42:42 -10:00
Jesse Wierzbinski c28628ebb3
feat(api): Add safeguard for incorrectly configured proxies 2024-05-17 09:38:38 -10:00
Jesse Wierzbinski 7a591a024e
feat(federation): Add bridge support to Lysand 2024-05-17 07:56:13 -10:00
Jesse Wierzbinski a603b602e6
fix(federation): 🐛 Fix multiple incorrect outputs in federation routes 2024-05-17 07:39:59 -10:00
Jesse Wierzbinski b4b8f51a5a
chore: 🚨 Run format 2024-05-16 23:39:10 -10:00
Jesse Wierzbinski 36f7299a77
chore(build): ⬆️ Update federation lib to 1.1.5 2024-05-16 23:38:21 -10:00
Jesse Wierzbinski dfe678ffae
refactor(federation): ♻️ Use new federation package to send inbox requests 2024-05-16 22:47:30 -10:00
Jesse Wierzbinski 820591dddc
fix(cli): ⚰️ Remove old placeholder code and replace it with real code 2024-05-16 22:41:38 -10:00
Jesse Wierzbinski 398da5fc3f
fix(federation): 🐛 Fix federation bug with already consumed body 2024-05-16 22:37:06 -10:00
Jesse Wierzbinski e2362604c7
test(api): Add tests for password reset 2024-05-16 22:34:42 -10:00
Jesse Wierzbinski 24288c95b5
feat(api): Implement password resets 2024-05-16 22:27:41 -10:00
Jesse Wierzbinski 1365987a1c
fix(federation): 🚑 Correct user inbox endpoint API route 2024-05-16 21:51:49 -10:00
Jesse Wierzbinski 0a82cdc59e
docs(config): 📝 Explain what redirect URL to put in OIDC config 2024-05-16 21:30:27 -10:00
Jesse Wierzbinski 606c7e290c
chore: 📦 Update all packages to latest version 2024-05-16 21:00:43 -10:00
Jesse Wierzbinski ec62906221
chore: 🔖 Update version to 0.6.0 2024-05-16 20:59:21 -10:00
Jesse Wierzbinski d4e1c0d95d
fix(api): 🐛 Fix incorrect OAuth2 linking logic 2024-05-16 20:58:27 -10:00
Jesse Wierzbinski 2db4f25ba6
feat(config): Allow frontend route customization and forcing OIDC 2024-05-16 18:05:06 -10:00
Jesse Wierzbinski b34166de93
docs(api): 📝 Document all API changes 2024-05-16 15:50:13 -10:00
Jesse Wierzbinski b1216a43f2
refactor(api): ♻️ Change route names, improve API endpoints to be more consistent with Mastodon API 2024-05-16 15:49:59 -10:00
Jesse Wierzbinski a6eb826b04
docs: 📝 Update contributing guide 2024-05-16 09:50:37 -10:00
Jesse Wierzbinski 517f0c631e
refactor: 🔨 Also run bun install in pre-commit hooks 2024-05-15 17:32:05 -10:00
Jesse Wierzbinski 119f9ea97b
fix(build): 💚 Update Lockfile 2024-05-15 17:29:46 -10:00
Jesse Wierzbinski 060b3980ba
fix(build): 🐛 Make the prepare command use bunx 2024-05-15 17:28:37 -10:00
Jesse Wierzbinski 11460a83ad
refactor(build): 🐛 Promote Husky to dependency 2024-05-15 17:26:59 -10:00
Jesse Wierzbinski 6fdc8b2b9a
fix(build): 🐛 More shenanigans?? 2024-05-15 17:25:27 -10:00
Jesse Wierzbinski 075a23124b
feat: Add Husky 2024-05-15 17:19:45 -10:00
Jesse Wierzbinski 2b5b82b465
fix: 🐛 Fix all TypeScript issues 2024-05-15 17:07:34 -10:00
Jesse Wierzbinski 29aa43f4ce
fix(build): 🐛 Continuation of previous commit 2024-05-15 16:51:59 -10:00
Jesse Wierzbinski 023b80f411
fix(build): 🐛 Changed the CI test config so it is valid 2024-05-15 16:50:07 -10:00
Jesse Wierzbinski fb31375b74
refactor(config): 🔥 Replace config validation with Zod 2024-05-15 16:37:25 -10:00
Jesse Wierzbinski 093337dd4f
refactor(federation): 🔥 Remove old types and federation code 2024-05-14 14:38:30 -10:00
Jesse Wierzbinski 5fd6a4e43d
feat(federation): Replace old types and federation validators with @lysand-org/federation 2024-05-14 14:35:13 -10:00
Jesse Wierzbinski 25d087a54b
feat(build): Start Lysand from CLI in prod 2024-05-13 11:54:51 -10:00
Jesse Wierzbinski 6b83336fa3
fix(api): Fix a test sending a body with a GET request 2024-05-13 11:48:58 -10:00
Jesse Wierzbinski 6c3fcf699e
feat(api): Add support for multithreaded API servers 2024-05-13 11:36:46 -10:00
Jesse Wierzbinski e502a2d8c8
fix(federation): 🐛 Fix missing regex error message 2024-05-13 11:35:36 -10:00
Jesse Wierzbinski 5e87f85851
refactor(federation): 🦺 Update Lysand object validatio to be stricter 2024-05-13 11:33:39 -10:00
Jesse Wierzbinski eb976250a4
fix(api): 🐛 Fix post editing not working with JSON content-type 2024-05-12 19:25:56 -10:00
Jesse Wierzbinski b83d76abf6
fix(api): 🐛 Fix only one emoji being parsed per post 2024-05-12 19:14:37 -10:00
Jesse Wierzbinski 0d4d894fd4
docs: 📝 Update docs to add guidance on setting up OIDC 2024-05-12 18:55:34 -10:00
Jesse Wierzbinski 7f6aeeb859
feat(api): Add secret rudimentary OIDC account linking support 2024-05-12 18:34:35 -10:00
Jesse Wierzbinski ff43b19122
fix(api): 🐛 Fix OIDC 2024-05-12 18:01:51 -10:00
Jesse Wierzbinski 29d7b09677
fix(api): 🐛 Deleting emojis now removes them from object storage 2024-05-12 16:52:19 -10:00
Jesse Wierzbinski 7846a03bcf
fix(api): 🐛 Fix incorrect content_type when uploading and converting emojis 2024-05-12 16:30:26 -10:00
Jesse Wierzbinski 4f070c9b65
fix(api): 🔒 Replace bad webfinger regex with good one 2024-05-12 16:27:40 -10:00
Jesse Wierzbinski 9ad0f88ff2
fix(api): 🐛 Local media uploader: don't error out when a file has already been uploaded 2024-05-12 16:18:07 -10:00
Jesse Wierzbinski da2520e60e
feat(api): Add user emoji upload capabilities 2024-05-12 16:09:57 -10:00
Jesse Wierzbinski 980f4c8021
fix(api): 🐛 Forbid uploading emojis with the same shortcode 2024-05-12 15:17:15 -10:00
Jesse Wierzbinski 8e5d68144c
fix(api): 🐛 Fix regression in emoji parsing caused by incorrectly changed regex 2024-05-12 15:07:55 -10:00
Jesse Wierzbinski c6c92e716f
fix(api): Remove code that checks for missing filenames 2024-05-12 14:35:55 -10:00
aprilthepink c4910fb7f9 feat: nix shell
Signed-off-by: aprilthepink <aprl@acab.dev>
2024-05-13 02:30:52 +02:00
Jesse Wierzbinski 303928f960
refactor(api): 🔥 Refactor media uploader code 2024-05-12 14:30:27 -10:00
Jesse Wierzbinski 9566387273
fix(api): 🐛 Fix oauth token endpoint returning an ISO-8601 string instead of a unix timestamp 2024-05-12 14:01:37 -10:00
Jesse Wierzbinski ff14e5a5d3
fix(api): 🐛 Fix auth could not error when token was wrong and authorization required on a route 2024-05-12 13:53:49 -10:00
Jesse Wierzbinski 14d3a243a2
fix(api): 🐛 Add safeguards to emoji upload routes 2024-05-12 13:43:58 -10:00
Jesse Wierzbinski 67bee695e6
feat(api): Add safeguard when using formdata without a boundary 2024-05-12 13:21:06 -10:00
Jesse Wierzbinski 3f9ec0bc80
feat(api): Add support for urn:ietf:wg:oauth:2.0:oob oauth redirect URI 2024-05-12 12:24:15 -10:00
Gaspard Wierzbinski e07337340d
Merge pull request #14 from lysand-org/feat/nixify
feat: flake devshell
2024-05-12 12:06:39 -10:00
aprilthepink a17b9a739e feat: flake devshell
Signed-off-by: aprilthepink <aprl@acab.dev>
2024-05-12 23:58:58 +02:00
Jesse Wierzbinski 6859ab5775
fix(api): 🐛 Fix incorrect ancestor order in note context endpoint 2024-05-11 16:44:00 -10:00
Jesse Wierzbinski 4713d0f93f
fix(api): 🐛 Add JSON support to every form that doesn't have a file parameter 2024-05-11 15:51:01 -10:00
Jesse Wierzbinski 9f0eab03f2
fix(api): 🐛 Add JSON support to /api/v1/statuses/:id 2024-05-11 15:47:25 -10:00
Jesse Wierzbinski 77cab0962d
chore: 📦 Update all packages to latest version 2024-05-11 15:36:47 -10:00
Jesse Wierzbinski 6205718f0d
chore: 📦 Update Bun to 1.1.8 2024-05-11 15:34:57 -10:00
Jesse Wierzbinski 2cde8d2dd9
test(api): Fix a failing test in CI due to tests assuming we use S3 2024-05-11 15:33:32 -10:00
Jesse Wierzbinski 8fedd1a07d
feat(api): Add new admin emoji API 2024-05-11 15:27:28 -10:00
Jesse Wierzbinski b979daa39a
feat(api): Allow more HTML tags in Markdown 2024-05-11 15:27:19 -10:00
Jesse Wierzbinski 4ce5dfeae3
fix(api): 🔒 Remove client_secret during oauth consent flow 2024-05-09 15:56:46 -10:00
Jesse Wierzbinski 5b03d93ef8
fix(build): ⬇️ Downgrade Bun to 1.1.4 in the building stage of the Dockerfile 2024-05-08 16:39:54 -10:00
Jesse Wierzbinski 692db9a334
fix(build): 🐛 Fix build error with unzipit by making it external 2024-05-08 16:29:41 -10:00
Jesse Wierzbinski 5bdb8360ea
feat(cli): Add more emoji commands to CLI (add, delete, list, import) 2024-05-08 16:07:33 -10:00
Jesse Wierzbinski e48f57a3d8
perf(database): Improve performance when fetching timelines by fetching all data in a single SQL query 2024-05-08 13:19:53 -10:00
Jesse Wierzbinski 26dfd14aaf
test(api): Add tests for account registration endpoint 2024-05-08 12:54:44 -10:00
Jesse Wierzbinski 20629b1712
fix(api): 🐛 oh my god stop breaking 2024-05-08 12:41:27 -10:00
Jesse Wierzbinski f71c8a50d3
fix(api): 🐛 Fix incorrect zod types in registration 2024-05-08 12:37:28 -10:00
Jesse Wierzbinski 52e29e2dee
Fix megalodon accounts not registering 2024-05-08 12:34:01 -10:00
Jesse Wierzbinski 3c3814a3c1
fix(api): 🐛 Fix favourited attribute not being correct on serialized API notes 2024-05-08 11:51:47 -10:00
Jesse Wierzbinski 5fcbcd0f07
fix(api): 🐛 Fix bug where likes wouldn't appear on API notes 2024-05-08 10:57:42 -10:00
Jesse Wierzbinski a9629b825b
fix(api): 🎨 Do wizardry on qs middleware to also work on multipart formData 2024-05-08 01:16:16 -10:00
Jesse Wierzbinski 2acd281c76
fix(cli): 🐛 Disable pretty dates when outputting data as JSON or CSV 2024-05-08 00:41:59 -10:00
Jesse Wierzbinski f0f9c78cc6
fix(cli): 🐛 Don't skip password hash when a password is provided manually 2024-05-08 08:23:44 +00:00
Jesse Wierzbinski 4b51985149
chore: 📦 Update packages 2024-05-08 08:15:11 +00:00
Jesse Wierzbinski c7423d7421
fix(api): 🐛 Fix slightly incorrect error message when logging in with wrong credentials 2024-05-08 08:09:22 +00:00
Jesse Wierzbinski f9c9a7d527
feat(api): 🎨 Allow login with either username or email 2024-05-08 08:02:05 +00:00
Jesse Wierzbinski 47c88dd7dd
fix(cli): 🐛 Remove plugin-help from oclif as it was causing bundling errors 2024-05-08 07:26:17 +00:00
Jesse Wierzbinski 19c14ef3fc
fix(cli): 🐛 Remove file extension from oclif entrypoint config
Allows both .ts and .js to be used (in dev and when bundled)
2024-05-08 00:39:34 +00:00
Jesse Wierzbinski a1fc86761d
build(cli): 🐛 Add package.json into build to make oclif happy 2024-05-08 00:35:00 +00:00
Jesse Wierzbinski 04cd140f6d
fix(cli): 🐛 Fix CLI not working in build
Missing code in cli/index.ts does that to you
2024-05-08 00:30:45 +00:00
Jesse Wierzbinski 6b17b91235
fix(build): 🐛 Modify CLI to properly bundle 2024-05-08 00:24:21 +00:00
Jesse Wierzbinski 0d278e4fa9
build(build): 🎨 Edit build script to look nicer and build CLI 2024-05-08 00:10:42 +00:00
Jesse Wierzbinski fc06b35c09
feat(cli): Add new CLI commands, move to project root 2024-05-08 00:10:14 +00:00
Jesse Wierzbinski 68f16f9101
ci(build): 🔒 Add SBOM and provenance data to all automatic Docker builds 2024-05-07 23:49:47 +00:00
Jesse Wierzbinski a621ff7271
fix(build): 🐛 Fix a bug preventing building caused by an old build command being used in Dockerfile 2024-05-07 23:42:38 +00:00
Jesse Wierzbinski 06c30b8af2
feat(cli): ♻️ Begin new CLI rewrite with oclif 2024-05-07 07:41:02 +00:00
Jesse Wierzbinski 7b05a34cce
refactor: 🔥 Remove dead code 2024-05-07 03:13:37 +00:00
Jesse Wierzbinski 592f7c0ac2
docs: 📝 Update docker-compose to make Lysand default to the latest tag 2024-05-06 19:23:32 +00:00
Jesse Wierzbinski af354a5fb8
docs: 📝 Update README to talk about Markdown support in Lysand 2024-05-06 19:20:41 +00:00
Jesse Wierzbinski b244889fd2
docs: 📝 Update docker-compose to add api host env variable to frontend 2024-05-06 19:17:33 +00:00
777 changed files with 126002 additions and 31450 deletions

18
.deepsource.toml Normal file
View file

@ -0,0 +1,18 @@
version = 1
test_patterns = ["**/*.test.ts"]
[[analyzers]]
name = "shell"
[[analyzers]]
name = "javascript"
[analyzers.meta]
environment = ["nodejs"]
[[analyzers]]
name = "docker"
[analyzers.meta]
dockerfile_paths = ["Dockerfile"]

View file

@ -1,10 +0,0 @@
# Bun doesn't run well on Musl but this seems to work
FROM oven/bun:1.1.7-alpine as base
RUN apk add --no-cache libstdc++ git bash curl openssh cloc
# Switch to Bash by editing /etc/passwd
RUN sed -i -e 's|/bin/ash|/bin/bash|g' /etc/passwd
# Extract Node from its docker image (node:22-alpine)
COPY --from=node:22-alpine /usr/local/bin/node /usr/local/bin/node

View file

@ -1,34 +0,0 @@
{
"name": "Lysand Dev Container",
"dockerFile": "Dockerfile",
"runArgs": [
"-v",
"${localWorkspaceFolder}/config:/workspace/config",
"-v",
"${localWorkspaceFolder}/logs:/workspace/logs",
"-v",
"${localWorkspaceFolder}/uploads:/workspace/uploads",
"--network=host"
],
"mounts": [
"source=node_modules,target=/workspace/node_modules,type=bind,consistency=cached",
"type=bind,source=/home/${localEnv:USER}/.ssh,target=/root/.ssh,readonly"
],
"customizations": {
"vscode": {
"settings": {
"terminal.integrated.shell.linux": "/bin/bash"
},
"extensions": [
"biomejs.biome",
"ms-vscode-remote.remote-containers",
"oven.bun-vscode",
"vivaxy.vscode-conventional-commits",
"EditorConfig.EditorConfig",
"tamasfe.even-better-toml",
"YoavBls.pretty-ts-errors",
"ms-vscode-remote.remote-containers"
]
}
}
}

9
.editorconfig Normal file
View file

@ -0,0 +1,9 @@
root = true
[*]
charset = utf-8
end_of_line = lf
indent_style = space
insert_final_newline = true
tab_width = 4
trim_trailing_whitespace = true

1
.envrc Normal file
View file

@ -0,0 +1 @@
use flake

View file

@ -1,39 +0,0 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: bug
assignees: ''
---
## Describe the bug
A clear and concise description of what the bug is.
## Steps to reproduce
Steps to reproduce the behavior, such as a cURL command, HTTP request, situation or code repository
## Expected behavior
A clear and concise description of what you expected to happen.
## Screenshots
If applicable, add screenshots to help explain your problem.
## Logs
Please upload logs onto a service like [Pastebin](https://pastebin.com/) or [Hastebin](https://hastebin.com/) and paste the link here. Don't paste the logs directly into the GitHub issue, as it just looks ugly and is hard to read.
## Environment
- OS: [e.g. Fedora 39]
- Bun version
- Postgres version
- Lysand commit ID or version
## Additional context
Add any other context about the problem here.

View file

@ -1,28 +0,0 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: enhancement
assignees: ''
---
## Is your feature request related to a problem? Please describe.
A clear and concise description of what the problem is, such as "I'm always frustrated when [...]" or "I can't do [...]"
## Describe the solution you'd like
What would you like to see implemented?
## Describe alternatives you've considered
If applicable, describe any alternative solutions or features you've considered.
## Additional context
Add any other context or screenshots about the feature request here.
## Are you willing to work on this feature?
If you are willing to work on this feature, please say so here.

View file

@ -1,117 +1,171 @@
[database]
# You can change the URL to the commit/tag you are using
#:schema https://raw.githubusercontent.com/versia-pub/server/main/config/config.schema.json
# All values marked as "sensitive" can be set to "PATH:/path/to/file" to read the value from a file (e.g. a secret manager)
[postgres]
# PostgreSQL database configuration
host = "localhost"
port = 5432
username = "lysand"
password = "lysand"
database = "lysand"
username = "versia"
# Sensitive value
password = "versia"
database = "versia"
# Additional read-only replicas
# [[postgres.replicas]]
# host = "other-host"
# port = 5432
# username = "versia"
# password = "mycoolpassword2"
# database = "replica1"
[redis.queue]
# A Redis database used for managing queues.
# Required for federation
host = "localhost"
port = 6379
password = ""
# Sensitive value
# password = "test"
database = 0
# A Redis database used for caching SQL queries.
# Optional, can be the same as the queue instance
# [redis.cache]
# host = "localhost"
# port = 6380
# database = 1
# password = ""
# Search and indexing configuration
[search]
# Enable indexing and searching?
enabled = false
[redis.cache]
host = "localhost"
port = 6379
password = ""
database = 1
enabled = false
[meilisearch]
# Optional if search is disabled
[search.sonic]
host = "localhost"
port = 40007
api_key = ""
enabled = false
# Sensitive value
password = ""
[signups]
# URL of your Terms of Service
tos_url = "https://example.com/tos"
# Whether to enable registrations or not
registration = true
rules = [
"Do not harass others",
"Be nice to people",
"Don't spam",
"Don't post illegal content",
]
[oidc]
# Run Lysand with this value missing to generate a new key
jwt_key = "MC4CAQAwBQYDK2VwBCIEID+H5n9PY3zVKZQcq4jrnE1IiRd2EWWr8ApuHUXmuOzl;MCowBQYDK2VwAyEAzenliNkgpXYsh3gXTnAoUWzlCPjIOppmAVx2DBlLsC8="
[registration]
# Can users sign up freely?
allow = true
# NOT IMPLEMENTED
require_approval = false
# Message to show to users when registration is disabled
# message = "ran out of spoons to moderate registrations, sorry"
[http]
# URL that the instance will be accessible at
base_url = "http://0.0.0.0:8080"
# Address to bind to (0.0.0.0 is suggested for proxies)
bind = "0.0.0.0"
bind_port = "8080"
bind_port = 8080
# Bans IPv4 or IPv6 IPs (wildcards, networks and ranges are supported)
banned_ips = []
# Banned user agents, regex format
banned_user_agents = [
# "curl\/7.68.0",
# "wget\/1.20.3",
]
[smtp]
# URL to an eventual HTTP proxy
# Will be used for all outgoing requests
# proxy_address = "http://localhost:8118"
# TLS configuration. You should probably be using a reverse proxy instead of this
# [http.tls]
# key = "/path/to/key.pem"
# cert = "/path/to/cert.pem"
# Sensitive value
# passphrase = "awawa"
# ca = "/path/to/ca.pem"
[frontend]
# Enable custom frontends (warning: not enabling this will make Versia Server only accessible via the Mastodon API)
# Frontends also control the OpenID flow, so if you disable this, you will need to use the Mastodon frontend
enabled = true
# Path that frontend files are served from
# Edit this property to serve custom frontends
# If this is not set, Versia Server will also check
# the VERSIA_FRONTEND_PATH environment variable
# path = ""
[frontend.routes]
# Special routes for your frontend, below are the defaults for Versia-FE
# Can be set to a route already used by Versia Server, as long as it is on a different HTTP method
# e.g. /oauth/authorize is a POST-only route, so you can serve a GET route at /oauth/authorize
# home = "/"
# login = "/oauth/authorize"
# consent = "/oauth/consent"
# register = "/register"
# password_reset = "/oauth/reset"
[frontend.settings]
# Arbitrary key/value pairs to be passed to the frontend
# This can be used to set up custom themes, etc on supported frontends.
# theme = "dark"
# NOT IMPLEMENTED
[email]
# Enable email sending
send_emails = false
# If send_emails is true, the following settings are required
# [email.smtp]
# SMTP server to use for sending emails
server = "smtp.example.com"
port = 465
username = "test@example.com"
password = "password123"
tls = true
# server = "smtp.example.com"
# port = 465
# username = "test@example.com"
# Sensitive value
# password = "password123"
# tls = true
[media]
# Can be "s3" or "local", where "local" uploads the file to the local filesystem
# If you need to change this value after setting up your instance, you must move all the files
# from one backend to the other manually
# Changing this value will not retroactively apply to existing data
# Don't forget to fill in the s3 config :3
backend = "local"
# Whether to check the hash of media when uploading to avoid duplication
deduplicate_media = true
# If media backend is "local", this is the folder where the files will be stored
local_uploads_folder = "uploads"
# Can be any path
uploads_path = "uploads"
[media.conversion]
# Whether to automatically convert images to another format on upload
convert_images = false
# Can be: "jxl", "webp", "avif", "png", "jpg", "heif"
# Can be: "image/jxl", "image/webp", "image/avif", "image/png", "image/jpeg", "image/heif", "image/gif"
# JXL support will likely not work
convert_to = "webp"
convert_to = "image/webp"
# Also convert SVG images?
convert_vectors = false
[s3]
# Can be left blank if you don't use the S3 media backend
endpoint = "https://s3-us-west-2.amazonaws.com"
access_key = ""
secret_access_key = ""
region = "us-west-2"
bucket_name = "lysand"
public_url = "https://cdn.example.com"
[email]
# Sends an email to moderators when a report is received
# NOT IMPLEMENTED
send_on_report = false
# Sends an email to moderators when a user is suspended
# NOT IMPLEMENTED
send_on_suspend = false
# Sends an email to moderators when a user is unsuspended
# NOT IMPLEMENTED
send_on_unsuspend = false
# [s3]
# Can be left commented if you don't use the S3 media backend
# endpoint = "https://s3.example.com"
# Sensitive value
# access_key = "XXXXX"
# Sensitive value
# secret_access_key = "XXX"
# region = "us-east-1"
# bucket_name = "versia"
# public_url = "https://cdn.example.com"
[validation]
# Self explanatory
max_displayname_size = 50
max_bio_size = 160
max_note_size = 5000
max_avatar_size = 5_000_000
max_header_size = 5_000_000
max_media_size = 40_000_000
max_media_attachments = 10
max_media_description_size = 1000
max_poll_options = 20
max_poll_option_size = 500
min_poll_duration = 60
max_poll_duration = 1893456000
max_username_size = 30
# An array of strings, defaults are from Akkoma
username_blacklist = [
".well-known",
"~",
# Checks user data
# Does not retroactively apply to previously entered data
[validation.accounts]
max_displayname_characters = 50
max_username_characters = 30
max_bio_characters = 5000
max_avatar_bytes = 5_000_000
max_header_bytes = 5_000_000
# Regex is allowed here
disallowed_usernames = [
"well-known",
"about",
"activities",
"api",
@ -137,12 +191,14 @@ username_blacklist = [
"search",
"mfa",
]
# Whether to blacklist known temporary email providers
blacklist_tempmail = false
# Additional email providers to blacklist
email_blacklist = []
# Valid URL schemes, otherwise the URL is parsed as text
url_scheme_whitelist = [
max_field_count = 10
max_field_name_characters = 1000
max_field_value_characters = 1000
max_pinned_notes = 20
[validation.notes]
max_characters = 5000
allowed_url_schemes = [
"http",
"https",
"ftp",
@ -160,123 +216,259 @@ url_scheme_whitelist = [
"mumble",
"ssb",
"gemini",
] # NOT IMPLEMENTED
enforce_mime_types = false
allowed_mime_types = [
"image/jpeg",
"image/png",
"image/gif",
"image/heic",
"image/heif",
"image/webp",
"image/avif",
"video/webm",
"video/mp4",
"video/quicktime",
"video/ogg",
"audio/wave",
"audio/wav",
"audio/x-wav",
"audio/x-pn-wave",
"audio/vnd.wave",
"audio/ogg",
"audio/vorbis",
"audio/mpeg",
"audio/mp3",
"audio/webm",
"audio/flac",
"audio/aac",
"audio/m4a",
"audio/x-m4a",
"audio/mp4",
"audio/3gpp",
"video/x-ms-asf",
]
max_attachments = 16
[defaults]
# Default visibility for new notes
visibility = "public"
# Default language for new notes
language = "en"
# Default avatar, must be a valid URL or ""
avatar = ""
# Default header, must be a valid URL or ""
header = ""
[validation.media]
max_bytes = 40_000_000
max_description_characters = 1000
# An empty array allows all MIME types
allowed_mime_types = []
[activitypub]
# Use ActivityPub Tombstones instead of deleting objects
use_tombstones = true
# Fetch all members of collections (followers, following, etc) when receiving them
# WARNING: This can be a lot of data, and is not recommended
fetch_all_collection_members = false # NOT IMPLEMENTED
[validation.emojis]
max_bytes = 1_000_000
max_shortcode_characters = 100
max_description_characters = 1000
# The following values must be instance domain names without "https" or glob patterns
# Rejects all activities from these instances (fediblocking)
reject_activities = []
# Force posts from this instance to be followers only
force_followers_only = [] # NOT IMPLEMENTED
# Discard all reports from these instances
discard_reports = [] # NOT IMPLEMENTED
# Discard all deletes from these instances
discard_deletes = []
# Discard all updates (edits) from these instances
discard_updates = []
# Discard all banners from these instances
discard_banners = [] # NOT IMPLEMENTED
# Discard all avatars from these instances
discard_avatars = [] # NOT IMPLEMENTED
# Discard all follow requests from these instances
discard_follows = []
# Force set these instances' media as sensitive
force_sensitive = [] # NOT IMPLEMENTED
# Remove theses instances' media
remove_media = [] # NOT IMPLEMENTED
[validation.polls]
max_options = 20
max_option_characters = 500
min_duration_seconds = 60
# 100 days
max_duration_seconds = 8_640_000
# Whether to verify HTTP signatures for every request (warning: can slow down your server
# significantly depending on processing power)
authorized_fetch = false
[validation.emails]
# Blocks over 10,000 common tempmail domains
disallow_tempmail = false
# Regex is allowed here
disallowed_domains = []
[instance]
name = "Lysand"
description = "A test instance of Lysand"
# URL to your instance logo (jpg files should be renamed to jpeg)
logo = ""
# URL to your instance banner (jpg files should be renamed to jpeg)
banner = ""
[validation.challenges]
# "Challenges" (aka captchas) are a way to verify that a user is human
# Versia Server's challenges use no external services, and are proof-of-work based
# This means that they do not require any user interaction, instead
# they require the user's computer to do a small amount of work
# The difficulty of the challenge, higher is will take more time to solve
difficulty = 50000
# Challenge expiration time in seconds
expiration = 300 # 5 minutes
# Leave this empty to generate a new key
# Sensitive value
key = "YBpAV0KZOeM/MZ4kOb2E9moH9gCUr00Co9V7ncGRJ3wbd/a9tLDKKFdI0BtOcnlpfx0ZBh0+w3WSvsl0TsesTg=="
[filters]
# Drop notes with these regex filters (only applies to new activities)
note_filters = [
# Block content that matches these regular expressions
[validation.filters]
note_content = [
# "(https?://)?(www\\.)?youtube\\.com/watch\\?v=[a-zA-Z0-9_-]+",
# "(https?://)?(www\\.)?youtu\\.be/[a-zA-Z0-9_-]+",
]
# Drop users with these regex filters (only applies to new activities)
username_filters = []
# Drop users with these regex filters (only applies to new activities)
displayname_filters = []
# Drop users with these regex filters (only applies to new activities)
bio_filters = []
emoji_filters = [] # NOT IMPLEMENTED
emoji_shortcode = []
username = []
displayname = []
bio = []
[notifications]
# Web Push Notifications configuration.
# Leave out to disable.
[notifications.push]
# Subject field embedded in the push notification
# subject = "mailto:joe@example.com"
#
[notifications.push.vapid_keys]
# VAPID keys for push notifications
# Run Versia Server with those values missing to generate new keys
# Sensitive value
public = "BBanhyj2_xWwbTsWld3T49VcAoKZHrVJTzF1f6Av2JwQY_wUi3CF9vZ0WeEcACRj6EEqQ7N35CkUh5epF7n4P_s"
# Sensitive value
private = "Eujaz7NsF0rKZOVrAFL7mMpFdl96f591ERsRn81unq0"
[defaults]
# Default visibility for new notes
# Can be public, unlisted, private or direct
# Private only sends to followers, unlisted doesn't show up in timelines
visibility = "public"
# Default language for new notes (ISO code)
language = "en"
# Default avatar, must be a valid URL or left out for a placeholder avatar
# avatar = ""
# Default header, must be a valid URL or left out for none
# header = ""
# A style name from https://www.dicebear.com/styles
placeholder_style = "thumbs"
[queues]
# Controls the delivery queue (for outbound federation)
[queues.delivery]
# Time in seconds to remove completed jobs
remove_after_complete_seconds = 31536000
# Time in seconds to remove failed jobs
remove_after_failure_seconds = 31536000
# Controls the inbox processing queue (for inbound federation)
[queues.inbox]
# Time in seconds to remove completed jobs
remove_after_complete_seconds = 31536000
# Time in seconds to remove failed jobs
remove_after_failure_seconds = 31536000
# Controls the fetch queue (for remote data refreshes)
[queues.fetch]
# Time in seconds to remove completed jobs
remove_after_complete_seconds = 31536000
# Time in seconds to remove failed jobs
remove_after_failure_seconds = 31536000
# Controls the push queue (for push notification delivery)
[queues.push]
# Time in seconds to remove completed jobs
remove_after_complete_seconds = 31536000
# Time in seconds to remove failed jobs
remove_after_failure_seconds = 31536000
# Controls the media queue (for media processing)
[queues.media]
# Time in seconds to remove completed jobs
remove_after_complete_seconds = 31536000
# Time in seconds to remove failed jobs
remove_after_failure_seconds = 31536000
[federation]
# This is a list of domain names, such as "mastodon.social" or "pleroma.site"
# These changes will not retroactively apply to existing data before they were changed
# For that, please use the CLI (in a later release)
# These instances will not be federated with
blocked = []
# These instances' data will only be shown to followers, not in public timelines
followers_only = []
[federation.discard]
# These objects will be discarded when received from these instances
reports = []
deletes = []
updates = []
media = []
follows = []
# If instance reactions are blocked, likes will also be discarded
likes = []
reactions = []
banners = []
avatars = []
# For bridge software, such as versia-pub/activitypub
# Bridges must be hosted separately from the main Versia Server process
# [federation.bridge]
# Only versia-ap exists for now
# software = "versia-ap"
# If this is empty, any bridge with the correct token
# will be able to send data to your instance
# v4, v6, ranges and wildcards are supported
# allowed_ips = ["192.168.1.0/24"]
# Token for the bridge software
# Bridge must have the same token!
# Sensitive value
# token = "mycooltoken"
# url = "https://ap.versia.social"
[instance]
name = "Versia"
description = "A Versia Server instance"
# Paths to instance long description, terms of service, and privacy policy
# These will be parsed as Markdown
#
# extended_description_path = "config/extended_description.md"
# tos_path = "config/tos.md"
# privacy_policy_path = "config/privacy_policy.md"
# Primary instance languages. ISO 639-1 codes.
languages = ["en"]
[instance.contact]
email = "staff@yourinstance.com"
[instance.branding]
# logo = "https://cdn.example.com/logo.png"
# banner = "https://cdn.example.com/banner.png"
# Used for federation. If left empty or missing, the server will generate one for you.
[instance.keys]
# Sensitive value
public = "MCowBQYDK2VwAyEASN0V5OWRbhRCnuhxfRLqpUOfszHozvrLLVhlIYLNTZM="
# Sensitive value
private = "MC4CAQAwBQYDK2VwBCIEIKaxDGMaW71OcCGMY+GKTZPtLPNlTvMFe3G5qXVHPhQM"
[[instance.rules]]
# Short description of the rule
text = "No hate speech"
# Longer version of the rule with additional information
hint = "Hate speech includes slurs, threats, and harassment."
[[instance.rules]]
text = "No spam"
# [[instance.rules]]
# ...etc
[permissions]
# Control default permissions for users
# Note that an anonymous user having a permission will not allow them
# to do things that require authentication (e.g. 'owner:notes' -> posting a note will need
# auth, but viewing a note will not)
# See https://server.versia.pub/api/roles#list-of-permissions for a list of all permissions
# Defaults to being able to login and manage their own content
# anonymous = []
# Defaults to identical to anonymous
# default = []
# Defaults to being able to manage all instance data, content, and users
# admin = []
[logging]
# Log all requests (warning: this is a lot of data)
log_requests = true
# Log request and their contents (warning: this is a lot of data)
log_requests_verbose = false
# For GDPR compliance, you can disable logging of IPs
log_ip = false
# Log all filtered objects
log_filters = true
# Available levels: trace, debug, info, warning, error, fatal
log_level = "info" # For console output
[ratelimits]
# Amount to multiply every route's duration by
duration_coeff = 1.0
# Amount to multiply every route's max by
max_coeff = 1.0
# [logging.file]
# path = "logs/versia.log"
# log_level = "info"
#
# [logging.file.rotation]
# max_size = 10_000_000 # 10 MB
# max_files = 10 # Keep 10 rotated files
#
# https://sentry.io support
# [logging.sentry]
# dsn = "https://example.com"
# debug = false
# sample_rate = 1.0
# traces_sample_rate = 1.0
# Can also be regex
# trace_propagation_targets = []
# max_breadcrumbs = 100
# environment = "production"
# log_level = "info"
[custom_ratelimits]
# Add in any API route in this style here
"/api/v1/timelines/public" = { duration = 60, max = 200 }
[authentication]
# Run Versia Server with this value missing to generate a new key
key = "ZWcwanRaQAqY3ChUro/Jey9XGQjzsxEed5iqTp4yFr8W6vEnXdz91F/Pu/uf7HBMbNeIK7V6aHsM0lq9onrO8Q=="
# The provider MUST support OpenID Connect with .well-known discovery
# Most notably, GitHub does not support this
# Redirect URLs in your OpenID provider can be set to this:
# <base_url>/oauth/sso/<provider_id>/callback*
# The asterisk is important, as it allows for any query parameters to be passed
# Authentik for example uses regex so it can be set to (regex):
# <base_url>/oauth/sso/<provider_id>/callback.*
# [[authentication.openid_providers]]
# name = "CPlusPatch ID"
# id = "cpluspatch-id"
# This MUST match the provider's issuer URI, including the trailing slash (or lack thereof)
# url = "https://id.cpluspatch.com/application/o/versia-testing/"
# client_id = "XXXX"
# Sensitive value
# client_secret = "XXXXX"
# icon = "https://cpluspatch.com/images/icons/logo.svg"

22
.github/copilot-instructions.md vendored Normal file
View file

@ -0,0 +1,22 @@
We use full TypeScript and ESM with Bun for our codebase. Please include relevant and detailed JSDoc comments for all functions and classes. Use explicit type annotations for all variables and function return values, such as:
```typescript
/**
* Adds two numbers together.
*
* @param {number} a
* @param {number} b
* @returns {number}
*/
const add = (a: number, b: number): number => a + b;
```
We always write TypeScript with double quotes and four spaces for indentation, so when your responses include TypeScript code, please follow those conventions.
Our codebase uses Drizzle as an ORM, which is exposed in the `@versia-server/kit/db` and `@versia-server/kit/tables` packages. This project uses a monorepo structure with Bun as the package manager.
The app has two modes: worker and API. The worker mode is used for background tasks, while the API mode serves HTTP requests. The entry point for the worker is `worker.ts`, and for the API, it is `api.ts`.
Run the typechecker with `bun run typecheck` to ensure that all TypeScript code is type-checked correctly. Run tests with `bun test` to ensure that all tests pass. Run the linter and formatter with `bun lint` to ensure that the code adheres to our style guidelines, and `bun lint --write` to automatically fix minor/formatting issues.
Cover all new functionality with tests, and ensure that all tests pass before submitting your code.

27
.github/workflows/check.yml vendored Normal file
View file

@ -0,0 +1,27 @@
name: Check Types
on:
workflow_call:
jobs:
tests:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
submodules: recursive
- name: Setup Bun
uses: oven-sh/setup-bun@v2
- name: Install NPM packages
run: |
bun install
- name: Run typechecks
run: |
bun run typecheck

27
.github/workflows/circular-imports.yml vendored Normal file
View file

@ -0,0 +1,27 @@
name: Check Circular Imports
on:
workflow_call:
jobs:
tests:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
submodules: recursive
- name: Setup Bun
uses: oven-sh/setup-bun@v2
- name: Install NPM packages
run: |
bun install
- name: Run typechecks
run: |
bun run detect-circular

View file

@ -9,7 +9,7 @@
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
name: "CodeQL Scan"
on:
push:
@ -46,11 +46,11 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v3
uses: actions/checkout@v4
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
@ -63,7 +63,7 @@ jobs:
# Autobuild attempts to build any compiled languages (C/C++, C#, Go, Java, or Swift).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v2
uses: github/codeql-action/autobuild@v3
# Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
@ -76,6 +76,6 @@ jobs:
# ./location_of_script_within_repo/buildscript.sh
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
uses: github/codeql-action/analyze@v3
with:
category: "/language:${{matrix.language}}"

View file

@ -1,79 +0,0 @@
name: Docker
# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.
on:
push:
branches: [ "main" ]
# Publish semver tags as releases.
tags: [ 'v*.*.*' ]
pull_request:
branches: [ "main" ]
env:
# Use docker.io for Docker Hub if empty
REGISTRY: ghcr.io
# github.repository as <account>/<repo>
IMAGE_NAME: ${{ github.repository }}
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
# This is used to complete the identity challenge
# with sigstore/fulcio when running outside of PRs.
id-token: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup QEMU
uses: docker/setup-qemu-action@v3
with:
platforms: all
# Set up BuildKit Docker container builder to be able to build
# multi-platform images and export cache
# https://github.com/docker/setup-buildx-action
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3 # v3.0.0
# Login against a Docker registry except on PR
# https://github.com/docker/login-action
- name: Log into registry ${{ env.REGISTRY }}
if: github.event_name != 'pull_request'
uses: docker/login-action@v3 # v3.0.0
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
# Extract metadata (tags, labels) for Docker
# https://github.com/docker/metadata-action
- name: Extract Docker metadata
id: meta
uses: docker/metadata-action@v5 # v5.0.0
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
# Build and push Docker image with Buildx (don't push on PR)
# https://github.com/docker/build-push-action
- name: Build and push Docker image
id: build-and-push
uses: docker/build-push-action@v5 # v5.0.0
with:
context: .
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
platforms: linux/amd64,linux/arm64
cache-from: type=gha
cache-to: type=gha,mode=max

98
.github/workflows/docker.yml vendored Normal file
View file

@ -0,0 +1,98 @@
name: Build Docker Images
on:
push:
branches: ["*"]
# Publish semver tags as releases.
tags: ["v*.*.*"]
pull_request:
branches: ["main"]
jobs:
lint:
uses: ./.github/workflows/lint.yml
check:
uses: ./.github/workflows/check.yml
tests:
uses: ./.github/workflows/tests.yml
detect-circular:
uses: ./.github/workflows/circular-imports.yml
build:
if: ${{ success() }}
needs: [lint, check, tests, detect-circular]
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
# This is used to complete the identity challenge
# with sigstore/fulcio when running outside of PRs.
id-token: write
strategy:
matrix:
include:
- container: worker
image_name: ${{ github.repository_owner }}/worker
dockerfile: Worker.Dockerfile
- container: server
image_name: ${{ github.repository_owner }}/server
dockerfile: Dockerfile
env:
REGISTRY: ghcr.io
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
submodules: recursive
- name: Setup QEMU
uses: docker/setup-qemu-action@v3
with:
platforms: all
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log into registry ${{ env.REGISTRY }}
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract Docker metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ matrix.image_name }}
tags: |
type=schedule
type=ref,event=branch
type=ref,event=tag
type=ref,event=pr
type=sha
- name: Get the commit hash
run: echo "GIT_COMMIT=$(git rev-parse --short ${{ github.sha }})" >> $GITHUB_ENV
- name: Build and push Docker image
id: build-and-push
uses: docker/build-push-action@v5
with:
context: .
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
build-args: |
GIT_COMMIT=${{ env.GIT_COMMIT }}
file: ${{ matrix.dockerfile }}
provenance: mode=max
sbom: true
platforms: linux/amd64,linux/arm64
cache-from: type=gha
cache-to: type=gha,mode=max

56
.github/workflows/docs.yml vendored Normal file
View file

@ -0,0 +1,56 @@
name: Deploy Docs to GitHub Pages
on:
push:
branches: [main]
workflow_dispatch:
# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
permissions:
contents: read
pages: write
id-token: write
# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
concurrency:
group: pages
cancel-in-progress: false
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: oven-sh/setup-bun@v2
- name: Setup Pages
uses: actions/configure-pages@v4
- name: Install dependencies
run: bun install
- name: Build with VitePress
run: bun run --filter="@versia-server/api" docs:build
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
path: packages/api/docs/.vitepress/dist
# Deployment job
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
needs: build
runs-on: ubuntu-latest
name: Deploy
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4

27
.github/workflows/lint.yml vendored Normal file
View file

@ -0,0 +1,27 @@
name: Lint & Format
on:
workflow_call:
jobs:
tests:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
submodules: recursive
- name: Setup Bun
uses: oven-sh/setup-bun@v2
- name: Install NPM packages
run: |
bun install
- name: Run linting
run: |
bunx @biomejs/biome ci .

8
.github/workflows/mirror.yml vendored Normal file
View file

@ -0,0 +1,8 @@
name: Mirror to Codeberg
on: [push]
jobs:
mirror:
name: Mirror
uses: versia-pub/.github/.github/workflows/mirror.yml@main
secrets: inherit

25
.github/workflows/nix-flake.yml vendored Normal file
View file

@ -0,0 +1,25 @@
name: Nix Build
on:
pull_request:
push:
branches: ["*"]
workflow_dispatch:
jobs:
check:
runs-on: ubuntu-latest
permissions:
id-token: "write"
contents: "read"
steps:
- uses: actions/checkout@v4
- uses: DeterminateSystems/nix-installer-action@main
with:
extra-conf: accept-flake-config = true
- uses: DeterminateSystems/magic-nix-cache-action@main
- uses: DeterminateSystems/flake-checker-action@main
- name: Build default package
run: nix build .
- name: Check flakes
run: nix flake check --allow-import-from-derivation

48
.github/workflows/publish.yml vendored Normal file
View file

@ -0,0 +1,48 @@
name: Build & Publish Packages
on:
workflow_dispatch:
inputs:
package:
description: "Package to publish"
required: true
type: choice
options:
- client
- sdk
tag:
description: "NPM tag to use"
required: true
type: choice
default: nightly
options:
- latest
- nightly
permissions:
contents: read
# For provenance generation
id-token: write
jobs:
publish:
runs-on: ubuntu-latest
environment: NPM Deploy
steps:
- name: Checkout
uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v2
- name: Install
run: bun install --frozen-lockfile
- name: Publish to NPM
working-directory: packages/${{ inputs.package }}
run: bun publish --provenance --tag ${{ inputs.tag }} --access public
env:
NPM_CONFIG_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Publish to JSR
working-directory: packages/${{ inputs.package }}
run: bunx jsr publish --allow-slow-types --allow-dirty

36
.github/workflows/test-publish.yml vendored Normal file
View file

@ -0,0 +1,36 @@
name: Test Publish
on:
push:
permissions:
contents: read
# For provenance generation
id-token: write
jobs:
# Build job
build:
runs-on: ubuntu-latest
environment: NPM Deploy
strategy:
matrix:
package: ["sdk", "client"]
steps:
- name: Checkout
uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v2
- name: Install
run: bun install --frozen-lockfile
- name: Publish to NPM
working-directory: packages/${{ matrix.package }}
env:
NPM_CONFIG_TOKEN: ${{ secrets.NPM_TOKEN }}
run: bun publish --dry-run --access public
- name: Publish to JSR
working-directory: packages/${{ matrix.package }}
run: bunx jsr publish --allow-slow-types --allow-dirty --dry-run

View file

@ -1,40 +1,44 @@
name: Tests
on:
push:
branches: ["*"]
pull_request:
# The branches below must be a subset of the branches above
branches: ["main"]
workflow_call:
jobs:
tests:
runs-on: ubuntu-latest
services:
postgres:
image: ghcr.io/lysand-org/postgres:main
image: postgres:17-alpine
ports:
- 5432:5432
env:
POSTGRES_DB: lysand
POSTGRES_USER: lysand
POSTGRES_PASSWORD: lysand
POSTGRES_DB: versia
POSTGRES_USER: versia
POSTGRES_PASSWORD: versia
volumes:
- lysand-data:/var/lib/postgresql/data
- versia-data:/var/lib/postgresql/data
options: --health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
redis:
image: redis:latest
ports:
- 6379:6379
options: --health-cmd "redis-cli ping"
--health-interval 10s
--health-timeout 5s
--health-retries 5
permissions:
contents: read
security-events: write
actions: read # only required for a private repository by github/codeql-action/upload-sarif to get the Action run status
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
submodules: recursive
- name: Setup Bun
uses: oven-sh/setup-bun@v1
uses: oven-sh/setup-bun@v2
- name: Install NPM packages
run: |
@ -46,4 +50,4 @@ jobs:
- name: Run tests
run: |
bun test
bun run test

18
.gitignore vendored
View file

@ -117,6 +117,10 @@ out
.nuxt
dist
# Nix build output
result
# Gatsby files
.cache/
@ -175,8 +179,12 @@ log.txt
*.log
build
config/extended_description_test.md
glitch-old
glitch
glitch.tar.gz
glitch-dev
*.pem
*.pem
oclif.manifest.json
.direnv/
tsconfig.tsbuildinfo
# Vitepress Docs
*/.vitepress/dist
*/.vitepress/cache

7
.madgerc Normal file
View file

@ -0,0 +1,7 @@
{
"detectiveOptions": {
"ts": {
"skipTypeImports": true
}
}
}

1
.npmrc Normal file
View file

@ -0,0 +1 @@
@jsr:registry=https://npm.jsr.io

13
.vscode/extensions.json vendored Normal file
View file

@ -0,0 +1,13 @@
{
"recommendations": [
"biomejs.biome",
"ms-vscode-remote.remote-containers",
"oven.bun-vscode",
"vivaxy.vscode-conventional-commits",
"EditorConfig.EditorConfig",
"tamasfe.even-better-toml",
"YoavBls.pretty-ts-errors",
"eamodio.gitlens"
],
"unwantedRecommendations": []
}

49
.vscode/launch.json vendored
View file

@ -1,15 +1,48 @@
{
"version": "0.2.0",
"configurations": [
{
"type": "node",
"name": "vscode-jest-tests.v2.lysand",
"request": "launch",
"args": ["test", "${jest.testFile}"],
"cwd": "/home/jessew/Dev/lysand",
"console": "integratedTerminal",
"type": "bun",
"internalConsoleOptions": "neverOpen",
"disableOptimisticBPs": true,
"program": "/home/jessew/.bun/bin/bun"
"request": "launch",
"name": "Debug File",
"program": "${file}",
"cwd": "${workspaceFolder}",
"stopOnEntry": false,
"watchMode": false
},
{
"type": "bun",
"internalConsoleOptions": "neverOpen",
"request": "launch",
"name": "Run File",
"program": "${file}",
"cwd": "${workspaceFolder}",
"watchMode": false
},
{
"type": "bun",
"internalConsoleOptions": "neverOpen",
"request": "attach",
"name": "Attach Bun",
"url": "ws://localhost:6499/",
"stopOnEntry": false
},
{
"type": "bun",
"internalConsoleOptions": "neverOpen",
"request": "launch",
"name": "Run index.ts",
"program": "${workspaceFolder}/index.ts",
"cwd": "${workspaceFolder}",
"watchMode": true
},
{
"type": "bun",
"internalConsoleOptions": "neverOpen",
"request": "launch",
"name": "Run tests",
"program": "test"
}
]
}

14
.vscode/settings.json vendored
View file

@ -1,13 +1,15 @@
{
"typescript.tsdk": "node_modules/typescript/lib",
"jest.jestCommandLine": "/home/jessew/.bun/bin/bun test",
"jest.rootPath": ".",
"conventionalCommits.scopes": [
"database",
"frontend",
"build",
"api",
"cli",
"federation"
]
"federation",
"config",
"worker",
"media",
"packages/client",
"packages/sdk"
],
"languageToolLinter.languageTool.ignoredWordsInWorkspace": ["versia"]
}

268
API.md
View file

@ -1,268 +0,0 @@
# API
The Lysand project uses the Mastodon API to interact with clients. However, the moderation API is custom-made for Lysand Server, as it allows for more fine-grained control over the server's behavior.
## Flags, ModTags and ModNotes
Flags are used by Lysand Server to automatically attribute tags to a status or account based on rules. ModTags and ModNotes are used by moderators to manually tag and take notes on statuses and accounts.
The difference between flags and modtags is that flags are automatically attributed by the server, while modtags are manually attributed by moderators.
### Flag Types
- `content_filter`: (Statuses only) The status contains content that was filtered by the server's content filter.
- `bio_filter`: (Accounts only) The account's bio contains content that was filtered by the server's content filter.
- `emoji_filter`: The status or account contains an emoji that was filtered by the server's content filter.
- `reported`: The status or account was previously reported by a user.
- `suspended`: The status or account was previously suspended by a moderator.
- `silenced`: The status or account was previously silenced by a moderator.
### ModTag Types
ModTag do not have set types and can be anything. Lysand Server autosuggest previously used tags when a moderator is adding a new tag to avoid duplicates.
### Data Format
```ts
type Flag = {
id: string,
// One of the following two fields will be present
flaggedStatus?: Status,
flaggedUser?: User,
flagType: string,
createdAt: string,
}
type ModTag = {
id: string,
// One of the following two fields will be present
taggedStatus?: Status,
taggedUser?: User,
mod: User,
tag: string,
createdAt: string,
}
type ModNote = {
id: string,
// One of the following two fields will be present
notedStatus?: Status,
notedUser?: User,
mod: User,
note: string,
createdAt: string,
}
```
The `User` and `Status` types are the same as the ones in the Mastodon API.
## Moderation API Routes
### `GET /api/v1/moderation/accounts/:id`
Returns full moderation data and flags for the account with the given ID.
Output format:
```ts
{
id: string, // Same ID as in account field
flags: Flag[],
modtags: ModTag[],
modnotes: ModNote[],
account: User,
}
```
### `GET /api/v1/moderation/statuses/:id`
Returns full moderation data and flags for the status with the given ID.
Output format:
```ts
{
id: string, // Same ID as in status field
flags: Flag[],
modtags: ModTag[],
modnotes: ModNote[],
status: Status,
}
```
### `POST /api/v1/moderation/accounts/:id/modtags`
Params:
- `tag`: string
Adds a modtag to the account with the given ID
### `POST /api/v1/moderation/statuses/:id/modtags`
Params:
- `tag`: string
Adds a modtag to the status with the given ID
### `POST /api/v1/moderation/accounts/:id/modnotes`
Params:
- `note`: string
Adds a modnote to the account with the given ID
### `POST /api/v1/moderation/statuses/:id/modnotes`
Params:
- `note`: string
Adds a modnote to the status with the given ID
### `DELETE /api/v1/moderation/accounts/:id/modtags/:modtag_id`
Deletes the modtag with the given ID from the account with the given ID
### `DELETE /api/v1/moderation/statuses/:id/modtags/:modtag_id`
Deletes the modtag with the given ID from the status with the given ID
### `DELETE /api/v1/moderation/accounts/:id/modnotes/:modnote_id`
Deletes the modnote with the given ID from the account with the given ID
### `DELETE /api/v1/moderation/statuses/:id/modnotes/:modnote_id`
Deletes the modnote with the given ID from the status with the given ID
### `GET /api/v1/moderation/modtags`
Returns a list of all modtags previously used by moderators
Output format:
```ts
{
tags: string[],
}
```
### `GET /api/v1/moderation/accounts/flags/search`
Allows moderators to search for accounts based on their flags, this can also include status flags
Params:
- `limit`: Number
- `min_id`: String. Returns results immediately newer than this ID. In effect, sets a cursor at this ID and paginates forward.
- `max_id`: String. All results returned will be lesser than this ID. In effect, sets an upper bound on results.
- `since_id`: String. All results returned will be greater than this ID. In effect, sets a lower bound on results.
- `flags`: String (optional). Comma-separated list of flag types to filter by. Can be left out to return accounts with at least one flag
- `flag_count`: Number (optional). Minimum number of flags to filter by
- `include_statuses`: Boolean (optional). If true, includes status flags in the search results
- `account_id`: Array of strings (optional). Filters accounts by account ID
This method returns a `Link` header the same way Mastodon does, to allow for pagination.
Output format:
```ts
{
accounts: {
account: User,
modnotes: ModNote[],
flags: Flag[],
statuses?: {
status: Status,
modnotes: ModNote[],
flags: Flag[],
}[],
}[],
}
```
### `GET /api/v1/moderation/statuses/flags/search`
Allows moderators to search for statuses based on their flags
Params:
- `limit`: Number
- `min_id`: String. Returns results immediately newer than this ID. In effect, sets a cursor at this ID and paginates forward.
- `max_id`: String. All results returned will be lesser than this ID. In effect, sets an upper bound on results.
- `since_id`: String. All results returned will be greater than this ID. In effect, sets a lower bound on results.
- `flags`: String (optional). Comma-separated list of flag types to filter by. Can be left out to return statuses with at least one flag
- `flag_count`: Number (optional). Minimum number of flags to filter by
- `account_id`: Array of strings (optional). Filters statuses by account ID
This method returns a `Link` header the same way Mastodon does, to allow for pagination.
Output format:
```ts
{
statuses: {
status: Status,
modnotes: ModNote[],
flags: Flag[],
}[],
}
```
### `GET /api/v1/moderation/accounts/modtags/search`
Allows moderators to search for accounts based on their modtags
Params:
- `limit`: Number
- `min_id`: String. Returns results immediately newer than this ID. In effect, sets a cursor at this ID and paginates forward.
- `max_id`: String. All results returned will be lesser than this ID. In effect, sets an upper bound on results.
- `since_id`: String. All results returned will be greater than this ID. In effect, sets a lower bound on results.
- `tags`: String (optional). Comma-separated list of tags to filter by. Can be left out to return accounts with at least one tag
- `tag_count`: Number (optional). Minimum number of tags to filter by
- `include_statuses`: Boolean (optional). If true, includes status tags in the search results
- `account_id`: Array of strings (optional). Filters accounts by account ID
This method returns a `Link` header the same way Mastodon does, to allow for pagination.
Output format:
```ts
{
accounts: {
account: User,
modnotes: ModNote[],
modtags: ModTag[],
statuses?: {
status: Status,
modnotes: ModNote[],
modtags: ModTag[],
}[],
}[],
}
```
### `GET /api/v1/moderation/statuses/modtags/search`
Allows moderators to search for statuses based on their modtags
Params:
- `limit`: Number
- `min_id`: String. Returns results immediately newer than this ID. In effect, sets a cursor at this ID and paginates forward.
- `max_id`: String. All results returned will be lesser than this ID. In effect, sets an upper bound on results.
- `since_id`: String. All results returned will be greater than this ID. In effect, sets a lower bound on results.
- `tags`: String (optional). Comma-separated list of tags to filter by. Can be left out to return statuses with at least one tag
- `tag_count`: Number (optional). Minimum number of tags to filter by
- `account_id`: Array of strings (optional). Filters statuses by account ID
- `include_statuses`: Boolean (optional). If true, includes status tags in the search results
This method returns a `Link` header the same way Mastodon does, to allow for pagination.
Output format:
```ts
{
statuses: {
status: Status,
modnotes: ModNote[],
modtags: ModTag[],
}[],
}
```

184
CHANGELOG.md Normal file
View file

@ -0,0 +1,184 @@
# `0.9.0` (upcoming)
## Features
### API
- [x] 🥺 Emoji Reactions are now available! You can react to any note with custom emojis.
- [x] 🔎 Added support for [batch account data API](https://docs.joinmastodon.org/methods/accounts/#index).
### Backend
- [x] 🚀 Upgraded Bun to `1.3.2`
# `0.8.0` • Federation 2: Electric Boogaloo
## Backwards Compatibility
Versia Server `0.8.0` is **not** backwards-compatible with `0.7.0`. This release includes some breaking changes to the database schema and configuration file.
Please see [Database Changes](#database-changes) and [New Configuration](#new-configuration) for more information.
## Features
### Federation
- [x] 🦄 Updated to [`Versia 0.5`](https://versia.pub/changelog).
- [x] 📦 Added support for new Versia features:
- [x] [**Instance Messaging Extension**](https://versia.pub/extensions/instance-messaging)
- [x] [**Shared Inboxes**](https://versia.pub/federation#inboxes)
- [x] 🔗 Changed entity URIs to be more readable (`example.org/objects/:id` → `example.org/{notes,likes,...}/:id`)
### API
- [x] 📲 Added [Push Notifications](https://docs.joinmastodon.org/methods/push) support.
- [x] 📖 Overhauled OpenAPI schemas to match [Mastodon API docs](https://docs.joinmastodon.org)
- [x] 👷 Improved [**Roles API**](https://server.versia.pub/api/roles) to allow for full role control (create, update, delete, assign).
- [x] ✏️ `<div>` and `<span>` tags are now allowed in Markdown.
- [x] 🔥 Removed nonstandard `/api/v1/accounts/id` endpoint (the same functionality was already possible with other endpoints).
- [x] ✨️ Implemented rate limiting support for API endpoints.
- [x] 🔒 Implemented `is_indexable` and `is_hiding_collections` fields to the [**Accounts API**](https://docs.joinmastodon.org/methods/accounts/#update_credentials).
- [x] ✨️ Muting other users now lets you specify a duration, after which the mute will be automatically removed.
- [x] 📰 All accounts now have an RSS/Atom feed attached to them.
### CLI
- [x] ⌨️ New commands!
- [x] ✨️ `cli user token` to generate API tokens.
- [x] 👷 Error messages are now prettier!
### Frontend
The way frontend is built and served has been changed. In the past, it was required to have a second process serving a frontend, which `versia-server` would proxy requests to. This is no longer the case.
Versia Server now serves static files directly from a configurable path, and `versia-fe` has been updated to support this.
### Backend
- [x] 🚀 Upgraded Bun to `1.2.13`
- [x] 🔥 Removed dependency on the `pg_uuidv7` extension. Versia Server can now be used with "vanilla" PostgreSQL.
- [x] 🖼️ Simplified media pipeline: this will improve S3 performance
- [x] 📈 It is now possible to disable media proxying for your CDN (offloading considerable bandwidth to your more optimized CDN).
- [x] 👷 Outbound federation, inbox processing, data fetching and media processing are now handled by a queue system.
- [x] 🌐 An administration panel is available at `/admin/queues` to monitor and manage queues.
- [x] 🔥 Removed support for **from-source** installations, as Versia Server is designed around containerization and maintaining support was a large burden.
- [x] ❄️ A [**Nix**](https://nixos.org/) package is now available for this project, packaged as a [Flake](https://wiki.nixos.org/wiki/Flakes). A **NixOS** module is also provided.
## New Configuration
Configuration parsing and validation has been overhauled. Unfortunately, this means that since a bunch of options have been renamed, you'll need to redownload [the default configuration file](config/config.example.toml) and reapply your changes.
## Database Changes
Various media-related attributes have been merged into a single `Medias` table. This will require a migration in order to preserve the old data.
Since very few instances are running `0.7.0`, we have decided to "rawdog it" instead of making a proper migration script (as that would take a ton of time that we don't have).
In the case that you've been running secret instances in the shadows, let us know and we'll help you out.
## Bug Fixes
- 🐛 All URIs in custom Markdown text are now correctly proxied.
- 🐛 Fixed several issues with the [ActivityPub Federation Bridge](https://github.com/versia-pub/activitypub) preventing it from operating properly.
- 🐛 Fixed incorrect content-type on some media when using S3.
- 🐛 All media content-type is now correctly fetched, instead of guessed from the file extension as before.
- 🐛 Fixed OpenAPI schema generation and `/docs` endpoint.
- 🐛 Logs folder is now automatically created if it doesn't exist.
- 🐛 Media hosted on the configured S3 bucket and on the local filesystem is no longer unnecessarily proxied.
- 🐛 Likes and Shares now federate properly.
# `0.7.0` • The Auth and APIs Update
> [!WARNING]
> This release marks the rename of the project from `Lysand` to `Versia`.
## Backwards Compatibility
Versia Server `0.7.0` is backwards compatible with `0.6.0`. However, some new features may not be available to older clients. Notably, `versia-fe` has had major improvements and will not work with `0.6.0`.
## Features
- Upgraded Bun to `1.1.34`. This brings performance upgrades and better stability.
- Added support for the [ActivityPub Federation Bridge](https://github.com/versia-pub/activitypub).
- Added support for the [Sonic](https://github.com/valeriansaliou/sonic) search indexer.
- Note deletions are now federated.
- Note edits are now federated.
- Added support for [Sentry](https://sentry.io).
- Added option for more federation debug logging.
- Added [**Roles API**](https://server.versia.pub/api/roles).
- Added [**Permissions API**](https://server.versia.pub/api/roles) and enabled it for every route.
- Added [**TOS and Privacy Policy**](https://server.versia.pub/api/mastodon) endpoints.
- Added [**Challenge API**](https://server.versia.pub/api/challenges). (basically CAPTCHAS). This can be enabled/disabled by administrators. No `versia-fe` support yet.
- Added ability to refetch user data from remote instances.
- Added ability to change the `username` of a user. ([Mastodon API extension](https://server.versia.pub/api/mastodon#api-v1-accounts-update-credentials)).
- Added an endpoint to get a user by its username.
- Add OpenID Connect registration support. Admins can now disable username/password registration entirely and still allow users to sign up via OpenID Connect.
- Add option to never convert vector images to a raster format.
- Refactor logging system to be more robust and easier to use. Log files are now automatically rotated.
- Add support for HTTP proxies.
- Add support for serving Versia over a Tor hidden service.
- Add global server error handler, to properly return 500 error messages to clients.
- Sign all federation HTTP requests.
- Add JSON schema for configuration file.
- Rewrite federation stack
- Updated federation to Versia 0.4
- Implement OAuth2 token revocation
- Add new **Plugin API**
## Plugin System
A new plugin system for extending Versia Server has been added in this release!
> [!NOTE]
>
> This is an internal feature and is not documented. Support for third-party plugins will be given on a "if we have time" basis, until the API is fully stabilized and documented
Plugins using this framework support:
- [x] Plugin hotswapping and hotreloading
- [x] Manifest files (JSON, JSON5, JSONC supported) with metadata (JSON schema provided)
- [x] Installation by dropping a folder into the plugins/ directory
- [x] Support for plugins having their own NPM dependencies
- [x] Support for storing plugins' configuration in the main config.toml (single source of truth)
- [x] Schema-based strict config validation (plugins can specify their own schemas)
- [x] Full type-safety
- [x] Custom hooks
- [x] FFI compatibility (with `bun:ffi` or Node's FFI)
- [x] Custom API route registration or overriding or middlewaring
- [x] Automatic OpenAPI schema generation for all installed plugins
- [x] End-to-end and unit testing supported
- [x] Automatic user input validation for API routes with schemas (specify a schema for the route and the server will take care of validating everything)
- [x] Access to internal database abstractions
- [x] Support for sending raw SQL to database (type-safe!)
- [x] Plugin autoload on startup with override controls (enable/disable)
As a demonstration of the power of this system and an effort to modularize the codebase further, OpenID functionality has been moved to a plugin. This plugin is required for login.
## Bug Fixes
- Fix favouriting/unfavouriting sometimes returning negative counts.
- Non-images will now properly be uploaded to object storage.
- Make account searches case-insensitive
- Fix image decoding error when passing media through proxy.
- OpenID Connect now correctly remembers and passes `state` parameter.
- OpenID Connect will not reject some correct but weird redirect URIs.
- Markdown posts will not have invisible anchor tags anymore (this messed up accessibility).
- Reverse proxies incorrectly reporting an HTTPS request as HTTP will now be handled correctly during OpenID Connect flows.
- API Relationships will now correctly return `requested_by`.
- Make process wait for Ctrl+C to exit on error, instead of exiting immediately. This fixes some issues with Docker restarting endlessly.
- Animated media will now stay animated when uploaded.
- Some instance metadata will no longer be missing from `/api/v2/instabnce` endpoint. In fact, it will now be more complete than Mastodon's implementation.
- The Origin HTTP header will no longer be used to determine the origin of a request. This was a security issue.
- New notes will no longer incorrectly be federated to _all_ remote users at once.
- Fix [Elk Client](https://elk.zone/) not being able to log in.
## Removals
- Remove old logging system, to be replaced by a new one.
- Removed Meilisearch support, in favor of Sonic. Follow instructions in the [installation guide](https://server.versia.pub/setup/installation) to set up Sonic.
- Removed explicit Glitch-FE support. Glitch-FE will still work, but must be hosted separately like any other frontend.
## Miscellaneous
- Remove Node.js from Docker build.
- Update all dependencies.

View file

@ -1,140 +0,0 @@
# Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, caste, color, religion, or sexual
identity and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the overall
community
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or advances of
any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email address,
without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement:
- CPlusPatch
- Matrix: @jesse:cpluspatch.dev
- E-Mail: contact@cpluspatch.com
- AprilThePimk
- Matrix: @aprl:uwu.is
- E-Mail: aprl+fossawareness@acab.dev
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series of
actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or permanent
ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within the
community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.1, available at
[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
Community Impact Guidelines were inspired by
[Mozilla's code of conduct enforcement ladder][Mozilla CoC].
For answers to common questions about this code of conduct, see the FAQ at
[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
[https://www.contributor-covenant.org/translations][translations].
[homepage]: https://www.contributor-covenant.org
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
[Mozilla CoC]: https://github.com/mozilla/diversity
[FAQ]: https://www.contributor-covenant.org/faq
[translations]: https://www.contributor-covenant.org/translations

View file

@ -1,18 +1,17 @@
# Contributing to Lysand
# Contributing to Versia
> [!NOTE]
> This document was authored by [@CPlusPatch](https://github.com/CPlusPatch).
Thank you for your interest in contributing to Lysand! We welcome contributions from everyone, regardless of their level of experience or expertise.
Thank you for your interest in contributing to Versia Server! We welcome contributions from everyone, regardless of their level of experience or expertise.
# Tech Stack
Lysand is built using the following technologies:
Versia Server is built using the following technologies:
- [Bun](https://bun.sh) - A JavaScript runtime similar to Node.js, but faster and with more features
- [PostgreSQL](https://www.postgresql.org/) - A relational database
- [`pg_uuidv7`](https://github.com/fboulnois/pg_uuidv7) - A PostgreSQL extension that provides a UUIDv7 data type
- [Nuxt](https://nuxt.com/) - A Vue.js framework, used for the frontend
- [Docker](https://www.docker.com/) - A containerization platform, used for development and deployment
- [Sharp](https://sharp.pixelplumbing.com/) - An image processing library, used for fast image resizing and converting
- [TypeScript](https://www.typescriptlang.org/) - A typed superset of JavaScript
@ -21,26 +20,54 @@ Lysand is built using the following technologies:
To get started, please follow these steps:
1. Fork the repository, clone it on your local system and make your own branch
2. Install the [Bun](https://bun.sh) runtime:
1. Install the [Bun](https://bun.sh) runtime:
```sh
curl -fsSL https://bun.sh/install | bash
```
1. Clone this repository
2. Clone this repository
```bash
git clone https://github.com/lysand-org/lysand.git
git clone https://github.com/versia-pub/server.git
```
2. Install the dependencies
3. Install the dependencies
```bash
bun install
```
3. Set up a PostgreSQL database (you need a special extension, please look at [the database documentation](database.md))
1. Set up a PostgreSQL database (you need a special extension, please look at [the database documentation](https://server.versia.pub/setup/database))
4. Copy the `config/config.toml.example` file to `config/config.toml` and edit it to set up the database connection and other settings.
2. Copy the `config/config.example.toml` file to `config/config.toml` and edit it to set up the database connection and other settings.
## HTTPS development
To develop with HTTPS, you need to generate a self-signed certificate. We will use [`mkcert`](https://github.com/FiloSottile/mkcert) for this purpose.
1. Install `mkcert`:
2. Generate a certificate for the domain you are using:
```sh
mkcert -install
# You can change the domain to whatever you want, but it must resolve via /etc/hosts
# *.localhost domains are automatically aliased to localhost by DNS
mkcert -key-file config/versia.localhost-key.pem -cert-file config/versia.localhost.pem versia.localhost
```
3. Edit the config to use your database and HTTPS certificates, e.g:
```toml
[http]
base_url = "https://versia.localhost:9900"
bind = "versia.localhost"
bind_port = 9900 # Change the port to whatever you want
[http.tls]
enabled = true
key = "config/versia.localhost-key.pem"
cert = "config/versia.localhost.pem"
passphrase = ""
ca = ""
```
Now, running the server will use the certificate you generated.
## Testing your changes
@ -51,48 +78,46 @@ bun dev
If your port number is lower than 1024, you may need to run the command as root.
### Running the FE
To start the frontend server, run:
```sh
bun fe:dev
```
This should be run in a separate process as the server.
## Running tests
To run the tests, run:
```sh
bun test
bun run test
```
The tests are located in the `tests/` directory and follow a Jest-like syntax. The server should be shut down before running the tests.
The `bun test` command will cause errors due to Bun bugs ([oven-sh/bun#7823](https://github.com/oven-sh/bun/issues/7823)). Use the `test` script instead.
The tests are located all around the codebase (filename `*.test.ts`) and follow a Jest-like syntax. The server should be shut down before running the tests.
## Code style
We use [Biome](https://biomejs.dev) to enforce a consistent code style. To check if your code is compliant, run:
```sh
bunx @biomejs/biome check .
bun lint
```
To automatically fix the issues, run:
```sh
bunx @biomejs/biome check . --apply
bun lint --write
```
You can also install the Biome Visual Studio Code extension and have it format your code automatically on save.
### ESLint rules
### TypeScript
Linting should not be ignored, except if they are false positives, in which case you can use a comment to disable the rule for the line or the file. If you need to disable a rule, please add a comment explaining why.
TypeScript errors should be ignored with `// @ts-expect-error` comments, as well as with a reason for being ignored.
To scan for all TypeScript errors, run:
```sh
bun typecheck
```
### Commit messages
We use [Conventional Commits](https://www.conventionalcommits.org) for our commit messages. This allows us to automatically generate the changelog and the version number.
We use [Conventional Commits](https://www.conventionalcommits.org) for our commit messages. This allows us to automatically generate the changelog and the version number, while also making it easier to understand what changes were made in each commit.
### Pull requests
@ -106,11 +131,11 @@ Tests **should** be written for all API routes and all functions that are not tr
#### Adding per-route tests
To add tests for a route, create a `route_file_name.test.ts` file in the same directory as the route itself. See [this example](/server/api/api/v1/timelines/home.test.ts) for help writing tests.
To add tests for a route, create a `route_file_name.test.ts` file in the same directory as the route itself. See [this example](/api/api/v1/timelines/home.test.ts) for help writing tests.
### Writing documentation
Documentation for the Lysand protocol is available on [lysand.org](https://lysand.org/). If you are thinking of modifying the protocol, please make sure to send a pull request over there to get it approved and merged before you send your pull request here.
Documentation for the Versia protocol is available on [versia.pub](https://versia.pub/). If you are thinking of modifying the protocol, please make sure to send a pull request over there to get it approved and merged before you send your pull request here.
This project should not need much documentation, but if you think that something needs to be documented, please add it to the README, docs or contribution guide.
@ -121,11 +146,11 @@ If you find a bug, please open an issue on GitHub. Please make sure to include t
- The steps to reproduce the bug
- The expected behavior
- The actual behavior
- The version of Lysand you are using
- The version of Versia Server you are using
- The version of Bun you are using
- The version of PostgreSQL you are using
- Your operating system and version
# License
Lysand is licensed under the [AGPLv3 or later](https://www.gnu.org/licenses/agpl-3.0.en.html) license. By contributing to Lysand, you agree to license your contributions under the same license.
Versia Server is licensed under the [AGPLv3 or later](https://www.gnu.org/licenses/agpl-3.0.en.html) license. By contributing to Versia, you agree to license your contributions under the same license.

View file

@ -1,7 +1,5 @@
# Bun doesn't run well on Musl but this seems to work
FROM imbios/bun-node:1.1.7-20-alpine as base
RUN apk add --no-cache libstdc++
# Node is required for building the project
FROM imbios/bun-node:latest-23-alpine AS base
# Install dependencies into temp directory
# This will cache them and speed up future builds
@ -10,40 +8,44 @@ FROM base AS install
RUN mkdir -p /temp
COPY . /temp
WORKDIR /temp
RUN bun install --frozen-lockfile --production
RUN bun install --production
FROM base as build
FROM base AS build
# Copy the project
RUN mkdir -p /temp
COPY . /temp
# Copy dependencies
COPY --from=install /temp/node_modules /temp/node_modules
# Build the project
WORKDIR /temp
RUN bun run prod-build
RUN bun run build api
WORKDIR /temp/dist
# Copy production dependencies and source code into final image
FROM oven/bun:1.1.7-alpine
FROM oven/bun:1.3.2-alpine
RUN apk add --no-cache libstdc++
# Create app directory
# Install libstdc++ for Bun and create app directory
RUN mkdir -p /app
COPY --from=build /temp/dist /app/dist
COPY entrypoint.sh /app
LABEL org.opencontainers.image.authors "Gaspard Wierzbinski (https://cpluspatch.dev)"
LABEL org.opencontainers.image.source "https://github.com/lysand-org/lysand"
LABEL org.opencontainers.image.vendor "Lysand Org"
LABEL org.opencontainers.image.licenses "AGPL-3.0-or-later"
LABEL org.opencontainers.image.title "Lysand Server"
LABEL org.opencontainers.image.description "Lysand Server docker image"
LABEL org.opencontainers.image.authors="Gaspard Wierzbinski (https://cpluspatch.com)"
LABEL org.opencontainers.image.source="https://github.com/versia-pub/server"
LABEL org.opencontainers.image.vendor="Versia Pub"
LABEL org.opencontainers.image.licenses="AGPL-3.0-or-later"
LABEL org.opencontainers.image.title="Versia Server"
LABEL org.opencontainers.image.description="Versia Server Docker image"
# Set current Git commit hash as an environment variable
ARG GIT_COMMIT
ENV GIT_COMMIT=$GIT_COMMIT
# CD to app
WORKDIR /app
ENV NODE_ENV=production
ENTRYPOINT [ "/bin/sh", "/app/entrypoint.sh" ]
# Run migrations and start the server
CMD [ "start" ]
CMD [ "bun", "run", "api.js" ]

111
README.md
View file

@ -1,30 +1,52 @@
<p align="center">
<a href="https://lysand.org"><img src="https://cdn.lysand.org/logo-long-dark.webp" alt="Lysand Logo" height="110"></a>
</p>
<div align="center">
<a href="https://versia.pub">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://cdn.versia.pub/branding/logo-dark.svg">
<source media="(prefers-color-scheme: light)" srcset="https://cdn.versia.pub/branding/logo-light.svg">
<img src="https://cdn.versia.pub/branding/logo-dark.svg" alt="Versia Logo" height="110" />
</picture>
</a>
</div>
![Postgres](https://img.shields.io/badge/postgres-%23316192.svg?style=for-the-badge&logo=postgresql&logoColor=white) ![Bun](https://img.shields.io/badge/Bun-%23000000.svg?style=for-the-badge&logo=bun&logoColor=white) ![VS Code Insiders](https://img.shields.io/badge/VS%20Code%20Insiders-35b393.svg?style=for-the-badge&logo=visual-studio-code&logoColor=white) ![TypeScript](https://img.shields.io/badge/typescript-%23007ACC.svg?style=for-the-badge&logo=typescript&logoColor=white) ![Linux](https://img.shields.io/badge/Linux-FCC624?style=for-the-badge&logo=linux&logoColor=black) ![Docker](https://img.shields.io/badge/docker-%230db7ed.svg?style=for-the-badge&logo=docker&logoColor=white) [![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-2.1-4baaaa?style=for-the-badge)](code_of_conduct.md)
<h2 align="center">
<strong><code>Versia Server</code></strong>
</h2>
<div align="center">
<img src="https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/typescript/typescript-original.svg" height="42" width="52" alt="TypeScript logo">
<img src="https://cdn.jsdelivr.net/gh/devicons/devicon/icons/postgresql/postgresql-original.svg" height="42" width="52" alt="PostgreSQL logo">
<img src="https://cdn.jsdelivr.net/gh/devicons/devicon/icons/docker/docker-original.svg" height="42" width="52" alt="Docker logo">
<img src="https://cdn.jsdelivr.net/gh/devicons/devicon/icons/bun/bun-original.svg" height="42" width="52" alt="Bun logo">
<img src="https://cdn.jsdelivr.net/gh/devicons/devicon/icons/vscode/vscode-original.svg" height="42" width="52" alt="VSCode logo">
<img src="https://cdn.jsdelivr.net/gh/devicons/devicon/icons/sentry/sentry-original.svg" height="42" width="52" alt="Sentry logo">
<img src="https://cdn.jsdelivr.net/gh/devicons/devicon/icons/linux/linux-original.svg" height="42" width="52" alt="Linux logo">
</div>
<br/>
## What is this?
**Lysand Server** is a federated social network server based on the [Lysand](https://lysand.org) protocol. It is currently in beta phase, with basic federation and almost complete Mastodon API support.
**Versia Server** (formerly Lysand Server) is a federated social network server based on the [Versia](https://versia.pub) protocol. It is currently in beta phase, with basic federation and almost complete Mastodon API support.
### Goals
- **Privacy**: Lysand is designed to be as private as possible. Unnecessary data is not stored, and data that is stored is done so securely.
- **Configurability**: High configurability is a key feature of Lysand. Almost every aspect of the server can be configured to suit your needs. If you feel like something is missing, please open an issue.
- **Security**: Lysand is designed with security in mind. It is built with modern security practices and technologies, and is constantly updated to ensure the highest level of security.
- **Performance**: Efficiency and speed are a key focus of Lysand. The design is non-monolithic, and is built to be as fast as possible.
- **Mastodon API compatibility**: Lysand is designed to be compatible with the Mastodon API, with Glitch-SOC extensions.
- **Privacy**: Versia Server is designed to be as private as possible. Unnecessary data is not stored, and data that is stored is done so securely.
- **Configurability**: High configurability is a key feature of Versia Server. Almost every aspect of the server can be configured to suit your needs. If you feel like something is missing, please open an issue.
- **Security**: Versia Server is designed with security in mind. It is built with modern security practices and technologies, and is constantly updated to ensure the highest level of security.
- **Performance**: Efficiency and speed are a key focus of Versia Server. The design is non-monolithic, and is built to be as fast as possible.
- **Mastodon API compatibility**: Versia Server is designed to be compatible with the Mastodon API, with [`glitch-soc`](https://github.com/glitch-soc/mastodon) extensions.
### Anti-Goals
- **Monolithic design**: Modularity and scaling is important to this project. This means that it is not a single, monolithic application, but rather a collection of smaller, more focused applications. (API layer, queue, database, frontend, etc.)
- **Complexity**: Both in code and in function, Lysand should be as simple as possible. This does not mean adding no features or functionality, but rather that the features and functionality that are added should be well-written and easy to understand.
- **Bloat**: Lysand should not be bloated with unnecessary features, packages, dependencies or code. It should be as lightweight as possible, while still being feature-rich.
- **Complexity**: Both in code and in function, Versia Server should be as simple as possible. This does not mean adding no features or functionality, but rather that the features and functionality that are added should be well-written and easy to understand.
- **Bloat**: Versia Server should not be bloated with unnecessary features, packages, dependencies or code. It should be as lightweight as possible, while still being feature-rich.
## Features
- [x] Federation (partial)
- [x] Versia Working Draft 4 federation (partial)
- [x] Hyper fast (thousands of HTTP requests per second)
- [x] S3 or local media storage
- [x] Deduplication of uploaded files
@ -32,18 +54,35 @@
- [x] Configurable defaults
- [x] Full regex-based filters for posts, users and media
- [x] Custom emoji support
- [x] Users can upload their own emojis for themselves
- [x] Automatic image conversion to WebP or other formats
- [x] Scripting-compatible CLI with JSON and CSV outputs
- [ ] Moderation tools
- [x] Mastodon API support (partial)
- [x] Markdown support just about everywhere: posts, profiles, profile fields, etc. Code blocks, tables, and more are supported.
- [ ] Advanced moderation tools (work in progress)
- [x] Fully compliant Mastodon API support (partial)
- [x] Glitch-SOC extensions
- [x] Full compatibility with many clients such as Megalodon
- [x] Ability to use your own frontends
- [x] Non-monolithic architecture, microservices can be hosted in infinite amounts on infinite servers
- [x] Ability to use all your threads
- [x] Support for SSO providers, as well as SSO-only registration.
- [x] Fully written in TypeScript and thoroughly unit tested
- [x] Automatic signed container builds for easy deployment
- [x] Docker and Podman supported
- [x] Invisible, Proof-of-Work local CAPTCHA for API requests
- [x] Advanced Roles and Permissions API.
- [x] HTTP proxy support
- [x] Tor hidden service support
- [x] Sentry logging support
- [x] Ability to change the domain name in a single config change, without any database edits
## Screenshots
You can visit [social.lysand.org](https://social.lysand.org) to see a live instance of Lysand with Lysand-FE.
You can visit [social.lysand.org](https://social.lysand.org) to see a live instance of Versia Server with Versia-FE.
## How do I run it?
Please see the [installation guide](docs/installation.md) for more information on how to install Lysand.
Please see the [installation guide](https://server.versia.pub/setup/installation) for more information on how to install Versia.
## Contributing
@ -52,13 +91,15 @@ Contributions are welcome! Please see the [CONTRIBUTING.md](CONTRIBUTING.md) fil
## Federation
The following extensions are currently supported or being worked on:
- `org.lysand:custom_emojis`: Custom emojis
- `org.lysand:polls`: Polls
- `org.lysand:microblogging`: Microblogging
- `pub.versia:custom_emojis`: Custom emojis
- `pub.versia:instance_messaging`: Instance Messaging
- `pub.versia:likes`: Likes
- `pub.versia:share`: Share
- `pub.versia:reactions`: Reactions
## API
Lysand implements the Mastodon API (as well as Glitch-Soc extensions). The API is currently almost fully complete, with some fringe functionality still being worked on.
Versia Server implements the Mastodon API (as well as `glitch-soc` extensions). The API is currently almost fully complete, with some fringe functionality still being worked on.
Working endpoints are:
@ -170,10 +211,10 @@ Working endpoints are:
- [ ] `/api/v2/suggestions`
- [x] `/oauth/authorize`
- [x] `/oauth/token`
- [ ] `/oauth/revoke`
- Admin API
- [x] `/oauth/revoke`
- Admin API
### Main work to do
### Main work to do for API
- [ ] Announcements
- [ ] Polls
@ -189,6 +230,28 @@ Working endpoints are:
- [ ] Reports
- [ ] Admin API
## Versia Server API
For Versia Server's own custom API, please see the [API documentation](https://server.versia.pub/api/emojis).
## License
This project is licensed under the [AGPL-3.0-or-later](LICENSE).
All Versia assets (icon, logo, banners, etc) are licensed under [CC-BY-NC-SA-4.0](https://creativecommons.org/licenses/by-nc-sa/4.0)
## Thanks!
Thanks to [**Fastly**](https://fastly.com) for providing us with support and resources to build Versia!
<br />
<p align="center">
<a href="https://fastly.com">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="assets/fastly-red.svg">
<source media="(prefers-color-scheme: light)" srcset="assets/fastly-red.svg">
<img src="assets/fastly-red.svg" alt="Fastly Logo" height="110" />
</picture>
</a>
</p>

View file

@ -1,22 +0,0 @@
# Security Policy
## Supported Versions
For now, only the released latest version of Lysand is supported for security updates. This will change as Lysand exits alpha status.
## Reporting a Vulnerability
If you find a vulnerability, please report it to [@CPlusPatch](https://github.com/CPlusPatch) at the following contact endpoints:
- [Matrix](https://matrix.to/#/@jesse:cpluspatch.dev)
- [E-mail](mailto:contact@cpluspatch.com)
Please do not report vulnerabilities publicly until they have been patched. If you would like to be credited for your discovery, please include your name and/or GitHub username in your report.
## Vulnerability Disclosure Policy
Lysand is an open-source project, and as such, we welcome security researchers to audit our code and report vulnerabilities. We will do our best to patch vulnerabilities as quickly as possible, and will credit researchers for their discoveries if they wish to be credited.
For security reasons, we ask that you do not publicly disclose vulnerabilities until they have been patched. We will do our best to patch vulnerabilities as quickly as possible, and will credit researchers for their discoveries if they wish to be credited.
Thank you for helping to keep Lysand secure! :3

51
Worker.Dockerfile Normal file
View file

@ -0,0 +1,51 @@
# Node is required for building the project
FROM imbios/bun-node:latest-23-alpine AS base
# Install dependencies into temp directory
# This will cache them and speed up future builds
FROM base AS install
RUN mkdir -p /temp
COPY . /temp
WORKDIR /temp
RUN bun install --production
FROM base AS build
# Copy the project
RUN mkdir -p /temp
COPY . /temp
# Copy dependencies
COPY --from=install /temp/node_modules /temp/node_modules
# Build the project
WORKDIR /temp
RUN bun run build worker
WORKDIR /temp/dist
# Copy production dependencies and source code into final image
FROM oven/bun:1.3.2-alpine
# Install libstdc++ for Bun and create app directory
RUN mkdir -p /app
COPY --from=build /temp/dist /app/dist
COPY entrypoint.sh /app
LABEL org.opencontainers.image.authors="Gaspard Wierzbinski (https://cpluspatch.com)"
LABEL org.opencontainers.image.source="https://github.com/versia-pub/server"
LABEL org.opencontainers.image.vendor="Versia Pub"
LABEL org.opencontainers.image.licenses="AGPL-3.0-or-later"
LABEL org.opencontainers.image.title="Versia Server Worker"
LABEL org.opencontainers.image.description="Versia Server Worker Docker image"
# Set current Git commit hash as an environment variable
ARG GIT_COMMIT
ENV GIT_COMMIT=$GIT_COMMIT
# CD to app
WORKDIR /app
ENV NODE_ENV=production
ENTRYPOINT [ "/bin/sh", "/app/entrypoint.sh" ]
# Run migrations and start the server
CMD [ "bun", "run", "worker.js" ]

19
api.ts Normal file
View file

@ -0,0 +1,19 @@
import process from "node:process";
import { appFactory } from "@versia-server/api";
import { config } from "@versia-server/config";
import { Youch } from "youch";
import { createServer } from "@/server.ts";
process.on("SIGINT", () => {
process.exit();
});
process.on("uncaughtException", async (error) => {
const youch = new Youch();
console.error(await youch.toANSI(error));
});
await import("@versia-server/api/setup");
createServer(config, await appFactory());

1
assets/fastly-black.svg Normal file
View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 198.27"><g id="Layer_2" data-name="Layer 2"><g id="Layer_1-2" data-name="Layer 1"><g id="Fastly_Logo_-_Red" data-name="Fastly Logo - Red"><g id="Fastly_Logo_-_Red-2" data-name="Fastly Logo - Red"><polygon points="348.44 20.35 348.44 153.94 388.57 153.94 388.57 133.53 375.31 133.53 375.31 0 348.44 0 348.44 20.35"/><path d="M0,133.53H13.64V69.08H0V51.35l13.64-2.24V31.17C13.64,9.43,18.37,0,46.09,0A115.17,115.17,0,0,1,65.38,2L61.7,23.85a49.78,49.78,0,0,0-9-.78c-9.76,0-12.23,1-12.23,10.51V49.11H60.79v20H40.51v64.45H54v20.4H0Z"/><path d="M334.78,127.08a53.11,53.11,0,0,1-10.54.84c-11.06.27-10.1-3.36-10.1-13.78V69.08h21v-20h-21V0H287.27V119.71c0,23.5,5.8,34.23,31.08,34.23,6,0,14.21-1.54,20.42-2.87Z"/><path d="M501.7,133.63a10.14,10.14,0,1,1-10.19,10.14,10.14,10.14,0,0,1,10.19-10.14m0,18.68a8.55,8.55,0,0,0,8.51-8.54,8.5,8.5,0,1,0-8.51,8.54m1.88-3.56-2.05-3h-1.42v3h-2.29v-10H502c2.46,0,4,1.24,4,3.45a3,3,0,0,1-2.08,3.09l2.49,3.42Zm-3.47-5h1.82c1,0,1.74-.4,1.74-1.5s-.7-1.45-1.68-1.45h-1.88Z"/><path d="M253.72,69V65.46A115.8,115.8,0,0,0,233.14,64c-12.5,0-14,6.63-14,10.23,0,5.08,1.74,7.83,15.29,10.79,19.8,4.45,39.69,9.09,39.69,33.64,0,23.29-12,35.32-37.21,35.32-16.88,0-33.26-3.63-45.76-6.8V127.08h20.35v3.56c8.75,1.69,17.93,1.52,22.73,1.52,13.34,0,15.49-7.17,15.49-11,0-5.29-3.82-7.83-16.32-10.37-23.56-4-42.25-12.07-42.25-36,0-22.65,15.14-31.54,40.37-31.54,17.09,0,30.08,2.65,42.59,5.83V69Z"/><path d="M127.84,85.09,118,93.69a5.25,5.25,0,1,0,3.19,3.2l8.72-9.75Z"/><path d="M171.25,127.07V43.46H144.37V51a55,55,0,0,0-18.11-6.77v-9.1h3.28V28.28H102.48v6.83h3.28v9.17a55.32,55.32,0,1,0,38.76,101.87l4.77,7.78h28.38V127.07Zm-26.64-26.83A28.42,28.42,0,0,1,117.73,127v-3.18h-3.22V127a28.43,28.43,0,0,1-26.68-26.89H91V96.91H87.85a28.42,28.42,0,0,1,26.66-26.65v3.16h3.22V70.25A28.42,28.42,0,0,1,144.61,97h-3.2v3.22Z"/><path d="M456.58,49.11H512v20H498.75l-34,83.62c-9.74,23.48-25.74,45.59-50.1,45.59a93.67,93.67,0,0,1-19.5-2l2.43-24.39a68.7,68.7,0,0,0,10.63,1.1c11.3,0,24-7,28-19.19L401.82,69.06H388.57v-20H444v20H430.78l19.51,48h0l19.51-48H456.58Z"/></g></g></g></g></svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

1
assets/fastly-red.svg Normal file
View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 198.27"><defs><style>.cls-1{fill:#ff282d;}</style></defs><g id="Layer_2" data-name="Layer 2"><g id="Layer_1-2" data-name="Layer 1"><g id="Fastly_Logo_-_Red" data-name="Fastly Logo - Red"><g id="Fastly_Logo_-_Red-2" data-name="Fastly Logo - Red"><polygon class="cls-1" points="348.44 20.35 348.44 153.94 388.57 153.94 388.57 133.53 375.31 133.53 375.31 0 348.44 0 348.44 20.35"/><path class="cls-1" d="M0,133.53H13.64V69.08H0V51.35l13.64-2.24V31.17C13.64,9.43,18.37,0,46.09,0A115.17,115.17,0,0,1,65.38,2L61.7,23.85a49.78,49.78,0,0,0-9-.78c-9.76,0-12.23,1-12.23,10.51V49.11H60.79v20H40.51v64.45H54v20.4H0Z"/><path class="cls-1" d="M334.78,127.08a53.11,53.11,0,0,1-10.54.84c-11.06.27-10.1-3.36-10.1-13.78V69.08h21v-20h-21V0H287.27V119.71c0,23.5,5.8,34.23,31.08,34.23,6,0,14.21-1.54,20.42-2.87Z"/><path class="cls-1" d="M501.7,133.63a10.14,10.14,0,1,1-10.19,10.14,10.14,10.14,0,0,1,10.19-10.14m0,18.68a8.55,8.55,0,0,0,8.51-8.54,8.5,8.5,0,1,0-8.51,8.54m1.88-3.56-2.05-3h-1.42v3h-2.29v-10H502c2.46,0,4,1.24,4,3.45a3,3,0,0,1-2.08,3.09l2.49,3.42Zm-3.47-5h1.82c1,0,1.74-.4,1.74-1.5s-.7-1.45-1.68-1.45h-1.88Z"/><path class="cls-1" d="M253.72,69V65.46A115.8,115.8,0,0,0,233.14,64c-12.5,0-14,6.63-14,10.23,0,5.08,1.74,7.83,15.29,10.79,19.8,4.45,39.69,9.09,39.69,33.64,0,23.29-12,35.32-37.21,35.32-16.88,0-33.26-3.63-45.76-6.8V127.08h20.35v3.56c8.75,1.69,17.93,1.52,22.73,1.52,13.34,0,15.49-7.17,15.49-11,0-5.29-3.82-7.83-16.32-10.37-23.56-4-42.25-12.07-42.25-36,0-22.65,15.14-31.54,40.37-31.54,17.09,0,30.08,2.65,42.59,5.83V69Z"/><path class="cls-1" d="M127.84,85.09,118,93.69a5.25,5.25,0,1,0,3.19,3.2l8.72-9.75Z"/><path class="cls-1" d="M171.25,127.07V43.46H144.37V51a55,55,0,0,0-18.11-6.77v-9.1h3.28V28.28H102.48v6.83h3.28v9.17a55.32,55.32,0,1,0,38.76,101.87l4.77,7.78h28.38V127.07Zm-26.64-26.83A28.42,28.42,0,0,1,117.73,127v-3.18h-3.22V127a28.43,28.43,0,0,1-26.68-26.89H91V96.91H87.85a28.42,28.42,0,0,1,26.66-26.65v3.16h3.22V70.25A28.42,28.42,0,0,1,144.61,97h-3.2v3.22Z"/><path class="cls-1" d="M456.58,49.11H512v20H498.75l-34,83.62c-9.74,23.48-25.74,45.59-50.1,45.59a93.67,93.67,0,0,1-19.5-2l2.43-24.39a68.7,68.7,0,0,0,10.63,1.1c11.3,0,24-7,28-19.19L401.82,69.06H388.57v-20H444v20H430.78l19.51,48h0l19.51-48H456.58Z"/></g></g></g></g></svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

1
assets/fastly-white.svg Normal file
View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 198.27"><defs><style>.cls-1{fill:#fff;}</style></defs><g id="Layer_2" data-name="Layer 2"><g id="Layer_1-2" data-name="Layer 1"><g id="Fastly_Logo_-_Red" data-name="Fastly Logo - Red"><g id="Fastly_Logo_-_Red-2" data-name="Fastly Logo - Red"><polygon class="cls-1" points="348.44 20.35 348.44 153.94 388.57 153.94 388.57 133.53 375.31 133.53 375.31 0 348.44 0 348.44 20.35"/><path class="cls-1" d="M0,133.53H13.64V69.08H0V51.35l13.64-2.24V31.17C13.64,9.43,18.37,0,46.09,0A115.17,115.17,0,0,1,65.38,2L61.7,23.85a49.78,49.78,0,0,0-9-.78c-9.76,0-12.23,1-12.23,10.51V49.11H60.79v20H40.51v64.45H54v20.4H0Z"/><path class="cls-1" d="M334.78,127.08a53.11,53.11,0,0,1-10.54.84c-11.06.27-10.1-3.36-10.1-13.78V69.08h21v-20h-21V0H287.27V119.71c0,23.5,5.8,34.23,31.08,34.23,6,0,14.21-1.54,20.42-2.87Z"/><path class="cls-1" d="M501.7,133.63a10.14,10.14,0,1,1-10.19,10.14,10.14,10.14,0,0,1,10.19-10.14m0,18.68a8.55,8.55,0,0,0,8.51-8.54,8.5,8.5,0,1,0-8.51,8.54m1.88-3.56-2.05-3h-1.42v3h-2.29v-10H502c2.46,0,4,1.24,4,3.45a3,3,0,0,1-2.08,3.09l2.49,3.42Zm-3.47-5h1.82c1,0,1.74-.4,1.74-1.5s-.7-1.45-1.68-1.45h-1.88Z"/><path class="cls-1" d="M253.72,69V65.46A115.8,115.8,0,0,0,233.14,64c-12.5,0-14,6.63-14,10.23,0,5.08,1.74,7.83,15.29,10.79,19.8,4.45,39.69,9.09,39.69,33.64,0,23.29-12,35.32-37.21,35.32-16.88,0-33.26-3.63-45.76-6.8V127.08h20.35v3.56c8.75,1.69,17.93,1.52,22.73,1.52,13.34,0,15.49-7.17,15.49-11,0-5.29-3.82-7.83-16.32-10.37-23.56-4-42.25-12.07-42.25-36,0-22.65,15.14-31.54,40.37-31.54,17.09,0,30.08,2.65,42.59,5.83V69Z"/><path class="cls-1" d="M127.84,85.09,118,93.69a5.25,5.25,0,1,0,3.19,3.2l8.72-9.75Z"/><path class="cls-1" d="M171.25,127.07V43.46H144.37V51a55,55,0,0,0-18.11-6.77v-9.1h3.28V28.28H102.48v6.83h3.28v9.17a55.32,55.32,0,1,0,38.76,101.87l4.77,7.78h28.38V127.07Zm-26.64-26.83A28.42,28.42,0,0,1,117.73,127v-3.18h-3.22V127a28.43,28.43,0,0,1-26.68-26.89H91V96.91H87.85a28.42,28.42,0,0,1,26.66-26.65v3.16h3.22V70.25A28.42,28.42,0,0,1,144.61,97h-3.2v3.22Z"/><path class="cls-1" d="M456.58,49.11H512v20H498.75l-34,83.62c-9.74,23.48-25.74,45.59-50.1,45.59a93.67,93.67,0,0,1-19.5-2l2.43-24.39a68.7,68.7,0,0,0,10.63,1.1c11.3,0,24-7,28-19.19L401.82,69.06H388.57v-20H444v20H430.78l19.51,48h0l19.51-48H456.58Z"/></g></g></g></g></svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

View file

@ -1,18 +0,0 @@
const timeBefore = performance.now();
const requests: Promise<Response>[] = [];
// Repeat 1000 times
for (let i = 0; i < 1000; i++) {
requests.push(
fetch("https://mastodon.social", {
method: "GET",
}),
);
}
await Promise.all(requests);
const timeAfter = performance.now();
console.log(`Time taken: ${timeAfter - timeBefore}ms`);

View file

@ -1 +0,0 @@
//

46
benchmarks/timeline.ts Normal file
View file

@ -0,0 +1,46 @@
import type { Status } from "@versia/client/schemas";
import {
fakeRequest,
getTestStatuses,
getTestUsers,
} from "@versia-server/tests";
import { bench, run } from "mitata";
import type { z } from "zod";
const { users, tokens, deleteUsers } = await getTestUsers(5);
await getTestStatuses(40, users[0]);
const testTimeline = async (): Promise<void> => {
const response = await fakeRequest("/api/v1/timelines/home", {
headers: {
Authorization: `Bearer ${tokens[0].data.accessToken}`,
},
});
const objects = (await response.json()) as z.infer<typeof Status>[];
if (objects.length !== 20) {
throw new Error("Invalid response (not 20 objects)");
}
};
const testInstance = async (): Promise<void> => {
const response = await fakeRequest("/api/v2/instance", {
headers: {
Authorization: `Bearer ${tokens[0].data.accessToken}`,
},
});
const object = (await response.json()) as Record<string, unknown>;
if (typeof object !== "object") {
throw new Error("Invalid response (not an object)");
}
};
bench("timeline", testTimeline).range("amount", 1, 1000);
bench("instance", testInstance).range("amount", 1, 1000);
await run();
await deleteUsers();

View file

@ -1,54 +0,0 @@
/**
* Usage: TOKEN=your_token_here bun benchmark:timeline <request_count>
*/
import chalk from "chalk";
import { config } from "config-manager";
const token = process.env.TOKEN;
const requestCount = Number(process.argv[2]) || 100;
if (!token) {
console.log(
`${chalk.red(
"✗",
)} No token provided. Provide one via the TOKEN environment variable.`,
);
process.exit(1);
}
const fetchTimeline = () =>
fetch(new URL("/api/v1/timelines/home", config.http.base_url), {
headers: {
Authorization: `Bearer ${token}`,
},
}).then((res) => res.ok);
const timeNow = performance.now();
const requests = Array.from({ length: requestCount }, () => fetchTimeline());
Promise.all(requests)
.then((results) => {
const timeTaken = performance.now() - timeNow;
if (results.every((t) => t)) {
console.log(`${chalk.green("✓")} All requests succeeded`);
} else {
console.log(
`${chalk.red("✗")} ${
results.filter((t) => !t).length
} requests failed`,
);
}
console.log(
`${chalk.green("✓")} ${
requests.length
} requests fulfilled in ${chalk.bold(
(timeTaken / 1000).toFixed(5),
)}s`,
);
})
.catch((err) => {
console.log(`${chalk.red("✗")} ${err}`);
process.exit(1);
});

View file

@ -1,20 +1,178 @@
{
"$schema": "https://biomejs.dev/schemas/1.6.4/schema.json",
"organizeImports": {
"$schema": "https://biomejs.dev/schemas/2.3.4/schema.json",
"assist": {
"actions": {
"source": {
"organizeImports": "on"
}
}
},
"vcs": {
"clientKind": "git",
"enabled": true,
"ignore": ["node_modules", "dist", "glitch", "glitch-dev"]
"useIgnoreFile": true
},
"linter": {
"enabled": true,
"rules": {
"recommended": true
},
"ignore": ["node_modules", "dist", "glitch", "glitch-dev"]
"style": {
"useNamingConvention": {
"level": "warn",
"options": {
"requireAscii": false,
"strictCase": false,
"conventions": [
{
"selector": {
"kind": "typeProperty"
},
"formats": [
"camelCase",
"CONSTANT_CASE",
"PascalCase",
"snake_case"
]
},
{
"selector": {
"kind": "objectLiteralProperty",
"scope": "any"
},
"formats": [
"camelCase",
"CONSTANT_CASE",
"PascalCase",
"snake_case"
]
}
]
}
},
"useLiteralEnumMembers": "error",
"noNegationElse": "error",
"noYodaExpression": "error",
"useBlockStatements": "error",
"useCollapsedElseIf": "error",
"useConsistentArrayType": {
"level": "error",
"options": {
"syntax": "shorthand"
}
},
"useConsistentBuiltinInstantiation": "error",
"useExplicitLengthCheck": "error",
"useForOf": "error",
"useNodeAssertStrict": "error",
"useShorthandAssign": "error",
"useThrowNewError": "error",
"useThrowOnlyError": "error",
"useNodejsImportProtocol": "error",
"useAsConstAssertion": "error",
"useEnumInitializers": "error",
"useSelfClosingElements": "error",
"useConst": "error",
"useSingleVarDeclarator": "error",
"noUnusedTemplateLiteral": "error",
"useNumberNamespace": "error",
"useAtIndex": "warn",
"noInferrableTypes": "error",
"useCollapsedIf": "warn",
"useExponentiationOperator": "error",
"useTemplate": "error",
"noParameterAssign": "error",
"noNonNullAssertion": "error",
"useDefaultParameterLast": "error",
"useConsistentMemberAccessibility": {
"level": "warn",
"options": {
"accessibility": "explicit"
}
},
"useImportType": "error",
"useExportType": "error",
"noUselessElse": "error",
"noProcessEnv": "error",
"useShorthandFunctionType": "error",
"useArrayLiterals": "error",
"noCommonJs": "warn",
"noExportedImports": "warn",
"noSubstr": "warn",
"useTrimStartEnd": "warn",
"noRestrictedImports": {
"options": {
"paths": {
"~/packages/": "Use the appropriate package instead of importing from the packages directory directly."
}
},
"level": "error"
}
},
"performance": {
"noDynamicNamespaceImportAccess": "warn"
},
"correctness": {
"useImportExtensions": "error",
"noConstantMathMinMaxClamp": "error",
"noUndeclaredDependencies": "error",
"noUnusedFunctionParameters": "error",
"noUnusedImports": "error",
"noUnusedPrivateClassMembers": "error"
},
"nursery": {
"noFloatingPromises": "error"
},
"complexity": {
"noForEach": "error",
"noImportantStyles": "off",
"noUselessStringConcat": "error",
"useDateNow": "error",
"noUselessStringRaw": "warn",
"noUselessEscapeInRegex": "warn",
"useSimplifiedLogicExpression": "error",
"useWhile": "error",
"useNumericLiterals": "error",
"noArguments": "error",
"noCommaOperator": "error"
},
"suspicious": {
"noDuplicateTestHooks": "error",
"noOctalEscape": "error",
"noTemplateCurlyInString": "warn",
"noEmptyBlockStatements": "error",
"useAdjacentOverloadSignatures": "warn",
"useGuardForIn": "warn",
"noDuplicateElseIf": "warn",
"noEvolvingTypes": "error",
"noIrregularWhitespace": "warn",
"noExportsInTest": "error",
"noVar": "error",
"useAwait": "error",
"useErrorMessage": "error",
"useNumberToFixedDigitsArgument": "error"
}
}
},
"overrides": [
{
"includes": ["**/packages/client/versia/client.ts"],
"linter": {
"rules": {
"style": {
"useNamingConvention": "off"
}
}
}
}
],
"formatter": {
"enabled": true,
"indentStyle": "space",
"indentWidth": 4,
"ignore": ["node_modules", "dist", "glitch", "glitch-dev"]
"indentWidth": 4
},
"javascript": {
"globals": ["HTMLRewriter", "BufferEncoding"]
},
"files": {
"includes": ["**"]
}
}

View file

@ -1,45 +1,55 @@
// Delete dist directory
import { $ } from "bun";
import { routes } from "~routes";
import process from "node:process";
import { $, build, file, write } from "bun";
import manifest from "./package.json" with { type: "json" };
console.log(`Building at ${process.cwd()}`);
console.log("Building...");
await $`rm -rf dist && mkdir dist`;
await Bun.build({
entrypoints: [
`${process.cwd()}/index.ts`,
`${process.cwd()}/cli.ts`,
// Force Bun to include endpoints
...Object.values(routes),
],
outdir: `${process.cwd()}/dist`,
const type = process.argv[2] as "api" | "worker";
if (type !== "api" && type !== "worker") {
throw new Error("Invalid build type. Use 'api' or 'worker'.");
}
const packages = Object.keys(manifest.dependencies)
.filter((dep) => dep.startsWith("@versia"))
.filter((dep) => dep !== "@versia-server/tests");
await build({
entrypoints: [`./${type}.ts`],
outdir: "dist",
target: "bun",
splitting: true,
minify: false,
external: ["bullmq", "frontend"],
}).then((output) => {
if (!output.success) {
console.log(output.logs);
}
minify: true,
external: [...packages],
});
// Fix for wrong Bun file resolution, replaces node_modules with ./node_modules inside all dynamic imports
// I apologize for this
await $`sed -i 's|import("node_modules/|import("./node_modules/|g' dist/*.js`;
await $`sed -i 's|import"node_modules/|import"./node_modules/|g' dist/**/*.js`;
// Replace /temp/node_modules with ./node_modules
await $`sed -i 's|/temp/node_modules|./node_modules|g' dist/**/*.js`;
console.log("Copying files...");
// Copy Drizzle migrations to dist
await $`cp -r drizzle dist/drizzle`;
// Copy each package into dist/node_modules
for (const pkg of packages) {
const directory = pkg.split("/")[1] || pkg;
await $`mkdir -p dist/node_modules/${pkg}`;
// Copy the built package files
await $`cp -rL packages/${directory}/{dist,package.json} dist/node_modules/${pkg}`;
// Copy Sharp to dist
await $`mkdir -p dist/node_modules/@img`;
await $`cp -r node_modules/@img/sharp-libvips-linux-* dist/node_modules/@img`;
await $`cp -r node_modules/@img/sharp-linux-* dist/node_modules/@img`;
// Rewrite package.json "exports" field to point to the dist directory and use .js extension
const packageJsonPath = `dist/node_modules/${pkg}/package.json`;
const packageJson = await file(packageJsonPath).json();
for (const [key, value] of Object.entries(packageJson.exports) as [
string,
{ import?: string },
][]) {
if (value.import) {
packageJson.exports[key] = {
import: value.import
.replace("./", "./dist/")
.replace(/\.ts$/, ".js"),
};
}
}
await write(packageJsonPath, JSON.stringify(packageJson, null, 4));
}
// Copy the Bee Movie script from pages
await $`cp beemovie.txt dist/beemovie.txt`;
console.log("Built!");
console.log("Build complete!");

1739
bun.lock Normal file

File diff suppressed because it is too large Load diff

BIN
bun.lockb

Binary file not shown.

View file

@ -1,2 +1,8 @@
[install.scopes]
"@jsr" = "https://npm.jsr.io"
[test]
preload = ["./packages/tests/setup.ts"]
[install]
linker = "hoisted"

View file

@ -0,0 +1,17 @@
/**
* @packageDocumentation
* @module MediaManager/Utils
*/
import { SHA256 } from "bun";
/**
* Generates a SHA-256 hash for a given file.
* @param file - The file to hash.
* @returns A promise that resolves to the SHA-256 hash of the file in hex format.
*/
export const getMediaHash = async (file: File): Promise<string> => {
const arrayBuffer = await file.arrayBuffer();
const hash = new SHA256().update(arrayBuffer).digest("hex");
return hash;
};

View file

@ -0,0 +1,63 @@
import { describe, expect, it } from "bun:test";
import { mockModule } from "@versia-server/tests";
import sharp from "sharp";
import { calculateBlurhash } from "./blurhash.ts";
describe("BlurhashPreprocessor", () => {
it("should calculate blurhash for a valid image", async () => {
const inputBuffer = await sharp({
create: {
width: 100,
height: 100,
channels: 3,
background: { r: 255, g: 0, b: 0 },
},
})
.png()
.toBuffer();
const inputFile = new File([inputBuffer as BlobPart], "test.png", {
type: "image/png",
});
const result = await calculateBlurhash(inputFile);
expect(result).toBeTypeOf("string");
expect(result).not.toBe("");
});
it("should return null blurhash for an invalid image", async () => {
const invalidFile = new File(["invalid image data"], "invalid.png", {
type: "image/png",
});
const result = await calculateBlurhash(invalidFile);
expect(result).toBeNull();
});
it("should handle errors during blurhash calculation", async () => {
const inputBuffer = await sharp({
create: {
width: 100,
height: 100,
channels: 3,
background: { r: 255, g: 0, b: 0 },
},
})
.png()
.toBuffer();
const inputFile = new File([inputBuffer as BlobPart], "test.png", {
type: "image/png",
});
using __ = await mockModule("blurhash", () => ({
encode: (): void => {
throw new Error("Test error");
},
}));
const result = await calculateBlurhash(inputFile);
expect(result).toBeNull();
});
});

View file

@ -0,0 +1,37 @@
import { encode } from "blurhash";
import sharp from "sharp";
export const calculateBlurhash = async (file: File): Promise<string | null> => {
try {
const arrayBuffer = await file.arrayBuffer();
const metadata = await sharp(arrayBuffer).metadata();
return new Promise<string | null>((resolve) => {
sharp(arrayBuffer)
.raw()
.ensureAlpha()
.toBuffer((err, buffer) => {
if (err) {
resolve(null);
return;
}
try {
resolve(
encode(
new Uint8ClampedArray(buffer),
metadata?.width ?? 0,
metadata?.height ?? 0,
4,
4,
) as string,
);
} catch {
resolve(null);
}
});
});
} catch {
return null;
}
};

View file

@ -0,0 +1,134 @@
import { describe, expect, it } from "bun:test";
import sharp from "sharp";
import { convertImage } from "./image-conversion.ts";
describe("ImageConversionPreprocessor", () => {
it("should convert a JPEG image to WebP", async () => {
const inputBuffer = await sharp({
create: {
width: 100,
height: 100,
channels: 3,
background: { r: 255, g: 0, b: 0 },
},
})
.jpeg()
.toBuffer();
const inputFile = new File([inputBuffer as BlobPart], "test.jpg", {
type: "image/jpeg",
});
const result = await convertImage(inputFile, "image/webp");
expect(result.type).toBe("image/webp");
expect(result.name).toBe("test.webp");
const resultBuffer = await result.arrayBuffer();
const metadata = await sharp(resultBuffer).metadata();
expect(metadata.format).toBe("webp");
});
it("should not convert SVG when convert_vector is false", async () => {
const svgContent =
'<svg xmlns="http://www.w3.org/2000/svg"><rect width="100" height="100" fill="red"/></svg>';
const inputFile = new File([svgContent], "test.svg", {
type: "image/svg+xml",
});
const result = await convertImage(inputFile, "image/webp");
expect(result).toBe(inputFile);
});
it("should convert SVG when convert_vector is true", async () => {
const svgContent =
'<svg xmlns="http://www.w3.org/2000/svg"><rect width="100" height="100" fill="red"/></svg>';
const inputFile = new File([svgContent], "test.svg", {
type: "image/svg+xml",
});
const result = await convertImage(inputFile, "image/webp", {
convertVectors: true,
});
expect(result.type).toBe("image/webp");
expect(result.name).toBe("test.webp");
});
it("should not convert unsupported file types", async () => {
const inputFile = new File(["test content"], "test.txt", {
type: "text/plain",
});
const result = await convertImage(inputFile, "image/webp");
expect(result).toBe(inputFile);
});
it("should throw an error for unsupported output format", async () => {
const inputBuffer = await sharp({
create: {
width: 100,
height: 100,
channels: 3,
background: { r: 255, g: 0, b: 0 },
},
})
.png()
.toBuffer();
const inputFile = new File([inputBuffer as BlobPart], "test.png", {
type: "image/png",
});
await expect(convertImage(inputFile, "image/bmp")).rejects.toThrow(
"Unsupported output format: image/bmp",
);
});
it("should convert animated GIF to WebP while preserving animation", async () => {
// Create a simple animated GIF
const inputBuffer = await sharp({
create: {
width: 100,
height: 100,
channels: 4,
background: { r: 255, g: 0, b: 0, alpha: 1 },
},
})
.gif()
.toBuffer();
const inputFile = new File([inputBuffer as BlobPart], "animated.gif", {
type: "image/gif",
});
const result = await convertImage(inputFile, "image/webp");
expect(result.type).toBe("image/webp");
expect(result.name).toBe("animated.webp");
const resultBuffer = await result.arrayBuffer();
const metadata = await sharp(resultBuffer).metadata();
expect(metadata.format).toBe("webp");
});
it("should handle files with spaces in the name", async () => {
const inputBuffer = await sharp({
create: {
width: 100,
height: 100,
channels: 3,
background: { r: 255, g: 0, b: 0 },
},
})
.png()
.toBuffer();
const inputFile = new File(
[inputBuffer as BlobPart],
"test image with spaces.png",
{ type: "image/png" },
);
const result = await convertImage(inputFile, "image/webp");
expect(result.type).toBe("image/webp");
expect(result.name).toBe("test image with spaces.webp");
});
});

View file

@ -0,0 +1,109 @@
/**
* @packageDocumentation
* @module MediaManager/Preprocessors
*/
import sharp from "sharp";
/**
* Supported input media formats.
*/
const supportedInputFormats = [
"image/png",
"image/jpeg",
"image/webp",
"image/avif",
"image/svg+xml",
"image/gif",
"image/tiff",
];
/**
* Supported output media formats.
*/
const supportedOutputFormats = [
"image/jpeg",
"image/png",
"image/webp",
"image/avif",
"image/gif",
"image/tiff",
];
/**
* Checks if a file is convertible.
* @param file - The file to check.
* @returns True if the file is convertible, false otherwise.
*/
const isConvertible = (
file: File,
options?: { convertVectors?: boolean },
): boolean => {
if (file.type === "image/svg+xml" && !options?.convertVectors) {
return false;
}
return supportedInputFormats.includes(file.type);
};
/**
* Extracts the filename from a path.
* @param path - The path to extract the filename from.
* @returns The extracted filename.
*/
const extractFilenameFromPath = (path: string): string => {
const pathParts = path.split(/(?<!\\)\//);
return pathParts.at(-1) as string;
};
/**
* Replaces the file extension in the filename.
* @param fileName - The original filename.
* @param newExtension - The new extension.
* @returns The filename with the new extension.
*/
const getReplacedFileName = (fileName: string, newExtension: string): string =>
extractFilenameFromPath(fileName).replace(/\.[^/.]+$/, `.${newExtension}`);
/**
* Converts an image file to the format specified in the configuration.
*
* @param file - The image file to convert.
* @param targetFormat - The target format to convert to.
* @returns The converted image file.
*/
export const convertImage = async (
file: File,
targetFormat: string,
options?: {
convertVectors?: boolean;
},
): Promise<File> => {
if (!isConvertible(file, options)) {
return file;
}
if (!supportedOutputFormats.includes(targetFormat)) {
throw new Error(`Unsupported output format: ${targetFormat}`);
}
const sharpCommand = sharp(await file.arrayBuffer(), {
animated: true,
});
const commandName = targetFormat.split("/")[1] as
| "jpeg"
| "png"
| "webp"
| "avif"
| "gif"
| "tiff";
const convertedBuffer = await sharpCommand[commandName]().toBuffer();
return new File(
[convertedBuffer as BlobPart],
getReplacedFileName(file.name, commandName),
{
type: targetFormat,
lastModified: Date.now(),
},
);
};

1759
cli.ts

File diff suppressed because it is too large Load diff

36
cli/index.ts Normal file
View file

@ -0,0 +1,36 @@
import { completionsPlugin } from "@clerc/plugin-completions";
import { friendlyErrorPlugin } from "@clerc/plugin-friendly-error";
import { helpPlugin } from "@clerc/plugin-help";
import { notFoundPlugin } from "@clerc/plugin-not-found";
import { versionPlugin } from "@clerc/plugin-version";
import { setupDatabase } from "@versia-server/kit/db";
import { searchManager } from "@versia-server/kit/search";
import { Clerc } from "clerc";
import pkg from "../package.json" with { type: "json" };
import { rebuildIndexCommand } from "./index/rebuild.ts";
import { refetchInstanceCommand } from "./instance/refetch.ts";
import { createUserCommand } from "./user/create.ts";
import { deleteUserCommand } from "./user/delete.ts";
import { refetchUserCommand } from "./user/refetch.ts";
import { generateTokenCommand } from "./user/token.ts";
await setupDatabase(false);
await searchManager.connect(true);
Clerc.create()
.scriptName("cli")
.name("Versia Server CLI")
.description("CLI interface for Versia Server")
.version(pkg.version)
.use(helpPlugin())
.use(versionPlugin())
.use(completionsPlugin())
.use(notFoundPlugin())
.use(friendlyErrorPlugin())
.command(createUserCommand)
.command(deleteUserCommand)
.command(generateTokenCommand)
.command(refetchUserCommand)
.command(rebuildIndexCommand)
.command(refetchInstanceCommand)
.parse();

62
cli/index/rebuild.ts Normal file
View file

@ -0,0 +1,62 @@
import { config } from "@versia-server/config";
import { SonicIndexType, searchManager } from "@versia-server/kit/search";
// @ts-expect-error - Root import is required or the Clec type definitions won't work
// biome-ignore lint/correctness/noUnusedImports: Root import is required or the Clec type definitions won't work
import { defineCommand, type Root } from "clerc";
import ora from "ora";
export const rebuildIndexCommand = defineCommand(
{
name: "index rebuild",
description: "Rebuild the search index.",
parameters: ["<type>"],
flags: {
"batch-size": {
description: "Number of records to process at once",
type: Number,
alias: "b",
default: 100,
},
},
},
async (context) => {
const { "batch-size": batchSize } = context.flags;
const { type } = context.parameters;
if (!config.search.enabled) {
throw new Error(
"Search is not enabled in the instance configuration.",
);
}
const spinner = ora("Rebuilding search indexes").start();
switch (type) {
case "accounts":
await searchManager.rebuildSearchIndexes(
[SonicIndexType.Accounts],
batchSize,
(progress) => {
spinner.text = `Rebuilding search indexes (${(progress * 100).toFixed(2)}%)`;
},
);
break;
case "statuses":
await searchManager.rebuildSearchIndexes(
[SonicIndexType.Statuses],
batchSize,
(progress) => {
spinner.text = `Rebuilding search indexes (${(progress * 100).toFixed(2)}%)`;
},
);
break;
default: {
throw new Error(
"Invalid index type. Can be 'accounts' or 'statuses'.",
);
}
}
spinner.succeed("Search indexes rebuilt");
},
);

37
cli/instance/refetch.ts Normal file
View file

@ -0,0 +1,37 @@
import { Instance } from "@versia-server/kit/db";
import { FetchJobType, fetchQueue } from "@versia-server/kit/queues/fetch";
import { Instances } from "@versia-server/kit/tables";
import chalk from "chalk";
// @ts-expect-error - Root import is required or the Clec type definitions won't work
// biome-ignore lint/correctness/noUnusedImports: Root import is required or the Clec type definitions won't work
import { defineCommand, type Root } from "clerc";
import { eq } from "drizzle-orm";
export const refetchInstanceCommand = defineCommand(
{
name: "instance refetch",
description: "Refetches metadata from remote instances.",
parameters: ["<url_or_host>"],
},
async (context) => {
const { urlOrHost } = context.parameters;
const host = URL.canParse(urlOrHost)
? new URL(urlOrHost).host
: urlOrHost;
const instance = await Instance.fromSql(eq(Instances.baseUrl, host));
if (!instance) {
throw new Error(`Instance ${chalk.gray(host)} not found.`);
}
await fetchQueue.add(FetchJobType.Instance, {
uri: new URL(`https://${instance.data.baseUrl}`).origin,
});
console.info(
`Refresh job enqueued for ${chalk.gray(instance.data.baseUrl)}.`,
);
},
);

90
cli/user/create.ts Normal file
View file

@ -0,0 +1,90 @@
import { config } from "@versia-server/config";
import { User } from "@versia-server/kit/db";
import { searchManager } from "@versia-server/kit/search";
import { Users } from "@versia-server/kit/tables";
import chalk from "chalk";
// @ts-expect-error - Root import is required or the Clec type definitions won't work
// biome-ignore lint/correctness/noUnusedImports: Root import is required or the Clec type definitions won't work
import { defineCommand, type Root } from "clerc";
import { and, eq, isNull } from "drizzle-orm";
import { renderUnicodeCompact } from "uqr";
export const createUserCommand = defineCommand(
{
name: "user create",
description: "Create a new user.",
parameters: ["<username>"],
flags: {
password: {
description: "Password for the new user",
type: String,
alias: "p",
},
email: {
description: "Email for the new user",
type: String,
alias: "e",
},
admin: {
description: "Make the new user an admin",
type: Boolean,
alias: "a",
},
},
},
async (context) => {
const { admin, email, password } = context.flags;
const { username } = context.parameters;
if (!/^[a-z0-9_-]+$/.test(username)) {
throw new Error("Username must be alphanumeric and lowercase.");
}
// Check if user already exists
const existingUser = await User.fromSql(
and(eq(Users.username, username), isNull(Users.instanceId)),
);
if (existingUser) {
throw new Error(`User ${chalk.gray(username)} is taken.`);
}
const user = await User.register(username, {
email,
password,
isAdmin: admin,
});
// Add to search index
await searchManager.addUser(user);
if (!user) {
throw new Error("Failed to create user.");
}
console.info(`User ${chalk.gray(username)} created.`);
if (!password) {
const token = await user.resetPassword();
const link = new URL(
`${config.frontend.routes.password_reset}?${new URLSearchParams(
{
token,
},
)}`,
config.http.base_url,
);
console.info(`Password reset link for ${chalk.gray(username)}:`);
console.info(chalk.blue(link.href));
const qrcode = renderUnicodeCompact(link.href, {
border: 2,
});
// Pad all lines of QR code with spaces
console.info(`\n ${qrcode.replaceAll("\n", "\n ")}`);
}
},
);

60
cli/user/delete.ts Normal file
View file

@ -0,0 +1,60 @@
import confirm from "@inquirer/confirm";
import chalk from "chalk";
// @ts-expect-error - Root import is required or the Clec type definitions won't work
// biome-ignore lint/correctness/noUnusedImports: Root import is required or the Clec type definitions won't work
import { defineCommand, type Root } from "clerc";
import { retrieveUser } from "../utils.ts";
export const deleteUserCommand = defineCommand(
{
name: "user delete",
alias: "user rm",
description:
"Delete a user from the database. Can use username or handle.",
parameters: ["<username_or_handle>"],
flags: {
confirm: {
description: "Ask for confirmation before deleting the user",
type: Boolean,
alias: "c",
default: true,
},
},
},
async (context) => {
const { confirm: confirmFlag } = context.flags;
const { usernameOrHandle } = context.parameters;
const user = await retrieveUser(usernameOrHandle);
if (!user) {
throw new Error(`User ${chalk.gray(usernameOrHandle)} not found.`);
}
console.info(`About to delete user ${chalk.gray(user.data.username)}!`);
console.info(`Username: ${chalk.blue(user.data.username)}`);
console.info(`Display Name: ${chalk.blue(user.data.displayName)}`);
console.info(`Created At: ${chalk.blue(user.data.createdAt)}`);
console.info(
`Instance: ${chalk.blue(user.data.instance?.baseUrl || "Local")}`,
);
if (confirmFlag) {
const choice = await confirm({
message: `Are you sure you want to delete this user? ${chalk.red(
"This is irreversible.",
)}`,
});
if (!choice) {
throw new Error("Operation aborted.");
}
}
await user.delete();
console.info(
`User ${chalk.gray(user.data.username)} has been deleted.`,
);
},
);

43
cli/user/refetch.ts Normal file
View file

@ -0,0 +1,43 @@
import { User } from "@versia-server/kit/db";
import chalk from "chalk";
// @ts-expect-error - Root import is required or the Clec type definitions won't work
// biome-ignore lint/correctness/noUnusedImports: Root import is required or the Clec type definitions won't work
import { defineCommand, type Root } from "clerc";
import ora from "ora";
import { retrieveUser } from "../utils.ts";
export const refetchUserCommand = defineCommand(
{
name: "user refetch",
description: "Refetches user data from their remote instance.",
parameters: ["<handle>"],
},
async (context) => {
const { handle } = context.parameters;
const user = await retrieveUser(handle);
if (!user) {
throw new Error(`User ${chalk.gray(handle)} not found.`);
}
if (user.local) {
throw new Error(
"This user is local and as such cannot be refetched.",
);
}
const spinner = ora("Refetching user").start();
try {
await User.fromVersia(user.uri);
} catch (error) {
spinner.fail(
`Failed to refetch user ${chalk.gray(user.data.username)}`,
);
throw error;
}
spinner.succeed(`User ${chalk.gray(user.data.username)} refetched.`);
},
);

50
cli/user/token.ts Normal file
View file

@ -0,0 +1,50 @@
import { Client, Token } from "@versia-server/kit/db";
import { randomUUIDv7 } from "bun";
import chalk from "chalk";
// @ts-expect-error - Root import is required or the Clec type definitions won't work
// biome-ignore lint/correctness/noUnusedImports: Root import is required or the Clec type definitions won't work
import { defineCommand, type Root } from "clerc";
import { randomString } from "@/math.ts";
import { retrieveUser } from "../utils.ts";
export const generateTokenCommand = defineCommand(
{
name: "user token",
description: "Generates a new access token for a user.",
parameters: ["<username>"],
},
async (context) => {
const { username } = context.parameters;
const user = await retrieveUser(username);
if (!user) {
throw new Error(`User ${chalk.gray(username)} not found.`);
}
const application = await Client.insert({
id:
user.id +
Buffer.from(
crypto.getRandomValues(new Uint8Array(32)),
).toString("base64"),
name: "Versia",
redirectUris: [],
scopes: ["openid", "profile", "email"],
secret: "",
});
const token = await Token.insert({
id: randomUUIDv7(),
accessToken: randomString(64, "base64url"),
scopes: ["read", "write", "follow"],
userId: user.id,
clientId: application.id,
});
console.info(
`Token generated for user ${chalk.gray(user.data.username)}.`,
);
console.info(`Access Token: ${chalk.blue(token.data.accessToken)}`);
},
);

23
cli/utils.ts Normal file
View file

@ -0,0 +1,23 @@
import { Instance, User } from "@versia-server/kit/db";
import { parseUserAddress } from "@versia-server/kit/parsers";
import { Users } from "@versia-server/kit/tables";
import { and, eq, isNull } from "drizzle-orm";
export const retrieveUser = async (
usernameOrHandle: string,
): Promise<User | null> => {
const { username, domain } = parseUserAddress(usernameOrHandle);
const instance = domain ? await Instance.resolveFromHost(domain) : null;
const user = await User.fromSql(
and(
eq(Users.username, username),
instance
? eq(Users.instanceId, instance.data.id)
: isNull(Users.instanceId),
),
);
return user;
};

1
config/config Symbolic link
View file

@ -0,0 +1 @@
../config

View file

@ -1,76 +1,69 @@
# Lysand Config
# All of these values can be changed via the CLI (they will be saved in a file named config.internal.toml
# in the same directory as this one)
# Changing this file does not require a restart, but might take a few seconds to apply
# This file will be merged with the CLI configuration, taking precedence over it
# You can change the URL to the commit/tag you are using
#:schema https://raw.githubusercontent.com/versia-pub/server/main/config/config.schema.json
[database]
# Main PostgreSQL database connection
# All values marked as "sensitive" can be set to "PATH:/path/to/file" to read the value from a file (e.g. a secret manager)
[postgres]
# PostgreSQL database configuration
host = "localhost"
port = 5432
username = "lysand"
password = "lysand"
database = "lysand"
username = "versia"
# Sensitive value
password = "mycoolpassword"
database = "versia"
# Additional read-only replicas
# [[postgres.replicas]]
# host = "other-host"
# port = 5432
# username = "versia"
# password = "mycoolpassword2"
# database = "replica1"
[redis.queue]
# Redis instance for storing the federation queue
# A Redis database used for managing queues.
# Required for federation
host = "localhost"
port = 6379
password = ""
# Sensitive value
# password = "test"
database = 0
[redis.cache]
# Redis instance to be used as a timeline cache
# A Redis database used for caching SQL queries.
# Optional, can be the same as the queue instance
host = "localhost"
port = 6379
password = ""
database = 1
# [redis.cache]
# host = "localhost"
# port = 6380
# database = 1
# password = ""
# Search and indexing configuration
[search]
# Enable indexing and searching?
enabled = false
[meilisearch]
# If Meilisearch is not configured, search will not be enabled
host = "localhost"
port = 7700
api_key = "______________________________"
enabled = false
# Optional if search is disabled
# [search.sonic]
# host = "localhost"
# port = 7700
# Sensitive value
# password = "test"
[signups]
# URL of your Terms of Service
tos_url = "https://my-site.com/tos"
# Whether to enable registrations or not
registration = true
rules = [
"Do not harass others",
"Be nice to people",
"Don't spam",
"Don't post illegal content",
]
[oidc]
# Run Lysand with this value missing to generate a new key
jwt_key = ""
# Delete this section if you don't want to use custom OAuth providers
# This is an example configuration
# The provider MUST support OpenID Connect with .well-known discovery
# Most notably, GitHub does not support this
[[oidc.providers]]
# Test with custom Authentik instance
name = "CPlusPatch ID"
id = "cpluspatch-id"
url = "https://id.cpluspatch.com/application/o/lysand-testing/"
client_id = "______________________________"
client_secret = "__________________________________"
icon = "https://cpluspatch.com/images/icons/logo.svg"
[registration]
# Can users sign up freely?
allow = true
# NOT IMPLEMENTED
require_approval = false
# Message to show to users when registration is disabled
# message = "ran out of spoons to moderate registrations, sorry"
[http]
# The full URL Lysand will be reachable by (paths are not supported)
base_url = "https://lysand.social"
# Address to bind to
# URL that the instance will be accessible at
base_url = "https://example.com"
# Address to bind to (0.0.0.0 is suggested for proxies)
bind = "0.0.0.0"
bind_port = "8080"
bind_port = 8080
# Bans IPv4 or IPv6 IPs (wildcards, networks and ranges are supported)
banned_ips = []
@ -80,111 +73,105 @@ banned_user_agents = [
# "wget\/1.20.3",
]
[http.tls]
# If these values are set, Lysand will use these files for TLS
enabled = false
key = "config/privatekey.pem"
cert = "config/certificate.pem"
passphrase = ""
ca = ""
# URL to an eventual HTTP proxy
# Will be used for all outgoing requests
# proxy_address = "http://localhost:8118"
[http.bait]
# Enable the bait feature (sends fake data to those who are flagged)
enabled = false
# Path to file of bait data (if not provided, Lysand will send the entire Bee Movie script)
send_file = ""
# IPs to send bait data to (wildcards, networks and ranges are supported)
bait_ips = ["127.0.0.1", "::1"]
# User agents to send bait data to (regex format)
bait_user_agents = ["curl", "wget"]
# TLS configuration. You should probably be using a reverse proxy instead of this
# [http.tls]
# key = "/path/to/key.pem"
# cert = "/path/to/cert.pem"
# Sensitive value
# passphrase = "awawa"
# ca = "/path/to/ca.pem"
[frontend]
# Enable custom frontends (warning: not enabling this or Glitch will make Lysand only accessible via the Mastodon API)
# Frontends also control the OAuth flow, so if you disable this, you will need to use the Mastodon frontend
# Enable custom frontends (warning: not enabling this will make Versia Server only accessible via the Mastodon API)
# Frontends also control the OpenID flow, so if you disable this, you will need to use the Mastodon frontend
enabled = true
# The URL to reach the frontend at (should be on a local network)
url = "http://localhost:3000"
# Path that frontend files are served from
# Edit this property to serve custom frontends
# If this is not set, Versia Server will also check
# the VERSIA_FRONTEND_PATH environment variable
# path = ""
[frontend.glitch]
# Enable the Glitch frontend integration
enabled = false
# Glitch assets folder
assets = "glitch"
# Server the assets were ripped from (and any eventual CDNs)
server = ["https://glitch.social", "https://static.glitch.social"]
[frontend.routes]
# Special routes for your frontend, below are the defaults for Versia-FE
# Can be set to a route already used by Versia Server, as long as it is on a different HTTP method
# e.g. /oauth/authorize is a POST-only route, so you can serve a GET route at /oauth/authorize
# home = "/"
# login = "/oauth/authorize"
# consent = "/oauth/consent"
# register = "/register"
# password_reset = "/oauth/reset"
[smtp]
[frontend.settings]
# Arbitrary key/value pairs to be passed to the frontend
# This can be used to set up custom themes, etc on supported frontends.
# theme = "dark"
# NOT IMPLEMENTED
[email]
# Enable email sending
send_emails = false
# If send_emails is true, the following settings are required
# [email.smtp]
# SMTP server to use for sending emails
server = "smtp.example.com"
port = 465
username = "test@example.com"
password = "____________"
tls = true
# Disable all email functions (this will allow people to sign up without verifying
# their email)
enabled = false
# server = "smtp.example.com"
# port = 465
# username = "test@example.com"
# Sensitive value
# password = "password123"
# tls = true
[media]
# Can be "s3" or "local", where "local" uploads the file to the local filesystem
# If you need to change this value after setting up your instance, you must move all the files
# from one backend to the other manually (the CLI will have an option to do this later)
# TODO: Add CLI command to move files
backend = "local"
# Whether to check the hash of media when uploading to avoid duplication
deduplicate_media = true
# Changing this value will not retroactively apply to existing data
# Don't forget to fill in the s3 config :3
backend = "s3"
# If media backend is "local", this is the folder where the files will be stored
# Can be any path
local_uploads_folder = "uploads"
uploads_path = "uploads"
[media.conversion]
# Whether to automatically convert images to another format on upload
convert_images = false
# Can be: "jxl", "webp", "avif", "png", "jpg", "heif"
convert_images = true
# Can be: "image/jxl", "image/webp", "image/avif", "image/png", "image/jpeg", "image/heif", "image/gif"
# JXL support will likely not work
convert_to = "webp"
convert_to = "image/webp"
# Also convert SVG images?
convert_vectors = false
[s3]
# Can be left blank if you don't use the S3 media backend
endpoint = "myhostname.banana.com"
access_key = "_____________"
secret_access_key = "_________________"
region = ""
bucket_name = "lysand"
public_url = "https://cdn.test.com"
[email]
# Sends an email to moderators when a report is received
send_on_report = false
# Sends an email to moderators when a user is suspended
send_on_suspend = false
# Sends an email to moderators when a user is unsuspended
send_on_unsuspend = false
# Verify user emails when signing up (except via OIDC)
verify_email = false
# [s3]
# Can be left commented if you don't use the S3 media backend
# endpoint = "https://s3.example.com"
# Sensitive value
# access_key = "XXXXX"
# Sensitive value
# secret_access_key = "XXX"
# region = "us-east-1"
# bucket_name = "versia"
# public_url = "https://cdn.example.com"
# Adds a prefix to the uploaded files
# path = "versia"
# Use path-style URLs during upload (e.g. https://s3.example.com/versia)
# instead of the default virtual-hosted style (e.g. https://versia.s3.example.com)
# This is required for some S3-compatible services, such as MinIO
# path_style = true
[validation]
# Checks user data
# Does not retroactively apply to previously entered data
max_displayname_size = 50
max_bio_size = 160
max_note_size = 5000
max_avatar_size = 5_000_000
max_header_size = 5_000_000
max_media_size = 40_000_000
max_media_attachments = 10
max_media_description_size = 1000
max_poll_options = 20
max_poll_option_size = 500
min_poll_duration = 60
max_poll_duration = 1893456000
max_username_size = 30
max_field_count = 10
max_field_name_size = 1000
max_field_value_size = 1000
# Forbidden usernames, defaults are from Akkoma
username_blacklist = [
".well-known",
"~",
[validation.accounts]
max_displayname_characters = 50
max_username_characters = 30
max_bio_characters = 5000
max_avatar_bytes = 5_000_000
max_header_bytes = 5_000_000
# Regex is allowed here
disallowed_usernames = [
"well-known",
"about",
"activities",
"api",
@ -210,12 +197,14 @@ username_blacklist = [
"search",
"mfa",
]
# Whether to blacklist known temporary email providers
blacklist_tempmail = false
# Additional email providers to blacklist (list of domains)
email_blacklist = []
# Valid URL schemes, otherwise the URL is parsed as text
url_scheme_whitelist = [
max_field_count = 10
max_field_name_characters = 1000
max_field_value_characters = 1000
max_pinned_notes = 20
[validation.notes]
max_characters = 5000
allowed_url_schemes = [
"http",
"https",
"ftp",
@ -234,40 +223,71 @@ url_scheme_whitelist = [
"ssb",
"gemini",
]
# Only allow those MIME types of data to be uploaded
# This can easily be spoofed, but if it is spoofed it will appear broken
# to normal clients until despoofed
enforce_mime_types = false
allowed_mime_types = [
"image/jpeg",
"image/png",
"image/gif",
"image/heic",
"image/heif",
"image/webp",
"image/avif",
"video/webm",
"video/mp4",
"video/quicktime",
"video/ogg",
"audio/wave",
"audio/wav",
"audio/x-wav",
"audio/x-pn-wave",
"audio/vnd.wave",
"audio/ogg",
"audio/vorbis",
"audio/mpeg",
"audio/mp3",
"audio/webm",
"audio/flac",
"audio/aac",
"audio/m4a",
"audio/x-m4a",
"audio/mp4",
"audio/3gpp",
"video/x-ms-asf",
max_attachments = 16
[validation.media]
max_bytes = 40_000_000
max_description_characters = 1000
# An empty array allows all MIME types
allowed_mime_types = []
[validation.emojis]
max_bytes = 1_000_000
max_shortcode_characters = 100
max_description_characters = 1000
[validation.polls]
max_options = 20
max_option_characters = 500
min_duration_seconds = 60
# 100 days
max_duration_seconds = 8_640_000
[validation.emails]
# Blocks over 10,000 common tempmail domains
disallow_tempmail = false
# Regex is allowed here
disallowed_domains = []
# [validation.challenges]
# "Challenges" (aka captchas) are a way to verify that a user is human
# Versia Server's challenges use no external services, and are proof-of-work based
# This means that they do not require any user interaction, instead
# they require the user's computer to do a small amount of work
# The difficulty of the challenge, higher is will take more time to solve
# difficulty = 50000
# Challenge expiration time in seconds
# expiration = 300 # 5 minutes
# Leave this empty to generate a new key
# Sensitive value
# key = ""
# Block content that matches these regular expressions
[validation.filters]
note_content = [
# "(https?://)?(www\\.)?youtube\\.com/watch\\?v=[a-zA-Z0-9_-]+",
# "(https?://)?(www\\.)?youtu\\.be/[a-zA-Z0-9_-]+",
]
emoji_shortcode = []
username = []
displayname = []
bio = []
[notifications]
# Web Push Notifications configuration.
# Leave out to disable.
# [notifications.push]
# Subject field embedded in the push notification
# subject = "mailto:joe@example.com"
#
# [notifications.push.vapid_keys]
# VAPID keys for push notifications
# Run Versia Server with those values missing to generate new keys
# Sensitive value
# public = ""
# Sensitive value
# private = ""
[defaults]
# Default visibility for new notes
@ -276,17 +296,53 @@ allowed_mime_types = [
visibility = "public"
# Default language for new notes (ISO code)
language = "en"
# Default avatar, must be a valid URL or "" for a placeholder avatar
avatar = ""
# Default header, must be a valid URL or "" for none
header = ""
# Default avatar, must be a valid URL or left out for a placeholder avatar
# avatar = ""
# Default header, must be a valid URL or left out for none
# header = ""
# A style name from https://www.dicebear.com/styles
placeholder_style = "thumbs"
[queues]
# Controls the delivery queue (for outbound federation)
[queues.delivery]
# Time in seconds to remove completed jobs
remove_after_complete_seconds = 31536000
# Time in seconds to remove failed jobs
remove_after_failure_seconds = 31536000
# Controls the inbox processing queue (for inbound federation)
[queues.inbox]
# Time in seconds to remove completed jobs
remove_after_complete_seconds = 31536000
# Time in seconds to remove failed jobs
remove_after_failure_seconds = 31536000
# Controls the fetch queue (for remote data refreshes)
[queues.fetch]
# Time in seconds to remove completed jobs
remove_after_complete_seconds = 31536000
# Time in seconds to remove failed jobs
remove_after_failure_seconds = 31536000
# Controls the push queue (for push notification delivery)
[queues.push]
# Time in seconds to remove completed jobs
remove_after_complete_seconds = 31536000
# Time in seconds to remove failed jobs
remove_after_failure_seconds = 31536000
# Controls the media queue (for media processing)
[queues.media]
# Time in seconds to remove completed jobs
remove_after_complete_seconds = 31536000
# Time in seconds to remove failed jobs
remove_after_failure_seconds = 31536000
[federation]
# This is a list of domain names, such as "mastodon.social" or "pleroma.site"
# These changes will not retroactively apply to existing data before they were changed
# For that, please use the CLI
# For that, please use the CLI (in a later release)
# These instances will not be federated with
blocked = []
@ -306,59 +362,119 @@ reactions = []
banners = []
avatars = []
# For bridge software, such as versia-pub/activitypub
# Bridges must be hosted separately from the main Versia Server process
# [federation.bridge]
# Only versia-ap exists for now
# software = "versia-ap"
# If this is empty, any bridge with the correct token
# will be able to send data to your instance
# v4, v6, ranges and wildcards are supported
# allowed_ips = ["192.168.1.0/24"]
# Token for the bridge software
# Bridge must have the same token!
# Sensitive value
# token = "mycooltoken"
# url = "https://ap.versia.social"
[instance]
name = "Lysand"
description = "A test instance of Lysand"
# Path to a file containing a longer description of your instance
# This will be parsed as Markdown
extended_description_path = ""
# URL to your instance logo (jpg files should be renamed to jpeg)
logo = ""
# URL to your instance banner (jpg files should be renamed to jpeg)
banner = ""
name = "Versia"
description = "A Versia Server instance"
# Paths to instance long description, terms of service, and privacy policy
# These will be parsed as Markdown
#
# extended_description_path = "config/extended_description.md"
# tos_path = "config/tos.md"
# privacy_policy_path = "config/privacy_policy.md"
[filters]
# Regex filters for federated and local data
# Does not apply retroactively (try the CLI for that)
# Primary instance languages. ISO 639-1 codes.
languages = ["en"]
# Note contents
note_content = [
# "(https?://)?(www\\.)?youtube\\.com/watch\\?v=[a-zA-Z0-9_-]+",
# "(https?://)?(www\\.)?youtu\\.be/[a-zA-Z0-9_-]+",
]
emoji = []
# These will drop users matching the filters
username = []
displayname = []
bio = []
[instance.contact]
# email = "staff@yourinstance.com"
[instance.branding]
# logo = "https://cdn.example.com/logo.png"
# banner = "https://cdn.example.com/banner.png"
# Used for federation. If left empty or missing, the server will generate one for you.
# [instance.keys]
# Sensitive value
# public = ""
# Sensitive value
# private = ""
[[instance.rules]]
# Short description of the rule
text = "No hate speech"
# Longer version of the rule with additional information
hint = "Hate speech includes slurs, threats, and harassment."
[[instance.rules]]
text = "No spam"
# [[instance.rules]]
# ...etc
[permissions]
# Control default permissions for users
# Note that an anonymous user having a permission will not allow them
# to do things that require authentication (e.g. 'owner:notes' -> posting a note will need
# auth, but viewing a note will not)
# See https://server.versia.pub/api/roles#list-of-permissions for a list of all permissions
# Defaults to being able to login and manage their own content
# anonymous = []
# Defaults to identical to anonymous
# default = []
# Defaults to being able to manage all instance data, content, and users
# admin = []
[logging]
# Log all requests (warning: this is a lot of data)
log_requests = false
# Log request and their contents (warning: this is a lot of data)
log_requests_verbose = false
# Available levels: debug, info, warning, error, critical
log_level = "info"
# For GDPR compliance, you can disable logging of IPs
log_ip = false
# Log all filtered objects
log_filters = true
# Available levels: trace, debug, info, warning, error, fatal
log_level = "info" # For console output
[logging.storage]
# Path to logfile for requests
requests = "logs/requests.log"
# [logging.file]
# path = "logs/versia.log"
# log_level = "info"
#
# [logging.file.rotation]
# max_size = 10_000_000 # 10 MB
# max_files = 10 # Keep 10 rotated files
#
# https://sentry.io support
# [logging.sentry]
# dsn = "https://example.com"
# debug = false
# sample_rate = 1.0
# traces_sample_rate = 1.0
# Can also be regex
# trace_propagation_targets = []
# max_breadcrumbs = 100
# environment = "production"
# log_level = "info"
[ratelimits]
# These settings apply to every route at once
# Amount to multiply every route's duration by
duration_coeff = 1.0
# Amount to multiply every route's max requests per [duration] by
max_coeff = 1.0
[authentication]
# Run Versia Server with this value missing to generate a new key
# key = ""
[custom_ratelimits]
# Add in any API route in this style here
# Applies before the global ratelimit changes
"/api/v1/accounts/:id/block" = { duration = 30, max = 60 }
"/api/v1/timelines/public" = { duration = 60, max = 200 }
# The provider MUST support OpenID Connect with .well-known discovery
# Most notably, GitHub does not support this
# Redirect URLs in your OpenID provider can be set to this:
# <base_url>/oauth/sso/<provider_id>/callback*
# The asterisk is important, as it allows for any query parameters to be passed
# Authentik for example uses regex so it can be set to (regex):
# <base_url>/oauth/sso/<provider_id>/callback.*
# [[authentication.openid_providers]]
# name = "CPlusPatch ID"
# id = "cpluspatch-id"
# This MUST match the provider's issuer URI, including the trailing slash (or lack thereof)
# url = "https://id.cpluspatch.com/application/o/versia-testing/"
# client_id = "XXXX"
# Sensitive value
# client_secret = "XXXXX"
# icon = "https://cpluspatch.com/images/icons/logo.svg"

2851
config/config.schema.json Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,10 +0,0 @@
// import { Queue } from "bullmq";
/* const federationQueue = new Queue("federation", {
connection: {
host: config.redis.queue.host,
port: Number(config.redis.queue.port),
password: config.redis.queue.password || undefined,
db: config.redis.queue.database || undefined,
},
}); */

View file

@ -1,36 +0,0 @@
import type { InferSelectModel } from "drizzle-orm";
import { db } from "~drizzle/db";
import type { Applications } from "~drizzle/schema";
import type { Application as APIApplication } from "~types/mastodon/application";
export type Application = InferSelectModel<typeof Applications>;
/**
* Retrieves the application associated with the given access token.
* @param token The access token to retrieve the application for.
* @returns The application associated with the given access token, or null if no such application exists.
*/
export const getFromToken = async (
token: string,
): Promise<Application | null> => {
const result = await db.query.Tokens.findFirst({
where: (tokens, { eq }) => eq(tokens.accessToken, token),
with: {
application: true,
},
});
return result?.application || null;
};
/**
* Converts this application to an API application.
* @returns The API application representation of this application.
*/
export const applicationToAPI = (app: Application): APIApplication => {
return {
name: app.name,
website: app.website,
vapid_key: app.vapidKey,
};
};

View file

@ -1,121 +0,0 @@
import { proxyUrl } from "@response";
import type { Config } from "config-manager";
import type { InferSelectModel } from "drizzle-orm";
import type * as Lysand from "lysand-types";
import { MediaBackendType } from "media-manager";
import { db } from "~drizzle/db";
import { Attachments } from "~drizzle/schema";
import type { AsyncAttachment as APIAsyncAttachment } from "~types/mastodon/async_attachment";
import type { Attachment as APIAttachment } from "~types/mastodon/attachment";
export type Attachment = InferSelectModel<typeof Attachments>;
export const attachmentToAPI = (
attachment: Attachment,
): APIAsyncAttachment | APIAttachment => {
let type = "unknown";
if (attachment.mimeType.startsWith("image/")) {
type = "image";
} else if (attachment.mimeType.startsWith("video/")) {
type = "video";
} else if (attachment.mimeType.startsWith("audio/")) {
type = "audio";
}
return {
id: attachment.id,
type: type as "image" | "video" | "audio" | "unknown",
url: proxyUrl(attachment.url) ?? "",
remote_url: proxyUrl(attachment.remoteUrl),
preview_url: proxyUrl(attachment.thumbnailUrl || attachment.url),
text_url: null,
meta: {
width: attachment.width || undefined,
height: attachment.height || undefined,
fps: attachment.fps || undefined,
size:
attachment.width && attachment.height
? `${attachment.width}x${attachment.height}`
: undefined,
duration: attachment.duration || undefined,
length: attachment.size?.toString() || undefined,
aspect:
attachment.width && attachment.height
? attachment.width / attachment.height
: undefined,
original: {
width: attachment.width || undefined,
height: attachment.height || undefined,
size:
attachment.width && attachment.height
? `${attachment.width}x${attachment.height}`
: undefined,
aspect:
attachment.width && attachment.height
? attachment.width / attachment.height
: undefined,
},
// Idk whether size or length is the right value
},
description: attachment.description,
blurhash: attachment.blurhash,
};
};
export const attachmentToLysand = (
attachment: Attachment,
): Lysand.ContentFormat => {
return {
[attachment.mimeType]: {
content: attachment.url,
blurhash: attachment.blurhash ?? undefined,
description: attachment.description ?? undefined,
duration: attachment.duration ?? undefined,
fps: attachment.fps ?? undefined,
height: attachment.height ?? undefined,
size: attachment.size ?? undefined,
hash: attachment.sha256
? {
sha256: attachment.sha256,
}
: undefined,
width: attachment.width ?? undefined,
},
};
};
export const attachmentFromLysand = async (
attachmentToConvert: Lysand.ContentFormat,
): Promise<InferSelectModel<typeof Attachments>> => {
const key = Object.keys(attachmentToConvert)[0];
const value = attachmentToConvert[key];
const result = await db
.insert(Attachments)
.values({
mimeType: key,
url: value.content,
description: value.description || undefined,
duration: value.duration || undefined,
fps: value.fps || undefined,
height: value.height || undefined,
size: value.size || undefined,
width: value.width || undefined,
sha256: value.hash?.sha256 || undefined,
blurhash: value.blurhash || undefined,
})
.returning();
return result[0];
};
export const getUrl = (name: string, config: Config) => {
if (config.media.backend === MediaBackendType.LOCAL) {
return new URL(`/media/${name}`, config.http.base_url).toString();
}
if (config.media.backend === MediaBackendType.S3) {
return new URL(`/${name}`, config.s3.public_url).toString();
}
return "";
};

View file

@ -1,115 +0,0 @@
import { proxyUrl } from "@response";
import { type InferSelectModel, and, eq } from "drizzle-orm";
import type * as Lysand from "lysand-types";
import { db } from "~drizzle/db";
import { Emojis, Instances } from "~drizzle/schema";
import type { Emoji as APIEmoji } from "~types/mastodon/emoji";
import { addInstanceIfNotExists } from "./Instance";
export type EmojiWithInstance = InferSelectModel<typeof Emojis> & {
instance: InferSelectModel<typeof Instances> | null;
};
/**
* Used for parsing emojis from local text
* @param text The text to parse
* @returns An array of emojis
*/
export const parseEmojis = async (text: string) => {
const regex = /:[a-zA-Z0-9_]+:/g;
const matches = text.match(regex);
if (!matches) return [];
const emojis = await db.query.Emojis.findMany({
where: (emoji, { eq, or }) =>
or(
...matches
.map((match) => match.replace(/:/g, ""))
.map((match) => eq(emoji.shortcode, match)),
),
with: {
instance: true,
},
});
return emojis;
};
/**
* Gets an emoji from the database, and fetches it from the remote instance if it doesn't exist.
* @param emoji Emoji to fetch
* @param host Host to fetch the emoji from if remote
* @returns The emoji
*/
export const fetchEmoji = async (
emojiToFetch: Lysand.Emoji,
host?: string,
): Promise<EmojiWithInstance> => {
const existingEmoji = await db
.select()
.from(Emojis)
.innerJoin(Instances, eq(Emojis.instanceId, Instances.id))
.where(
and(
eq(Emojis.shortcode, emojiToFetch.name),
host ? eq(Instances.baseUrl, host) : undefined,
),
)
.limit(1);
if (existingEmoji[0])
return {
...existingEmoji[0].Emojis,
instance: existingEmoji[0].Instances,
};
const foundInstance = host ? await addInstanceIfNotExists(host) : null;
const result = (
await db
.insert(Emojis)
.values({
shortcode: emojiToFetch.name,
url: Object.entries(emojiToFetch.url)[0][1].content,
alt:
emojiToFetch.alt ||
Object.entries(emojiToFetch.url)[0][1].description ||
undefined,
contentType: Object.keys(emojiToFetch.url)[0],
visibleInPicker: true,
instanceId: foundInstance?.id,
})
.returning()
)[0];
return {
...result,
instance: foundInstance,
};
};
/**
* Converts the emoji to an APIEmoji object.
* @returns The APIEmoji object.
*/
export const emojiToAPI = (emoji: EmojiWithInstance): APIEmoji => {
return {
shortcode: emoji.shortcode,
static_url: proxyUrl(emoji.url) ?? "", // TODO: Add static version
url: proxyUrl(emoji.url) ?? "",
visible_in_picker: emoji.visibleInPicker,
category: undefined,
};
};
export const emojiToLysand = (emoji: EmojiWithInstance): Lysand.Emoji => {
return {
name: emoji.shortcode,
url: {
[emoji.contentType]: {
content: emoji.url,
description: emoji.alt || undefined,
},
},
alt: emoji.alt || undefined,
};
};

View file

@ -1,64 +0,0 @@
import { config } from "config-manager";
import type * as Lysand from "lysand-types";
import type { User } from "~packages/database-interface/user";
export const localObjectURI = (id: string) => `/objects/${id}`;
export const objectToInboxRequest = async (
object: Lysand.Entity,
author: User,
userToSendTo: User,
): Promise<Request> => {
if (userToSendTo.isLocal() || !userToSendTo.getUser().endpoints?.inbox) {
throw new Error("UserToSendTo has no inbox or is a local user");
}
if (author.isRemote()) {
throw new Error("Author is a remote user");
}
const privateKey = await crypto.subtle.importKey(
"pkcs8",
Buffer.from(author.getUser().privateKey ?? "", "base64"),
"Ed25519",
false,
["sign"],
);
const digest = await crypto.subtle.digest(
"SHA-256",
new TextEncoder().encode(JSON.stringify(object)),
);
const userInbox = new URL(userToSendTo.getUser().endpoints?.inbox ?? "");
const date = new Date();
const signature = await crypto.subtle.sign(
"Ed25519",
privateKey,
new TextEncoder().encode(
`(request-target): post ${userInbox.pathname}\n` +
`host: ${userInbox.host}\n` +
`date: ${date.toISOString()}\n` +
`digest: SHA-256=${Buffer.from(new Uint8Array(digest)).toString(
"base64",
)}\n`,
),
);
const signatureBase64 = Buffer.from(new Uint8Array(signature)).toString(
"base64",
);
return new Request(userInbox, {
method: "POST",
headers: {
"Content-Type": "application/json",
Date: date.toISOString(),
Origin: new URL(config.http.base_url).host,
Signature: `keyId="${author.getUri()}",algorithm="ed25519",headers="(request-target) host date digest",signature="${signatureBase64}"`,
},
body: JSON.stringify(object),
});
};

View file

@ -1,50 +0,0 @@
import type * as Lysand from "lysand-types";
import { db } from "~drizzle/db";
import { Instances } from "~drizzle/schema";
/**
* Represents an instance in the database.
*/
/**
* Adds an instance to the database if it doesn't already exist.
* @param url
* @returns Either the database instance if it already exists, or a newly created instance.
*/
export const addInstanceIfNotExists = async (url: string) => {
const origin = new URL(url).origin;
const host = new URL(url).host;
const found = await db.query.Instances.findFirst({
where: (instance, { eq }) => eq(instance.baseUrl, host),
});
if (found) return found;
console.log(`Fetching instance metadata for ${origin}`);
// Fetch the instance configuration
const metadata = (await fetch(new URL("/.well-known/lysand", origin)).then(
(res) => res.json(),
)) as Lysand.ServerMetadata;
if (metadata.type !== "ServerMetadata") {
throw new Error("Invalid instance metadata (wrong type)");
}
if (!(metadata.name && metadata.version)) {
throw new Error("Invalid instance metadata (missing name or version)");
}
return (
await db
.insert(Instances)
.values({
baseUrl: host,
name: metadata.name,
version: metadata.version,
logo: metadata.logo,
})
.returning()
)[0];
};

View file

@ -1,77 +0,0 @@
import { config } from "config-manager";
import { type InferSelectModel, and, eq } from "drizzle-orm";
import type * as Lysand from "lysand-types";
import { db } from "~drizzle/db";
import { Likes, Notifications } from "~drizzle/schema";
import type { Note } from "~packages/database-interface/note";
import type { User } from "~packages/database-interface/user";
export type Like = InferSelectModel<typeof Likes>;
/**
* Represents a Like entity in the database.
*/
export const likeToLysand = (like: Like): Lysand.Like => {
return {
id: like.id,
// biome-ignore lint/suspicious/noExplicitAny: to be rewritten
author: (like as any).liker?.uri,
type: "Like",
created_at: new Date(like.createdAt).toISOString(),
// biome-ignore lint/suspicious/noExplicitAny: to be rewritten
object: (like as any).liked?.uri,
uri: new URL(`/objects/${like.id}`, config.http.base_url).toString(),
};
};
/**
* Create a like
* @param user User liking the status
* @param note Status being liked
*/
export const createLike = async (user: User, note: Note) => {
await db.insert(Likes).values({
likedId: note.id,
likerId: user.id,
});
if (note.getAuthor().getUser().instanceId === user.getUser().instanceId) {
// Notify the user that their post has been favourited
await db.insert(Notifications).values({
accountId: user.id,
type: "favourite",
notifiedId: note.getAuthor().id,
noteId: note.id,
});
} else {
// TODO: Add database jobs for federating this
}
};
/**
* Delete a like
* @param user User deleting their like
* @param note Status being unliked
*/
export const deleteLike = async (user: User, note: Note) => {
await db
.delete(Likes)
.where(and(eq(Likes.likedId, note.id), eq(Likes.likerId, user.id)));
// Notify the user that their post has been favourited
await db
.delete(Notifications)
.where(
and(
eq(Notifications.accountId, user.id),
eq(Notifications.type, "favourite"),
eq(Notifications.notifiedId, note.getAuthor().id),
eq(Notifications.noteId, note.id),
),
);
if (user.isLocal() && note.getAuthor().isRemote()) {
// User is local, federate the delete
// TODO: Federate this
}
};

View file

@ -1,63 +0,0 @@
import type { InferSelectModel } from "drizzle-orm";
import { db } from "~drizzle/db";
import type { Notifications } from "~drizzle/schema";
import { Note } from "~packages/database-interface/note";
import { User } from "~packages/database-interface/user";
import type { Notification as APINotification } from "~types/mastodon/notification";
import type { StatusWithRelations } from "./Status";
import {
type UserWithRelations,
transformOutputToUserWithRelations,
userExtrasTemplate,
userRelations,
} from "./User";
export type Notification = InferSelectModel<typeof Notifications>;
export type NotificationWithRelations = Notification & {
status: StatusWithRelations | null;
account: UserWithRelations;
};
export const findManyNotifications = async (
query: Parameters<typeof db.query.Notifications.findMany>[0],
): Promise<NotificationWithRelations[]> => {
const output = await db.query.Notifications.findMany({
...query,
with: {
...query?.with,
account: {
with: {
...userRelations,
},
extras: userExtrasTemplate("Notifications_account"),
},
},
extras: {
...query?.extras,
},
});
return await Promise.all(
output.map(async (notif) => ({
...notif,
account: transformOutputToUserWithRelations(notif.account),
status: (await Note.fromId(notif.noteId))?.getStatus() ?? null,
})),
);
};
export const notificationToAPI = async (
notification: NotificationWithRelations,
): Promise<APINotification> => {
const account = new User(notification.account);
return {
account: account.toAPI(),
created_at: new Date(notification.createdAt).toISOString(),
id: notification.id,
type: notification.type,
status: notification.status
? await Note.fromStatus(notification.status).toAPI(account)
: undefined,
};
};

View file

@ -1,90 +0,0 @@
import type { InferSelectModel } from "drizzle-orm";
import type * as Lysand from "lysand-types";
import { db } from "~drizzle/db";
import { LysandObjects } from "~drizzle/schema";
import { findFirstUser } from "./User";
export type LysandObject = InferSelectModel<typeof LysandObjects>;
/**
* Represents a Lysand object in the database.
*/
export const createFromObject = async (
object: Lysand.Entity,
authorUri: string,
) => {
const foundObject = await db.query.LysandObjects.findFirst({
where: (o, { eq }) => eq(o.remoteId, object.id),
with: {
author: true,
},
});
if (foundObject) {
return foundObject;
}
const author = await findFirstUser({
where: (user, { eq }) => eq(user.uri, authorUri),
});
return await db.insert(LysandObjects).values({
authorId: author?.id,
createdAt: new Date(object.created_at).toISOString(),
extensions: object.extensions,
remoteId: object.id,
type: object.type,
uri: object.uri,
// Rest of data (remove id, author, created_at, extensions, type, uri)
extraData: Object.fromEntries(
Object.entries(object).filter(
([key]) =>
![
"id",
"author",
"created_at",
"extensions",
"type",
"uri",
].includes(key),
),
),
});
};
export const toLysand = (lyObject: LysandObject): Lysand.Entity => {
return {
id: lyObject.remoteId || lyObject.id,
created_at: new Date(lyObject.createdAt).toISOString(),
type: lyObject.type,
uri: lyObject.uri,
...(lyObject.extraData as object),
// @ts-expect-error Assume stored JSON is valid
extensions: lyObject.extensions as object,
};
};
export const isPublication = (lyObject: LysandObject): boolean => {
return lyObject.type === "Note" || lyObject.type === "Patch";
};
export const isAction = (lyObject: LysandObject): boolean => {
return [
"Like",
"Follow",
"Dislike",
"FollowAccept",
"FollowReject",
"Undo",
"Announce",
].includes(lyObject.type);
};
export const isActor = (lyObject: LysandObject): boolean => {
return lyObject.type === "User";
};
export const isExtension = (lyObject: LysandObject): boolean => {
return lyObject.type === "Extension";
};

View file

@ -1,123 +0,0 @@
import { config } from "config-manager";
// import { Worker } from "bullmq";
/* export const federationWorker = new Worker(
"federation",
async job => {
await job.updateProgress(0);
switch (job.name) {
case "federation": {
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
const statusId = job.data.id as string;
const status = await client.status.findUnique({
where: { id: statusId },
include: statusAndUserRelations,
});
if (!status) return;
// Only get remote users that follow the author of the status, and the remote mentioned users
const peopleToSendTo = await client.user.findMany({
where: {
OR: [
["public", "unlisted", "private"].includes(
status.visibility
)
? {
relationships: {
some: {
subjectId: status.authorId,
following: true,
},
},
instanceId: {
not: null,
},
}
: {},
// Mentioned users
{
id: {
in: status.mentions.map(m => m.id),
},
instanceId: {
not: null,
},
},
],
},
});
let peopleDone = 0;
// Spawn sendToServer job for each user
for (const person of peopleToSendTo) {
await federationQueue.add("sendToServer", {
id: statusId,
user: person,
});
peopleDone++;
await job.updateProgress(
Math.round((peopleDone / peopleToSendTo.length) * 100)
);
}
break;
}
case "sendToServer": {
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
const statusId = job.data.id as string;
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
const user = job.data.user as User;
const status = await client.status.findUnique({
where: { id: statusId },
include: statusAndUserRelations,
});
if (!status) return;
const response = await federateStatusTo(
status,
status.author,
user
);
if (response.status !== 200) {
throw new Error(
`Federation error: ${response.status} ${response.statusText}`
);
}
break;
}
}
await job.updateProgress(100);
return true;
},
{
connection: {
host: config.redis.queue.host,
port: config.redis.queue.port,
password: config.redis.queue.password,
db: config.redis.queue.database || undefined,
},
removeOnComplete: {
count: 400,
},
removeOnFail: {
count: 3000,
},
}
); */
export const addStatusFederationJob = async (statusId: string) => {
/* await federationQueue.add("federation", {
id: statusId,
}); */
};

View file

@ -1,88 +0,0 @@
import type { InferSelectModel } from "drizzle-orm";
import { db } from "~drizzle/db";
import { Relationships } from "~drizzle/schema";
import type { User } from "~packages/database-interface/user";
import type { Relationship as APIRelationship } from "~types/mastodon/relationship";
import type { UserType } from "./User";
export type Relationship = InferSelectModel<typeof Relationships>;
/**
* Creates a new relationship between two users.
* @param owner The user who owns the relationship.
* @param other The user who is the subject of the relationship.
* @returns The newly created relationship.
*/
export const createNewRelationship = async (
owner: User,
other: User,
): Promise<Relationship> => {
return (
await db
.insert(Relationships)
.values({
ownerId: owner.id,
subjectId: other.id,
languages: [],
following: false,
showingReblogs: false,
notifying: false,
followedBy: false,
blocking: false,
blockedBy: false,
muting: false,
mutingNotifications: false,
requested: false,
domainBlocking: false,
endorsed: false,
note: "",
updatedAt: new Date().toISOString(),
})
.returning()
)[0];
};
export const checkForBidirectionalRelationships = async (
user1: User,
user2: User,
createIfNotExists = true,
): Promise<boolean> => {
const relationship1 = await db.query.Relationships.findFirst({
where: (rel, { and, eq }) =>
and(eq(rel.ownerId, user1.id), eq(rel.subjectId, user2.id)),
});
const relationship2 = await db.query.Relationships.findFirst({
where: (rel, { and, eq }) =>
and(eq(rel.ownerId, user2.id), eq(rel.subjectId, user1.id)),
});
if (!relationship1 && !relationship2 && createIfNotExists) {
await createNewRelationship(user1, user2);
await createNewRelationship(user2, user1);
}
return !!relationship1 && !!relationship2;
};
/**
* Converts the relationship to an API-friendly format.
* @returns The API-friendly relationship.
*/
export const relationshipToAPI = (rel: Relationship): APIRelationship => {
return {
blocked_by: rel.blockedBy,
blocking: rel.blocking,
domain_blocking: rel.domainBlocking,
endorsed: rel.endorsed,
followed_by: rel.followedBy,
following: rel.following,
id: rel.subjectId,
muting: rel.muting,
muting_notifications: rel.mutingNotifications,
notifying: rel.notifying,
requested: rel.requested,
showing_reblogs: rel.showingReblogs,
note: rel.note,
};
};

View file

@ -1,656 +0,0 @@
import markdownItTaskLists from "@hackmd/markdown-it-task-lists";
import { dualLogger } from "@loggers";
import { sanitizeHtml } from "@sanitization";
import { config } from "config-manager";
import {
type InferSelectModel,
and,
eq,
inArray,
isNull,
or,
sql,
} from "drizzle-orm";
import linkifyHtml from "linkify-html";
import type * as Lysand from "lysand-types";
import {
anyOf,
charIn,
createRegExp,
digit,
exactly,
global,
letter,
maybe,
oneOrMore,
} from "magic-regexp";
import MarkdownIt from "markdown-it";
import markdownItAnchor from "markdown-it-anchor";
import markdownItContainer from "markdown-it-container";
import markdownItTocDoneRight from "markdown-it-toc-done-right";
import { db } from "~drizzle/db";
import { type Attachments, Instances, Notes, Users } from "~drizzle/schema";
import { Note } from "~packages/database-interface/note";
import { User } from "~packages/database-interface/user";
import { LogLevel } from "~packages/log-manager";
import type { Status as APIStatus } from "~types/mastodon/status";
import type { Application } from "./Application";
import { attachmentFromLysand } from "./Attachment";
import { type EmojiWithInstance, fetchEmoji } from "./Emoji";
import { objectToInboxRequest } from "./Federation";
import type { Like } from "./Like";
import {
type UserType,
type UserWithInstance,
type UserWithRelations,
resolveWebFinger,
transformOutputToUserWithRelations,
userExtrasTemplate,
userRelations,
} from "./User";
export type Status = InferSelectModel<typeof Notes>;
export type StatusWithRelations = Status & {
author: UserWithRelations;
mentions: UserWithInstance[];
attachments: InferSelectModel<typeof Attachments>[];
reblog: StatusWithoutRecursiveRelations | null;
emojis: EmojiWithInstance[];
likes: Like[];
reply: Status | null;
quote: Status | null;
application: Application | null;
reblogCount: number;
likeCount: number;
replyCount: number;
};
export type StatusWithoutRecursiveRelations = Omit<
StatusWithRelations,
"reply" | "quote" | "reblog"
>;
export const noteExtras = {
reblogCount:
sql`(SELECT COUNT(*) FROM "Notes" WHERE "Notes"."reblogId" = "Notes".id)`.as(
"reblog_count",
),
likeCount:
sql`(SELECT COUNT(*) FROM "Likes" WHERE "Likes"."likedId" = "Notes".id)`.as(
"like_count",
),
replyCount:
sql`(SELECT COUNT(*) FROM "Notes" WHERE "Notes"."replyId" = "Notes".id)`.as(
"reply_count",
),
};
/**
* Wrapper against the Status object to make it easier to work with
* @param query
* @returns
*/
export const findManyNotes = async (
query: Parameters<typeof db.query.Notes.findMany>[0],
): Promise<StatusWithRelations[]> => {
const output = await db.query.Notes.findMany({
...query,
with: {
...query?.with,
attachments: {
where: (attachment, { eq }) =>
eq(attachment.noteId, sql`"Notes"."id"`),
},
emojis: {
with: {
emoji: {
with: {
instance: true,
},
},
},
},
author: {
with: {
...userRelations,
},
extras: userExtrasTemplate("Notes_author"),
},
mentions: {
with: {
user: {
with: {
instance: true,
},
},
},
},
reblog: {
with: {
attachments: true,
emojis: {
with: {
emoji: {
with: {
instance: true,
},
},
},
},
likes: true,
application: true,
mentions: {
with: {
user: {
with: userRelations,
extras: userExtrasTemplate(
"Notes_reblog_mentions_user",
),
},
},
},
author: {
with: {
...userRelations,
},
extras: userExtrasTemplate("Notes_reblog_author"),
},
},
extras: {
...noteExtras,
},
},
reply: true,
quote: true,
},
extras: {
...noteExtras,
...query?.extras,
},
});
return output.map((post) => ({
...post,
author: transformOutputToUserWithRelations(post.author),
mentions: post.mentions.map((mention) => ({
...mention.user,
endpoints: mention.user.endpoints,
})),
emojis: (post.emojis ?? []).map((emoji) => emoji.emoji),
reblog: post.reblog && {
...post.reblog,
author: transformOutputToUserWithRelations(post.reblog.author),
mentions: post.reblog.mentions.map((mention) => ({
...mention.user,
endpoints: mention.user.endpoints,
})),
emojis: (post.reblog.emojis ?? []).map((emoji) => emoji.emoji),
reblogCount: Number(post.reblog.reblogCount),
likeCount: Number(post.reblog.likeCount),
replyCount: Number(post.reblog.replyCount),
},
reblogCount: Number(post.reblogCount),
likeCount: Number(post.likeCount),
replyCount: Number(post.replyCount),
}));
};
export const findFirstNote = async (
query: Parameters<typeof db.query.Notes.findFirst>[0],
): Promise<StatusWithRelations | null> => {
const output = await db.query.Notes.findFirst({
...query,
with: {
...query?.with,
attachments: {
where: (attachment, { eq }) =>
eq(attachment.noteId, sql`"Notes"."id"`),
},
emojis: {
with: {
emoji: {
with: {
instance: true,
},
},
},
},
author: {
with: {
...userRelations,
},
extras: userExtrasTemplate("Notes_author"),
},
mentions: {
with: {
user: {
with: {
instance: true,
},
},
},
},
reblog: {
with: {
attachments: true,
emojis: {
with: {
emoji: {
with: {
instance: true,
},
},
},
},
likes: true,
application: true,
mentions: {
with: {
user: {
with: userRelations,
extras: userExtrasTemplate(
"Notes_reblog_mentions_user",
),
},
},
},
author: {
with: {
...userRelations,
},
extras: userExtrasTemplate("Notes_reblog_author"),
},
},
extras: {
...noteExtras,
},
},
reply: true,
quote: true,
},
extras: {
...noteExtras,
...query?.extras,
},
});
if (!output) return null;
return {
...output,
author: transformOutputToUserWithRelations(output.author),
mentions: output.mentions.map((mention) => ({
...mention.user,
endpoints: mention.user.endpoints,
})),
emojis: (output.emojis ?? []).map((emoji) => emoji.emoji),
reblog: output.reblog && {
...output.reblog,
author: transformOutputToUserWithRelations(output.reblog.author),
mentions: output.reblog.mentions.map((mention) => ({
...mention.user,
endpoints: mention.user.endpoints,
})),
emojis: (output.reblog.emojis ?? []).map((emoji) => emoji.emoji),
reblogCount: Number(output.reblog.reblogCount),
likeCount: Number(output.reblog.likeCount),
replyCount: Number(output.reblog.replyCount),
},
reblogCount: Number(output.reblogCount),
likeCount: Number(output.likeCount),
replyCount: Number(output.replyCount),
};
};
export const resolveNote = async (
uri?: string,
providedNote?: Lysand.Note,
): Promise<Note> => {
if (!uri && !providedNote) {
throw new Error("No URI or note provided");
}
const foundStatus = await Note.fromSql(
eq(Notes.uri, uri ?? providedNote?.uri ?? ""),
);
if (foundStatus) return foundStatus;
let note: Lysand.Note | null = providedNote ?? null;
if (uri) {
if (!URL.canParse(uri)) {
throw new Error(`Invalid URI to parse ${uri}`);
}
const response = await fetch(uri, {
method: "GET",
headers: {
Accept: "application/json",
},
});
note = (await response.json()) as Lysand.Note;
}
if (!note) {
throw new Error("No note was able to be fetched");
}
if (note.type !== "Note") {
throw new Error("Invalid object type");
}
if (!note.author) {
throw new Error("Invalid object author");
}
const author = await User.resolve(note.author);
if (!author) {
throw new Error("Invalid object author");
}
const attachments = [];
for (const attachment of note.attachments ?? []) {
const resolvedAttachment = await attachmentFromLysand(attachment).catch(
(e) => {
dualLogger.logError(
LogLevel.ERROR,
"Federation.StatusResolver",
e,
);
return null;
},
);
if (resolvedAttachment) {
attachments.push(resolvedAttachment);
}
}
const emojis = [];
for (const emoji of note.extensions?.["org.lysand:custom_emojis"]?.emojis ??
[]) {
const resolvedEmoji = await fetchEmoji(emoji).catch((e) => {
dualLogger.logError(LogLevel.ERROR, "Federation.StatusResolver", e);
return null;
});
if (resolvedEmoji) {
emojis.push(resolvedEmoji);
}
}
const createdNote = await Note.fromData(
author,
note.content ?? {
"text/plain": {
content: "",
},
},
note.visibility as APIStatus["visibility"],
note.is_sensitive ?? false,
note.subject ?? "",
emojis,
note.uri,
await Promise.all(
(note.mentions ?? [])
.map((mention) => User.resolve(mention))
.filter((mention) => mention !== null) as Promise<User>[],
),
attachments.map((a) => a.id),
note.replies_to
? (await resolveNote(note.replies_to)).getStatus().id
: undefined,
note.quotes
? (await resolveNote(note.quotes)).getStatus().id
: undefined,
);
if (!createdNote) {
throw new Error("Failed to create status");
}
return createdNote;
};
export const createMentionRegExp = () =>
createRegExp(
exactly("@"),
oneOrMore(anyOf(letter.lowercase, digit, charIn("-"))).groupedAs(
"username",
),
maybe(
exactly("@"),
oneOrMore(anyOf(letter, digit, charIn("_-.:"))).groupedAs("domain"),
),
[global],
);
/**
* Get people mentioned in the content (match @username or @username@domain.com mentions)
* @param text The text to parse mentions from.
* @returns An array of users mentioned in the text.
*/
export const parseTextMentions = async (text: string): Promise<User[]> => {
const mentionedPeople = [...text.matchAll(createMentionRegExp())] ?? [];
if (mentionedPeople.length === 0) return [];
const baseUrlHost = new URL(config.http.base_url).host;
const isLocal = (host?: string) => host === baseUrlHost || !host;
const foundUsers = await db
.select({
id: Users.id,
username: Users.username,
baseUrl: Instances.baseUrl,
})
.from(Users)
.leftJoin(Instances, eq(Users.instanceId, Instances.id))
.where(
or(
...mentionedPeople.map((person) =>
and(
eq(Users.username, person?.[1] ?? ""),
isLocal(person?.[2])
? isNull(Users.instanceId)
: eq(Instances.baseUrl, person?.[2] ?? ""),
),
),
),
);
const notFoundRemoteUsers = mentionedPeople.filter(
(person) =>
!isLocal(person?.[2]) &&
!foundUsers.find(
(user) =>
user.username === person?.[1] &&
user.baseUrl === person?.[2],
),
);
const finalList =
foundUsers.length > 0
? await User.manyFromSql(
inArray(
Users.id,
foundUsers.map((u) => u.id),
),
)
: [];
// Attempt to resolve mentions that were not found
for (const person of notFoundRemoteUsers) {
const user = await resolveWebFinger(
person?.[1] ?? "",
person?.[2] ?? "",
);
if (user) {
finalList.push(user);
}
}
return finalList;
};
export const replaceTextMentions = async (text: string, mentions: User[]) => {
let finalText = text;
for (const mention of mentions) {
const user = mention.getUser();
// Replace @username and @username@domain
if (user.instance) {
finalText = finalText.replace(
createRegExp(
exactly(`@${user.username}@${user.instance.baseUrl}`),
[global],
),
`<a class="u-url mention" rel="nofollow noopener noreferrer" target="_blank" href="${mention.getUri()}">@${
user.username
}@${user.instance.baseUrl}</a>`,
);
} else {
finalText = finalText.replace(
// Only replace @username if it doesn't have another @ right after
createRegExp(
exactly(`@${user.username}`)
.notBefore(anyOf(letter, digit, charIn("@")))
.notAfter(anyOf(letter, digit, charIn("@"))),
[global],
),
`<a class="u-url mention" rel="nofollow noopener noreferrer" target="_blank" href="${mention.getUri()}">@${
user.username
}</a>`,
);
finalText = finalText.replace(
createRegExp(
exactly(
`@${user.username}@${
new URL(config.http.base_url).host
}`,
),
[global],
),
`<a class="u-url mention" rel="nofollow noopener noreferrer" target="_blank" href="${mention.getUri()}">@${
user.username
}</a>`,
);
}
}
return finalText;
};
export const contentToHtml = async (
content: Lysand.ContentFormat,
mentions: User[] = [],
): Promise<string> => {
let htmlContent: string;
if (content["text/html"]) {
htmlContent = await sanitizeHtml(content["text/html"].content);
} else if (content["text/markdown"]) {
htmlContent = await sanitizeHtml(
await markdownParse(content["text/markdown"].content),
);
} else if (content["text/plain"]?.content) {
// Split by newline and add <p> tags
htmlContent = (await sanitizeHtml(content["text/plain"].content))
.split("\n")
.map((line) => `<p>${line}</p>`)
.join("\n");
} else {
htmlContent = "";
}
// Replace mentions text
htmlContent = await replaceTextMentions(htmlContent, mentions ?? []);
// Linkify
htmlContent = linkifyHtml(htmlContent, {
defaultProtocol: "https",
validate: {
email: () => false,
},
target: "_blank",
rel: "nofollow noopener noreferrer",
});
return htmlContent;
};
export const markdownParse = async (content: string) => {
return (await getMarkdownRenderer()).render(content);
};
export const getMarkdownRenderer = async () => {
const renderer = MarkdownIt({
html: true,
linkify: true,
});
renderer.use(markdownItAnchor, {
permalink: markdownItAnchor.permalink.ariaHidden({
symbol: "",
placement: "before",
}),
});
renderer.use(markdownItTocDoneRight, {
containerClass: "toc",
level: [1, 2, 3, 4],
listType: "ul",
listClass: "toc-list",
itemClass: "toc-item",
linkClass: "toc-link",
});
renderer.use(markdownItTaskLists);
renderer.use(markdownItContainer);
return renderer;
};
export const federateNote = async (note: Note) => {
for (const user of await note.getUsersToFederateTo()) {
// TODO: Add queue system
const request = await objectToInboxRequest(
note.toLysand(),
note.getAuthor(),
user,
);
// Send request
const response = await fetch(request);
if (!response.ok) {
dualLogger.log(
LogLevel.DEBUG,
"Federation.Status",
await response.text(),
);
dualLogger.log(
LogLevel.ERROR,
"Federation.Status",
`Failed to federate status ${
note.getStatus().id
} to ${user.getUri()}`,
);
}
}
};
export const isFavouritedBy = async (status: Status, user: UserType) => {
return !!(await db.query.Likes.findFirst({
where: (like, { and, eq }) =>
and(eq(like.likerId, user.id), eq(like.likedId, status.id)),
}));
};

View file

@ -1,11 +0,0 @@
import type { InferSelectModel } from "drizzle-orm";
import type { Tokens } from "~drizzle/schema";
/**
* The type of token.
*/
export enum TokenType {
BEARER = "Bearer",
}
export type Token = InferSelectModel<typeof Tokens>;

View file

@ -1,550 +0,0 @@
import { dualLogger } from "@loggers";
import { addUserToMeilisearch } from "@meilisearch";
import { config } from "config-manager";
import { type InferSelectModel, and, eq, inArray, sql } from "drizzle-orm";
import type * as Lysand from "lysand-types";
import { db } from "~drizzle/db";
import {
Applications,
Instances,
Notifications,
Relationships,
Tokens,
Users,
} from "~drizzle/schema";
import { User } from "~packages/database-interface/user";
import { LogLevel } from "~packages/log-manager";
import type { Application } from "./Application";
import type { EmojiWithInstance } from "./Emoji";
import { objectToInboxRequest } from "./Federation";
import { createNewRelationship } from "./Relationship";
import type { Token } from "./Token";
export type UserType = InferSelectModel<typeof Users>;
export type UserWithInstance = UserType & {
instance: InferSelectModel<typeof Instances> | null;
};
export type UserWithRelations = UserType & {
instance: InferSelectModel<typeof Instances> | null;
emojis: EmojiWithInstance[];
followerCount: number;
followingCount: number;
statusCount: number;
};
export type UserWithRelationsAndRelationships = UserWithRelations & {
relationships: InferSelectModel<typeof Relationships>[];
relationshipSubjects: InferSelectModel<typeof Relationships>[];
};
export const userRelations: {
instance: true;
emojis: {
with: {
emoji: {
with: {
instance: true;
};
};
};
};
} = {
instance: true,
emojis: {
with: {
emoji: {
with: {
instance: true,
},
},
},
},
};
export const userExtras = {
followerCount:
sql`(SELECT COUNT(*) FROM "Relationships" "relationships" WHERE ("relationships"."ownerId" = "Users".id AND "relationships"."following" = true))`.as(
"follower_count",
),
followingCount:
sql`(SELECT COUNT(*) FROM "Relationships" "relationshipSubjects" WHERE ("relationshipSubjects"."subjectId" = "Users".id AND "relationshipSubjects"."following" = true))`.as(
"following_count",
),
statusCount:
sql`(SELECT COUNT(*) FROM "Notes" WHERE "Notes"."authorId" = "Users".id)`.as(
"status_count",
),
};
export const userExtrasTemplate = (name: string) => ({
// @ts-ignore
followerCount: sql([
`(SELECT COUNT(*) FROM "Relationships" "relationships" WHERE ("relationships"."ownerId" = "${name}".id AND "relationships"."following" = true))`,
]).as("follower_count"),
// @ts-ignore
followingCount: sql([
`(SELECT COUNT(*) FROM "Relationships" "relationshipSubjects" WHERE ("relationshipSubjects"."subjectId" = "${name}".id AND "relationshipSubjects"."following" = true))`,
]).as("following_count"),
// @ts-ignore
statusCount: sql([
`(SELECT COUNT(*) FROM "Notes" WHERE "Notes"."authorId" = "${name}".id)`,
]).as("status_count"),
});
export interface AuthData {
user: User | null;
token: string;
application: Application | null;
}
export const getFromRequest = async (req: Request): Promise<AuthData> => {
// Check auth token
const token = req.headers.get("Authorization")?.split(" ")[1] || "";
const { user, application } =
await retrieveUserAndApplicationFromToken(token);
return { user, token, application };
};
export const getFromHeader = async (value: string): Promise<AuthData> => {
const token = value.split(" ")[1];
const { user, application } =
await retrieveUserAndApplicationFromToken(token);
return { user, token, application };
};
export const followRequestUser = async (
follower: User,
followee: User,
relationshipId: string,
reblogs = false,
notify = false,
languages: string[] = [],
): Promise<InferSelectModel<typeof Relationships>> => {
const isRemote = followee.isRemote();
const updatedRelationship = (
await db
.update(Relationships)
.set({
following: isRemote ? false : !followee.getUser().isLocked,
requested: isRemote ? true : followee.getUser().isLocked,
showingReblogs: reblogs,
notifying: notify,
languages: languages,
})
.where(eq(Relationships.id, relationshipId))
.returning()
)[0];
if (isRemote) {
// Federate
// TODO: Make database job
const request = await objectToInboxRequest(
followRequestToLysand(follower, followee),
follower,
followee,
);
// Send request
const response = await fetch(request);
if (!response.ok) {
dualLogger.log(
LogLevel.DEBUG,
"Federation.FollowRequest",
await response.text(),
);
dualLogger.log(
LogLevel.ERROR,
"Federation.FollowRequest",
`Failed to federate follow request from ${
follower.id
} to ${followee.getUri()}`,
);
return (
await db
.update(Relationships)
.set({
following: false,
requested: false,
})
.where(eq(Relationships.id, relationshipId))
.returning()
)[0];
}
} else {
await db.insert(Notifications).values({
accountId: follower.id,
type: followee.getUser().isLocked ? "follow_request" : "follow",
notifiedId: followee.id,
});
}
return updatedRelationship;
};
export const sendFollowAccept = async (follower: User, followee: User) => {
// TODO: Make database job
const request = await objectToInboxRequest(
followAcceptToLysand(follower, followee),
followee,
follower,
);
// Send request
const response = await fetch(request);
if (!response.ok) {
dualLogger.log(
LogLevel.DEBUG,
"Federation.FollowAccept",
await response.text(),
);
dualLogger.log(
LogLevel.ERROR,
"Federation.FollowAccept",
`Failed to federate follow accept from ${
followee.id
} to ${follower.getUri()}`,
);
}
};
export const sendFollowReject = async (follower: User, followee: User) => {
// TODO: Make database job
const request = await objectToInboxRequest(
followRejectToLysand(follower, followee),
followee,
follower,
);
// Send request
const response = await fetch(request);
if (!response.ok) {
dualLogger.log(
LogLevel.DEBUG,
"Federation.FollowReject",
await response.text(),
);
dualLogger.log(
LogLevel.ERROR,
"Federation.FollowReject",
`Failed to federate follow reject from ${
followee.id
} to ${follower.getUri()}`,
);
}
};
export const transformOutputToUserWithRelations = (
user: Omit<UserType, "endpoints"> & {
followerCount: unknown;
followingCount: unknown;
statusCount: unknown;
emojis: {
userId: string;
emojiId: string;
emoji?: EmojiWithInstance;
}[];
instance: InferSelectModel<typeof Instances> | null;
endpoints: unknown;
},
): UserWithRelations => {
return {
...user,
followerCount: Number(user.followerCount),
followingCount: Number(user.followingCount),
statusCount: Number(user.statusCount),
endpoints:
user.endpoints ??
({} as Partial<{
dislikes: string;
featured: string;
likes: string;
followers: string;
following: string;
inbox: string;
outbox: string;
}>),
emojis: user.emojis.map(
(emoji) =>
(emoji as unknown as Record<string, object>)
.emoji as EmojiWithInstance,
),
};
};
export const findManyUsers = async (
query: Parameters<typeof db.query.Users.findMany>[0],
): Promise<UserWithRelations[]> => {
const output = await db.query.Users.findMany({
...query,
with: {
...userRelations,
...query?.with,
},
extras: {
...userExtras,
...query?.extras,
},
});
return output.map((user) => transformOutputToUserWithRelations(user));
};
export const findFirstUser = async (
query: Parameters<typeof db.query.Users.findFirst>[0],
): Promise<UserWithRelations | null> => {
const output = await db.query.Users.findFirst({
...query,
with: {
...userRelations,
...query?.with,
},
extras: {
...userExtras,
...query?.extras,
},
});
if (!output) return null;
return transformOutputToUserWithRelations(output);
};
/**
* Resolves a WebFinger identifier to a user.
* @param identifier Either a UUID or a username
*/
export const resolveWebFinger = async (
identifier: string,
host: string,
): Promise<User | null> => {
// Check if user not already in database
const foundUser = await db
.select()
.from(Users)
.innerJoin(Instances, eq(Users.instanceId, Instances.id))
.where(and(eq(Users.username, identifier), eq(Instances.baseUrl, host)))
.limit(1);
if (foundUser[0]) return await User.fromId(foundUser[0].Users.id);
const hostWithProtocol = host.startsWith("http") ? host : `https://${host}`;
const response = await fetch(
new URL(
`/.well-known/webfinger?${new URLSearchParams({
resource: `acct:${identifier}@${host}`,
})}`,
hostWithProtocol,
),
{
method: "GET",
headers: {
Accept: "application/json",
},
},
);
if (response.status === 404) {
return null;
}
const data = (await response.json()) as {
subject: string;
links: {
rel: string;
type: string;
href: string;
}[];
};
if (!data.subject || !data.links) {
throw new Error(
"Invalid WebFinger data (missing subject or links from response)",
);
}
const relevantLink = data.links.find((link) => link.rel === "self");
if (!relevantLink) {
throw new Error(
"Invalid WebFinger data (missing link with rel: 'self')",
);
}
return User.resolve(relevantLink.href);
};
/**
* Parses mentions from a list of URIs
*/
export const parseMentionsUris = async (
mentions: string[],
): Promise<User[]> => {
return await User.manyFromSql(inArray(Users.uri, mentions));
};
/**
* Retrieves a user from a token.
* @param access_token The access token to retrieve the user from.
* @returns The user associated with the given access token.
*/
export const retrieveUserFromToken = async (
access_token: string,
): Promise<User | null> => {
if (!access_token) return null;
const token = await retrieveToken(access_token);
if (!token || !token.userId) return null;
const user = await User.fromId(token.userId);
return user;
};
export const retrieveUserAndApplicationFromToken = async (
access_token: string,
): Promise<{
user: User | null;
application: Application | null;
}> => {
if (!access_token) return { user: null, application: null };
const output = (
await db
.select({
token: Tokens,
application: Applications,
})
.from(Tokens)
.leftJoin(Applications, eq(Tokens.applicationId, Applications.id))
.where(eq(Tokens.accessToken, access_token))
.limit(1)
)[0];
if (!output?.token.userId) return { user: null, application: null };
const user = await User.fromId(output.token.userId);
return { user, application: output.application ?? null };
};
export const retrieveToken = async (
access_token: string,
): Promise<Token | null> => {
if (!access_token) return null;
return (
(await db.query.Tokens.findFirst({
where: (tokens, { eq }) => eq(tokens.accessToken, access_token),
})) ?? null
);
};
/**
* Gets the relationship to another user.
* @param other The other user to get the relationship to.
* @returns The relationship to the other user.
*/
export const getRelationshipToOtherUser = async (
user: User,
other: User,
): Promise<InferSelectModel<typeof Relationships>> => {
const foundRelationship = await db.query.Relationships.findFirst({
where: (relationship, { and, eq }) =>
and(
eq(relationship.ownerId, user.id),
eq(relationship.subjectId, other.id),
),
});
if (!foundRelationship) {
// Create new relationship
const newRelationship = await createNewRelationship(user, other);
return newRelationship;
}
return foundRelationship;
};
export const followRequestToLysand = (
follower: User,
followee: User,
): Lysand.Follow => {
if (follower.isRemote()) {
throw new Error("Follower must be a local user");
}
if (!followee.isRemote()) {
throw new Error("Followee must be a remote user");
}
if (!followee.getUser().uri) {
throw new Error("Followee must have a URI in database");
}
const id = crypto.randomUUID();
return {
type: "Follow",
id: id,
author: follower.getUri(),
followee: followee.getUri(),
created_at: new Date().toISOString(),
uri: new URL(`/follows/${id}`, config.http.base_url).toString(),
};
};
export const followAcceptToLysand = (
follower: User,
followee: User,
): Lysand.FollowAccept => {
if (!follower.isRemote()) {
throw new Error("Follower must be a remote user");
}
if (followee.isRemote()) {
throw new Error("Followee must be a local user");
}
if (!follower.getUser().uri) {
throw new Error("Follower must have a URI in database");
}
const id = crypto.randomUUID();
return {
type: "FollowAccept",
id: id,
author: followee.getUri(),
created_at: new Date().toISOString(),
follower: follower.getUri(),
uri: new URL(`/follows/${id}`, config.http.base_url).toString(),
};
};
export const followRejectToLysand = (
follower: User,
followee: User,
): Lysand.FollowReject => {
return {
...followAcceptToLysand(follower, followee),
type: "FollowReject",
};
};

View file

@ -1,62 +1,65 @@
services:
lysand:
build: ghcr.io/lysand-org/lysand:main
versia:
image: ghcr.io/versia-pub/server:main
volumes:
- ./logs:/app/dist/logs
- ./config:/app/dist/config
- ./config:/app/dist/config:ro
- ./uploads:/app/dist/uploads
- ./glitch:/app/dist/glitch
restart: unless-stopped
container_name: lysand
container_name: versia
tty: true
networks:
- lysand-net
depends-on:
- versia-net
depends_on:
- db
- redis
- meilisearch
- fe
fe:
image: ghcr.io/lysand-org/lysand-fe:main
container_name: lysand-fe
- sonic
worker:
image: ghcr.io/versia-pub/worker:main
volumes:
- ./logs:/app/dist/logs
- ./config:/app/dist/config:ro
restart: unless-stopped
container_name: versia-worker
tty: true
networks:
- lysand-net
- versia-net
depends_on:
- db
- redis
db:
image: ghcr.io/lysand-org/postgres:main
container_name: lysand-db
image: postgres:17-alpine
container_name: versia-db
restart: unless-stopped
environment:
POSTGRES_DB: lysand
POSTGRES_USER: lysand
POSTGRES_PASSWORD: _______________
POSTGRES_DB: versia
POSTGRES_USER: versia
POSTGRES_PASSWORD: versia
networks:
- lysand-net
- versia-net
volumes:
- ./db-data:/var/lib/postgresql/data
redis:
image: redis:alpine
container_name: lysand-redis
container_name: versia-redis
volumes:
- ./redis-data:/data
restart: unless-stopped
networks:
- lysand-net
- versia-net
meilisearch:
stdin_open: true
environment:
- MEILI_MASTER_KEY=__________________
tty: true
networks:
- lysand-net
sonic:
volumes:
- ./meili-data:/meili_data
image: getmeili/meilisearch:v1.7
container_name: lysand-meilisearch
- ./config.cfg:/etc/sonic.cfg
- ./store:/var/lib/sonic/store/
image: valeriansaliou/sonic:v1.4.9
container_name: versia-sonic
restart: unless-stopped
networks:
- versia-net
networks:
lysand-net:
versia-net:

98
docs/.vitepress/config.ts Normal file
View file

@ -0,0 +1,98 @@
import taskLists from "@hackmd/markdown-it-task-lists";
import implicitFigures from "markdown-it-image-figures";
import { defineConfig } from "vitepress";
import { tabsMarkdownPlugin } from "vitepress-plugin-tabs";
// https://vitepress.dev/reference/site-config
export default defineConfig({
title: "Versia Server Docs",
lang: "en-US",
description: "Documentation for Versia Server APIs",
markdown: {
config: (md): void => {
md.use(implicitFigures, {
figcaption: "alt",
copyAttrs: "^class$",
});
md.use(taskLists);
md.use(tabsMarkdownPlugin);
},
math: true,
},
cleanUrls: true,
themeConfig: {
// https://vitepress.dev/reference/default-theme-config
nav: [
{ text: "Home", link: "/" },
{
text: "Versia Protocol",
link: "https://versia.pub",
target: "_blank",
},
],
sidebar: [
{
text: "Installation",
items: [
{
text: "Normal",
link: "/setup/installation",
},
{
text: "Nix",
link: "/setup/nix",
},
],
},
{
text: "CLI",
link: "/cli",
},
{
text: "API",
items: [
{
text: "Reactions",
link: "/api/reactions",
},
{
text: "Challenges",
link: "/api/challenges",
},
{
text: "Mastodon Extensions",
link: "/api/mastodon",
},
],
},
{
text: "Frontend",
items: [
{
text: "Authentication",
link: "/frontend/auth",
},
{
text: "Routes",
link: "/frontend/routes",
},
],
},
],
socialLinks: [
{ icon: "github", link: "https://github.com/versia-pub/server" },
],
search: {
provider: "local",
},
logo: "https://cdn.versia.pub/branding/icon.svg",
},
head: [["link", { rel: "icon", href: "/favicon.png", type: "image/png" }]],
titleTemplate: ":title • Versia Server Docs",
});

View file

@ -0,0 +1,14 @@
import type { Theme } from "vitepress";
import DefaultTheme from "vitepress/theme";
// https://vitepress.dev/guide/custom-theme
import { h, type VNode } from "vue";
import "./style.css";
export default {
extends: DefaultTheme,
Layout: (): VNode => {
return h(DefaultTheme.Layout, null, {
// https://vitepress.dev/guide/extending-default-theme#layout-slots
});
},
} satisfies Theme;

View file

@ -0,0 +1,138 @@
/**
* Customize default theme styling by overriding CSS variables:
* https://github.com/vuejs/vitepress/blob/main/src/client/theme-default/styles/vars.css
*/
/**
* Colors
*
* Each colors have exact same color scale system with 3 levels of solid
* colors with different brightness, and 1 soft color.
*
* - `XXX-1`: The most solid color used mainly for colored text. It must
* satisfy the contrast ratio against when used on top of `XXX-soft`.
*
* - `XXX-2`: The color used mainly for hover state of the button.
*
* - `XXX-3`: The color for solid background, such as bg color of the button.
* It must satisfy the contrast ratio with pure white (#ffffff) text on
* top of it.
*
* - `XXX-soft`: The color used for subtle background such as custom container
* or badges. It must satisfy the contrast ratio when putting `XXX-1` colors
* on top of it.
*
* The soft color must be semi transparent alpha channel. This is crucial
* because it allows adding multiple "soft" colors on top of each other
* to create a accent, such as when having inline code block inside
* custom containers.
*
* - `default`: The color used purely for subtle indication without any
* special meanings attached to it such as bg color for menu hover state.
*
* - `brand`: Used for primary brand colors, such as link text, button with
* brand theme, etc.
*
* - `tip`: Used to indicate useful information. The default theme uses the
* brand color for this by default.
*
* - `warning`: Used to indicate warning to the users. Used in custom
* container, badges, etc.
*
* - `danger`: Used to show error, or dangerous message to the users. Used
* in custom container, badges, etc.
* -------------------------------------------------------------------------- */
:root {
--vp-c-default-1: var(--vp-c-gray-1);
--vp-c-default-2: var(--vp-c-gray-2);
--vp-c-default-3: var(--vp-c-gray-3);
--vp-c-default-soft: var(--vp-c-gray-soft);
--vp-c-brand-1: var(--vp-c-indigo-1);
--vp-c-brand-2: var(--vp-c-indigo-2);
--vp-c-brand-3: var(--vp-c-indigo-3);
--vp-c-brand-soft: var(--vp-c-indigo-soft);
--vp-c-tip-1: var(--vp-c-brand-1);
--vp-c-tip-2: var(--vp-c-brand-2);
--vp-c-tip-3: var(--vp-c-brand-3);
--vp-c-tip-soft: var(--vp-c-brand-soft);
--vp-c-warning-1: var(--vp-c-yellow-1);
--vp-c-warning-2: var(--vp-c-yellow-2);
--vp-c-warning-3: var(--vp-c-yellow-3);
--vp-c-warning-soft: var(--vp-c-yellow-soft);
--vp-c-danger-1: var(--vp-c-red-1);
--vp-c-danger-2: var(--vp-c-red-2);
--vp-c-danger-3: var(--vp-c-red-3);
--vp-c-danger-soft: var(--vp-c-red-soft);
}
/**
* Component: Button
* -------------------------------------------------------------------------- */
:root {
--vp-button-brand-border: transparent;
--vp-button-brand-text: var(--vp-c-white);
--vp-button-brand-bg: var(--vp-c-brand-3);
--vp-button-brand-hover-border: transparent;
--vp-button-brand-hover-text: var(--vp-c-white);
--vp-button-brand-hover-bg: var(--vp-c-brand-2);
--vp-button-brand-active-border: transparent;
--vp-button-brand-active-text: var(--vp-c-white);
--vp-button-brand-active-bg: var(--vp-c-brand-1);
}
/**
* Component: Home
* -------------------------------------------------------------------------- */
:root {
--vp-home-hero-name-color: transparent;
--vp-home-hero-name-background: -webkit-linear-gradient(
120deg,
#e6a9fe 30%,
#bd34fe
);
--vp-home-hero-image-background-image: linear-gradient(
-45deg,
#e6a9fe 50%,
#bd34fe 50%
);
--vp-home-hero-image-filter: blur(44px);
}
@media (min-width: 640px) {
:root {
--vp-home-hero-image-filter: blur(56px);
}
}
@media (min-width: 960px) {
:root {
--vp-home-hero-image-filter: blur(68px);
}
}
/**
* Component: Custom Block
* -------------------------------------------------------------------------- */
:root {
--vp-custom-block-tip-border: transparent;
--vp-custom-block-tip-text: var(--vp-c-text-1);
--vp-custom-block-tip-bg: var(--vp-c-brand-soft);
--vp-custom-block-tip-code-bg: var(--vp-c-brand-soft);
}
/**
* Component: Algolia
* -------------------------------------------------------------------------- */
.DocSearch {
--docsearch-primary-color: var(--vp-c-brand-1) !important;
}

37
docs/api/challenges.md Normal file
View file

@ -0,0 +1,37 @@
# Challenges API
Some API routes may require a cryptographic challenge to be solved before the request can be made. This is to prevent abuse of the API by bots and other malicious actors. The challenge is a simple mathematical problem that can be solved by any client.
This is a form of proof of work CAPTCHA, and should be mostly invisible to users. The challenge is generated by the server and sent to the client, which must solve it and send the solution back to the server.
## Solving a Challenge
Challenges are powered by the [Altcha](https://altcha.org/) library. You may either reimplement their solution code (which is very simple), or use [`altcha-lib`](https://github.com/altcha-org/altcha-lib) to solve the challenges.
## Request Challenge
To request a challenge, you may use the [`POST /api/v1/challenges`](https://vs.cpluspatch.com/docs#tag/challenges/POST/api/v1/challenges) endpoint.
## Sending a Solution
To send a solution with any request, add the following headers:
- `X-Challenge-Solution`: A base64 encoded string of the following JSON object:
```ts
{
number: number; // Solution to the challenge
algorithm: "SHA-256" | "SHA-384" | "SHA-512";
challenge: string;
salt: string,
signature: string,
}
```
Example: `{"number": 42, "algorithm": "SHA-256", "challenge": "xxxx", "salt": "abc", "signature": "def"}` -> `eyJudW1iZXIiOjQyLCJhbGdvcml0aG0iOiJTSEEtMjU2IiwiY2hhbGxlbmdlIjoieHh4eCIsInNhbHQiOiJhYmMiLCJzaWduYXR1cmUiOiJkZWYifQ==`
A challenge solution is valid for 5 minutes (configurable) after the challenge is generated. No solved challenge may be used more than once.
## Routes Requiring Challenges
If challenges are enabled, the following routes will require a challenge to be solved before the request can be made:
- `POST /api/v1/accounts`
Routes requiring challenges may eventually be expanded or made configurable.

420
docs/api/mastodon.md Normal file
View file

@ -0,0 +1,420 @@
# Mastodon API Extensions
Versia Server extends several Mastodon API endpoints to provide additional functionality. These endpoints are not part of the official Mastodon API, but are provided by Versia Server to enhance the user experience.
## Refetch User
```http
POST /api/v1/accounts/:id/refetch
```
Refetches the user's profile information from remote servers. Does not work for local users.
- **Returns**: [`Account`](https://docs.joinmastodon.org/entities/Account/)
- **Authentication**: Required
- **Permissions**: `read:account`
- **Version History**:
- `0.7.0`: Added.
### Request
#### Example
```http
POST /api/v1/accounts/364fd13f-28b5-4e88-badd-ce3e533f0d02/refetch
Authorization: Bearer ...
```
### Response
#### `400 Bad Request`
The user is a local user and cannot be refetched.
#### `200 OK`
New user data.
Example from the [Mastodon API documentation](https://docs.joinmastodon.org/entities/Account/):
```json
{
"id": "23634",
"username": "noiob",
"acct": "noiob@awoo.space",
"display_name": "ikea shark fan account",
"locked": false,
"bot": false,
"created_at": "2017-02-08T02:00:53.274Z",
"note": "<p>:ms_rainbow_flag: :ms_bisexual_flagweb: :ms_nonbinary_flag: <a href=\"https://awoo.space/tags/awoo\" class=\"mention hashtag\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">#<span>awoo</span}.space <a href=\"https://awoo.space/tags/admin\" class=\"mention hashtag\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">#<span>admin</span} ~ <a href=\"https://awoo.space/tags/bi\" class=\"mention hashtag\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">#<span>bi</span} ~ <a href=\"https://awoo.space/tags/nonbinary\" class=\"mention hashtag\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">#<span>nonbinary</span} ~ compsci student ~ likes video <a href=\"https://awoo.space/tags/games\" class=\"mention hashtag\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">#<span>games</span} and weird/ old electronics and will post obsessively about both ~ avatar by <span class=\"h-card\"><a href=\"https://weirder.earth/@dzuk\" class=\"u-url mention\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">@<span>dzuk</span}</span></p>",
"url": "https://awoo.space/@noiob",
"avatar": "https://files.mastodon.social/accounts/avatars/000/023/634/original/6ca8804dc46800ad.png",
"avatar_static": "https://files.mastodon.social/accounts/avatars/000/023/634/original/6ca8804dc46800ad.png",
"header": "https://files.mastodon.social/accounts/headers/000/023/634/original/256eb8d7ac40f49a.png",
"header_static": "https://files.mastodon.social/accounts/headers/000/023/634/original/256eb8d7ac40f49a.png",
"followers_count": 547,
"following_count": 404,
"statuses_count": 28468,
"last_status_at": "2019-11-17",
"emojis": [
{
"shortcode": "ms_rainbow_flag",
"url": "https://files.mastodon.social/custom_emojis/images/000/028/691/original/6de008d6281f4f59.png",
"static_url": "https://files.mastodon.social/custom_emojis/images/000/028/691/static/6de008d6281f4f59.png",
"visible_in_picker": true
},
{
"shortcode": "ms_bisexual_flag",
"url": "https://files.mastodon.social/custom_emojis/images/000/050/744/original/02f94a5fca7eaf78.png",
"static_url": "https://files.mastodon.social/custom_emojis/images/000/050/744/static/02f94a5fca7eaf78.png",
"visible_in_picker": true
},
{
"shortcode": "ms_nonbinary_flag",
"url": "https://files.mastodon.social/custom_emojis/images/000/105/099/original/8106088bd4782072.png",
"static_url": "https://files.mastodon.social/custom_emojis/images/000/105/099/static/8106088bd4782072.png",
"visible_in_picker": true
}
],
"fields": [
{
"name": "Pronouns",
"value": "they/them",
"verified_at": null
},
{
"name": "Alt",
"value": "<span class=\"h-card\"><a href=\"https://cybre.space/@noiob\" class=\"u-url mention\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">@<span>noiob</span}</span>",
"verified_at": null
},
{
"name": "Bots",
"value": "<span class=\"h-card\"><a href=\"https://botsin.space/@darksouls\" class=\"u-url mention\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">@<span>darksouls</span}</span>, <span class=\"h-card\"><a href=\"https://botsin.space/@nierautomata\" class=\"u-url mention\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">@<span>nierautomata</span}</span>, <span class=\"h-card\"><a href=\"https://mastodon.social/@fedi\" class=\"u-url mention\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">@<span>fedi</span}</span>, code for <span class=\"h-card\"><a href=\"https://botsin.space/@awoobot\" class=\"u-url mention\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">@<span>awoobot</span}</span>",
"verified_at": null
},
{
"name": "Website",
"value": "<a href=\"http://shork.xyz\" rel=\"nofollow noopener noreferrer\" target=\"_blank\"><span class=\"invisible\">http://</span><span class=\"\">shork.xyz</span><span class=\"invisible\"></span}",
"verified_at": "2019-11-10T10:31:10.744+00:00"
}
]
}
```
## Get User By Username
```http
GET /api/v1/accounts/id?username=:username
```
Retrieves a user by their username.
- **Returns**: [`Account`](https://docs.joinmastodon.org/entities/Account/)
- **Authentication**: Not required
- **Permissions**: `read:account`
- **Version History**:
- `0.7.0`: Added.
### Request
#### Example
```http
GET /api/v1/accounts/id?username=bobleponge
```
### Response
#### `404 Not Found`
No user with that username was found.
#### `200 OK`
User data.
Example from the [Mastodon API documentation](https://docs.joinmastodon.org/entities/Account/):
```json
{
"id": "23634",
"username": "noiob",
"acct": "noiob@awoo.space",
"display_name": "ikea shark fan account",
"locked": false,
"bot": false,
"created_at": "2017-02-08T02:00:53.274Z",
"note": "<p>:ms_rainbow_flag: :ms_bisexual_flagweb: :ms_nonbinary_flag: <a href=\"https://awoo.space/tags/awoo\" class=\"mention hashtag\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">#<span>awoo</span}.space <a href=\"https://awoo.space/tags/admin\" class=\"mention hashtag\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">#<span>admin</span} ~ <a href=\"https://awoo.space/tags/bi\" class=\"mention hashtag\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">#<span>bi</span} ~ <a href=\"https://awoo.space/tags/nonbinary\" class=\"mention hashtag\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">#<span>nonbinary</span} ~ compsci student ~ likes video <a href=\"https://awoo.space/tags/games\" class=\"mention hashtag\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">#<span>games</span} and weird/ old electronics and will post obsessively about both ~ avatar by <span class=\"h-card\"><a href=\"https://weirder.earth/@dzuk\" class=\"u-url mention\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">@<span>dzuk</span}</span></p>",
"url": "https://awoo.space/@noiob",
"avatar": "https://files.mastodon.social/accounts/avatars/000/023/634/original/6ca8804dc46800ad.png",
"avatar_static": "https://files.mastodon.social/accounts/avatars/000/023/634/original/6ca8804dc46800ad.png",
"header": "https://files.mastodon.social/accounts/headers/000/023/634/original/256eb8d7ac40f49a.png",
"header_static": "https://files.mastodon.social/accounts/headers/000/023/634/original/256eb8d7ac40f49a.png",
"followers_count": 547,
"following_count": 404,
"statuses_count": 28468,
"last_status_at": "2019-11-17",
"emojis": [
{
"shortcode": "ms_rainbow_flag",
"url": "https://files.mastodon.social/custom_emojis/images/000/028/691/original/6de008d6281f4f59.png",
"static_url": "https://files.mastodon.social/custom_emojis/images/000/028/691/static/6de008d6281f4f59.png",
"visible_in_picker": true
},
{
"shortcode": "ms_bisexual_flag",
"url": "https://files.mastodon.social/custom_emojis/images/000/050/744/original/02f94a5fca7eaf78.png",
"static_url": "https://files.mastodon.social/custom_emojis/images/000/050/744/static/02f94a5fca7eaf78.png",
"visible_in_picker": true
},
{
"shortcode": "ms_nonbinary_flag",
"url": "https://files.mastodon.social/custom_emojis/images/000/105/099/original/8106088bd4782072.png",
"static_url": "https://files.mastodon.social/custom_emojis/images/000/105/099/static/8106088bd4782072.png",
"visible_in_picker": true
}
],
"fields": [
{
"name": "Pronouns",
"value": "they/them",
"verified_at": null
},
{
"name": "Alt",
"value": "<span class=\"h-card\"><a href=\"https://cybre.space/@noiob\" class=\"u-url mention\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">@<span>noiob</span}</span>",
"verified_at": null
},
{
"name": "Bots",
"value": "<span class=\"h-card\"><a href=\"https://botsin.space/@darksouls\" class=\"u-url mention\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">@<span>darksouls</span}</span>, <span class=\"h-card\"><a href=\"https://botsin.space/@nierautomata\" class=\"u-url mention\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">@<span>nierautomata</span}</span>, <span class=\"h-card\"><a href=\"https://mastodon.social/@fedi\" class=\"u-url mention\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">@<span>fedi</span}</span>, code for <span class=\"h-card\"><a href=\"https://botsin.space/@awoobot\" class=\"u-url mention\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">@<span>awoobot</span}</span>",
"verified_at": null
},
{
"name": "Website",
"value": "<a href=\"http://shork.xyz\" rel=\"nofollow noopener noreferrer\" target=\"_blank\"><span class=\"invisible\">http://</span><span class=\"\">shork.xyz</span><span class=\"invisible\"></span}",
"verified_at": "2019-11-10T10:31:10.744+00:00"
}
]
}
```
## Get Instance TOS
```http
GET /api/v1/instance/tos
```
Returns the instance's Terms of Service, as configured in the instance settings.
- **Returns**: [`ExtendedDescription`](https://docs.joinmastodon.org/entities/ExtendedDescription/)
- **Authentication**: Not required
- **Permissions**: None
- **Version History**:
- `0.7.0`: Added.
### Request
#### Example
```http
GET /api/v1/instance/tos
```
### Response
#### `200 OK`
Instance's Terms of Service.
```json
{
"updated_at": "2019-11-17T00:00:00.000Z",
"content": "<h1>TOS</h1>\n<p>These are the terms of service for this instance.</p>",
}
```
## Get Instance Privacy Policy
```http
GET /api/v1/instance/privacy_policy
```
Returns the instance's Privacy Policy, as configured in the instance settings.
- **Returns**: [`ExtendedDescription`](https://docs.joinmastodon.org/entities/ExtendedDescription/)
- **Authentication**: Not required
- **Permissions**: None
- **Version History**:
- `0.7.0`: Added.
### Request
#### Example
```http
GET /api/v1/instance/privacy_policy
```
### Response
#### `200 OK`
Instance's Privacy Policy.
```json
{
"updated_at": "2019-11-17T00:00:00.000Z",
"content": "<h1>Privacy Policy</h1>\n<p>This is the privacy policy for this instance.</p>",
}
```
## `/api/v1/instance`
Extra attributes have been added to the `/api/v1/instance` endpoint.
```ts
interface SSOProvider {
id: string;
name: string;
icon?: string;
}
type ExtendedInstance = Instance & {
banner: string | null;
versia_version: string;
sso: {
forced: boolean;
providers: SSOProvider[];
};
}
```
### `banner`
The URL of the instance's banner image.
### `versia_version`
The version of Versia Server running on the instance.
The normal `version` field is always set to `"4.3.0+glitch"` or similar, to not confuse clients that expect a Mastodon instance.
### `sso`
Single Sign-On (SSO) settings for the instance. This object contains two fields:
- `forced`: If this is enabled, normal identifier/password login is disabled and login must be done through SSO.
- `providers`: An array of external OpenID Connect providers that users can link their accounts to. Each provider object contains the following fields:
- `id`: The issuer ID of the OpenID Connect provider.
- `name`: The name of the provider.
- `icon`: The URL of the provider's icon. Optional.
## `/api/v2/instance`
Extra attributes have been added to the `/api/v2/instance` endpoint. These are identical to the `/api/v1/instance` endpoint, except that the `banner` attribute uses the normal Mastodon API attribute.
```ts
type ExtendedInstanceV2 = InstanceV2 & {
versia_version: string;
configuration: Instance["configuration"] & {
emojis: {
// In bytes
emoji_size_limit: number;
max_emoji_shortcode_characters: number;
max_emoji_description_characters: number;
};
};
sso: {
forced: boolean;
providers: SSOProvider[];
};
}
```
### `versia_version`
The version of Versia Server running on the instance.
The normal `version` field is always set to `"4.3.0+glitch"` or similar, to not confuse clients that expect a Mastodon instance.
### `sso`
Single Sign-On (SSO) settings for the instance. This object contains two fields:
- `forced`: If this is enabled, normal identifier/password login is disabled and login must be done through SSO.
- `providers`: An array of external OpenID Connect providers that users can link their accounts to. Each provider object contains the following fields:
- `id`: The issuer ID of the OpenID Connect provider.
- `name`: The name of the provider.
- `icon`: The URL of the provider's icon. Optional.
## `Account`
Two extra attributes have been added to all returned [`Account`](https://docs.joinmastodon.org/entities/Account/) objects.
This object is returned on routes such as `/api/v1/accounts/:id`, `/api/v1/accounts/verify_credentials`, etc.
```ts
type ExtendedAccount = Account & {
roles: Role[];
uri: string;
}
```
### `roles`
An array of `Roles` that the user has.
### `uri`
URI of the account's Versia entity (for federation). Similar to Mastodon's `uri` field on notes.
## `Status`
One attribute has been added to all returned [`Status`](https://docs.joinmastodon.org/entities/Status/) objects.
This object is returned on routes such as `/api/v1/statuses/:id`, `/api/v1/statuses/:id/context`, etc.
```ts
type URL = string;
interface NoteReaction {
name: string;
count: number;
me: boolean;
url: URL;
}
type ExtendedStatus = Status & {
reactions: NoteReaction[];
}
```
```json
{
...
"reactions": [
{
"name": "like",
"count": 3,
"me": true,
},
{
"name": "blobfox",
"count": 1,
"me": false,
}
]
}
```
### `reactions`
An array of all the [`NoteReactions`](./reactions.md#reaction) for the note. Data for the custom emoji (e.g. URL) can be found in the `emojis` field of the [`Status`](https://docs.joinmastodon.org/entities/Status#emojis).
## `/api/v1/accounts/update_credentials`
The `username` parameter can now (optionally) be set to change the user's handle.
> [!WARNING]
> Clients should indicate to users that changing their handle will break existing links to their profile. This is reversible, but the old handle will be available for anyone to claim.

175
docs/api/reactions.md Normal file
View file

@ -0,0 +1,175 @@
# Reactions API
This API is used to send reactions to notes.
## Reaction
```typescript
type UUID = string;
interface NoteReaction {
name: string;
count: number;
me: boolean;
}
type NoteReactionWithAccounts = NoteReaction & {
account_ids: UUID[];
}
```
## Get Reactions
All reactions attached to a [`Status`](https://docs.joinmastodon.org/entities/Status) can be found on the note itself, [in the `reactions` field](./mastodon.md#reactions).
## Get Users Who Reacted
```http
GET /api/v1/statuses/:id/reactions
```
Get a list of all the users who reacted to a note. Only IDs are returned, not full account objects, to improve performance on very popular notes.
- **Returns:** [`NoteReactionWithAccounts[]`](#reaction)
- **Authentication:** Not required
- **Permissions:** `read:reaction`
- **Version History**:
- `0.8.0`: Added.
### Request
#### Example
```http
GET /api/v1/statuses/123/reactions
```
### Response
#### `200 OK`
List of reactions and associated users. The `me` field is `true` if the current user has reacted with that emoji.
Data for the custom emoji (e.g. URL) can be found in the `emojis` field of the [`Status`](https://docs.joinmastodon.org/entities/Status#emojis).
```json
[
{
"name": "like",
"count": 3,
"me": true,
"account_ids": ["1", "2", "3"]
},
{
"name": "blobfox-coffee",
"count": 1,
"me": false,
"account_ids": ["4"]
}
]
```
## Add Reaction
```http
PUT /api/v1/statuses/:id/reactions/:name
```
Add a reaction to a note.
- **Returns:** [`Status`](https://docs.joinmastodon.org/entities/Status)
- **Authentication:** Required
- **Permissions:** `owner:reaction`
- **Version History**:
- `0.8.0`: Added.
### Request
- `name` (string, required): Either a custom emoji shortcode or a Unicode emoji.
#### Example
```http
PUT /api/v1/statuses/123/reactions/blobfox-coffee
Authorization: Bearer ...
```
```http
PUT /api/v1/statuses/123/reactions/👍
Authorization: Bearer ...
```
### Response
#### `201 Created`
Returns the updated note.
```json
{
"id": "123",
...
"reactions": [
{
"name": "👍",
"count": 3,
"me": true
},
{
"name": "blobfox-coffee",
"count": 1,
"me": false
}
]
}
```
## Remove Reaction
```http
DELETE /api/v1/statuses/:id/reactions/:name
```
Remove a reaction from a note.
- **Returns:** [`Status`](https://docs.joinmastodon.org/entities/Status)
- **Authentication:** Required
- **Permissions:** `owner:reaction`
- **Version History**:
- `0.8.0`: Added.
### Request
- `name` (string, required): Either a custom emoji shortcode or a Unicode emoji.
#### Example
```http
DELETE /api/v1/statuses/123/reactions/blobfox-coffee
Authorization: Bearer ...
```
```http
DELETE /api/v1/statuses/123/reactions
Authorization: Bearer ...
```
### Response
#### `200 OK`
Returns the updated note. If the reaction was not found, the note is returned as is.
```json
{
"id": "123",
...
"reactions": [
{
"name": "👍",
"count": 3,
"me": true
}
]
}
```

View file

@ -1,24 +0,0 @@
# Lysand CLI
Lysand includes a built-in, scripting-compatible CLI that can be used to manage the server. This CLI can be used to create and delete users, manage the database and more. It can also output data in JSON or CSV format, making it easy to use in scripts.
## Using the CLI
Lysand includes a built-in CLI for managing the server. To use it, simply run the following command:
```bash
# Development
bun cli help
# Source installs
bun run dist/cli.js help
# Docker
docker compose exec -it lysand /bin/sh /app/entrypoint.sh cli help
```
You can use the `help` command to see a list of available commands. These include creating users, deleting users and more. Each command also has a `--help,-h` flag that you can use to see more information about the command.
## Scripting with the CLI
Some CLI commands that return data as tables can be used in scripts. To convert them to JSON or CSV, some commands allow you to specify a `--format` flag that can be either `"json"` or `"csv"`. See `bun cli help` or `bun cli <command> -h` for more information.
Flags can be used in any order and anywhere in the script (except for the `bun cli` command itself). The command arguments themselves must be in the correct order, however.Z

21
docs/cli/index.md Normal file
View file

@ -0,0 +1,21 @@
# Versia Server CLI
Versia Server includes a built-in, scripting-compatible CLI that can be used to manage the server. This CLI can be used to create and delete users, manage the database and more. It can also output data in JSON or CSV format, making it easy to use in scripts.
## Using the CLI
Versia Server includes a built-in CLI for managing the server. To use it, simply run the following command:
```bash
# Docker
# Replace `versia` with the name of your container
docker compose exec -it versia sh /app/entrypoint.sh cli help
```
You can use the `help` command to see a list of available commands. These include creating users, deleting users and more. Each command also has a `--help,-h` flag that you can use to see more information about the command.
## Scripting with the CLI
Some CLI commands that return data as tables can be used in scripts. To convert them to JSON or CSV, some commands allow you to specify a `--format` flag that can be either `"json"` or `"csv"`. See `cli help` or `cli <command> -h` for more information.
Flags can be used in any order and anywhere in the script (except for the `cli` command itself).

View file

@ -1,10 +0,0 @@
# Installing the database
Lysand uses a special PostgreSQL extension called `pg_uuidv7` to generate UUIDs. This extension is required for Lysand to work properly. To install it, you can either use the pre-made Docker image or install it manually.
## Using the Docker image
Lysand offers a pre-made Docker image for PostgreSQL with the extension already installed. Use `ghcr.io/lysand-org/postgres:main` as your Docker image name to use it.
## Manual installation

87
docs/frontend/auth.md Normal file
View file

@ -0,0 +1,87 @@
# Frontend Authentication
Multiple API routes are exposed for authentication, to be used by frontend developers.
> [!INFO]
>
> These are different from the Client API routes, which are used by clients to interact with the Mastodon API.
A frontend is a web application that is designed to be the primary user interface for an instance. It is used also used by clients to perform authentication.
## Get Frontend Configuration
```http
GET /api/v1/frontend/config
```
Retrieves the frontend configuration for the instance. This returns whatever the `frontend.settings` object is set to in the Versia Server configuration.
This behaves like the `/api/v1/preferences` endpoint in the Mastodon API, but is specific to the frontend. These values are arbitrary and can be used for anything.
Frontend developers should always namespace their keys to avoid conflicts with other keys.
- **Returns**: Object with arbitrary keys and values.
- **Authentication**: Not required
- **Permissions**: None
- **Version History**:
- `0.7.0`: Added.
### Request
#### Example
```http
GET /api/v1/frontend/config
```
### Response
#### `200 OK`
Frontend configuration.
```json
{
"pub.versia.fe:theme": "dark",
"pub.versia.fe:custom_css": "body { background-color: black; }",
"net.googly.frontend:spoiler_image": "https://example.com/spoiler.png"
}
```
## SSO Sign In
```http
POST /oauth/sso
```
Allows users to sign in to the instance using an external OpenID Connect provider.
- **Returns**: `302 Found` with a `Location` header to redirect the user to the next step.
- **Authentication**: Not required
- **Permissions**: None
- **Version History**:
- `0.7.0`: First documented.
### Request
#### Query Parameters
- `client_id` (string, required): Client ID of the [application](https://docs.joinmastodon.org/entities/Application/) that is making the request.
- `issuer` (string, required): The ID of the OpenID Connect provider, as found in `/api/{v1,v2}/instance`.
#### Example
```http
POST /oauth/sso?client_id=123&issuer=google
```
### Response
#### `302 Found`
Redirects the user to the OpenID Connect provider's login page.
```http
HTTP/2.0 302 Found
Location: https://accounts.google.com/o/oauth2/auth?client_id=123&redirect_uri=https%3A%2F%2Fexample.com%2Fauth&response_type=code&scope=openid%20email&state=123
```

53
docs/frontend/routes.md Normal file
View file

@ -0,0 +1,53 @@
# Frontend Routes
Frontend implementors must implement these routes for correct operation of the instance.
The location of these routes can be configured in the Versia Server configuration at `frontend.routes`:
## Login Form
```http
GET /oauth/authorize
```
This route should display a login form for the user to enter their username and password, as well as a list of OpenID providers to use if available.
The form should submit to the OpenID Connect flow.
Configurable in the Versia Server configuration at `frontend.routes.login`.
## Consent Form
```http
GET /oauth/consent
```
This route should display a consent form for the user to approve the requested application permissions, after logging in.
The form should submit an OpenID Connect authorization request at `POST /oauth/authorize`, with the correct [application](https://docs.joinmastodon.org/entities/Application/) data (client ID, redirect URI, etc.). Do not forget the JWT cookie.
### Submission Example
```http
POST /oauth/authorize
Content-Type: application/json
Cookie: jwt=...
{
"client_id": "client_id",
"response_type": "code",
"redirect_uri": "https://example.com/callback",
"scope": "read write",
"state": "state123",
"code_challenge": "code_challenge",
"code_challenge_method": "S256",
"response_type": "code"
}
```
### Submission Response
```http
HTTP/2.0 302 Found
Location: https://example.com/callback?code=code&state=state123
```

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