Compare commits

..

122 Commits
1.3.0 ... 1.5.1

Author SHA1 Message Date
Milo Schwartz
6b4e52a725 Merge pull request #877 from fosrl/dev
1.5.1
2025-06-09 17:40:35 -04:00
miloschwartz
e41eafd497 enhance link styling and bump traefik version 2025-06-09 17:38:18 -04:00
Owen Schwartz
d70396a664 Merge pull request #876 from thijsvanloef/fix/define-files-in-eslint
Add Typescript specific linting & define files in eslint
2025-06-09 17:15:23 -04:00
Thijs van Loef
3d59556bcd readd accidentally removed line 2025-06-09 22:38:38 +02:00
Thijs van Loef
c7018e92b0 Merge branch 'dev' into fix/define-files-in-eslint 2025-06-09 22:36:56 +02:00
Thijs van Loef
a575bace39 add typescript eslint 2025-06-09 22:32:38 +02:00
Thijs van Loef
2047aa30e1 add typescript specific linting 2025-06-09 22:25:27 +02:00
Thijs van Loef
3b10453af3 define files in eslint 2025-06-09 22:18:38 +02:00
Owen
3257edc2a0 Add link to docs for newt 2025-06-09 14:23:53 -04:00
miloschwartz
cd54e7dd38 pick first port on select container 2025-06-09 13:01:53 -04:00
Owen
9177eaba22 Merge branch 'main' into dev 2025-06-09 11:22:35 -04:00
Owen Schwartz
33ae2e08cc Merge pull request #872 from Lokowitz/fix-dependabot
fix - dependabot
2025-06-09 11:17:45 -04:00
Owen Schwartz
4fc61386d3 Merge pull request #867 from thijsvanloef/fix-767/unit-aware-sorting
Fix - Sort Data In/Out correctly in Sites Management page in the Pangolin Dashboard
2025-06-09 11:17:15 -04:00
Owen
c409266954 Fix #860 2025-06-09 11:13:58 -04:00
Owen
57315a36ee Merge branch 'main' into dev 2025-06-09 11:07:42 -04:00
Owen Schwartz
63637b91a8 Merge pull request #874 from fosrl/dependabot/npm_and_yarn/dev-patch-updates-637b1bbe0a
Bump the dev-patch-updates group across 1 directory with 4 updates
2025-06-09 11:06:32 -04:00
Owen Schwartz
09238cd98a Merge pull request #873 from fosrl/dependabot/npm_and_yarn/prod-patch-updates-1931de1053
Bump zod from 3.25.46 to 3.25.56 in the prod-patch-updates group across 1 directory
2025-06-09 11:06:08 -04:00
Owen Schwartz
67b149ce4b Merge pull request #834 from fosrl/dependabot/npm_and_yarn/tar-fs-2.1.3
Bump tar-fs from 2.1.2 to 2.1.3
2025-06-09 11:05:24 -04:00
Thijs van Loef
96151de814 improve readability 2025-06-09 13:18:22 +02:00
Thijs van Loef
f2e461a1ee improve readability 2025-06-09 13:06:08 +02:00
dependabot[bot]
8125622c98 Bump the dev-patch-updates group across 1 directory with 4 updates
Bumps the dev-patch-updates group with 4 updates in the / directory: [@types/cookie-parser](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/cookie-parser), [@types/cors](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/cors), [@types/react-dom](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react-dom) and [react-email](https://github.com/resend/react-email/tree/HEAD/packages/react-email).


Updates `@types/cookie-parser` from 1.4.8 to 1.4.9
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/cookie-parser)

Updates `@types/cors` from 2.8.18 to 2.8.19
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/cors)

Updates `@types/react-dom` from 19.1.5 to 19.1.6
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react-dom)

Updates `react-email` from 4.0.15 to 4.0.16
- [Release notes](https://github.com/resend/react-email/releases)
- [Changelog](https://github.com/resend/react-email/blob/canary/packages/react-email/CHANGELOG.md)
- [Commits](https://github.com/resend/react-email/commits/react-email@4.0.16/packages/react-email)

---
updated-dependencies:
- dependency-name: "@types/cookie-parser"
  dependency-version: 1.4.9
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-patch-updates
- dependency-name: "@types/cors"
  dependency-version: 2.8.19
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-patch-updates
- dependency-name: "@types/react-dom"
  dependency-version: 19.1.6
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-patch-updates
- dependency-name: react-email
  dependency-version: 4.0.16
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-patch-updates
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-09 02:06:40 +00:00
dependabot[bot]
1a6942ccc9 Bump zod in the prod-patch-updates group across 1 directory
Bumps the prod-patch-updates group with 1 update in the / directory: [zod](https://github.com/colinhacks/zod).


Updates `zod` from 3.25.46 to 3.25.56
- [Release notes](https://github.com/colinhacks/zod/releases)
- [Commits](https://github.com/colinhacks/zod/compare/v3.25.46...v3.25.56)

---
updated-dependencies:
- dependency-name: zod
  dependency-version: 3.25.56
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: prod-patch-updates
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-09 02:05:59 +00:00
Marvin
7b0e1df778 Update dependabot.yml 2025-06-08 17:29:14 +02:00
Owen Schwartz
7d6b114d67 Merge pull request #869 from Lokowitz/fix-package
Fix - package-lock
2025-06-08 08:47:40 -04:00
Lokowitz
a169256770 update package-lock 2025-06-08 12:30:32 +00:00
Lokowitz
2e54afd72f rebuild package-lock 2025-06-08 12:26:52 +00:00
Thijs van Loef
26207bd951 add datasize helper and add correct sorting to sitestable 2025-06-08 00:07:49 +02:00
Owen
3ed681e277 Bump temp version 2025-06-06 12:16:58 -04:00
Owen
c135b5e3cf Dont request unless its a newt 2025-06-06 12:15:15 -04:00
Owen
e648307f0b Merge branch 'main' into dev 2025-06-06 12:04:24 -04:00
Owen Schwartz
0e4f35e87a Merge pull request #855 from Lokowitz/main
fix - removed package-lock.json from .gitignore
2025-06-06 12:00:49 -04:00
Marvin
553dffd4ee removed package-lock.json from .gitignore
update package-lock.json
2025-06-06 07:00:07 +00:00
miloschwartz
c1fd38ac39 fix typo 2025-06-05 15:48:37 -04:00
miloschwartz
33e2798313 Merge branch 'main' into dev 2025-06-05 14:44:55 -04:00
miloschwartz
f0cb65f65c dont import db in nextjs 2025-06-05 14:44:34 -04:00
Milo Schwartz
9bb6cb14a6 Merge pull request #843 from fosrl/dev
1.5.0
2025-06-05 12:11:47 -04:00
miloschwartz
b6f67e0f0b Merge branch 'main' into dev 2025-06-05 12:11:37 -04:00
miloschwartz
980545c636 dont throw if fail to migration config 2025-06-05 11:55:59 -04:00
miloschwartz
92135ff9c1 minor visal adjustments to docker container view 2025-06-05 11:51:48 -04:00
Owen
ab843b1a43 Clean up unused 2025-06-04 17:42:19 -04:00
miloschwartz
4593edbb45 add get role to integration api 2025-06-04 17:28:46 -04:00
Owen
96b451843c Update placeholder 2025-06-04 17:27:10 -04:00
Owen
54aa3ce7d8 Comment the socket status for now 2025-06-04 17:18:42 -04:00
Owen
45a70152ee Merge branch 'dev' of github.com:fosrl/pangolin into dev 2025-06-04 17:17:56 -04:00
miloschwartz
8c5f00a446 remove .sqlite from Dockerfile 2025-06-04 17:17:18 -04:00
miloschwartz
af98610d0d fix migration number and add allowed_headers migration 2025-06-04 17:15:11 -04:00
Owen
875ec662ad Fix retry 2025-06-04 16:05:41 -04:00
Owen
8800ec9675 Merge branch 'dev' of github.com:fosrl/pangolin into dev 2025-06-04 16:02:52 -04:00
Owen
df4da75c57 Dont do socket on non-newt sites 2025-06-04 16:02:45 -04:00
miloschwartz
717dfae26c look for ipv6 in brackets and fix cors headers in install config 2025-06-04 15:56:16 -04:00
Owen
58a2a9dcc9 Fix db import for pg 2025-06-04 15:24:15 -04:00
Owen
27a0df4ed4 Add migration for 1.4.0 2025-06-04 15:16:42 -04:00
Milo Schwartz
6fc6f325a7 Merge pull request #807 from pyrho/feat/auth-header
send user data to badger when authenticated
2025-06-04 12:17:23 -04:00
miloschwartz
b46e49922c Merge branch 'dev' into postgres 2025-06-04 12:04:28 -04:00
miloschwartz
2cca561e51 support postgresql as database option 2025-06-04 12:02:07 -04:00
Owen
17919192e0 Speed up when the button shows 2025-06-03 21:04:08 -04:00
dependabot[bot]
9f979c5019 Bump tar-fs from 2.1.2 to 2.1.3
Bumps [tar-fs](https://github.com/mafintosh/tar-fs) from 2.1.2 to 2.1.3.
- [Commits](https://github.com/mafintosh/tar-fs/commits)

---
updated-dependencies:
- dependency-name: tar-fs
  dependency-version: 2.1.3
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-03 09:32:28 +00:00
Owen Schwartz
c3d2c34279 Merge pull request #831 from TheresaQWQ/patch-1
fix typo
2025-06-02 22:42:17 -04:00
风间苏苏
430f187fde fix typo 2025-06-03 10:37:27 +08:00
Owen
f438d2ddbf Watch target inputs 2025-06-02 21:13:57 -04:00
Owen
6d519af198 Rollback select packages; update fav 2025-06-02 20:47:22 -04:00
Owen Schwartz
a34e88257d Merge pull request #823 from fosrl/dependabot/npm_and_yarn/multi-5726d3b8f4
Bump esbuild and drizzle-kit
2025-06-02 09:08:28 -04:00
Owen
ea0ab0e63c Merge branch 'dev' into improbableone-hub-dev 2025-06-01 23:31:11 -04:00
dependabot[bot]
80375cd0dc Bump esbuild and drizzle-kit
Bumps [esbuild](https://github.com/evanw/esbuild) to 0.25.2 and updates ancestor dependency [drizzle-kit](https://github.com/drizzle-team/drizzle-orm). These dependencies need to be updated together.


Updates `esbuild` from 0.18.20 to 0.25.2
- [Release notes](https://github.com/evanw/esbuild/releases)
- [Changelog](https://github.com/evanw/esbuild/blob/main/CHANGELOG-2023.md)
- [Commits](https://github.com/evanw/esbuild/compare/v0.18.20...v0.25.2)

Updates `drizzle-kit` from 0.30.6 to 0.31.1
- [Release notes](https://github.com/drizzle-team/drizzle-orm/releases)
- [Commits](https://github.com/drizzle-team/drizzle-orm/compare/drizzle-kit@0.30.6...drizzle-kit@0.31.1)

---
updated-dependencies:
- dependency-name: esbuild
  dependency-version: 0.25.2
  dependency-type: indirect
- dependency-name: drizzle-kit
  dependency-version: 0.31.1
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-02 03:26:24 +00:00
Owen
f817ba7664 Merge branch 'dev' of github.com:fosrl/pangolin into dev 2025-06-01 23:19:18 -04:00
Owen
3398088e03 Merge branch 'Lokowitz-main' into dev 2025-06-01 23:18:58 -04:00
Owen
e586dd50f4 Merge branch 'main' of github.com:Lokowitz/pangolin into Lokowitz-main 2025-06-01 23:15:54 -04:00
Owen Schwartz
5a71c0ba65 Merge pull request #817 from PrtmPhlp/main
docker run command in detached mode
2025-06-01 22:32:22 -04:00
Marvin
f13b6abd78 Update dependabot.yml 2025-06-01 15:50:14 +02:00
Marvin
34c6b590d7 Update dependabot.yml 2025-06-01 15:47:54 +02:00
Marvin
ab797203eb Update dependabot.yml 2025-06-01 15:46:23 +02:00
Marvin
30e8b1f0fe Update dependabot.yml 2025-06-01 15:39:20 +02:00
dependabot[bot]
d03bee98f5 Bump yargs from 17.7.2 to 18.0.0 in the dev-major-updates group (#119)
Bumps the dev-major-updates group with 1 update: [yargs](https://github.com/yargs/yargs).


Updates `yargs` from 17.7.2 to 18.0.0
- [Release notes](https://github.com/yargs/yargs/releases)
- [Changelog](https://github.com/yargs/yargs/blob/main/CHANGELOG.md)
- [Commits](https://github.com/yargs/yargs/compare/v17.7.2...v18.0.0)

---
updated-dependencies:
- dependency-name: yargs
  dependency-version: 18.0.0
  dependency-type: direct:development
  update-type: version-update:semver-major
  dependency-group: dev-major-updates
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-01 15:25:08 +02:00
dependabot[bot]
fa365fb7b8 Bump the dev-minor-updates group with 4 updates (#118)
Bumps the dev-minor-updates group with 4 updates: [@dotenvx/dotenvx](https://github.com/dotenvx/dotenvx), [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node), [drizzle-kit](https://github.com/drizzle-team/drizzle-orm) and [typescript](https://github.com/microsoft/TypeScript).


Updates `@dotenvx/dotenvx` from 1.32.0 to 1.44.1
- [Release notes](https://github.com/dotenvx/dotenvx/releases)
- [Changelog](https://github.com/dotenvx/dotenvx/blob/main/CHANGELOG.md)
- [Commits](https://github.com/dotenvx/dotenvx/compare/v1.32.0...v1.44.1)

Updates `@types/node` from 22.10.10 to 22.15.29
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

Updates `drizzle-kit` from 0.30.6 to 0.31.1
- [Release notes](https://github.com/drizzle-team/drizzle-orm/releases)
- [Commits](https://github.com/drizzle-team/drizzle-orm/compare/drizzle-kit@0.30.6...drizzle-kit@0.31.1)

Updates `typescript` from 5.7.3 to 5.8.3
- [Release notes](https://github.com/microsoft/TypeScript/releases)
- [Changelog](https://github.com/microsoft/TypeScript/blob/main/azure-pipelines.release-publish.yml)
- [Commits](https://github.com/microsoft/TypeScript/compare/v5.7.3...v5.8.3)

---
updated-dependencies:
- dependency-name: "@dotenvx/dotenvx"
  dependency-version: 1.44.1
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: dev-minor-updates
- dependency-name: "@types/node"
  dependency-version: 22.15.29
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: dev-minor-updates
- dependency-name: drizzle-kit
  dependency-version: 0.31.1
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: dev-minor-updates
- dependency-name: typescript
  dependency-version: 5.8.3
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: dev-minor-updates
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-01 15:16:59 +02:00
dependabot[bot]
ea1cd4b0d4 Bump the prod-minor-updates group with 27 updates (#121)
* Bump the prod-minor-updates group with 27 updates

Bumps the prod-minor-updates group with 27 updates:

| Package | From | To |
| --- | --- | --- |
| [@radix-ui/react-checkbox](https://github.com/radix-ui/primitives) | `1.1.3` | `1.3.2` |
| [@radix-ui/react-radio-group](https://github.com/radix-ui/primitives) | `1.2.2` | `1.3.7` |
| [@radix-ui/react-select](https://github.com/radix-ui/primitives) | `2.1.4` | `2.2.5` |
| [@radix-ui/react-slot](https://github.com/radix-ui/primitives) | `1.1.1` | `1.2.3` |
| [@radix-ui/react-switch](https://github.com/radix-ui/primitives) | `1.1.2` | `1.2.5` |
| [@react-email/render](https://github.com/resend/react-email/tree/HEAD/packages/render) | `1.0.6` | `1.1.2` |
| [@tanstack/react-table](https://github.com/TanStack/table/tree/HEAD/packages/react-table) | `8.20.6` | `8.21.3` |
| [arctic](https://github.com/pilcrowonpaper/arctic) | `3.6.0` | `3.7.0` |
| [axios](https://github.com/axios/axios) | `1.8.4` | `1.9.0` |
| [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) | `11.7.0` | `11.10.0` |
| [cmdk](https://github.com/pacocoursey/cmdk/tree/HEAD/cmdk) | `1.0.4` | `1.1.1` |
| [drizzle-orm](https://github.com/drizzle-team/drizzle-orm) | `0.38.3` | `0.44.1` |
| [eslint](https://github.com/eslint/eslint) | `9.17.0` | `9.28.0` |
| [eslint-config-next](https://github.com/vercel/next.js/tree/HEAD/packages/eslint-config-next) | `15.1.3` | `15.3.3` |
| [helmet](https://github.com/helmetjs/helmet) | `8.0.0` | `8.1.0` |
| [lucide-react](https://github.com/lucide-icons/lucide/tree/HEAD/packages/lucide-react) | `0.469.0` | `0.511.0` |
| [next](https://github.com/vercel/next.js) | `15.2.4` | `15.3.3` |
| [npm](https://github.com/npm/cli) | `11.2.0` | `11.4.1` |
| [react](https://github.com/facebook/react/tree/HEAD/packages/react) | `19.0.0` | `19.1.0` |
| [@types/react](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react) | `19.1.1` | `19.1.6` |
| [react-dom](https://github.com/facebook/react/tree/HEAD/packages/react-dom) | `19.0.0` | `19.1.0` |
| [@types/react-dom](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react-dom) | `19.1.2` | `19.1.5` |
| [react-hook-form](https://github.com/react-hook-form/react-hook-form) | `7.54.2` | `7.56.4` |
| [semver](https://github.com/npm/node-semver) | `7.6.3` | `7.7.2` |
| [@types/semver](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/semver) | `7.5.8` | `7.7.0` |
| [tw-animate-css](https://github.com/Wombosvideo/tw-animate-css) | `1.2.8` | `1.3.2` |
| [zod](https://github.com/colinhacks/zod) | `3.24.1` | `3.25.46` |


Updates `@radix-ui/react-checkbox` from 1.1.3 to 1.3.2
- [Changelog](https://github.com/radix-ui/primitives/blob/main/release-process.md)
- [Commits](https://github.com/radix-ui/primitives/commits)

Updates `@radix-ui/react-radio-group` from 1.2.2 to 1.3.7
- [Changelog](https://github.com/radix-ui/primitives/blob/main/release-process.md)
- [Commits](https://github.com/radix-ui/primitives/commits)

Updates `@radix-ui/react-select` from 2.1.4 to 2.2.5
- [Changelog](https://github.com/radix-ui/primitives/blob/main/release-process.md)
- [Commits](https://github.com/radix-ui/primitives/commits)

Updates `@radix-ui/react-slot` from 1.1.1 to 1.2.3
- [Changelog](https://github.com/radix-ui/primitives/blob/main/release-process.md)
- [Commits](https://github.com/radix-ui/primitives/commits)

Updates `@radix-ui/react-switch` from 1.1.2 to 1.2.5
- [Changelog](https://github.com/radix-ui/primitives/blob/main/release-process.md)
- [Commits](https://github.com/radix-ui/primitives/commits)

Updates `@react-email/render` from 1.0.6 to 1.1.2
- [Release notes](https://github.com/resend/react-email/releases)
- [Changelog](https://github.com/resend/react-email/blob/canary/packages/render/CHANGELOG.md)
- [Commits](https://github.com/resend/react-email/commits/@react-email/render@1.1.2/packages/render)

Updates `@tanstack/react-table` from 8.20.6 to 8.21.3
- [Release notes](https://github.com/TanStack/table/releases)
- [Commits](https://github.com/TanStack/table/commits/v8.21.3/packages/react-table)

Updates `arctic` from 3.6.0 to 3.7.0
- [Release notes](https://github.com/pilcrowonpaper/arctic/releases)
- [Commits](https://github.com/pilcrowonpaper/arctic/compare/v3.6.0...v3.7.0)

Updates `axios` from 1.8.4 to 1.9.0
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v1.8.4...v1.9.0)

Updates `better-sqlite3` from 11.7.0 to 11.10.0
- [Release notes](https://github.com/WiseLibs/better-sqlite3/releases)
- [Commits](https://github.com/WiseLibs/better-sqlite3/compare/v11.7.0...v11.10.0)

Updates `cmdk` from 1.0.4 to 1.1.1
- [Release notes](https://github.com/pacocoursey/cmdk/releases)
- [Commits](https://github.com/pacocoursey/cmdk/commits/v1.1.1/cmdk)

Updates `drizzle-orm` from 0.38.3 to 0.44.1
- [Release notes](https://github.com/drizzle-team/drizzle-orm/releases)
- [Commits](https://github.com/drizzle-team/drizzle-orm/compare/0.38.3...0.44.1)

Updates `eslint` from 9.17.0 to 9.28.0
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/compare/v9.17.0...v9.28.0)

Updates `eslint-config-next` from 15.1.3 to 15.3.3
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/commits/v15.3.3/packages/eslint-config-next)

Updates `helmet` from 8.0.0 to 8.1.0
- [Changelog](https://github.com/helmetjs/helmet/blob/main/CHANGELOG.md)
- [Commits](https://github.com/helmetjs/helmet/compare/v8.0.0...v8.1.0)

Updates `lucide-react` from 0.469.0 to 0.511.0
- [Release notes](https://github.com/lucide-icons/lucide/releases)
- [Commits](https://github.com/lucide-icons/lucide/commits/0.511.0/packages/lucide-react)

Updates `next` from 15.2.4 to 15.3.3
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/compare/v15.2.4...v15.3.3)

Updates `npm` from 11.2.0 to 11.4.1
- [Release notes](https://github.com/npm/cli/releases)
- [Changelog](https://github.com/npm/cli/blob/latest/CHANGELOG.md)
- [Commits](https://github.com/npm/cli/compare/v11.2.0...v11.4.1)

Updates `react` from 19.0.0 to 19.1.0
- [Release notes](https://github.com/facebook/react/releases)
- [Changelog](https://github.com/facebook/react/blob/main/CHANGELOG.md)
- [Commits](https://github.com/facebook/react/commits/v19.1.0/packages/react)

Updates `@types/react` from 19.1.1 to 19.1.6
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react)

Updates `react-dom` from 19.0.0 to 19.1.0
- [Release notes](https://github.com/facebook/react/releases)
- [Changelog](https://github.com/facebook/react/blob/main/CHANGELOG.md)
- [Commits](https://github.com/facebook/react/commits/v19.1.0/packages/react-dom)

Updates `@types/react-dom` from 19.1.2 to 19.1.5
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react-dom)

Updates `react-hook-form` from 7.54.2 to 7.56.4
- [Release notes](https://github.com/react-hook-form/react-hook-form/releases)
- [Changelog](https://github.com/react-hook-form/react-hook-form/blob/master/CHANGELOG.md)
- [Commits](https://github.com/react-hook-form/react-hook-form/compare/v7.54.2...v7.56.4)

Updates `semver` from 7.6.3 to 7.7.2
- [Release notes](https://github.com/npm/node-semver/releases)
- [Changelog](https://github.com/npm/node-semver/blob/main/CHANGELOG.md)
- [Commits](https://github.com/npm/node-semver/compare/v7.6.3...v7.7.2)

Updates `@types/semver` from 7.5.8 to 7.7.0
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/semver)

Updates `tw-animate-css` from 1.2.8 to 1.3.2
- [Release notes](https://github.com/Wombosvideo/tw-animate-css/releases)
- [Commits](https://github.com/Wombosvideo/tw-animate-css/compare/v1.2.8...v1.3.2)

Updates `zod` from 3.24.1 to 3.25.46
- [Release notes](https://github.com/colinhacks/zod/releases)
- [Commits](https://github.com/colinhacks/zod/compare/v3.24.1...v3.25.46)

---
updated-dependencies:
- dependency-name: "@radix-ui/react-checkbox"
  dependency-version: 1.3.2
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: prod-minor-updates
- dependency-name: "@radix-ui/react-radio-group"
  dependency-version: 1.3.7
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: prod-minor-updates
- dependency-name: "@radix-ui/react-select"
  dependency-version: 2.2.5
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: prod-minor-updates
- dependency-name: "@radix-ui/react-slot"
  dependency-version: 1.2.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: prod-minor-updates
- dependency-name: "@radix-ui/react-switch"
  dependency-version: 1.2.5
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: prod-minor-updates
- dependency-name: "@react-email/render"
  dependency-version: 1.1.2
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: prod-minor-updates
- dependency-name: "@tanstack/react-table"
  dependency-version: 8.21.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: prod-minor-updates
- dependency-name: arctic
  dependency-version: 3.7.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: prod-minor-updates
- dependency-name: axios
  dependency-version: 1.9.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: prod-minor-updates
- dependency-name: better-sqlite3
  dependency-version: 11.10.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: prod-minor-updates
- dependency-name: cmdk
  dependency-version: 1.1.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: prod-minor-updates
- dependency-name: drizzle-orm
  dependency-version: 0.44.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: prod-minor-updates
- dependency-name: eslint
  dependency-version: 9.28.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: prod-minor-updates
- dependency-name: eslint-config-next
  dependency-version: 15.3.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: prod-minor-updates
- dependency-name: helmet
  dependency-version: 8.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: prod-minor-updates
- dependency-name: lucide-react
  dependency-version: 0.511.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: prod-minor-updates
- dependency-name: next
  dependency-version: 15.3.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: prod-minor-updates
- dependency-name: npm
  dependency-version: 11.4.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: prod-minor-updates
- dependency-name: react
  dependency-version: 19.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: prod-minor-updates
- dependency-name: "@types/react"
  dependency-version: 19.1.6
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: prod-minor-updates
- dependency-name: react-dom
  dependency-version: 19.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: prod-minor-updates
- dependency-name: "@types/react-dom"
  dependency-version: 19.1.5
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: prod-minor-updates
- dependency-name: react-hook-form
  dependency-version: 7.56.4
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: prod-minor-updates
- dependency-name: semver
  dependency-version: 7.7.2
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: prod-minor-updates
- dependency-name: "@types/semver"
  dependency-version: 7.7.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: prod-minor-updates
- dependency-name: tw-animate-css
  dependency-version: 1.3.2
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: prod-minor-updates
- dependency-name: zod
  dependency-version: 3.25.46
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: prod-minor-updates
...

Signed-off-by: dependabot[bot] <support@github.com>

* modified:   package-lock.json
	modified:   package.json

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Marvin <127591405+Lokowitz@users.noreply.github.com>
2025-06-01 15:07:49 +02:00
dependabot[bot]
be0c7444e9 Bump node from 20-alpine to 24-alpine in the major-updates group (#111)
Bumps the major-updates group with 1 update: node.


Updates `node` from 20-alpine to 24-alpine

---
updated-dependencies:
- dependency-name: node
  dependency-version: 24-alpine
  dependency-type: direct:production
  dependency-group: major-updates
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-01 14:12:08 +02:00
dependabot[bot]
858c809514 Bump the dev-patch-updates group with 8 updates (#117)
Bumps the dev-patch-updates group with 8 updates:

| Package | From | To |
| --- | --- | --- |
| [@tailwindcss/postcss](https://github.com/tailwindlabs/tailwindcss/tree/HEAD/packages/@tailwindcss-postcss) | `4.1.4` | `4.1.8` |
| [@types/cors](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/cors) | `2.8.17` | `2.8.18` |
| [esbuild](https://github.com/evanw/esbuild) | `0.25.2` | `0.25.5` |
| [postcss](https://github.com/postcss/postcss) | `8.5.1` | `8.5.4` |
| [react-email](https://github.com/resend/react-email/tree/HEAD/packages/react-email) | `4.0.6` | `4.0.15` |
| [tailwindcss](https://github.com/tailwindlabs/tailwindcss/tree/HEAD/packages/tailwindcss) | `4.1.4` | `4.1.8` |
| [tsc-alias](https://github.com/justkey007/tsc-alias) | `1.8.10` | `1.8.16` |
| [tsx](https://github.com/privatenumber/tsx) | `4.19.3` | `4.19.4` |


Updates `@tailwindcss/postcss` from 4.1.4 to 4.1.8
- [Release notes](https://github.com/tailwindlabs/tailwindcss/releases)
- [Changelog](https://github.com/tailwindlabs/tailwindcss/blob/main/CHANGELOG.md)
- [Commits](https://github.com/tailwindlabs/tailwindcss/commits/v4.1.8/packages/@tailwindcss-postcss)

Updates `@types/cors` from 2.8.17 to 2.8.18
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/cors)

Updates `esbuild` from 0.25.2 to 0.25.5
- [Release notes](https://github.com/evanw/esbuild/releases)
- [Changelog](https://github.com/evanw/esbuild/blob/main/CHANGELOG.md)
- [Commits](https://github.com/evanw/esbuild/compare/v0.25.2...v0.25.5)

Updates `postcss` from 8.5.1 to 8.5.4
- [Release notes](https://github.com/postcss/postcss/releases)
- [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md)
- [Commits](https://github.com/postcss/postcss/compare/8.5.1...8.5.4)

Updates `react-email` from 4.0.6 to 4.0.15
- [Release notes](https://github.com/resend/react-email/releases)
- [Changelog](https://github.com/resend/react-email/blob/canary/packages/react-email/CHANGELOG.md)
- [Commits](https://github.com/resend/react-email/commits/react-email@4.0.15/packages/react-email)

Updates `tailwindcss` from 4.1.4 to 4.1.8
- [Release notes](https://github.com/tailwindlabs/tailwindcss/releases)
- [Changelog](https://github.com/tailwindlabs/tailwindcss/blob/main/CHANGELOG.md)
- [Commits](https://github.com/tailwindlabs/tailwindcss/commits/v4.1.8/packages/tailwindcss)

Updates `tsc-alias` from 1.8.10 to 1.8.16
- [Release notes](https://github.com/justkey007/tsc-alias/releases)
- [Commits](https://github.com/justkey007/tsc-alias/compare/v1.8.10...v1.8.16)

Updates `tsx` from 4.19.3 to 4.19.4
- [Release notes](https://github.com/privatenumber/tsx/releases)
- [Changelog](https://github.com/privatenumber/tsx/blob/master/release.config.cjs)
- [Commits](https://github.com/privatenumber/tsx/compare/v4.19.3...v4.19.4)

---
updated-dependencies:
- dependency-name: "@tailwindcss/postcss"
  dependency-version: 4.1.8
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-patch-updates
- dependency-name: "@types/cors"
  dependency-version: 2.8.18
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-patch-updates
- dependency-name: esbuild
  dependency-version: 0.25.5
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-patch-updates
- dependency-name: postcss
  dependency-version: 8.5.4
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-patch-updates
- dependency-name: react-email
  dependency-version: 4.0.15
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-patch-updates
- dependency-name: tailwindcss
  dependency-version: 4.1.8
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-patch-updates
- dependency-name: tsc-alias
  dependency-version: 1.8.16
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-patch-updates
- dependency-name: tsx
  dependency-version: 4.19.4
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-patch-updates
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-01 14:11:51 +02:00
dependabot[bot]
10ff2c8a65 Bump the prod-patch-updates group with 19 updates (#120)
Bumps the prod-patch-updates group with 19 updates:

| Package | From | To |
| --- | --- | --- |
| [@asteasolutions/zod-to-openapi](https://github.com/asteasolutions/zod-to-openapi) | `7.3.0` | `7.3.2` |
| [@radix-ui/react-avatar](https://github.com/radix-ui/primitives) | `1.1.2` | `1.1.10` |
| [@radix-ui/react-collapsible](https://github.com/radix-ui/primitives) | `1.1.2` | `1.1.11` |
| [@radix-ui/react-dialog](https://github.com/radix-ui/primitives) | `1.1.4` | `1.1.14` |
| [@radix-ui/react-dropdown-menu](https://github.com/radix-ui/primitives) | `2.1.4` | `2.1.15` |
| [@radix-ui/react-label](https://github.com/radix-ui/primitives) | `2.1.1` | `2.1.7` |
| [@radix-ui/react-popover](https://github.com/radix-ui/primitives) | `1.1.4` | `1.1.14` |
| [@radix-ui/react-progress](https://github.com/radix-ui/primitives) | `1.1.4` | `1.1.7` |
| [@radix-ui/react-separator](https://github.com/radix-ui/primitives) | `1.1.1` | `1.1.7` |
| [@radix-ui/react-tabs](https://github.com/radix-ui/primitives) | `1.1.2` | `1.1.12` |
| [@radix-ui/react-toast](https://github.com/radix-ui/primitives) | `1.2.4` | `1.2.14` |
| [@react-email/components](https://github.com/resend/react-email/tree/HEAD/packages/components) | `0.0.36` | `0.0.41` |
| [@react-email/tailwind](https://github.com/resend/react-email/tree/HEAD/packages/tailwind) | `1.0.4` | `1.0.5` |
| [glob](https://github.com/isaacs/node-glob) | `11.0.0` | `11.0.2` |
| [input-otp](https://github.com/guilhermerodz/input-otp/tree/HEAD/packages/input-otp) | `1.4.1` | `1.4.2` |
| [next-themes](https://github.com/pacocoursey/next-themes) | `0.4.4` | `0.4.6` |
| [ws](https://github.com/websockets/ws) | `8.18.0` | `8.18.2` |
| [@types/ws](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/ws) | `8.5.13` | `8.18.1` |
| [zod-validation-error](https://github.com/causaly/zod-validation-error) | `3.4.0` | `3.4.1` |


Updates `@asteasolutions/zod-to-openapi` from 7.3.0 to 7.3.2
- [Release notes](https://github.com/asteasolutions/zod-to-openapi/releases)
- [Commits](https://github.com/asteasolutions/zod-to-openapi/compare/v7.3.0...v7.3.2)

Updates `@radix-ui/react-avatar` from 1.1.2 to 1.1.10
- [Changelog](https://github.com/radix-ui/primitives/blob/main/release-process.md)
- [Commits](https://github.com/radix-ui/primitives/commits)

Updates `@radix-ui/react-collapsible` from 1.1.2 to 1.1.11
- [Changelog](https://github.com/radix-ui/primitives/blob/main/release-process.md)
- [Commits](https://github.com/radix-ui/primitives/commits)

Updates `@radix-ui/react-dialog` from 1.1.4 to 1.1.14
- [Changelog](https://github.com/radix-ui/primitives/blob/main/release-process.md)
- [Commits](https://github.com/radix-ui/primitives/commits)

Updates `@radix-ui/react-dropdown-menu` from 2.1.4 to 2.1.15
- [Changelog](https://github.com/radix-ui/primitives/blob/main/release-process.md)
- [Commits](https://github.com/radix-ui/primitives/commits)

Updates `@radix-ui/react-label` from 2.1.1 to 2.1.7
- [Changelog](https://github.com/radix-ui/primitives/blob/main/release-process.md)
- [Commits](https://github.com/radix-ui/primitives/commits)

Updates `@radix-ui/react-popover` from 1.1.4 to 1.1.14
- [Changelog](https://github.com/radix-ui/primitives/blob/main/release-process.md)
- [Commits](https://github.com/radix-ui/primitives/commits)

Updates `@radix-ui/react-progress` from 1.1.4 to 1.1.7
- [Changelog](https://github.com/radix-ui/primitives/blob/main/release-process.md)
- [Commits](https://github.com/radix-ui/primitives/commits)

Updates `@radix-ui/react-separator` from 1.1.1 to 1.1.7
- [Changelog](https://github.com/radix-ui/primitives/blob/main/release-process.md)
- [Commits](https://github.com/radix-ui/primitives/commits)

Updates `@radix-ui/react-tabs` from 1.1.2 to 1.1.12
- [Changelog](https://github.com/radix-ui/primitives/blob/main/release-process.md)
- [Commits](https://github.com/radix-ui/primitives/commits)

Updates `@radix-ui/react-toast` from 1.2.4 to 1.2.14
- [Changelog](https://github.com/radix-ui/primitives/blob/main/release-process.md)
- [Commits](https://github.com/radix-ui/primitives/commits)

Updates `@react-email/components` from 0.0.36 to 0.0.41
- [Release notes](https://github.com/resend/react-email/releases)
- [Changelog](https://github.com/resend/react-email/blob/canary/packages/components/CHANGELOG.md)
- [Commits](https://github.com/resend/react-email/commits/@react-email/components@0.0.41/packages/components)

Updates `@react-email/tailwind` from 1.0.4 to 1.0.5
- [Release notes](https://github.com/resend/react-email/releases)
- [Changelog](https://github.com/resend/react-email/blob/canary/packages/tailwind/CHANGELOG.md)
- [Commits](https://github.com/resend/react-email/commits/@react-email/tailwind@1.0.5/packages/tailwind)

Updates `glob` from 11.0.0 to 11.0.2
- [Changelog](https://github.com/isaacs/node-glob/blob/main/changelog.md)
- [Commits](https://github.com/isaacs/node-glob/compare/v11.0.0...v11.0.2)

Updates `input-otp` from 1.4.1 to 1.4.2
- [Changelog](https://github.com/guilhermerodz/input-otp/blob/master/CHANGELOG.md)
- [Commits](https://github.com/guilhermerodz/input-otp/commits/HEAD/packages/input-otp)

Updates `next-themes` from 0.4.4 to 0.4.6
- [Release notes](https://github.com/pacocoursey/next-themes/releases)
- [Commits](https://github.com/pacocoursey/next-themes/compare/v0.4.4...v0.4.6)

Updates `ws` from 8.18.0 to 8.18.2
- [Release notes](https://github.com/websockets/ws/releases)
- [Commits](https://github.com/websockets/ws/compare/8.18.0...8.18.2)

Updates `@types/ws` from 8.5.13 to 8.18.1
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/ws)

Updates `zod-validation-error` from 3.4.0 to 3.4.1
- [Release notes](https://github.com/causaly/zod-validation-error/releases)
- [Changelog](https://github.com/causaly/zod-validation-error/blob/main/CHANGELOG.md)
- [Commits](https://github.com/causaly/zod-validation-error/compare/v3.4.0...v3.4.1)

---
updated-dependencies:
- dependency-name: "@asteasolutions/zod-to-openapi"
  dependency-version: 7.3.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: prod-patch-updates
- dependency-name: "@radix-ui/react-avatar"
  dependency-version: 1.1.10
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: prod-patch-updates
- dependency-name: "@radix-ui/react-collapsible"
  dependency-version: 1.1.11
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: prod-patch-updates
- dependency-name: "@radix-ui/react-dialog"
  dependency-version: 1.1.14
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: prod-patch-updates
- dependency-name: "@radix-ui/react-dropdown-menu"
  dependency-version: 2.1.15
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: prod-patch-updates
- dependency-name: "@radix-ui/react-label"
  dependency-version: 2.1.7
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: prod-patch-updates
- dependency-name: "@radix-ui/react-popover"
  dependency-version: 1.1.14
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: prod-patch-updates
- dependency-name: "@radix-ui/react-progress"
  dependency-version: 1.1.7
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: prod-patch-updates
- dependency-name: "@radix-ui/react-separator"
  dependency-version: 1.1.7
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: prod-patch-updates
- dependency-name: "@radix-ui/react-tabs"
  dependency-version: 1.1.12
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: prod-patch-updates
- dependency-name: "@radix-ui/react-toast"
  dependency-version: 1.2.14
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: prod-patch-updates
- dependency-name: "@react-email/components"
  dependency-version: 0.0.41
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: prod-patch-updates
- dependency-name: "@react-email/tailwind"
  dependency-version: 1.0.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: prod-patch-updates
- dependency-name: glob
  dependency-version: 11.0.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: prod-patch-updates
- dependency-name: input-otp
  dependency-version: 1.4.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: prod-patch-updates
- dependency-name: next-themes
  dependency-version: 0.4.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: prod-patch-updates
- dependency-name: ws
  dependency-version: 8.18.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: prod-patch-updates
- dependency-name: "@types/ws"
  dependency-version: 8.18.1
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: prod-patch-updates
- dependency-name: zod-validation-error
  dependency-version: 3.4.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: prod-patch-updates
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-01 14:08:55 +02:00
PrtmPhlp
167d0b6867 unnecessary file 2025-06-01 12:19:09 +02:00
PrtmPhlp
8c121daf6c docker run command in detached mode 2025-06-01 12:15:14 +02:00
Marvin
a23d437bd3 Create dependabot.yml 2025-06-01 11:01:55 +02:00
Owen
cd280d1396 Also start the service 2025-05-31 11:02:53 -04:00
Owen
d18200739a Check if docker is running 2025-05-31 11:01:03 -04:00
Owen
a62b2e8d10 Use 41 for dnf5 2025-05-31 10:56:38 -04:00
Owen Schwartz
c92069a1f4 Merge pull request #796 from socheatsok78/non-root-installer
Allow installer to run without requires `sudo`
2025-05-31 10:48:47 -04:00
Damien Rajon
c5e37c1608 send user data to badger when authenticated 2025-05-30 20:37:21 +02:00
Rajesh V
948eb7f6d0 docker socket 2025-05-29 22:34:05 +05:30
miloschwartz
62a0104e70 Merge branch 'dev' into postgres 2025-05-29 12:09:56 -04:00
miloschwartz
6dd8db5cd1 add new logo 2025-05-29 12:09:49 -04:00
Socheat Sok
9ea7275371 Ensure installer check if current user is in docker group 2025-05-29 22:49:42 +07:00
Socheat Sok
c997b8625f Re: "Allow installer to run without sudo & only need it when need to install Docker" 2025-05-29 21:55:50 +07:00
Socheat Sok
6f3514199a Revert "Allow installer to run without sudo & only need it when need to install Docker"
This reverts commit 56fd366a7d.
2025-05-29 21:45:57 +07:00
Owen
0cfc4d7dad Update for new dnf 2025-05-29 10:40:15 -04:00
Socheat Sok
56fd366a7d Allow installer to run without sudo & only need it when need to install Docker 2025-05-29 16:05:31 +07:00
Owen
23b5dcfbed Update readme 2025-05-27 20:47:23 -04:00
miloschwartz
30ebbaaef0 Merge branch 'dev' into postgres 2025-05-27 17:19:43 -04:00
Owen Schwartz
b467d6afa1 Merge pull request #779 from jpgnz/main
README.md - Append https:// to geoblock
2025-05-24 23:59:11 -04:00
James Graham
373441b7ab Fix geolock url in README.md
Geoblock url needed https:// appended
2025-05-25 13:36:44 +12:00
Owen Schwartz
825730052b Merge pull request #736 from TuncTaylan/traefik-log-rotation
traefik log rotation with default values
2025-05-16 10:28:48 -04:00
Owen Schwartz
edc8716297 Merge pull request #735 from TuncTaylan/traefik-update
update to traefik v3.4.0
2025-05-16 10:27:50 -04:00
Taylan
3ee4aaf194 log rotation with default values 2025-05-16 09:20:40 +02:00
Taylan
b9a5d486b9 update to v3.4.0 2025-05-16 09:16:49 +02:00
miloschwartz
dc66ebeed6 Merge branch 'dev' into postgres 2025-05-13 15:08:05 -04:00
Milo Schwartz
1f584bf3e8 Merge pull request #717 from fosrl/dev
1.4.0
2025-05-13 11:12:03 -04:00
miloschwartz
5b0200154a add feature parity 2025-05-13 11:09:38 -04:00
miloschwartz
1e55d96376 add different driver 2025-05-12 17:21:03 -04:00
Milo Schwartz
a512148348 Merge pull request #701 from fosrl/dev
1.3.2
2025-05-10 11:30:07 -04:00
miloschwartz
d9eccd6c13 bump version 2025-05-10 11:28:22 -04:00
miloschwartz
492669f68a set default congig values 2025-05-09 18:32:14 -04:00
miloschwartz
caded23b51 allow root path 2025-05-09 17:37:55 -04:00
miloschwartz
e9cc48a3ae fix bug causing duplicate targets 2025-05-09 17:18:42 -04:00
miloschwartz
4ed98c227b fix setting tlsServerName and hostHeader conflict 2025-05-09 17:12:01 -04:00
miloschwartz
f66fb7d4a3 fix justification for profile icon 2025-05-09 17:09:22 -04:00
miloschwartz
f25990a9a7 add id token and claims to debug logs 2025-05-09 16:46:51 -04:00
miloschwartz
21d5b67ef1 Merge branch 'main' into dev 2025-05-09 16:44:09 -04:00
Milo Schwartz
198810121c Update README.md 2025-05-09 16:44:01 -04:00
Owen
83c0379c6b Merge branch 'dev' of github.com:fosrl/pangolin into dev 2025-05-03 22:04:29 -04:00
Milo Schwartz
21f1326045 Merge pull request #651 from fosrl/dev
Dev
2025-05-03 13:07:18 -04:00
miloschwartz
f62e32724c Merge branch 'main' into dev 2025-05-03 12:43:50 -04:00
miloschwartz
5e052a446a 1.3.1 2025-05-03 12:25:02 -04:00
Owen
9a167b5acb Dont overwrite the secret and crowdsec vars 2025-05-02 14:16:10 -04:00
Milo Schwartz
5d2f3186cc Update README.md 2025-05-02 12:25:49 -04:00
322 changed files with 7865 additions and 4716 deletions

35
.github/dependabot.yml vendored Normal file
View File

@@ -0,0 +1,35 @@
version: 2
updates:
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "daily"
groups:
dev-patch-updates:
dependency-type: "development"
update-types:
- "patch"
dev-minor-updates:
dependency-type: "development"
update-types:
- "minor"
prod-patch-updates:
dependency-type: "production"
update-types:
- "patch"
prod-minor-updates:
dependency-type: "production"
update-types:
- "minor"
- package-ecosystem: "docker"
directory: "/"
schedule:
interval: "daily"
groups:
patch-updates:
update-types:
- "patch"
minor-updates:
update-types:
- "minor"

View File

@@ -8,9 +8,11 @@ RUN npm install
COPY . .
RUN npx drizzle-kit generate --dialect sqlite --schema ./server/db/schemas/ --out init
RUN echo 'export * from "./sqlite";' > server/db/index.ts
RUN npm run build
RUN npx drizzle-kit generate --dialect sqlite --schema ./server/db/sqlite/schema.ts --out init
RUN npm run build:sqlite
FROM node:20-alpine AS runner
@@ -32,4 +34,4 @@ COPY server/db/names.json ./dist/names.json
COPY public ./public
CMD ["npm", "start"]
CMD ["npm", "run", "start:sqlite"]

37
Dockerfile.pg Normal file
View File

@@ -0,0 +1,37 @@
FROM node:20-alpine AS builder
WORKDIR /app
# COPY package.json package-lock.json ./
COPY package.json ./
RUN npm install
COPY . .
RUN echo 'export * from "./pg";' > server/db/index.ts
RUN npx drizzle-kit generate --dialect postgresql --schema ./server/db/pg/schema.ts --out init
RUN npm run build:pg
FROM node:20-alpine AS runner
WORKDIR /app
# Curl used for the health checks
RUN apk add --no-cache curl
# COPY package.json package-lock.json ./
COPY package.json ./
RUN npm install --only=production && npm cache clean --force
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/init ./dist/init
COPY server/db/names.json ./dist/names.json
COPY public ./public
CMD ["npm", "run", "start:pg"]

32
LICENSE
View File

@@ -1,35 +1,3 @@
Copyright (c) 2025 Fossorial, LLC.
Portions of this software are licensed as follows:
* All files that include a header specifying they are licensed under the
"Fossorial Commercial License" are governed by the Fossorial Commercial
License terms. The specific terms applicable to each customer depend on the
commercial license tier agreed upon in writing with Fossorial LLC.
Unauthorized use, copying, modification, or distribution is strictly
prohibited.
* All files that include a header specifying they are licensed under the GNU
Affero General Public License, Version 3 ("AGPL-3"), are governed by the
AGPL-3 terms. A full copy of the AGPL-3 license is provided below. However,
these files are also available under the Fossorial Commercial License if a
separate commercial license agreement has been executed between the customer
and Fossorial LLC.
* All files without a license header are, by default, licensed under the GNU
Affero General Public License, Version 3 (AGPL-3). These files may also be
made available under the Fossorial Commercial License upon agreement with
Fossorial LLC.
* All third-party components included in this repository are licensed under
their respective original licenses, as provided by their authors.
Please consult the header of each individual file to determine the applicable
license. For AGPL-3 licensed files, dual-licensing under the Fossorial
Commercial License is available subject to written agreement with Fossorial
LLC.
GNU AFFERO GENERAL PUBLIC LICENSE
Version 3, 19 November 2007

View File

@@ -5,6 +5,8 @@ build-release:
fi
docker buildx build --platform linux/arm64,linux/amd64 -t fosrl/pangolin:latest -f Dockerfile --push .
docker buildx build --platform linux/arm64,linux/amd64 -t fosrl/pangolin:$(tag) -f Dockerfile --push .
docker buildx build --platform linux/arm64,linux/amd64 -t fosrl/pangolin:postgresql-latest -f Dockerfile.pg --push .
docker buildx build --platform linux/arm64,linux/amd64 -t fosrl/pangolin:postgresql-$(tag) -f Dockerfile.pg --push .
build-arm:
docker buildx build --platform linux/arm64 -t fosrl/pangolin:latest .

View File

@@ -1,15 +1,13 @@
<div align="center">
<h2 align="center"><a href="https://fossorial.io"><img alt="pangolin" src="public/logo//word_mark.png" width="400" /></a></h2>
[![Documentation](https://img.shields.io/badge/docs-latest-blue.svg?style=flat-square)](https://docs.fossorial.io/)
[![Docker](https://img.shields.io/docker/pulls/fosrl/pangolin?style=flat-square)](https://hub.docker.com/r/fosrl/pangolin)
![Stars](https://img.shields.io/github/stars/fosrl/pangolin?style=flat-square)
[![Discord](https://img.shields.io/discord/1325658630518865980?logo=discord&style=flat-square)](https://discord.gg/HCJR8Xhme4)
[![Youtube](https://img.shields.io/badge/YouTube-red?logo=youtube&logoColor=white&style=flat-square)](https://www.youtube.com/@fossorial-app)
<h2>
<picture>
<source media="(prefers-color-scheme: dark)" srcset="public/logo/word_mark_white.png">
<img alt="Pangolin Logo" src="public/logo/word_mark_black.png" width="250">
</picture>
</h2>
</div>
<h3 align="center">Tunneled Mesh Reverse Proxy Server with Access Control</h3>
<h4 align="center">Tunneled Reverse Proxy Server with Access Control</h4>
<div align="center">
_Your own self-hosted zero trust tunnel._
@@ -30,6 +28,12 @@ _Your own self-hosted zero trust tunnel._
Contact Us
</a>
</h5>
[![Docker](https://img.shields.io/docker/pulls/fosrl/pangolin?style=flat-square)](https://hub.docker.com/r/fosrl/pangolin)
![Stars](https://img.shields.io/github/stars/fosrl/pangolin?style=flat-square)
[![Discord](https://img.shields.io/discord/1325658630518865980?logo=discord&style=flat-square)](https://discord.gg/HCJR8Xhme4)
[![Youtube](https://img.shields.io/badge/YouTube-red?logo=youtube&logoColor=white&style=flat-square)](https://www.youtube.com/@fossorial-app)
</div>
Pangolin is a self-hosted tunneled reverse proxy server with identity and access control, designed to securely expose private resources on distributed networks. Acting as a central hub, it connects isolated networks — even those behind restrictive firewalls — through encrypted tunnels, enabling easy access to remote services without opening ports.
@@ -83,7 +87,7 @@ _Resources page of Pangolin dashboard (dark mode) showing multiple resources ava
### Modular Design
- Extend functionality with existing [Traefik](https://github.com/traefik/traefik) plugins, such as [CrowdSec](https://plugins.traefik.io/plugins/6335346ca4caa9ddeffda116/crowdsec-bouncer-traefik-plugin) and [Geoblock](github.com/PascalMinder/geoblock).
- Extend functionality with existing [Traefik](https://github.com/traefik/traefik) plugins, such as [CrowdSec](https://plugins.traefik.io/plugins/6335346ca4caa9ddeffda116/crowdsec-bouncer-traefik-plugin) and [Geoblock](https://github.com/PascalMinder/geoblock).
- **Automatically install and configure Crowdsec via Pangolin's installer script.**
- Attach as many sites to the central server as you wish.
@@ -96,19 +100,19 @@ _Resources page of Pangolin dashboard (dark mode) showing multiple resources ava
- Deploy the Docker Compose stack onto a VPS hosted on a cloud platform like RackNerd, Amazon EC2, DigitalOcean Droplet, or similar. There are many cheap VPS hosting options available to suit your needs.
> [!TIP]
> Many of our users have had a great experience with [RackNerd](https://my.racknerd.com/aff.php?aff=13788). Depending on promotions, you can likely get a **VPS with 1 vCPU, 1GB RAM, and ~20GB SSD for just around $12/year**. That's a great deal!
> Many of our users have had a great experience with [RackNerd](https://my.racknerd.com/aff.php?aff=13788). Depending on promotions, you can get a [**VPS with 1 vCPU, 1GB RAM, and ~20GB SSD for just around $12/year**](https://my.racknerd.com/aff.php?aff=13788&pid=912). That's a great deal!
> We are part of the [RackNerd](https://my.racknerd.com/aff.php?aff=13788) affiliate program, so if you purchase through [our link](https://my.racknerd.com/aff.php?aff=13788), we receive a small commission which helps us maintain the project and keep it free for everyone.
2. **Domain Configuration**:
1. **Domain Configuration**:
- Point your domain name to the VPS and configure Pangolin with your preferred settings.
3. **Connect Private Sites**:
2. **Connect Private Sites**:
- Install Newt or use another WireGuard client on private sites.
- Automatically establish a connection from these sites to the central server.
4. **Expose Resources**:
3. **Expose Resources**:
- Add resources to the central server and configure access control rules.
- Access these resources securely from anywhere.
@@ -122,8 +126,6 @@ You can use Pangolin as an easy way to expose your business applications to your
**Use Case Example - IoT Networks**:
IoT networks are often fragmented and difficult to manage. By deploying Pangolin on a central server, you can connect all your IoT sites via Newt or another WireGuard client. This creates a simple, secure, and centralized way to access IoT resources without the need for intricate networking setups.
_Resources page of Pangolin dashboard (dark mode) showing HTTPS and TCP resources with access control rules._
## Similar Projects and Inspirations
**Cloudflare Tunnels**:

View File

@@ -35,7 +35,7 @@ services:
- 80:80 # Port for traefik because of the network_mode
traefik:
image: traefik:v3.3.3
image: traefik:v3.4.0
container_name: traefik
restart: unless-stopped
network_mode: service:gerbil # Ports appear on the gerbil service

12
drizzle.pg.config.ts Normal file
View File

@@ -0,0 +1,12 @@
import { defineConfig } from "drizzle-kit";
import path from "path";
export default defineConfig({
dialect: "postgresql",
schema: path.join("server", "db", "pg", "schema.ts"),
out: path.join("server", "migrations"),
verbose: true,
dbCredentials: {
url: process.env.DATABASE_URL as string
}
});

View File

@@ -4,7 +4,7 @@ import path from "path";
export default defineConfig({
dialect: "sqlite",
schema: path.join("server", "db", "schemas"),
schema: path.join("server", "db", "sqlite", "schema.ts"),
out: path.join("server", "migrations"),
verbose: true,
dbCredentials: {

View File

@@ -1,9 +1,12 @@
// eslint.config.js
export default [
import tseslint from 'typescript-eslint';
export default tseslint.config(
tseslint.configs.recommended,
{
files: ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx"],
rules: {
semi: "error",
"prefer-const": "error"
}
}
];
);

View File

@@ -26,7 +26,7 @@ server:
cors:
origins: ["https://{{.DashboardDomain}}"]
methods: ["GET", "POST", "PUT", "DELETE", "PATCH"]
headers: ["X-CSRF-Token", "Content-Type"]
allowed_headers: ["X-CSRF-Token", "Content-Type"]
credentials: false
traefik:

View File

@@ -21,6 +21,10 @@ experimental:
log:
level: "INFO"
format: "json" # Log format changed to json for better parsing
maxSize: 100
maxBackups: 3
maxAge: 3
compress: true
accessLog: # We enable access logs as json
filePath: "/var/log/traefik/access.log"

View File

@@ -35,7 +35,7 @@ services:
- 80:80 # Port for traefik because of the network_mode
{{end}}
traefik:
image: traefik:v3.3.6
image: traefik:v3.4.1
container_name: traefik
restart: unless-stopped
{{if .InstallGerbil}}
@@ -58,4 +58,4 @@ services:
networks:
default:
driver: bridge
name: pangolin
name: pangolin

View File

@@ -18,6 +18,10 @@ experimental:
log:
level: "INFO"
format: "common"
maxSize: 100
maxBackups: 3
maxAge: 3
compress: true
certificatesResolvers:
letsencrypt:

View File

@@ -9,6 +9,7 @@ import (
"io/fs"
"os"
"os/exec"
"os/user"
"path/filepath"
"runtime"
"strings"
@@ -17,6 +18,7 @@ import (
"time"
"unicode"
"math/rand"
"strconv"
"golang.org/x/term"
)
@@ -57,22 +59,31 @@ type Config struct {
func main() {
reader := bufio.NewReader(os.Stdin)
// check if the user is root
if os.Geteuid() != 0 {
fmt.Println("This script must be run as root")
// check if docker is not installed and the user is root
if !isDockerInstalled() {
if os.Geteuid() != 0 {
fmt.Println("Docker is not installed. Please install Docker manually or run this installer as root.")
os.Exit(1)
}
}
// check if the user is in the docker group (linux only)
if !isUserInDockerGroup() {
fmt.Println("You are not in the docker group.")
fmt.Println("The installer will not be able to run docker commands without running it as root.")
os.Exit(1)
}
var config Config
config.DoCrowdsecInstall = false
config.Secret = generateRandomSecretKey()
// check if there is already a config file
if _, err := os.Stat("config/config.yml"); err != nil {
config = collectUserInput(reader)
loadVersions(&config)
config.DoCrowdsecInstall = false
config.Secret = generateRandomSecretKey()
if err := createConfigFiles(config); err != nil {
fmt.Printf("Error creating config files: %v\n", err)
os.Exit(1)
@@ -83,6 +94,27 @@ func main() {
if !isDockerInstalled() && runtime.GOOS == "linux" {
if readBool(reader, "Docker is not installed. Would you like to install it?", true) {
installDocker()
// try to start docker service but ignore errors
if err := startDockerService(); err != nil {
fmt.Println("Error starting Docker service:", err)
} else {
fmt.Println("Docker service started successfully!")
}
// wait 10 seconds for docker to start checking if docker is running every 2 seconds
fmt.Println("Waiting for Docker to start...")
for i := 0; i < 5; i++ {
if isDockerRunning() {
fmt.Println("Docker is running!")
break
}
fmt.Println("Docker is not running yet, waiting...")
time.Sleep(2 * time.Second)
}
if !isDockerRunning() {
fmt.Println("Docker is still not running after 10 seconds. Please check the installation.")
os.Exit(1)
}
fmt.Println("Docker installed successfully!")
}
}
@@ -397,7 +429,7 @@ func installDocker() error {
return fmt.Errorf("failed to detect Linux distribution: %v", err)
}
osRelease := string(output)
// Detect system architecture
archCmd := exec.Command("uname", "-m")
archOutput, err := archCmd.Output()
@@ -405,7 +437,7 @@ func installDocker() error {
return fmt.Errorf("failed to detect system architecture: %v", err)
}
arch := strings.TrimSpace(string(archOutput))
// Map architecture to Docker's architecture naming
var dockerArch string
switch arch {
@@ -438,11 +470,31 @@ func installDocker() error {
apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
`, dockerArch))
case strings.Contains(osRelease, "ID=fedora"):
installCmd = exec.Command("bash", "-c", `
// Detect Fedora version to handle DNF 5 changes
versionCmd := exec.Command("bash", "-c", "grep VERSION_ID /etc/os-release | cut -d'=' -f2 | tr -d '\"'")
versionOutput, err := versionCmd.Output()
var fedoraVersion int
if err == nil {
if v, parseErr := strconv.Atoi(strings.TrimSpace(string(versionOutput))); parseErr == nil {
fedoraVersion = v
}
}
// Use appropriate DNF syntax based on version
var repoCmd string
if fedoraVersion >= 41 {
// DNF 5 syntax for Fedora 41+
repoCmd = "dnf config-manager addrepo --from-repofile=https://download.docker.com/linux/fedora/docker-ce.repo"
} else {
// DNF 4 syntax for Fedora < 41
repoCmd = "dnf config-manager --add-repo https://download.docker.com/linux/fedora/docker-ce.repo"
}
installCmd = exec.Command("bash", "-c", fmt.Sprintf(`
dnf -y install dnf-plugins-core &&
dnf config-manager --add-repo https://download.docker.com/linux/fedora/docker-ce.repo &&
%s &&
dnf install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
`)
`, repoCmd))
case strings.Contains(osRelease, "ID=opensuse") || strings.Contains(osRelease, "ID=\"opensuse-"):
installCmd = exec.Command("bash", "-c", `
zypper install -y docker docker-compose &&
@@ -466,11 +518,26 @@ func installDocker() error {
default:
return fmt.Errorf("unsupported Linux distribution")
}
installCmd.Stdout = os.Stdout
installCmd.Stderr = os.Stderr
return installCmd.Run()
}
func startDockerService() error {
if runtime.GOOS == "linux" {
cmd := exec.Command("systemctl", "enable", "--now", "docker")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
return cmd.Run()
} else if runtime.GOOS == "darwin" {
// On macOS, Docker is usually started via the Docker Desktop application
fmt.Println("Please start Docker Desktop manually on macOS.")
return nil
}
return fmt.Errorf("unsupported operating system for starting Docker service")
}
func isDockerInstalled() bool {
cmd := exec.Command("docker", "--version")
if err := cmd.Run(); err != nil {
@@ -479,6 +546,43 @@ func isDockerInstalled() bool {
return true
}
func isUserInDockerGroup() bool {
if runtime.GOOS == "darwin" {
// Docker group is not applicable on macOS
// So we assume that the user can run Docker commands
return true
}
if os.Geteuid() == 0 {
return true // Root user can run Docker commands anyway
}
// Check if the current user is in the docker group
if dockerGroup, err := user.LookupGroup("docker"); err == nil {
if currentUser, err := user.Current(); err == nil {
if currentUserGroupIds, err := currentUser.GroupIds(); err == nil {
for _, groupId := range currentUserGroupIds {
if groupId == dockerGroup.Gid {
return true
}
}
}
}
}
// Eventually, if any of the checks fail, we assume the user cannot run Docker commands
return false
}
// isDockerRunning checks if the Docker daemon is running by using the `docker info` command.
func isDockerRunning() bool {
cmd := exec.Command("docker", "info")
if err := cmd.Run(); err != nil {
return false
}
return true
}
// executeDockerComposeCommandWithArgs executes the appropriate docker command with arguments supplied
func executeDockerComposeCommandWithArgs(args ...string) error {
var cmd *exec.Cmd
@@ -619,4 +723,4 @@ func generateRandomSecretKey() string {
b[i] = charset[seededRand.Intn(len(charset))]
}
return string(b)
}
}

View File

@@ -1,7 +1,7 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
eslint: {
ignoreDuringBuilds: true,
ignoreDuringBuilds: true
},
output: "standalone"
};

6097
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -12,100 +12,108 @@
"license": "SEE LICENSE IN LICENSE AND README.md",
"scripts": {
"dev": "NODE_ENV=development ENVIRONMENT=dev tsx watch server/index.ts",
"db:generate": "drizzle-kit generate",
"db:push": "npx tsx server/db/migrate.ts",
"db:studio": "drizzle-kit studio",
"build": "mkdir -p dist && next build && node esbuild.mjs -e server/index.ts -o dist/server.mjs && node esbuild.mjs -e server/setup/migrations.ts -o dist/migrations.mjs",
"start": "NODE_OPTIONS=--enable-source-maps NODE_ENV=development ENVIRONMENT=prod sh -c 'node dist/migrations.mjs && node dist/server.mjs'",
"db:pg:generate": "drizzle-kit generate --config=./drizzle.pg.config.ts",
"db:sqlite:generate": "drizzle-kit generate --config=./drizzle.sqlite.config.ts",
"db:pg:push": "npx tsx server/db/pg/migrate.ts",
"db:sqlite:push": "npx tsx server/db/sqlite/migrate.ts",
"db:sqlite:studio": "drizzle-kit studio --config=./drizzle.sqlite.config.ts",
"db:pg:studio": "drizzle-kit studio --config=./drizzle.pg.config.ts",
"db:clear-migrations": "rm -rf server/migrations",
"build:sqlite": "mkdir -p dist && next build && node esbuild.mjs -e server/index.ts -o dist/server.mjs && node esbuild.mjs -e server/setup/migrationsSqlite.ts -o dist/migrations.mjs",
"build:pg": "mkdir -p dist && next build && node esbuild.mjs -e server/index.ts -o dist/server.mjs && node esbuild.mjs -e server/setup/migrationsPg.ts -o dist/migrations.mjs",
"start:sqlite": "DB_TYPE=sqlite NODE_OPTIONS=--enable-source-maps NODE_ENV=development ENVIRONMENT=prod sh -c 'node dist/migrations.mjs && node dist/server.mjs'",
"start:pg": "DB_TYPE=pg NODE_OPTIONS=--enable-source-maps NODE_ENV=development ENVIRONMENT=prod sh -c 'node dist/migrations.mjs && node dist/server.mjs'",
"email": "email dev --dir server/emails/templates --port 3005"
},
"dependencies": {
"@asteasolutions/zod-to-openapi": "^7.3.0",
"@asteasolutions/zod-to-openapi": "^7.3.2",
"@hookform/resolvers": "3.9.1",
"@node-rs/argon2": "2.0.2",
"@node-rs/argon2": "^2.0.2",
"@oslojs/crypto": "1.0.1",
"@oslojs/encoding": "1.1.0",
"@radix-ui/react-avatar": "1.1.2",
"@radix-ui/react-checkbox": "1.1.3",
"@radix-ui/react-collapsible": "1.1.2",
"@radix-ui/react-dialog": "1.1.4",
"@radix-ui/react-dropdown-menu": "2.1.4",
"@radix-ui/react-avatar": "1.1.10",
"@radix-ui/react-checkbox": "1.3.2",
"@radix-ui/react-collapsible": "1.1.11",
"@radix-ui/react-dialog": "1.1.14",
"@radix-ui/react-dropdown-menu": "2.1.15",
"@radix-ui/react-icons": "1.3.2",
"@radix-ui/react-label": "2.1.1",
"@radix-ui/react-popover": "1.1.4",
"@radix-ui/react-progress": "^1.1.4",
"@radix-ui/react-radio-group": "1.2.2",
"@radix-ui/react-select": "2.1.4",
"@radix-ui/react-separator": "1.1.1",
"@radix-ui/react-slot": "1.1.1",
"@radix-ui/react-switch": "1.1.2",
"@radix-ui/react-tabs": "1.1.2",
"@radix-ui/react-toast": "1.2.4",
"@react-email/components": "0.0.36",
"@react-email/render": "^1.0.6",
"@react-email/tailwind": "1.0.4",
"@radix-ui/react-label": "2.1.7",
"@radix-ui/react-popover": "1.1.14",
"@radix-ui/react-progress": "^1.1.7",
"@radix-ui/react-radio-group": "1.3.7",
"@radix-ui/react-scroll-area": "^1.2.9",
"@radix-ui/react-select": "2.2.5",
"@radix-ui/react-separator": "1.1.7",
"@radix-ui/react-slot": "1.2.3",
"@radix-ui/react-switch": "1.2.5",
"@radix-ui/react-tabs": "1.1.12",
"@radix-ui/react-toast": "1.2.14",
"@react-email/components": "0.0.41",
"@react-email/render": "^1.1.2",
"@react-email/tailwind": "1.0.5",
"@tailwindcss/forms": "^0.5.10",
"@tanstack/react-table": "8.20.6",
"arctic": "^3.6.0",
"axios": "1.8.4",
"@tanstack/react-table": "8.21.3",
"arctic": "^3.7.0",
"axios": "1.9.0",
"better-sqlite3": "11.7.0",
"canvas-confetti": "1.9.3",
"class-variance-authority": "0.7.1",
"clsx": "2.1.1",
"cmdk": "1.0.4",
"cmdk": "1.1.1",
"cookie": "^1.0.2",
"cookie-parser": "1.4.7",
"cookies": "^0.9.1",
"cors": "2.8.5",
"crypto-js": "^4.2.0",
"drizzle-orm": "0.38.3",
"eslint": "9.17.0",
"eslint-config-next": "15.1.3",
"eslint": "9.28.0",
"eslint-config-next": "15.3.3",
"express": "4.21.2",
"express-rate-limit": "7.5.0",
"glob": "11.0.0",
"helmet": "8.0.0",
"glob": "11.0.2",
"helmet": "8.1.0",
"http-errors": "2.0.0",
"i": "^0.3.7",
"input-otp": "1.4.1",
"input-otp": "1.4.2",
"jmespath": "^0.16.0",
"js-yaml": "4.1.0",
"jsonwebtoken": "^9.0.2",
"lucide-react": "0.469.0",
"lucide-react": "0.511.0",
"moment": "2.30.1",
"next": "15.2.4",
"next-themes": "0.4.4",
"next": "15.3.3",
"next-themes": "0.4.6",
"node-cache": "5.1.2",
"node-fetch": "3.3.2",
"nodemailer": "6.9.16",
"npm": "^11.2.0",
"npm": "^11.4.1",
"oslo": "1.2.1",
"pg": "^8.16.0",
"qrcode.react": "4.2.0",
"react": "19.0.0",
"react-dom": "19.0.0",
"react": "19.1.0",
"react-dom": "19.1.0",
"react-easy-sort": "^1.6.0",
"react-hook-form": "7.54.2",
"react-hook-form": "7.56.4",
"react-icons": "^5.5.0",
"rebuild": "0.1.2",
"semver": "7.6.3",
"semver": "7.7.2",
"swagger-ui-express": "^5.0.1",
"tailwind-merge": "2.6.0",
"tw-animate-css": "^1.2.5",
"tw-animate-css": "^1.3.3",
"uuid": "^11.1.0",
"vaul": "1.1.2",
"winston": "3.17.0",
"winston-daily-rotate-file": "5.0.0",
"ws": "8.18.0",
"zod": "3.24.1",
"zod-validation-error": "3.4.0"
"ws": "8.18.2",
"zod": "3.25.56",
"zod-validation-error": "3.4.1"
},
"devDependencies": {
"@dotenvx/dotenvx": "1.32.0",
"@dotenvx/dotenvx": "1.44.1",
"@esbuild-plugins/tsconfig-paths": "0.1.2",
"@tailwindcss/postcss": "^4.1.3",
"@tailwindcss/postcss": "^4.1.8",
"@types/better-sqlite3": "7.6.12",
"@types/cookie-parser": "1.4.8",
"@types/cors": "2.8.17",
"@types/cookie-parser": "1.4.9",
"@types/cors": "2.8.19",
"@types/crypto-js": "^4.2.2",
"@types/express": "5.0.0",
"@types/jmespath": "^0.15.2",
@@ -113,22 +121,23 @@
"@types/jsonwebtoken": "^9.0.9",
"@types/node": "^22",
"@types/nodemailer": "6.4.17",
"@types/react": "19.1.1",
"@types/react-dom": "19.1.2",
"@types/semver": "7.5.8",
"@types/react": "19.1.6",
"@types/react-dom": "19.1.6",
"@types/semver": "7.7.0",
"@types/swagger-ui-express": "^4.1.8",
"@types/ws": "8.5.13",
"@types/ws": "8.18.1",
"@types/yargs": "17.0.33",
"drizzle-kit": "0.30.6",
"esbuild": "0.25.2",
"drizzle-kit": "0.31.1",
"esbuild": "0.25.5",
"esbuild-node-externals": "1.18.0",
"postcss": "^8",
"react-email": "4.0.6",
"react-email": "4.0.16",
"tailwindcss": "^4.1.4",
"tsc-alias": "1.8.10",
"tsx": "4.19.3",
"tsc-alias": "1.8.16",
"tsx": "4.19.4",
"typescript": "^5",
"yargs": "17.7.2"
"typescript-eslint": "^8.34.0",
"yargs": "18.0.0"
},
"overrides": {
"emblor": {

View File

@@ -1,22 +1,21 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="900.82861"
height="955.20648"
viewBox="0 0 238.34422 252.7317"
version="1.1"
x="0px"
y="0px"
viewBox="0 0 399.99999 400.00002"
enable-background="new 0 0 419.528 419.528"
xml:space="preserve"
id="svg52"
sodipodi:docname="noun-pangolin-1798092.svg"
width="400"
height="400"
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
id="svg420"
inkscape:export-filename="logo.svg"
inkscape:export-xdpi="221.14999"
inkscape:export-ydpi="221.14999"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><defs
id="defs56" /><sodipodi:namedview
id="namedview54"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview422"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
@@ -24,15 +23,18 @@
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
showgrid="false"
inkscape:zoom="1.9583914"
inkscape:cx="209.86611"
inkscape:cy="262.20499"
inkscape:window-width="3840"
inkscape:window-height="2136"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg52" /><path
d="m 62.232921,184.91974 c 0,2.431 -1.97,4.402 -4.399,4.402 -2.429,0 -4.399,-1.972 -4.399,-4.402 0,-2.429 1.97,-4.399 4.399,-4.399 2.429,-10e-4 4.399,1.97 4.399,4.399 z m 58.993999,-4.821 c -25.943999,-2.826 -38.978999,7.453 -71.181999,31.357 -27.572,20.467 -32.767,4.381 -31.748,-2.614 1.499,-10.282 25.222,-58.573 48.079,-88.461 28.273,7.34 49.869999,30.727 54.850999,59.718 z m -55.915999,4.821 c 0,-4.131 -3.349,-7.478 -7.478,-7.478 -4.129,0 -7.478,3.347 -7.478,7.478 0,4.131 3.349,7.481 7.478,7.481 4.13,0 7.478,-3.35 7.478,-7.481 z m -15.032,48.424 -0.234,14.041 20.413,22.687 -9.818,7.353 33.306,27.492 -11.759,8.124 42.631999,19.939 -10.825,9.747 48.291,8.078 -7.526,10.307 48.758,-4.531 -3.997,11.725 53.916,-18.153 -2.76,13.357 48.077,-34.345 1.479,13.562 34.087,-48.576 7.478,14.206 15.187,-58.89 10.391,8.533 -2.14,-57.884 13.814,5.13 -21.082,-51.204 13.404,0.048 -33.696,-42.131 15.312,-1.366 -47.026,-32.831002 14.255,-8.399 -54.817,-14.682 9.257,-11.695 -49.625,0.352 0.6,-13.337 -38.537,14.084 -1.597,-12.689 -29.984,21.429 -6.446,-10.852 -22.59,26.504 -7.021,-9.572 -18.923,30.294 -9.595999,-8.744 -16.754,30.138002 c 31.509999,10.197 54.979999,37.951 59.126999,71.547 0.404,0.087 -22.37,31.257 10.955,57.85 -0.576,-2.985 -6.113,-53.902 47.496,-57.61 26.668,-1.844 48.4,21.666 48.4,48.399 0,8.184 -2.05,15.883 -5.636,22.64 -15.927,29.611 -64.858,30.755 -80.429,30.596 -45.154,-0.459 -104.051999,-51.521 -104.051999,-51.521 z"
id="path46" /></svg>
inkscape:document-units="mm"
showgrid="false" />
<defs
id="defs417" />
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-13.119542,-5.9258171)">
<path
d="m 213.66176,90.072122 c 4.95655,0 8.97383,4.018046 8.97383,8.973827 0,4.956581 -4.01728,8.974621 -8.97383,8.974621 -4.95657,0 -8.97462,-4.01804 -8.97462,-8.974621 0,-4.955781 4.01805,-8.973827 8.97462,-8.973827 z m 35.2316,37.450998 c -0.90048,29.80928 -23.66033,69.21262 -54.51292,79.34466 -36.04206,11.836 -63.40991,-5.92226 -72.08409,-26.74061 -6.75754,-16.21966 -1.65117,-35.62363 10.96266,-43.83669 10.6506,-6.93533 30.48543,-8.76736 47.15454,2.19144 -5.85627,-15.34246 -21.62491,-25.4256 -35.59101,-28.49424 -13.96613,-3.06867 -28.38324,0.43858 -38.74504,5.69946 13.29071,-14.68572 44.40801,-28.946049 78.24077,-10.95958 22.67676,12.05491 32.43775,28.93208 42.0489,51.72763 C 251.59637,117.87858 234.026,71.411066 203.39074,43.794029 172.15544,15.636686 129.95516,4.340214 97.668803,6.103155 108.32483,12.678273 120.84625,22.06586 132.41209,33.053363 81.298533,26.697169 39.174705,38.314245 13.119542,73.749217 27.67508,70.878527 46.868833,69.073666 65.974711,70.016861 28.737658,96.252107 7.1124298,140.38147 18.105298,186.43137 c 6.718497,-11.74129 16.767711,-25.84558 28.726275,-38.62863 -3.677175,34.36994 1.42836,80.83745 45.62293,110.85478 -2.25587,-9.42394 -4.08014,-20.88443 -4.91466,-33.0154 20.673197,16.1282 50.685067,29.42205 87.917917,20.24096 65.77679,-16.21975 83.34719,-79.78335 73.4356,-118.35996"
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0776283"
id="path32" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@@ -1,39 +1,22 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="900.82861"
height="955.20648"
viewBox="0 0 238.34422 252.7317"
version="1.1"
x="0px"
y="0px"
viewBox="0 0 399.99999 400.00002"
enable-background="new 0 0 419.528 419.528"
xml:space="preserve"
id="svg52"
sodipodi:docname="pangolin_orange.svg"
width="400"
height="400"
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
id="svg420"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><defs
id="defs56" /><sodipodi:namedview
id="namedview54"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
showgrid="false"
inkscape:zoom="1.9583914"
inkscape:cx="127.40048"
inkscape:cy="262.71561"
inkscape:window-width="1436"
inkscape:window-height="1236"
inkscape:window-x="2208"
inkscape:window-y="511"
inkscape:window-maximized="0"
inkscape:current-layer="svg52" /><path
d="m 62.232921,184.91974 c 0,2.431 -1.97,4.402 -4.399,4.402 -2.429,0 -4.399,-1.972 -4.399,-4.402 0,-2.429 1.97,-4.399 4.399,-4.399 2.429,-10e-4 4.399,1.97 4.399,4.399 z m 58.993999,-4.821 c -25.943999,-2.826 -38.978999,7.453 -71.181999,31.357 -27.572,20.467 -32.767,4.381 -31.748,-2.614 1.499,-10.282 25.222,-58.573 48.079,-88.461 28.273,7.34 49.869999,30.727 54.850999,59.718 z m -55.915999,4.821 c 0,-4.131 -3.349,-7.478 -7.478,-7.478 -4.129,0 -7.478,3.347 -7.478,7.478 0,4.131 3.349,7.481 7.478,7.481 4.13,0 7.478,-3.35 7.478,-7.481 z m -15.032,48.424 -0.234,14.041 20.413,22.687 -9.818,7.353 33.306,27.492 -11.759,8.124 42.631999,19.939 -10.825,9.747 48.291,8.078 -7.526,10.307 48.758,-4.531 -3.997,11.725 53.916,-18.153 -2.76,13.357 48.077,-34.345 1.479,13.562 34.087,-48.576 7.478,14.206 15.187,-58.89 10.391,8.533 -2.14,-57.884 13.814,5.13 -21.082,-51.204 13.404,0.048 -33.696,-42.131 15.312,-1.366 -47.026,-32.831002 14.255,-8.399 -54.817,-14.682 9.257,-11.695 -49.625,0.352 0.6,-13.337 -38.537,14.084 -1.597,-12.689 -29.984,21.429 -6.446,-10.852 -22.59,26.504 -7.021,-9.572 -18.923,30.294 -9.595999,-8.744 -16.754,30.138002 c 31.509999,10.197 54.979999,37.951 59.126999,71.547 0.404,0.087 -22.37,31.257 10.955,57.85 -0.576,-2.985 -6.113,-53.902 47.496,-57.61 26.668,-1.844 48.4,21.666 48.4,48.399 0,8.184 -2.05,15.883 -5.636,22.64 -15.927,29.611 -64.858,30.755 -80.429,30.596 -45.154,-0.459 -104.051999,-51.521 -104.051999,-51.521 z"
id="path46"
style="fill:#f97315;fill-opacity:1" /></svg>
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs417" />
<g
id="layer1"
transform="translate(-13.119542,-5.9258171)">
<path
d="m 213.66176,90.072122 c 4.95655,0 8.97383,4.018046 8.97383,8.973827 0,4.956581 -4.01728,8.974621 -8.97383,8.974621 -4.95657,0 -8.97462,-4.01804 -8.97462,-8.974621 0,-4.955781 4.01805,-8.973827 8.97462,-8.973827 z m 35.2316,37.450998 c -0.90048,29.80928 -23.66033,69.21262 -54.51292,79.34466 -36.04206,11.836 -63.40991,-5.92226 -72.08409,-26.74061 -6.75754,-16.21966 -1.65117,-35.62363 10.96266,-43.83669 10.6506,-6.93533 30.48543,-8.76736 47.15454,2.19144 -5.85627,-15.34246 -21.62491,-25.4256 -35.59101,-28.49424 -13.96613,-3.06867 -28.38324,0.43858 -38.74504,5.69946 13.29071,-14.68572 44.40801,-28.946049 78.24077,-10.95958 22.67676,12.05491 32.43775,28.93208 42.0489,51.72763 C 251.59637,117.87858 234.026,71.411066 203.39074,43.794029 172.15544,15.636686 129.95516,4.340214 97.668803,6.103155 108.32483,12.678273 120.84625,22.06586 132.41209,33.053363 81.298533,26.697169 39.174705,38.314245 13.119542,73.749217 27.67508,70.878527 46.868833,69.073666 65.974711,70.016861 28.737658,96.252107 7.1124298,140.38147 18.105298,186.43137 c 6.718497,-11.74129 16.767711,-25.84558 28.726275,-38.62863 -3.677175,34.36994 1.42836,80.83745 45.62293,110.85478 -2.25587,-9.42394 -4.08014,-20.88443 -4.91466,-33.0154 20.673197,16.1282 50.685067,29.42205 87.917917,20.24096 65.77679,-16.21975 83.34719,-79.78335 73.4356,-118.35996"
style="fill:#f36118;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0776283"
id="path32" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

View File

@@ -1,6 +1,6 @@
import { Request } from "express";
import { db } from "@server/db";
import { userActions, roleActions, userOrgs } from "@server/db/schemas";
import { userActions, roleActions, userOrgs } from "@server/db";
import { and, eq } from "drizzle-orm";
import createHttpError from "http-errors";
import HttpCode from "@server/types/HttpCode";

View File

@@ -1,6 +1,6 @@
import db from "@server/db";
import { db } from "@server/db";
import { and, eq } from "drizzle-orm";
import { roleResources, userResources } from "@server/db/schemas";
import { roleResources, userResources } from "@server/db";
export async function canUserAccessResource({
userId,

View File

@@ -1,5 +1,5 @@
import db from "@server/db";
import { UserInvite, userInvites } from "@server/db/schemas";
import { db } from "@server/db";
import { UserInvite, userInvites } from "@server/db";
import { isWithinExpirationDate } from "oslo";
import { verifyPassword } from "./password";
import { eq } from "drizzle-orm";

View File

@@ -1,5 +1,5 @@
import { db } from '@server/db';
import { limitsTable } from '@server/db/schemas';
import { limitsTable } from '@server/db';
import { and, eq } from 'drizzle-orm';
import createHttpError from 'http-errors';
import HttpCode from '@server/types/HttpCode';

View File

@@ -1,5 +1,5 @@
import db from "@server/db";
import { resourceOtp } from "@server/db/schemas";
import { db } from "@server/db";
import { resourceOtp } from "@server/db";
import { and, eq } from "drizzle-orm";
import { createDate, isWithinExpirationDate, TimeSpan } from "oslo";
import { alphabet, generateRandomString, sha256 } from "oslo/crypto";

View File

@@ -1,7 +1,7 @@
import { TimeSpan, createDate } from "oslo";
import { generateRandomString, alphabet } from "oslo/crypto";
import db from "@server/db";
import { users, emailVerificationCodes } from "@server/db/schemas";
import { db } from "@server/db";
import { users, emailVerificationCodes } from "@server/db";
import { eq } from "drizzle-orm";
import { sendEmail } from "@server/emails";
import config from "@server/lib/config";

View File

@@ -9,8 +9,8 @@ import {
sessions,
User,
users
} from "@server/db/schemas";
import db from "@server/db";
} from "@server/db";
import { db } from "@server/db";
import { eq, inArray } from "drizzle-orm";
import config from "@server/lib/config";
import type { RandomReader } from "@oslojs/crypto/random";

View File

@@ -2,8 +2,8 @@ import {
encodeHexLowerCase,
} from "@oslojs/encoding";
import { sha256 } from "@oslojs/crypto/sha2";
import { Newt, newts, newtSessions, NewtSession } from "@server/db/schemas";
import db from "@server/db";
import { Newt, newts, newtSessions, NewtSession } from "@server/db";
import { db } from "@server/db";
import { eq } from "drizzle-orm";
export const EXPIRES = 1000 * 60 * 60 * 24 * 30;

View File

@@ -1,7 +1,7 @@
import { encodeHexLowerCase } from "@oslojs/encoding";
import { sha256 } from "@oslojs/crypto/sha2";
import { resourceSessions, ResourceSession } from "@server/db/schemas";
import db from "@server/db";
import { resourceSessions, ResourceSession } from "@server/db";
import { db } from "@server/db";
import { eq, and } from "drizzle-orm";
import config from "@server/lib/config";

View File

@@ -1,6 +1,6 @@
import { verify } from "@node-rs/argon2";
import db from "@server/db";
import { twoFactorBackupCodes } from "@server/db/schemas";
import { db } from "@server/db";
import { twoFactorBackupCodes } from "@server/db";
import { eq } from "drizzle-orm";
import { decodeHex } from "oslo/encoding";
import { TOTPController } from "oslo/otp";

View File

@@ -1,10 +1,10 @@
import db from "@server/db";
import { db } from "@server/db";
import {
Resource,
ResourceAccessToken,
resourceAccessToken,
resources
} from "@server/db/schemas";
} from "@server/db";
import { and, eq } from "drizzle-orm";
import { isWithinExpirationDate } from "oslo";
import { verifyPassword } from "./password";

72
server/db/README.md Normal file
View File

@@ -0,0 +1,72 @@
# Database
Pangolin can use a Postgres or SQLite database to store its data.
## Development
### Postgres
To use Postgres, edit `server/db/index.ts` to export all from `server/db/pg/index.ts`:
```typescript
export * from "./pg";
```
Make sure you have a valid config file with a connection string:
```yaml
postgres:
connection_string: postgresql://postgres:postgres@localhost:5432
```
You can run an ephemeral Postgres database for local development using Docker:
```bash
docker run -d \
--name postgres \
--rm \
-p 5432:5432 \
-e POSTGRES_PASSWORD=postgres \
-v $(mktemp -d):/var/lib/postgresql/data \
postgres:17
```
### Schema
`server/db/pg/schema.ts` and `server/db/sqlite/schema.ts` contain the database schema definitions. These need to be kept in sync with with each other.
Stick to common data types and avoid Postgres-specific features to ensure compatibility with SQLite.
### SQLite
To use SQLite, edit `server/db/index.ts` to export all from `server/db/sqlite/index.ts`:
```typescript
export * from "./sqlite";
```
No edits to the config are needed. If you keep the Postgres config, it will be ignored.
## Generate and Push Migrations
Ensure drizzle-kit is installed.
### Postgres
You must have a connection string in your config file, as shown above.
```bash
npm run db:pg:generate
npm run db:pg:push
```
### SQLite
```bash
npm run db:sqlite:generate
npm run db:sqlite:push
```
## Build Time
There is a dockerfile for each database type. The dockerfile swaps out the `server/db/index.ts` file to use the correct database type.

View File

@@ -1,52 +1,2 @@
import { drizzle } from "drizzle-orm/better-sqlite3";
import Database from "better-sqlite3";
import * as schema from "@server/db/schemas";
import path from "path";
import fs from "fs/promises";
import { APP_PATH } from "@server/lib/consts";
import { existsSync, mkdirSync } from "fs";
export const location = path.join(APP_PATH, "db", "db.sqlite");
export const exists = await checkFileExists(location);
bootstrapVolume();
const sqlite = new Database(location);
export const db = drizzle(sqlite, { schema });
export default db;
async function checkFileExists(filePath: string): Promise<boolean> {
try {
await fs.access(filePath);
return true;
} catch {
return false;
}
}
function bootstrapVolume() {
const appPath = APP_PATH;
const dbDir = path.join(appPath, "db");
const logsDir = path.join(appPath, "logs");
// check if the db directory exists and create it if it doesn't
if (!existsSync(dbDir)) {
mkdirSync(dbDir, { recursive: true });
}
// check if the logs directory exists and create it if it doesn't
if (!existsSync(logsDir)) {
mkdirSync(logsDir, { recursive: true });
}
// THIS IS FOR TRAEFIK; NOT REALLY NEEDED, BUT JUST IN CASE
const traefikDir = path.join(appPath, "traefik");
// check if the traefik directory exists and create it if it doesn't
if (!existsSync(traefikDir)) {
mkdirSync(traefikDir, { recursive: true });
}
}
export * from "./sqlite";
// export * from "./pg";

View File

@@ -1,7 +1,7 @@
import { join } from "path";
import { readFileSync } from "fs";
import { db } from "@server/db";
import { exitNodes, sites } from "./schemas/schema";
import { exitNodes, sites } from "@server/db";
import { eq, and } from "drizzle-orm";
import { __DIRNAME } from "@server/lib/consts";

17
server/db/pg/driver.ts Normal file
View File

@@ -0,0 +1,17 @@
import { drizzle as DrizzlePostgres } from "drizzle-orm/node-postgres";
import { readConfigFile } from "@server/lib/readConfigFile";
function createDb() {
const config = readConfigFile();
const connectionString = config.postgres?.connection_string;
if (!connectionString) {
throw new Error("Postgres connection string is not defined in the configuration file.");
}
return DrizzlePostgres(connectionString);
}
export const db = createDb();
export default db;

View File

@@ -1 +1,2 @@
export * from "./driver";
export * from "./schema";

20
server/db/pg/migrate.ts Normal file
View File

@@ -0,0 +1,20 @@
import { migrate } from "drizzle-orm/node-postgres/migrator";
import db from "./driver";
import path from "path";
const migrationsFolder = path.join("server/migrations");
const runMigrations = async () => {
console.log("Running migrations...");
try {
await migrate(db as any, {
migrationsFolder: migrationsFolder
});
console.log("Migrations completed successfully.");
} catch (error) {
console.error("Error running migrations:", error);
process.exit(1);
}
};
runMigrations();

532
server/db/pg/schema.ts Normal file
View File

@@ -0,0 +1,532 @@
import {
pgTable,
serial,
varchar,
boolean,
integer,
bigint,
real
} from "drizzle-orm/pg-core";
import { InferSelectModel } from "drizzle-orm";
export const domains = pgTable("domains", {
domainId: varchar("domainId").primaryKey(),
baseDomain: varchar("baseDomain").notNull(),
configManaged: boolean("configManaged").notNull().default(false)
});
export const orgs = pgTable("orgs", {
orgId: varchar("orgId").primaryKey(),
name: varchar("name").notNull()
});
export const orgDomains = pgTable("orgDomains", {
orgId: varchar("orgId")
.notNull()
.references(() => orgs.orgId, { onDelete: "cascade" }),
domainId: varchar("domainId")
.notNull()
.references(() => domains.domainId, { onDelete: "cascade" })
});
export const sites = pgTable("sites", {
siteId: serial("siteId").primaryKey(),
orgId: varchar("orgId")
.references(() => orgs.orgId, {
onDelete: "cascade"
})
.notNull(),
niceId: varchar("niceId").notNull(),
exitNodeId: integer("exitNode").references(() => exitNodes.exitNodeId, {
onDelete: "set null"
}),
name: varchar("name").notNull(),
pubKey: varchar("pubKey"),
subnet: varchar("subnet").notNull(),
megabytesIn: real("bytesIn"),
megabytesOut: real("bytesOut"),
lastBandwidthUpdate: varchar("lastBandwidthUpdate"),
type: varchar("type").notNull(), // "newt" or "wireguard"
online: boolean("online").notNull().default(false),
dockerSocketEnabled: boolean("dockerSocketEnabled").notNull().default(true)
});
export const resources = pgTable("resources", {
resourceId: serial("resourceId").primaryKey(),
siteId: integer("siteId")
.references(() => sites.siteId, {
onDelete: "cascade"
})
.notNull(),
orgId: varchar("orgId")
.references(() => orgs.orgId, {
onDelete: "cascade"
})
.notNull(),
name: varchar("name").notNull(),
subdomain: varchar("subdomain"),
fullDomain: varchar("fullDomain"),
domainId: varchar("domainId").references(() => domains.domainId, {
onDelete: "set null"
}),
ssl: boolean("ssl").notNull().default(false),
blockAccess: boolean("blockAccess").notNull().default(false),
sso: boolean("sso").notNull().default(true),
http: boolean("http").notNull().default(true),
protocol: varchar("protocol").notNull(),
proxyPort: integer("proxyPort"),
emailWhitelistEnabled: boolean("emailWhitelistEnabled")
.notNull()
.default(false),
isBaseDomain: boolean("isBaseDomain"),
applyRules: boolean("applyRules").notNull().default(false),
enabled: boolean("enabled").notNull().default(true),
stickySession: boolean("stickySession").notNull().default(false),
tlsServerName: varchar("tlsServerName"),
setHostHeader: varchar("setHostHeader")
});
export const targets = pgTable("targets", {
targetId: serial("targetId").primaryKey(),
resourceId: integer("resourceId")
.references(() => resources.resourceId, {
onDelete: "cascade"
})
.notNull(),
ip: varchar("ip").notNull(),
method: varchar("method"),
port: integer("port").notNull(),
internalPort: integer("internalPort"),
enabled: boolean("enabled").notNull().default(true)
});
export const exitNodes = pgTable("exitNodes", {
exitNodeId: serial("exitNodeId").primaryKey(),
name: varchar("name").notNull(),
address: varchar("address").notNull(),
endpoint: varchar("endpoint").notNull(),
publicKey: varchar("publicKey").notNull(),
listenPort: integer("listenPort").notNull(),
reachableAt: varchar("reachableAt")
});
export const users = pgTable("user", {
userId: varchar("id").primaryKey(),
email: varchar("email"),
username: varchar("username").notNull(),
name: varchar("name"),
type: varchar("type").notNull(), // "internal", "oidc"
idpId: integer("idpId").references(() => idp.idpId, {
onDelete: "cascade"
}),
passwordHash: varchar("passwordHash"),
twoFactorEnabled: boolean("twoFactorEnabled").notNull().default(false),
twoFactorSecret: varchar("twoFactorSecret"),
emailVerified: boolean("emailVerified").notNull().default(false),
dateCreated: varchar("dateCreated").notNull(),
serverAdmin: boolean("serverAdmin").notNull().default(false)
});
export const newts = pgTable("newt", {
newtId: varchar("id").primaryKey(),
secretHash: varchar("secretHash").notNull(),
dateCreated: varchar("dateCreated").notNull(),
siteId: integer("siteId").references(() => sites.siteId, {
onDelete: "cascade"
})
});
export const twoFactorBackupCodes = pgTable("twoFactorBackupCodes", {
codeId: serial("id").primaryKey(),
userId: varchar("userId")
.notNull()
.references(() => users.userId, { onDelete: "cascade" }),
codeHash: varchar("codeHash").notNull()
});
export const sessions = pgTable("session", {
sessionId: varchar("id").primaryKey(),
userId: varchar("userId")
.notNull()
.references(() => users.userId, { onDelete: "cascade" }),
expiresAt: bigint("expiresAt", { mode: "number" }).notNull()
});
export const newtSessions = pgTable("newtSession", {
sessionId: varchar("id").primaryKey(),
newtId: varchar("newtId")
.notNull()
.references(() => newts.newtId, { onDelete: "cascade" }),
expiresAt: bigint("expiresAt", { mode: "number" }).notNull()
});
export const userOrgs = pgTable("userOrgs", {
userId: varchar("userId")
.notNull()
.references(() => users.userId, { onDelete: "cascade" }),
orgId: varchar("orgId")
.references(() => orgs.orgId, {
onDelete: "cascade"
})
.notNull(),
roleId: integer("roleId")
.notNull()
.references(() => roles.roleId),
isOwner: boolean("isOwner").notNull().default(false)
});
export const emailVerificationCodes = pgTable("emailVerificationCodes", {
codeId: serial("id").primaryKey(),
userId: varchar("userId")
.notNull()
.references(() => users.userId, { onDelete: "cascade" }),
email: varchar("email").notNull(),
code: varchar("code").notNull(),
expiresAt: bigint("expiresAt", { mode: "number" }).notNull()
});
export const passwordResetTokens = pgTable("passwordResetTokens", {
tokenId: serial("id").primaryKey(),
email: varchar("email").notNull(),
userId: varchar("userId")
.notNull()
.references(() => users.userId, { onDelete: "cascade" }),
tokenHash: varchar("tokenHash").notNull(),
expiresAt: bigint("expiresAt", { mode: "number" }).notNull()
});
export const actions = pgTable("actions", {
actionId: varchar("actionId").primaryKey(),
name: varchar("name"),
description: varchar("description")
});
export const roles = pgTable("roles", {
roleId: serial("roleId").primaryKey(),
orgId: varchar("orgId")
.references(() => orgs.orgId, {
onDelete: "cascade"
})
.notNull(),
isAdmin: boolean("isAdmin"),
name: varchar("name").notNull(),
description: varchar("description")
});
export const roleActions = pgTable("roleActions", {
roleId: integer("roleId")
.notNull()
.references(() => roles.roleId, { onDelete: "cascade" }),
actionId: varchar("actionId")
.notNull()
.references(() => actions.actionId, { onDelete: "cascade" }),
orgId: varchar("orgId")
.notNull()
.references(() => orgs.orgId, { onDelete: "cascade" })
});
export const userActions = pgTable("userActions", {
userId: varchar("userId")
.notNull()
.references(() => users.userId, { onDelete: "cascade" }),
actionId: varchar("actionId")
.notNull()
.references(() => actions.actionId, { onDelete: "cascade" }),
orgId: varchar("orgId")
.notNull()
.references(() => orgs.orgId, { onDelete: "cascade" })
});
export const roleSites = pgTable("roleSites", {
roleId: integer("roleId")
.notNull()
.references(() => roles.roleId, { onDelete: "cascade" }),
siteId: integer("siteId")
.notNull()
.references(() => sites.siteId, { onDelete: "cascade" })
});
export const userSites = pgTable("userSites", {
userId: varchar("userId")
.notNull()
.references(() => users.userId, { onDelete: "cascade" }),
siteId: integer("siteId")
.notNull()
.references(() => sites.siteId, { onDelete: "cascade" })
});
export const roleResources = pgTable("roleResources", {
roleId: integer("roleId")
.notNull()
.references(() => roles.roleId, { onDelete: "cascade" }),
resourceId: integer("resourceId")
.notNull()
.references(() => resources.resourceId, { onDelete: "cascade" })
});
export const userResources = pgTable("userResources", {
userId: varchar("userId")
.notNull()
.references(() => users.userId, { onDelete: "cascade" }),
resourceId: integer("resourceId")
.notNull()
.references(() => resources.resourceId, { onDelete: "cascade" })
});
export const limitsTable = pgTable("limits", {
limitId: serial("limitId").primaryKey(),
orgId: varchar("orgId")
.references(() => orgs.orgId, {
onDelete: "cascade"
})
.notNull(),
name: varchar("name").notNull(),
value: bigint("value", { mode: "number" }).notNull(),
description: varchar("description")
});
export const userInvites = pgTable("userInvites", {
inviteId: varchar("inviteId").primaryKey(),
orgId: varchar("orgId")
.notNull()
.references(() => orgs.orgId, { onDelete: "cascade" }),
email: varchar("email").notNull(),
expiresAt: bigint("expiresAt", { mode: "number" }).notNull(),
tokenHash: varchar("token").notNull(),
roleId: integer("roleId")
.notNull()
.references(() => roles.roleId, { onDelete: "cascade" })
});
export const resourcePincode = pgTable("resourcePincode", {
pincodeId: serial("pincodeId").primaryKey(),
resourceId: integer("resourceId")
.notNull()
.references(() => resources.resourceId, { onDelete: "cascade" }),
pincodeHash: varchar("pincodeHash").notNull(),
digitLength: integer("digitLength").notNull()
});
export const resourcePassword = pgTable("resourcePassword", {
passwordId: serial("passwordId").primaryKey(),
resourceId: integer("resourceId")
.notNull()
.references(() => resources.resourceId, { onDelete: "cascade" }),
passwordHash: varchar("passwordHash").notNull()
});
export const resourceAccessToken = pgTable("resourceAccessToken", {
accessTokenId: varchar("accessTokenId").primaryKey(),
orgId: varchar("orgId")
.notNull()
.references(() => orgs.orgId, { onDelete: "cascade" }),
resourceId: integer("resourceId")
.notNull()
.references(() => resources.resourceId, { onDelete: "cascade" }),
tokenHash: varchar("tokenHash").notNull(),
sessionLength: bigint("sessionLength", { mode: "number" }).notNull(),
expiresAt: bigint("expiresAt", { mode: "number" }),
title: varchar("title"),
description: varchar("description"),
createdAt: bigint("createdAt", { mode: "number" }).notNull()
});
export const resourceSessions = pgTable("resourceSessions", {
sessionId: varchar("id").primaryKey(),
resourceId: integer("resourceId")
.notNull()
.references(() => resources.resourceId, { onDelete: "cascade" }),
expiresAt: bigint("expiresAt", { mode: "number" }).notNull(),
sessionLength: bigint("sessionLength", { mode: "number" }).notNull(),
doNotExtend: boolean("doNotExtend").notNull().default(false),
isRequestToken: boolean("isRequestToken"),
userSessionId: varchar("userSessionId").references(
() => sessions.sessionId,
{
onDelete: "cascade"
}
),
passwordId: integer("passwordId").references(
() => resourcePassword.passwordId,
{
onDelete: "cascade"
}
),
pincodeId: integer("pincodeId").references(
() => resourcePincode.pincodeId,
{
onDelete: "cascade"
}
),
whitelistId: integer("whitelistId").references(
() => resourceWhitelist.whitelistId,
{
onDelete: "cascade"
}
),
accessTokenId: varchar("accessTokenId").references(
() => resourceAccessToken.accessTokenId,
{
onDelete: "cascade"
}
)
});
export const resourceWhitelist = pgTable("resourceWhitelist", {
whitelistId: serial("id").primaryKey(),
email: varchar("email").notNull(),
resourceId: integer("resourceId")
.notNull()
.references(() => resources.resourceId, { onDelete: "cascade" })
});
export const resourceOtp = pgTable("resourceOtp", {
otpId: serial("otpId").primaryKey(),
resourceId: integer("resourceId")
.notNull()
.references(() => resources.resourceId, { onDelete: "cascade" }),
email: varchar("email").notNull(),
otpHash: varchar("otpHash").notNull(),
expiresAt: bigint("expiresAt", { mode: "number" }).notNull()
});
export const versionMigrations = pgTable("versionMigrations", {
version: varchar("version").primaryKey(),
executedAt: bigint("executedAt", { mode: "number" }).notNull()
});
export const resourceRules = pgTable("resourceRules", {
ruleId: serial("ruleId").primaryKey(),
resourceId: integer("resourceId")
.notNull()
.references(() => resources.resourceId, { onDelete: "cascade" }),
enabled: boolean("enabled").notNull().default(true),
priority: integer("priority").notNull(),
action: varchar("action").notNull(), // ACCEPT, DROP
match: varchar("match").notNull(), // CIDR, PATH, IP
value: varchar("value").notNull()
});
export const supporterKey = pgTable("supporterKey", {
keyId: serial("keyId").primaryKey(),
key: varchar("key").notNull(),
githubUsername: varchar("githubUsername").notNull(),
phrase: varchar("phrase"),
tier: varchar("tier"),
valid: boolean("valid").notNull().default(false)
});
export const idp = pgTable("idp", {
idpId: serial("idpId").primaryKey(),
name: varchar("name").notNull(),
type: varchar("type").notNull(),
defaultRoleMapping: varchar("defaultRoleMapping"),
defaultOrgMapping: varchar("defaultOrgMapping"),
autoProvision: boolean("autoProvision").notNull().default(false)
});
export const idpOidcConfig = pgTable("idpOidcConfig", {
idpOauthConfigId: serial("idpOauthConfigId").primaryKey(),
idpId: integer("idpId")
.notNull()
.references(() => idp.idpId, { onDelete: "cascade" }),
clientId: varchar("clientId").notNull(),
clientSecret: varchar("clientSecret").notNull(),
authUrl: varchar("authUrl").notNull(),
tokenUrl: varchar("tokenUrl").notNull(),
identifierPath: varchar("identifierPath").notNull(),
emailPath: varchar("emailPath"),
namePath: varchar("namePath"),
scopes: varchar("scopes").notNull()
});
export const licenseKey = pgTable("licenseKey", {
licenseKeyId: varchar("licenseKeyId").primaryKey().notNull(),
instanceId: varchar("instanceId").notNull(),
token: varchar("token").notNull()
});
export const hostMeta = pgTable("hostMeta", {
hostMetaId: varchar("hostMetaId").primaryKey().notNull(),
createdAt: bigint("createdAt", { mode: "number" }).notNull()
});
export const apiKeys = pgTable("apiKeys", {
apiKeyId: varchar("apiKeyId").primaryKey(),
name: varchar("name").notNull(),
apiKeyHash: varchar("apiKeyHash").notNull(),
lastChars: varchar("lastChars").notNull(),
createdAt: varchar("dateCreated").notNull(),
isRoot: boolean("isRoot").notNull().default(false)
});
export const apiKeyActions = pgTable("apiKeyActions", {
apiKeyId: varchar("apiKeyId")
.notNull()
.references(() => apiKeys.apiKeyId, { onDelete: "cascade" }),
actionId: varchar("actionId")
.notNull()
.references(() => actions.actionId, { onDelete: "cascade" })
});
export const apiKeyOrg = pgTable("apiKeyOrg", {
apiKeyId: varchar("apiKeyId")
.notNull()
.references(() => apiKeys.apiKeyId, { onDelete: "cascade" }),
orgId: varchar("orgId")
.references(() => orgs.orgId, {
onDelete: "cascade"
})
.notNull()
});
export const idpOrg = pgTable("idpOrg", {
idpId: integer("idpId")
.notNull()
.references(() => idp.idpId, { onDelete: "cascade" }),
orgId: varchar("orgId")
.notNull()
.references(() => orgs.orgId, { onDelete: "cascade" }),
roleMapping: varchar("roleMapping"),
orgMapping: varchar("orgMapping")
});
export type Org = InferSelectModel<typeof orgs>;
export type User = InferSelectModel<typeof users>;
export type Site = InferSelectModel<typeof sites>;
export type Resource = InferSelectModel<typeof resources>;
export type ExitNode = InferSelectModel<typeof exitNodes>;
export type Target = InferSelectModel<typeof targets>;
export type Session = InferSelectModel<typeof sessions>;
export type Newt = InferSelectModel<typeof newts>;
export type NewtSession = InferSelectModel<typeof newtSessions>;
export type EmailVerificationCode = InferSelectModel<
typeof emailVerificationCodes
>;
export type TwoFactorBackupCode = InferSelectModel<typeof twoFactorBackupCodes>;
export type PasswordResetToken = InferSelectModel<typeof passwordResetTokens>;
export type Role = InferSelectModel<typeof roles>;
export type Action = InferSelectModel<typeof actions>;
export type RoleAction = InferSelectModel<typeof roleActions>;
export type UserAction = InferSelectModel<typeof userActions>;
export type RoleSite = InferSelectModel<typeof roleSites>;
export type UserSite = InferSelectModel<typeof userSites>;
export type RoleResource = InferSelectModel<typeof roleResources>;
export type UserResource = InferSelectModel<typeof userResources>;
export type Limit = InferSelectModel<typeof limitsTable>;
export type UserInvite = InferSelectModel<typeof userInvites>;
export type UserOrg = InferSelectModel<typeof userOrgs>;
export type ResourceSession = InferSelectModel<typeof resourceSessions>;
export type ResourcePincode = InferSelectModel<typeof resourcePincode>;
export type ResourcePassword = InferSelectModel<typeof resourcePassword>;
export type ResourceOtp = InferSelectModel<typeof resourceOtp>;
export type ResourceAccessToken = InferSelectModel<typeof resourceAccessToken>;
export type ResourceWhitelist = InferSelectModel<typeof resourceWhitelist>;
export type VersionMigration = InferSelectModel<typeof versionMigrations>;
export type ResourceRule = InferSelectModel<typeof resourceRules>;
export type Domain = InferSelectModel<typeof domains>;
export type SupporterKey = InferSelectModel<typeof supporterKey>;
export type Idp = InferSelectModel<typeof idp>;
export type ApiKey = InferSelectModel<typeof apiKeys>;
export type ApiKeyAction = InferSelectModel<typeof apiKeyActions>;
export type ApiKeyOrg = InferSelectModel<typeof apiKeyOrg>;

View File

@@ -0,0 +1,58 @@
import { drizzle as DrizzleSqlite } from "drizzle-orm/better-sqlite3";
import Database from "better-sqlite3";
import * as schema from "./schema";
import path from "path";
import fs from "fs/promises";
import { APP_PATH } from "@server/lib/consts";
import { existsSync, mkdirSync } from "fs";
import { readConfigFile } from "@server/lib/readConfigFile";
export const location = path.join(APP_PATH, "db", "db.sqlite");
export const exists = await checkFileExists(location);
bootstrapVolume();
function createDb() {
const config = readConfigFile();
const sqlite = new Database(location);
return DrizzleSqlite(sqlite, { schema });
}
export const db = createDb();
export default db;
async function checkFileExists(filePath: string): Promise<boolean> {
try {
await fs.access(filePath);
return true;
} catch {
return false;
}
}
function bootstrapVolume() {
const appPath = APP_PATH;
const dbDir = path.join(appPath, "db");
const logsDir = path.join(appPath, "logs");
// check if the db directory exists and create it if it doesn't
if (!existsSync(dbDir)) {
mkdirSync(dbDir, { recursive: true });
}
// check if the logs directory exists and create it if it doesn't
if (!existsSync(logsDir)) {
mkdirSync(logsDir, { recursive: true });
}
// THIS IS FOR TRAEFIK; NOT REALLY NEEDED, BUT JUST IN CASE
const traefikDir = path.join(appPath, "traefik");
// check if the traefik directory exists and create it if it doesn't
if (!existsSync(traefikDir)) {
mkdirSync(traefikDir, { recursive: true });
}
}

View File

@@ -0,0 +1,2 @@
export * from "./driver";
export * from "./schema";

View File

@@ -1,5 +1,5 @@
import { migrate } from "drizzle-orm/better-sqlite3/migrator";
import db from "@server/db";
import db from "./driver";
import path from "path";
const migrationsFolder = path.join("server/migrations");
@@ -7,7 +7,7 @@ const migrationsFolder = path.join("server/migrations");
const runMigrations = async () => {
console.log("Running migrations...");
try {
migrate(db, {
migrate(db as any, {
migrationsFolder: migrationsFolder,
});
console.log("Migrations completed successfully.");

View File

@@ -41,7 +41,10 @@ export const sites = sqliteTable("sites", {
megabytesOut: integer("bytesOut"),
lastBandwidthUpdate: text("lastBandwidthUpdate"),
type: text("type").notNull(), // "newt" or "wireguard"
online: integer("online", { mode: "boolean" }).notNull().default(false)
online: integer("online", { mode: "boolean" }).notNull().default(false),
dockerSocketEnabled: integer("dockerSocketEnabled", { mode: "boolean" })
.notNull()
.default(true)
});
export const resources = sqliteTable("resources", {

View File

@@ -4,9 +4,9 @@ import { runSetupFunctions } from "./setup";
import { createApiServer } from "./apiServer";
import { createNextServer } from "./nextServer";
import { createInternalServer } from "./internalServer";
import { ApiKey, ApiKeyOrg, Session, User, UserOrg } from "./db/schemas";
import { ApiKey, ApiKeyOrg, Session, User, UserOrg } from "@server/db";
import { createIntegrationApiServer } from "./integrationApiServer";
import license from "./license/license.js";
import config from "@server/lib/config";
async function startServers() {
await runSetupFunctions();
@@ -17,7 +17,7 @@ async function startServers() {
const nextServer = await createNextServer();
let integrationServer;
if (await license.isUnlocked()) {
if (config.getRawConfig().flags?.enable_integration_api) {
integrationServer = createIntegrationApiServer();
}

View File

@@ -1,8 +1,3 @@
// This file is licensed under the Fossorial Commercial License.
// Unauthorized use, copying, modification, or distribution is strictly prohibited.
//
// Copyright (c) 2025 Fossorial LLC. All rights reserved.
import express from "express";
import cors from "cors";
import cookieParser from "cookie-parser";
@@ -11,7 +6,6 @@ import logger from "@server/logger";
import {
errorHandlerMiddleware,
notFoundMiddleware,
verifyValidLicense
} from "@server/middlewares";
import { authenticated, unauthenticated } from "@server/routers/integration";
import { logIncomingMiddleware } from "./middlewares/logIncoming";
@@ -26,8 +20,6 @@ const externalPort = config.getRawConfig().server.integration_port;
export function createIntegrationApiServer() {
const apiServer = express();
apiServer.use(verifyValidLicense);
if (config.getRawConfig().server.trust_proxy) {
apiServer.set("trust proxy", 1);
}

View File

@@ -1,6 +1,6 @@
import db from "@server/db";
import { db } from "@server/db";
import { and, eq } from "drizzle-orm";
import { roleResources, userResources } from "@server/db/schemas";
import { roleResources, userResources } from "@server/db";
export async function canUserAccessResource({
userId,

View File

@@ -1,173 +1,10 @@
import fs from "fs";
import yaml from "js-yaml";
import { z } from "zod";
import { fromError } from "zod-validation-error";
import {
__DIRNAME,
APP_VERSION,
configFilePath1,
configFilePath2
} from "@server/lib/consts";
import { passwordSchema } from "@server/auth/passwordSchema";
import stoi from "./stoi";
import db from "@server/db";
import { SupporterKey, supporterKey } from "@server/db/schemas";
import { __DIRNAME, APP_VERSION } from "@server/lib/consts";
import { db } from "@server/db";
import { SupporterKey, supporterKey } from "@server/db";
import { eq } from "drizzle-orm";
import { license } from "@server/license/license";
const portSchema = z.number().positive().gt(0).lte(65535);
const getEnvOrYaml = (envVar: string) => (valFromYaml: any) => {
return process.env[envVar] ?? valFromYaml;
};
const configSchema = z.object({
app: z.object({
dashboard_url: z
.string()
.url()
.optional()
.pipe(z.string().url())
.transform((url) => url.toLowerCase()),
log_level: z.enum(["debug", "info", "warn", "error"]),
save_logs: z.boolean(),
log_failed_attempts: z.boolean().optional()
}),
domains: z
.record(
z.string(),
z.object({
base_domain: z
.string()
.nonempty("base_domain must not be empty")
.transform((url) => url.toLowerCase()),
cert_resolver: z.string().optional(),
prefer_wildcard_cert: z.boolean().optional()
})
)
.refine(
(domains) => {
const keys = Object.keys(domains);
if (keys.length === 0) {
return false;
}
return true;
},
{
message: "At least one domain must be defined"
}
),
server: z.object({
integration_port: portSchema
.optional()
.transform(stoi)
.pipe(portSchema.optional()),
external_port: portSchema.optional().transform(stoi).pipe(portSchema),
internal_port: portSchema.optional().transform(stoi).pipe(portSchema),
next_port: portSchema.optional().transform(stoi).pipe(portSchema),
internal_hostname: z.string().transform((url) => url.toLowerCase()),
session_cookie_name: z.string(),
resource_access_token_param: z.string(),
resource_access_token_headers: z.object({
id: z.string(),
token: z.string()
}),
resource_session_request_param: z.string(),
dashboard_session_length_hours: z
.number()
.positive()
.gt(0)
.optional()
.default(720),
resource_session_length_hours: z
.number()
.positive()
.gt(0)
.optional()
.default(720),
cors: z
.object({
origins: z.array(z.string()).optional(),
methods: z.array(z.string()).optional(),
allowed_headers: z.array(z.string()).optional(),
credentials: z.boolean().optional()
})
.optional(),
trust_proxy: z.boolean().optional().default(true),
secret: z
.string()
.optional()
.transform(getEnvOrYaml("SERVER_SECRET"))
.pipe(z.string().min(8))
}),
traefik: z.object({
http_entrypoint: z.string(),
https_entrypoint: z.string().optional(),
additional_middlewares: z.array(z.string()).optional()
}),
gerbil: z.object({
start_port: portSchema.optional().transform(stoi).pipe(portSchema),
base_endpoint: z
.string()
.optional()
.pipe(z.string())
.transform((url) => url.toLowerCase()),
use_subdomain: z.boolean(),
subnet_group: z.string(),
block_size: z.number().positive().gt(0),
site_block_size: z.number().positive().gt(0)
}),
rate_limits: z.object({
global: z.object({
window_minutes: z.number().positive().gt(0),
max_requests: z.number().positive().gt(0)
}),
auth: z
.object({
window_minutes: z.number().positive().gt(0),
max_requests: z.number().positive().gt(0)
})
.optional()
}),
email: z
.object({
smtp_host: z.string().optional(),
smtp_port: portSchema.optional(),
smtp_user: z.string().optional(),
smtp_pass: z.string().optional(),
smtp_secure: z.boolean().optional(),
smtp_tls_reject_unauthorized: z.boolean().optional(),
no_reply: z.string().email().optional()
})
.optional(),
users: z.object({
server_admin: z.object({
email: z
.string()
.email()
.optional()
.transform(getEnvOrYaml("USERS_SERVERADMIN_EMAIL"))
.pipe(z.string().email())
.transform((v) => v.toLowerCase()),
password: passwordSchema
.optional()
.transform(getEnvOrYaml("USERS_SERVERADMIN_PASSWORD"))
.pipe(passwordSchema)
})
}),
flags: z
.object({
require_email_verification: z.boolean().optional(),
disable_signup_without_invite: z.boolean().optional(),
disable_user_create_org: z.boolean().optional(),
allow_raw_resources: z.boolean().optional(),
allow_base_domain_resources: z.boolean().optional(),
allow_local_sites: z.boolean().optional()
})
.optional()
});
import { configSchema, readConfigFile } from "./readConfigFile";
export class Config {
private rawConfig!: z.infer<typeof configSchema>;
@@ -179,96 +16,57 @@ export class Config {
isDev: boolean = process.env.ENVIRONMENT !== "prod";
constructor() {
this.loadConfig();
this.load();
}
public loadConfig() {
const loadConfig = (configPath: string) => {
try {
const yamlContent = fs.readFileSync(configPath, "utf8");
const config = yaml.load(yamlContent);
return config;
} catch (error) {
if (error instanceof Error) {
throw new Error(
`Error loading configuration file: ${error.message}`
);
}
throw error;
}
};
let environment: any;
if (fs.existsSync(configFilePath1)) {
environment = loadConfig(configFilePath1);
} else if (fs.existsSync(configFilePath2)) {
environment = loadConfig(configFilePath2);
}
if (process.env.APP_BASE_DOMAIN) {
console.log(
"You're using deprecated environment variables. Transition to the configuration file. https://docs.fossorial.io/"
);
}
if (!environment) {
throw new Error(
"No configuration file found. Please create one. https://docs.fossorial.io/"
);
}
const parsedConfig = configSchema.safeParse(environment);
if (!parsedConfig.success) {
const errors = fromError(parsedConfig.error);
throw new Error(`Invalid configuration file: ${errors}`);
}
public load() {
const parsedConfig = readConfigFile();
process.env.APP_VERSION = APP_VERSION;
process.env.NEXT_PORT = parsedConfig.data.server.next_port.toString();
process.env.NEXT_PORT = parsedConfig.server.next_port.toString();
process.env.SERVER_EXTERNAL_PORT =
parsedConfig.data.server.external_port.toString();
parsedConfig.server.external_port.toString();
process.env.SERVER_INTERNAL_PORT =
parsedConfig.data.server.internal_port.toString();
process.env.FLAGS_EMAIL_VERIFICATION_REQUIRED = parsedConfig.data.flags
parsedConfig.server.internal_port.toString();
process.env.FLAGS_EMAIL_VERIFICATION_REQUIRED = parsedConfig.flags
?.require_email_verification
? "true"
: "false";
process.env.FLAGS_ALLOW_RAW_RESOURCES = parsedConfig.data.flags
process.env.FLAGS_ALLOW_RAW_RESOURCES = parsedConfig.flags
?.allow_raw_resources
? "true"
: "false";
process.env.SESSION_COOKIE_NAME =
parsedConfig.data.server.session_cookie_name;
process.env.EMAIL_ENABLED = parsedConfig.data.email ? "true" : "false";
process.env.DISABLE_SIGNUP_WITHOUT_INVITE = parsedConfig.data.flags
parsedConfig.server.session_cookie_name;
process.env.EMAIL_ENABLED = parsedConfig.email ? "true" : "false";
process.env.DISABLE_SIGNUP_WITHOUT_INVITE = parsedConfig.flags
?.disable_signup_without_invite
? "true"
: "false";
process.env.DISABLE_USER_CREATE_ORG = parsedConfig.data.flags
process.env.DISABLE_USER_CREATE_ORG = parsedConfig.flags
?.disable_user_create_org
? "true"
: "false";
process.env.RESOURCE_ACCESS_TOKEN_PARAM =
parsedConfig.data.server.resource_access_token_param;
parsedConfig.server.resource_access_token_param;
process.env.RESOURCE_ACCESS_TOKEN_HEADERS_ID =
parsedConfig.data.server.resource_access_token_headers.id;
parsedConfig.server.resource_access_token_headers.id;
process.env.RESOURCE_ACCESS_TOKEN_HEADERS_TOKEN =
parsedConfig.data.server.resource_access_token_headers.token;
parsedConfig.server.resource_access_token_headers.token;
process.env.RESOURCE_SESSION_REQUEST_PARAM =
parsedConfig.data.server.resource_session_request_param;
process.env.FLAGS_ALLOW_BASE_DOMAIN_RESOURCES = parsedConfig.data.flags
parsedConfig.server.resource_session_request_param;
process.env.FLAGS_ALLOW_BASE_DOMAIN_RESOURCES = parsedConfig.flags
?.allow_base_domain_resources
? "true"
: "false";
process.env.DASHBOARD_URL = parsedConfig.data.app.dashboard_url;
process.env.DASHBOARD_URL = parsedConfig.app.dashboard_url;
license.setServerSecret(parsedConfig.data.server.secret);
license.setServerSecret(parsedConfig.server.secret);
this.checkKeyStatus();
this.rawConfig = parsedConfig.data;
this.rawConfig = parsedConfig;
}
private async checkKeyStatus() {

View File

@@ -2,7 +2,7 @@ import path from "path";
import { fileURLToPath } from "url";
// This is a placeholder value replaced by the build process
export const APP_VERSION = "1.3.0";
export const APP_VERSION = "1.5.1";
export const __FILENAME = fileURLToPath(import.meta.url);
export const __DIRNAME = path.dirname(__FILENAME);

View File

@@ -17,7 +17,7 @@ function detectIpVersion(ip: string): IPVersion {
*/
function ipToBigInt(ip: string): bigint {
const version = detectIpVersion(ip);
if (version === 4) {
return ip.split('.')
.reduce((acc, octet) => {
@@ -105,7 +105,7 @@ export function cidrToRange(cidr: string): IPRange {
const version = detectIpVersion(ip);
const prefixBits = parseInt(prefix);
const ipBigInt = ipToBigInt(ip);
// Validate prefix length
const maxPrefix = version === 4 ? 32 : 128;
if (prefixBits < 0 || prefixBits > maxPrefix) {
@@ -116,7 +116,7 @@ export function cidrToRange(cidr: string): IPRange {
const mask = BigInt.asUintN(version === 4 ? 64 : 128, (BigInt(1) << shiftBits) - BigInt(1));
const start = ipBigInt & ~mask;
const end = start | mask;
return { start, end };
}
@@ -136,17 +136,17 @@ export function findNextAvailableCidr(
if (!startCidr && existingCidrs.length === 0) {
return null;
}
// If no existing CIDRs, use the IP version from startCidr
const version = startCidr
const version = startCidr
? detectIpVersion(startCidr.split('/')[0])
: 4; // Default to IPv4 if no startCidr provided
// Use appropriate default startCidr if none provided
startCidr = startCidr || (version === 4 ? "0.0.0.0/0" : "::/0");
// If there are existing CIDRs, ensure all are same version
if (existingCidrs.length > 0 &&
if (existingCidrs.length > 0 &&
existingCidrs.some(cidr => detectIpVersion(cidr.split('/')[0]) !== version)) {
throw new Error('All CIDRs must be of the same IP version');
}
@@ -196,12 +196,14 @@ export function findNextAvailableCidr(
export function isIpInCidr(ip: string, cidr: string): boolean {
const ipVersion = detectIpVersion(ip);
const cidrVersion = detectIpVersion(cidr.split('/')[0]);
// If IP versions don't match, the IP cannot be in the CIDR range
if (ipVersion !== cidrVersion) {
throw new Error('IP address and CIDR must be of the same version');
// throw new Erorr
return false;
}
const ipBigInt = ipToBigInt(ip);
const range = cidrToRange(cidr);
return ipBigInt >= range.start && ipBigInt <= range.end;
}
}

View File

@@ -0,0 +1,264 @@
import fs from "fs";
import yaml from "js-yaml";
import { configFilePath1, configFilePath2 } from "./consts";
import { z } from "zod";
import stoi from "./stoi";
import { passwordSchema } from "@server/auth/passwordSchema";
import { fromError } from "zod-validation-error";
const portSchema = z.number().positive().gt(0).lte(65535);
const getEnvOrYaml = (envVar: string) => (valFromYaml: any) => {
return process.env[envVar] ?? valFromYaml;
};
export const configSchema = z.object({
app: z.object({
dashboard_url: z
.string()
.url()
.optional()
.pipe(z.string().url())
.transform((url) => url.toLowerCase()),
log_level: z
.enum(["debug", "info", "warn", "error"])
.optional()
.default("info"),
save_logs: z.boolean().optional().default(false),
log_failed_attempts: z.boolean().optional().default(false)
}),
domains: z
.record(
z.string(),
z.object({
base_domain: z
.string()
.nonempty("base_domain must not be empty")
.transform((url) => url.toLowerCase()),
cert_resolver: z.string().optional().default("letsencrypt"),
prefer_wildcard_cert: z.boolean().optional().default(false)
})
)
.refine(
(domains) => {
const keys = Object.keys(domains);
if (keys.length === 0) {
return false;
}
return true;
},
{
message: "At least one domain must be defined"
}
),
server: z.object({
integration_port: portSchema
.optional()
.default(3003)
.transform(stoi)
.pipe(portSchema.optional()),
external_port: portSchema
.optional()
.default(3000)
.transform(stoi)
.pipe(portSchema),
internal_port: portSchema
.optional()
.default(3001)
.transform(stoi)
.pipe(portSchema),
next_port: portSchema
.optional()
.default(3002)
.transform(stoi)
.pipe(portSchema),
internal_hostname: z
.string()
.optional()
.default("pangolin")
.transform((url) => url.toLowerCase()),
session_cookie_name: z.string().optional().default("p_session_token"),
resource_access_token_param: z.string().optional().default("p_token"),
resource_access_token_headers: z
.object({
id: z.string().optional().default("P-Access-Token-Id"),
token: z.string().optional().default("P-Access-Token")
})
.optional()
.default({}),
resource_session_request_param: z
.string()
.optional()
.default("resource_session_request_param"),
dashboard_session_length_hours: z
.number()
.positive()
.gt(0)
.optional()
.default(720),
resource_session_length_hours: z
.number()
.positive()
.gt(0)
.optional()
.default(720),
cors: z
.object({
origins: z.array(z.string()).optional(),
methods: z.array(z.string()).optional(),
allowed_headers: z.array(z.string()).optional(),
credentials: z.boolean().optional()
})
.optional(),
trust_proxy: z.boolean().optional().default(true),
secret: z
.string()
.optional()
.transform(getEnvOrYaml("SERVER_SECRET"))
.pipe(z.string().min(8))
}),
postgres: z
.object({
connection_string: z.string().optional()
})
.default({}),
traefik: z
.object({
http_entrypoint: z.string().optional().default("web"),
https_entrypoint: z.string().optional().default("websecure"),
additional_middlewares: z.array(z.string()).optional()
})
.optional()
.default({}),
gerbil: z
.object({
start_port: portSchema
.optional()
.default(51820)
.transform(stoi)
.pipe(portSchema),
base_endpoint: z
.string()
.optional()
.pipe(z.string())
.transform((url) => url.toLowerCase()),
use_subdomain: z.boolean().optional().default(false),
subnet_group: z.string().optional().default("100.89.137.0/20"),
block_size: z.number().positive().gt(0).optional().default(24),
site_block_size: z.number().positive().gt(0).optional().default(30)
})
.optional()
.default({}),
rate_limits: z
.object({
global: z
.object({
window_minutes: z
.number()
.positive()
.gt(0)
.optional()
.default(1),
max_requests: z
.number()
.positive()
.gt(0)
.optional()
.default(500)
})
.optional()
.default({}),
auth: z
.object({
window_minutes: z.number().positive().gt(0),
max_requests: z.number().positive().gt(0)
})
.optional()
})
.optional()
.default({}),
email: z
.object({
smtp_host: z.string().optional(),
smtp_port: portSchema.optional(),
smtp_user: z.string().optional(),
smtp_pass: z.string().optional(),
smtp_secure: z.boolean().optional(),
smtp_tls_reject_unauthorized: z.boolean().optional(),
no_reply: z.string().email().optional()
})
.optional(),
users: z.object({
server_admin: z.object({
email: z
.string()
.email()
.optional()
.transform(getEnvOrYaml("USERS_SERVERADMIN_EMAIL"))
.pipe(z.string().email())
.transform((v) => v.toLowerCase()),
password: passwordSchema
.optional()
.transform(getEnvOrYaml("USERS_SERVERADMIN_PASSWORD"))
.pipe(passwordSchema)
})
}),
flags: z
.object({
require_email_verification: z.boolean().optional(),
disable_signup_without_invite: z.boolean().optional(),
disable_user_create_org: z.boolean().optional(),
allow_raw_resources: z.boolean().optional(),
allow_base_domain_resources: z.boolean().optional(),
allow_local_sites: z.boolean().optional(),
enable_integration_api: z.boolean().optional()
})
.optional()
});
export function readConfigFile() {
const loadConfig = (configPath: string) => {
try {
const yamlContent = fs.readFileSync(configPath, "utf8");
const config = yaml.load(yamlContent);
return config;
} catch (error) {
if (error instanceof Error) {
throw new Error(
`Error loading configuration file: ${error.message}`
);
}
throw error;
}
};
let environment: any;
if (fs.existsSync(configFilePath1)) {
environment = loadConfig(configFilePath1);
} else if (fs.existsSync(configFilePath2)) {
environment = loadConfig(configFilePath2);
}
if (process.env.APP_BASE_DOMAIN) {
console.log(
"You're using deprecated environment variables. Transition to the configuration file. https://docs.fossorial.io/"
);
}
if (!environment) {
throw new Error(
"No configuration file found. Please create one. https://docs.fossorial.io/"
);
}
const parsedConfig = configSchema.safeParse(environment);
if (!parsedConfig.success) {
const errors = fromError(parsedConfig.error);
throw new Error(`Invalid configuration file: ${errors}`);
}
return parsedConfig.data;
}

View File

@@ -9,6 +9,10 @@ export function isValidIP(ip: string): boolean {
}
export function isValidUrlGlobPattern(pattern: string): boolean {
if (pattern === "/") {
return true;
}
// Remove leading slash if present
pattern = pattern.startsWith("/") ? pattern.slice(1) : pattern;

View File

@@ -1,10 +1,5 @@
// This file is licensed under the Fossorial Commercial License.
// Unauthorized use, copying, modification, or distribution is strictly prohibited.
//
// Copyright (c) 2025 Fossorial LLC. All rights reserved.
import db from "@server/db";
import { hostMeta, licenseKey, sites } from "@server/db/schemas";
import { db } from "@server/db";
import { hostMeta, licenseKey, sites } from "@server/db";
import logger from "@server/logger";
import NodeCache from "node-cache";
import { validateJWT } from "./licenseJwt";

View File

@@ -1,8 +1,3 @@
// This file is licensed under the Fossorial Commercial License.
// Unauthorized use, copying, modification, or distribution is strictly prohibited.
//
// Copyright (c) 2025 Fossorial LLC. All rights reserved.
import * as crypto from "crypto";
/**

View File

@@ -1,6 +1,6 @@
import { Request, Response, NextFunction } from "express";
import { db } from "@server/db";
import { userOrgs, orgs } from "@server/db/schemas";
import { userOrgs, orgs } from "@server/db";
import { eq } from "drizzle-orm";
import createHttpError from "http-errors";
import HttpCode from "@server/types/HttpCode";

View File

@@ -1,8 +1,3 @@
// This file is licensed under the Fossorial Commercial License.
// Unauthorized use, copying, modification, or distribution is strictly prohibited.
//
// Copyright (c) 2025 Fossorial LLC. All rights reserved.
export * from "./verifyApiKey";
export * from "./verifyApiKeyOrgAccess";
export * from "./verifyApiKeyHasAction";

View File

@@ -1,11 +1,6 @@
// This file is licensed under the Fossorial Commercial License.
// Unauthorized use, copying, modification, or distribution is strictly prohibited.
//
// Copyright (c) 2025 Fossorial LLC. All rights reserved.
import { Request, Response, NextFunction } from "express";
import { db } from "@server/db";
import { resourceAccessToken, resources, apiKeyOrg } from "@server/db/schemas";
import { resourceAccessToken, resources, apiKeyOrg } from "@server/db";
import { and, eq } from "drizzle-orm";
import createHttpError from "http-errors";
import HttpCode from "@server/types/HttpCode";

View File

@@ -1,11 +1,6 @@
// This file is licensed under the Fossorial Commercial License.
// Unauthorized use, copying, modification, or distribution is strictly prohibited.
//
// Copyright (c) 2025 Fossorial LLC. All rights reserved.
import { verifyPassword } from "@server/auth/password";
import db from "@server/db";
import { apiKeys } from "@server/db/schemas";
import { db } from "@server/db";
import { apiKeys } from "@server/db";
import logger from "@server/logger";
import HttpCode from "@server/types/HttpCode";
import { eq } from "drizzle-orm";

View File

@@ -1,11 +1,6 @@
// This file is licensed under the Fossorial Commercial License.
// Unauthorized use, copying, modification, or distribution is strictly prohibited.
//
// Copyright (c) 2025 Fossorial LLC. All rights reserved.
import { Request, Response, NextFunction } from "express";
import { db } from "@server/db";
import { apiKeys, apiKeyOrg } from "@server/db/schemas";
import { apiKeys, apiKeyOrg } from "@server/db";
import { and, eq, or } from "drizzle-orm";
import createHttpError from "http-errors";
import HttpCode from "@server/types/HttpCode";

View File

@@ -1,15 +1,10 @@
// This file is licensed under the Fossorial Commercial License.
// Unauthorized use, copying, modification, or distribution is strictly prohibited.
//
// Copyright (c) 2025 Fossorial LLC. All rights reserved.
import { Request, Response, NextFunction } from "express";
import createHttpError from "http-errors";
import HttpCode from "@server/types/HttpCode";
import logger from "@server/logger";
import { ActionsEnum } from "@server/auth/actions";
import db from "@server/db";
import { apiKeyActions } from "@server/db/schemas";
import { db } from "@server/db";
import { apiKeyActions } from "@server/db";
import { and, eq } from "drizzle-orm";
export function verifyApiKeyHasAction(action: ActionsEnum) {

View File

@@ -1,8 +1,3 @@
// This file is licensed under the Fossorial Commercial License.
// Unauthorized use, copying, modification, or distribution is strictly prohibited.
//
// Copyright (c) 2025 Fossorial LLC. All rights reserved.
import logger from "@server/logger";
import HttpCode from "@server/types/HttpCode";
import { Request, Response, NextFunction } from "express";

View File

@@ -1,11 +1,6 @@
// This file is licensed under the Fossorial Commercial License.
// Unauthorized use, copying, modification, or distribution is strictly prohibited.
//
// Copyright (c) 2025 Fossorial LLC. All rights reserved.
import { Request, Response, NextFunction } from "express";
import { db } from "@server/db";
import { apiKeyOrg } from "@server/db/schemas";
import { apiKeyOrg } from "@server/db";
import { and, eq } from "drizzle-orm";
import createHttpError from "http-errors";
import HttpCode from "@server/types/HttpCode";

View File

@@ -1,11 +1,6 @@
// This file is licensed under the Fossorial Commercial License.
// Unauthorized use, copying, modification, or distribution is strictly prohibited.
//
// Copyright (c) 2025 Fossorial LLC. All rights reserved.
import { Request, Response, NextFunction } from "express";
import { db } from "@server/db";
import { resources, apiKeyOrg } from "@server/db/schemas";
import { resources, apiKeyOrg } from "@server/db";
import { eq, and } from "drizzle-orm";
import createHttpError from "http-errors";
import HttpCode from "@server/types/HttpCode";

View File

@@ -1,11 +1,6 @@
// This file is licensed under the Fossorial Commercial License.
// Unauthorized use, copying, modification, or distribution is strictly prohibited.
//
// Copyright (c) 2025 Fossorial LLC. All rights reserved.
import { Request, Response, NextFunction } from "express";
import { db } from "@server/db";
import { roles, apiKeyOrg } from "@server/db/schemas";
import { roles, apiKeyOrg } from "@server/db";
import { and, eq, inArray } from "drizzle-orm";
import createHttpError from "http-errors";
import HttpCode from "@server/types/HttpCode";

View File

@@ -1,11 +1,6 @@
// This file is licensed under the Fossorial Commercial License.
// Unauthorized use, copying, modification, or distribution is strictly prohibited.
//
// Copyright (c) 2025 Fossorial LLC. All rights reserved.
import { Request, Response, NextFunction } from "express";
import { db } from "@server/db";
import { userOrgs } from "@server/db/schemas";
import { userOrgs } from "@server/db";
import { and, eq, inArray } from "drizzle-orm";
import createHttpError from "http-errors";
import HttpCode from "@server/types/HttpCode";

View File

@@ -1,14 +1,9 @@
// This file is licensed under the Fossorial Commercial License.
// Unauthorized use, copying, modification, or distribution is strictly prohibited.
//
// Copyright (c) 2025 Fossorial LLC. All rights reserved.
import { Request, Response, NextFunction } from "express";
import { db } from "@server/db";
import {
sites,
apiKeyOrg
} from "@server/db/schemas";
} from "@server/db";
import { and, eq, or } from "drizzle-orm";
import createHttpError from "http-errors";
import HttpCode from "@server/types/HttpCode";

View File

@@ -1,11 +1,6 @@
// This file is licensed under the Fossorial Commercial License.
// Unauthorized use, copying, modification, or distribution is strictly prohibited.
//
// Copyright (c) 2025 Fossorial LLC. All rights reserved.
import { Request, Response, NextFunction } from "express";
import { db } from "@server/db";
import { resources, targets, apiKeyOrg } from "@server/db/schemas";
import { resources, targets, apiKeyOrg } from "@server/db";
import { and, eq } from "drizzle-orm";
import createHttpError from "http-errors";
import HttpCode from "@server/types/HttpCode";

View File

@@ -1,11 +1,6 @@
// This file is licensed under the Fossorial Commercial License.
// Unauthorized use, copying, modification, or distribution is strictly prohibited.
//
// Copyright (c) 2025 Fossorial LLC. All rights reserved.
import { Request, Response, NextFunction } from "express";
import { db } from "@server/db";
import { userOrgs } from "@server/db/schemas";
import { userOrgs } from "@server/db";
import { and, eq } from "drizzle-orm";
import createHttpError from "http-errors";
import HttpCode from "@server/types/HttpCode";

View File

@@ -1,6 +1,6 @@
import { Request, Response, NextFunction } from "express";
import { db } from "@server/db";
import { resourceAccessToken, resources, userOrgs } from "@server/db/schemas";
import { resourceAccessToken, resources, userOrgs } from "@server/db";
import { and, eq } from "drizzle-orm";
import createHttpError from "http-errors";
import HttpCode from "@server/types/HttpCode";

View File

@@ -1,6 +1,6 @@
import { Request, Response, NextFunction } from "express";
import { db } from "@server/db";
import { roles, userOrgs } from "@server/db/schemas";
import { roles, userOrgs } from "@server/db";
import { and, eq } from "drizzle-orm";
import createHttpError from "http-errors";
import HttpCode from "@server/types/HttpCode";

View File

@@ -1,11 +1,6 @@
// This file is licensed under the Fossorial Commercial License.
// Unauthorized use, copying, modification, or distribution is strictly prohibited.
//
// Copyright (c) 2025 Fossorial LLC. All rights reserved.
import { Request, Response, NextFunction } from "express";
import { db } from "@server/db";
import { userOrgs, apiKeys, apiKeyOrg } from "@server/db/schemas";
import { userOrgs, apiKeys, apiKeyOrg } from "@server/db";
import { and, eq, or } from "drizzle-orm";
import createHttpError from "http-errors";
import HttpCode from "@server/types/HttpCode";

View File

@@ -1,6 +1,6 @@
import { Request, Response, NextFunction } from "express";
import { db } from "@server/db";
import { userOrgs } from "@server/db/schemas";
import { userOrgs } from "@server/db";
import { and, eq } from "drizzle-orm";
import createHttpError from "http-errors";
import HttpCode from "@server/types/HttpCode";

View File

@@ -5,7 +5,7 @@ import {
userOrgs,
userResources,
roleResources,
} from "@server/db/schemas";
} from "@server/db";
import { and, eq } from "drizzle-orm";
import createHttpError from "http-errors";
import HttpCode from "@server/types/HttpCode";

View File

@@ -1,6 +1,6 @@
import { Request, Response, NextFunction } from "express";
import { db } from "@server/db";
import { roles, userOrgs } from "@server/db/schemas";
import { roles, userOrgs } from "@server/db";
import { and, eq, inArray } from "drizzle-orm";
import createHttpError from "http-errors";
import HttpCode from "@server/types/HttpCode";

View File

@@ -1,7 +1,7 @@
import { NextFunction, Response } from "express";
import ErrorResponse from "@server/types/ErrorResponse";
import { db } from "@server/db";
import { users } from "@server/db/schemas";
import { users } from "@server/db";
import { eq } from "drizzle-orm";
import createHttpError from "http-errors";
import HttpCode from "@server/types/HttpCode";

View File

@@ -1,6 +1,6 @@
import { Request, Response, NextFunction } from "express";
import { db } from "@server/db";
import { userOrgs } from "@server/db/schemas";
import { userOrgs } from "@server/db";
import { and, eq, inArray, or } from "drizzle-orm";
import createHttpError from "http-errors";
import HttpCode from "@server/types/HttpCode";

View File

@@ -6,7 +6,7 @@ import {
userSites,
roleSites,
roles,
} from "@server/db/schemas";
} from "@server/db";
import { and, eq, or } from "drizzle-orm";
import createHttpError from "http-errors";
import HttpCode from "@server/types/HttpCode";

View File

@@ -1,6 +1,6 @@
import { Request, Response, NextFunction } from "express";
import { db } from "@server/db";
import { resources, targets, userOrgs } from "@server/db/schemas";
import { resources, targets, userOrgs } from "@server/db";
import { and, eq } from "drizzle-orm";
import createHttpError from "http-errors";
import HttpCode from "@server/types/HttpCode";

View File

@@ -1,7 +1,7 @@
import { NextFunction, Response } from "express";
import ErrorResponse from "@server/types/ErrorResponse";
import { db } from "@server/db";
import { users } from "@server/db/schemas";
import { users } from "@server/db";
import { eq } from "drizzle-orm";
import createHttpError from "http-errors";
import HttpCode from "@server/types/HttpCode";

View File

@@ -1,6 +1,6 @@
import { Request, Response, NextFunction } from "express";
import { db } from "@server/db";
import { userOrgs } from "@server/db/schemas";
import { userOrgs } from "@server/db";
import { and, eq, or } from "drizzle-orm";
import createHttpError from "http-errors";
import HttpCode from "@server/types/HttpCode";

View File

@@ -1,6 +1,6 @@
import { Request, Response, NextFunction } from "express";
import { db } from "@server/db";
import { userOrgs } from "@server/db/schemas";
import { userOrgs } from "@server/db";
import { and, eq } from "drizzle-orm";
import createHttpError from "http-errors";
import HttpCode from "@server/types/HttpCode";

View File

@@ -1,8 +1,3 @@
// This file is licensed under the Fossorial Commercial License.
// Unauthorized use, copying, modification, or distribution is strictly prohibited.
//
// Copyright (c) 2025 Fossorial LLC. All rights reserved.
import { Request, Response, NextFunction } from "express";
import createHttpError from "http-errors";
import HttpCode from "@server/types/HttpCode";

View File

@@ -5,9 +5,9 @@ import HttpCode from "@server/types/HttpCode";
import createHttpError from "http-errors";
import logger from "@server/logger";
import { fromError } from "zod-validation-error";
import { resourceAccessToken } from "@server/db/schemas";
import { resourceAccessToken } from "@server/db";
import { and, eq } from "drizzle-orm";
import db from "@server/db";
import { db } from "@server/db";
import { OpenAPITags, registry } from "@server/openApi";
const deleteAccessTokenParamsSchema = z

View File

@@ -4,12 +4,12 @@ import {
generateIdFromEntropySize,
SESSION_COOKIE_EXPIRES
} from "@server/auth/sessions/app";
import db from "@server/db";
import { db } from "@server/db";
import {
ResourceAccessToken,
resourceAccessToken,
resources
} from "@server/db/schemas";
} from "@server/db";
import HttpCode from "@server/types/HttpCode";
import response from "@server/lib/response";
import { eq } from "drizzle-orm";

View File

@@ -7,7 +7,7 @@ import {
roleResources,
resourceAccessToken,
sites
} from "@server/db/schemas";
} from "@server/db";
import response from "@server/lib/response";
import HttpCode from "@server/types/HttpCode";
import createHttpError from "http-errors";

View File

@@ -1,13 +1,8 @@
// This file is licensed under the Fossorial Commercial License.
// Unauthorized use, copying, modification, or distribution is strictly prohibited.
//
// Copyright (c) 2025 Fossorial LLC. All rights reserved.
import { NextFunction, Request, Response } from "express";
import db from "@server/db";
import { db } from "@server/db";
import HttpCode from "@server/types/HttpCode";
import { z } from "zod";
import { apiKeyOrg, apiKeys } from "@server/db/schemas";
import { apiKeyOrg, apiKeys } from "@server/db";
import { fromError } from "zod-validation-error";
import createHttpError from "http-errors";
import response from "@server/lib/response";

View File

@@ -1,13 +1,8 @@
// This file is licensed under the Fossorial Commercial License.
// Unauthorized use, copying, modification, or distribution is strictly prohibited.
//
// Copyright (c) 2025 Fossorial LLC. All rights reserved.
import { NextFunction, Request, Response } from "express";
import db from "@server/db";
import { db } from "@server/db";
import HttpCode from "@server/types/HttpCode";
import { z } from "zod";
import { apiKeyOrg, apiKeys, orgs } from "@server/db/schemas";
import { apiKeyOrg, apiKeys, orgs } from "@server/db";
import { fromError } from "zod-validation-error";
import createHttpError from "http-errors";
import response from "@server/lib/response";

View File

@@ -1,12 +1,7 @@
// This file is licensed under the Fossorial Commercial License.
// Unauthorized use, copying, modification, or distribution is strictly prohibited.
//
// Copyright (c) 2025 Fossorial LLC. All rights reserved.
import { Request, Response, NextFunction } from "express";
import { z } from "zod";
import { db } from "@server/db";
import { apiKeys } from "@server/db/schemas";
import { apiKeys } from "@server/db";
import { eq } from "drizzle-orm";
import response from "@server/lib/response";
import HttpCode from "@server/types/HttpCode";

View File

@@ -1,12 +1,7 @@
// This file is licensed under the Fossorial Commercial License.
// Unauthorized use, copying, modification, or distribution is strictly prohibited.
//
// Copyright (c) 2025 Fossorial LLC. All rights reserved.
import { Request, Response, NextFunction } from "express";
import { z } from "zod";
import { db } from "@server/db";
import { apiKeyOrg, apiKeys } from "@server/db/schemas";
import { apiKeyOrg, apiKeys } from "@server/db";
import { and, eq } from "drizzle-orm";
import response from "@server/lib/response";
import HttpCode from "@server/types/HttpCode";

View File

@@ -1,12 +1,7 @@
// This file is licensed under the Fossorial Commercial License.
// Unauthorized use, copying, modification, or distribution is strictly prohibited.
//
// Copyright (c) 2025 Fossorial LLC. All rights reserved.
import { Request, Response, NextFunction } from "express";
import { z } from "zod";
import { db } from "@server/db";
import { apiKeys } from "@server/db/schemas";
import { apiKeys } from "@server/db";
import { eq } from "drizzle-orm";
import response from "@server/lib/response";
import HttpCode from "@server/types/HttpCode";

View File

@@ -1,8 +1,3 @@
// This file is licensed under the Fossorial Commercial License.
// Unauthorized use, copying, modification, or distribution is strictly prohibited.
//
// Copyright (c) 2025 Fossorial LLC. All rights reserved.
export * from "./createRootApiKey";
export * from "./deleteApiKey";
export * from "./getApiKey";

View File

@@ -1,10 +1,5 @@
// This file is licensed under the Fossorial Commercial License.
// Unauthorized use, copying, modification, or distribution is strictly prohibited.
//
// Copyright (c) 2025 Fossorial LLC. All rights reserved.
import { db } from "@server/db";
import { actions, apiKeyActions, apiKeyOrg, apiKeys } from "@server/db/schemas";
import { actions, apiKeyActions, apiKeyOrg, apiKeys } from "@server/db";
import logger from "@server/logger";
import HttpCode from "@server/types/HttpCode";
import response from "@server/lib/response";

View File

@@ -1,10 +1,5 @@
// This file is licensed under the Fossorial Commercial License.
// Unauthorized use, copying, modification, or distribution is strictly prohibited.
//
// Copyright (c) 2025 Fossorial LLC. All rights reserved.
import { db } from "@server/db";
import { apiKeyOrg, apiKeys } from "@server/db/schemas";
import { apiKeyOrg, apiKeys } from "@server/db";
import logger from "@server/logger";
import HttpCode from "@server/types/HttpCode";
import response from "@server/lib/response";

View File

@@ -1,10 +1,5 @@
// This file is licensed under the Fossorial Commercial License.
// Unauthorized use, copying, modification, or distribution is strictly prohibited.
//
// Copyright (c) 2025 Fossorial LLC. All rights reserved.
import { db } from "@server/db";
import { apiKeys } from "@server/db/schemas";
import { apiKeys } from "@server/db";
import logger from "@server/logger";
import HttpCode from "@server/types/HttpCode";
import response from "@server/lib/response";

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