started integrating auth with lucia

This commit is contained in:
Milo Schwartz 2024-10-01 20:48:03 -04:00
parent a33a8d7367
commit fc5dca136f
No known key found for this signature in database
20 changed files with 1341 additions and 61 deletions

844
package-lock.json generated
View file

@ -8,12 +8,16 @@
"name": "@fossorial/pangolin",
"version": "0.1.0",
"dependencies": {
"@lucia-auth/adapter-drizzle": "1.1.0",
"@node-rs/argon2": "1.8.3",
"axios": "1.7.7",
"better-sqlite3": "11.3.0",
"cors": "2.8.5",
"drizzle-orm": "0.33.0",
"express": "4.21.0",
"helmet": "7.1.0",
"http-errors": "2.0.0",
"lucia": "3.2.0",
"next": "14.2.13",
"react": "^18",
"react-dom": "^18",
@ -2767,6 +2771,33 @@
"integrity": "sha512-AHy0vjc+n/4w/8Mif+w86qpppHuF3AyXbcWW+R/W7GNA3F5/p2nuhlkCJaTXSLZheB4l1rtHzOfr9A7NwoR/Zg==",
"dev": true
},
"node_modules/@emnapi/core": {
"version": "0.45.0",
"resolved": "https://registry.npmjs.org/@emnapi/core/-/core-0.45.0.tgz",
"integrity": "sha512-DPWjcUDQkCeEM4VnljEOEcXdAD7pp8zSZsgOujk/LGIwCXWbXJngin+MO4zbH429lzeC3WbYLGjE2MaUOwzpyw==",
"optional": true,
"dependencies": {
"tslib": "^2.4.0"
}
},
"node_modules/@emnapi/runtime": {
"version": "0.45.0",
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-0.45.0.tgz",
"integrity": "sha512-Txumi3td7J4A/xTTwlssKieHKTGl3j4A1tglBx72auZ49YK7ePY6XZricgIg9mnZT4xPfA+UPCUdnhRuEFDL+w==",
"optional": true,
"dependencies": {
"tslib": "^2.4.0"
}
},
"node_modules/@emnapi/wasi-threads": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.0.1.tgz",
"integrity": "sha512-iIBu7mwkq4UQGeMEM8bLwNK962nXdhodeScX4slfQnRhEMMzvYivHhutCIk8uojvmASXXPC2WNEjwxFWk72Oqw==",
"optional": true,
"dependencies": {
"tslib": "^2.4.0"
}
},
"node_modules/@esbuild-kit/core-utils": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/@esbuild-kit/core-utils/-/core-utils-3.3.2.tgz",
@ -4734,6 +4765,54 @@
"@jridgewell/sourcemap-codec": "^1.4.14"
}
},
"node_modules/@lucia-auth/adapter-drizzle": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@lucia-auth/adapter-drizzle/-/adapter-drizzle-1.1.0.tgz",
"integrity": "sha512-iCTnZWvfI5lLZOdUHZYiXA1jaspIFEeo2extLxQ3DjP3uOVys7IPwBi7zezLIRu9dhro4H4Kji+7gSYyjcef2A==",
"peerDependencies": {
"drizzle-orm": ">= 0.29 <1",
"lucia": "3.x"
}
},
"node_modules/@napi-rs/wasm-runtime": {
"version": "0.2.4",
"resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.4.tgz",
"integrity": "sha512-9zESzOO5aDByvhIAsOy9TbpZ0Ur2AJbUI7UT73kcUTS2mxAMHOBaa1st/jAymNoCtvrit99kkzT1FZuXVcgfIQ==",
"optional": true,
"dependencies": {
"@emnapi/core": "^1.1.0",
"@emnapi/runtime": "^1.1.0",
"@tybys/wasm-util": "^0.9.0"
}
},
"node_modules/@napi-rs/wasm-runtime/node_modules/@emnapi/core": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.2.0.tgz",
"integrity": "sha512-E7Vgw78I93we4ZWdYCb4DGAwRROGkMIXk7/y87UmANR+J6qsWusmC3gLt0H+O0KOt5e6O38U8oJamgbudrES/w==",
"optional": true,
"dependencies": {
"@emnapi/wasi-threads": "1.0.1",
"tslib": "^2.4.0"
}
},
"node_modules/@napi-rs/wasm-runtime/node_modules/@emnapi/runtime": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.2.0.tgz",
"integrity": "sha512-bV21/9LQmcQeCPEg3BDFtvwL6cwiTMksYNWQQ4KOxCZikEGalWtenoZ0wCiukJINlGCIi2KXx01g4FoH/LxpzQ==",
"optional": true,
"dependencies": {
"tslib": "^2.4.0"
}
},
"node_modules/@napi-rs/wasm-runtime/node_modules/@tybys/wasm-util": {
"version": "0.9.0",
"resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.9.0.tgz",
"integrity": "sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw==",
"optional": true,
"dependencies": {
"tslib": "^2.4.0"
}
},
"node_modules/@next/env": {
"version": "14.2.13",
"resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.13.tgz",
@ -4922,6 +5001,481 @@
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/@node-rs/argon2": {
"version": "1.8.3",
"resolved": "https://registry.npmjs.org/@node-rs/argon2/-/argon2-1.8.3.tgz",
"integrity": "sha512-sf/QAEI59hsMEEE2J8vO4hKrXrv4Oplte3KI2N4MhMDYpytH0drkVfErmHBfWFZxxIEK03fX1WsBNswS2nIZKg==",
"engines": {
"node": ">= 10"
},
"optionalDependencies": {
"@node-rs/argon2-android-arm-eabi": "1.8.3",
"@node-rs/argon2-android-arm64": "1.8.3",
"@node-rs/argon2-darwin-arm64": "1.8.3",
"@node-rs/argon2-darwin-x64": "1.8.3",
"@node-rs/argon2-freebsd-x64": "1.8.3",
"@node-rs/argon2-linux-arm-gnueabihf": "1.8.3",
"@node-rs/argon2-linux-arm64-gnu": "1.8.3",
"@node-rs/argon2-linux-arm64-musl": "1.8.3",
"@node-rs/argon2-linux-x64-gnu": "1.8.3",
"@node-rs/argon2-linux-x64-musl": "1.8.3",
"@node-rs/argon2-wasm32-wasi": "1.8.3",
"@node-rs/argon2-win32-arm64-msvc": "1.8.3",
"@node-rs/argon2-win32-ia32-msvc": "1.8.3",
"@node-rs/argon2-win32-x64-msvc": "1.8.3"
}
},
"node_modules/@node-rs/argon2-android-arm-eabi": {
"version": "1.8.3",
"resolved": "https://registry.npmjs.org/@node-rs/argon2-android-arm-eabi/-/argon2-android-arm-eabi-1.8.3.tgz",
"integrity": "sha512-JFZPlNM0A8Og+Tncb8UZsQrhEMlbHBXPsT3hRoKImzVmTmq28Os0ucFWow0AACp2coLHBSydXH3Dh0lZup3rWw==",
"cpu": [
"arm"
],
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@node-rs/argon2-android-arm64": {
"version": "1.8.3",
"resolved": "https://registry.npmjs.org/@node-rs/argon2-android-arm64/-/argon2-android-arm64-1.8.3.tgz",
"integrity": "sha512-zaf8P3T92caeW2xnMA7P1QvRA4pIt/04oilYP44XlTCtMye//vwXDMeK53sl7dvYiJKnzAWDRx41k8vZvpZazg==",
"cpu": [
"arm64"
],
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@node-rs/argon2-darwin-arm64": {
"version": "1.8.3",
"resolved": "https://registry.npmjs.org/@node-rs/argon2-darwin-arm64/-/argon2-darwin-arm64-1.8.3.tgz",
"integrity": "sha512-DV/IbmLGdNXBtXb5o2UI5ba6kvqXqPAJgmMOTUCuHeBSp992GlLHdfU4rzGu0dNrxudBnunNZv+crd0YdEQSUA==",
"cpu": [
"arm64"
],
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@node-rs/argon2-darwin-x64": {
"version": "1.8.3",
"resolved": "https://registry.npmjs.org/@node-rs/argon2-darwin-x64/-/argon2-darwin-x64-1.8.3.tgz",
"integrity": "sha512-YMjmBGFZhLfYjfQ2gll9A+BZu/zAMV7lWZIbKxb7ZgEofILQwuGmExjDtY3Jplido/6leCEdpmlk2oIsME00LA==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@node-rs/argon2-freebsd-x64": {
"version": "1.8.3",
"resolved": "https://registry.npmjs.org/@node-rs/argon2-freebsd-x64/-/argon2-freebsd-x64-1.8.3.tgz",
"integrity": "sha512-Hq3Rj5Yb2RolTG/luRPnv+XiGCbi5nAK25Pc8ou/tVapwX+iktEm/NXbxc5zsMxraYVkCvfdwBjweC5O+KqCGw==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@node-rs/argon2-linux-arm-gnueabihf": {
"version": "1.8.3",
"resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-arm-gnueabihf/-/argon2-linux-arm-gnueabihf-1.8.3.tgz",
"integrity": "sha512-x49l8RgzKoG0/V0IXa5rrEl1TcJEc936ctlYFvqcunSOyowZ6kiWtrp1qrbOR8gbaNILl11KTF52vF6+h8UlEQ==",
"cpu": [
"arm"
],
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@node-rs/argon2-linux-arm64-gnu": {
"version": "1.8.3",
"resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-arm64-gnu/-/argon2-linux-arm64-gnu-1.8.3.tgz",
"integrity": "sha512-gJesam/qA63reGkb9qJ2TjFSLBtY41zQh2oei7nfnYsmVQPuHHWItJxEa1Bm21SPW53gZex4jFJbDIgj0+PxIw==",
"cpu": [
"arm64"
],
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@node-rs/argon2-linux-arm64-musl": {
"version": "1.8.3",
"resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-arm64-musl/-/argon2-linux-arm64-musl-1.8.3.tgz",
"integrity": "sha512-7O6kQdSKzB4Tjx/EBa8zKIxnmLkQE8VdJgPm6Ksrpn+ueo0mx2xf76fIDnbbTCtm3UbB+y+FkTo2wLA7tOqIKg==",
"cpu": [
"arm64"
],
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@node-rs/argon2-linux-x64-gnu": {
"version": "1.8.3",
"resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-x64-gnu/-/argon2-linux-x64-gnu-1.8.3.tgz",
"integrity": "sha512-OBH+EFG7BGjFyldaao2H2gSCLmjtrrwf420B1L+lFn7JLW9UAjsIPFKAcWsYwPa/PwYzIge9Y7SGcpqlsSEX0w==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@node-rs/argon2-linux-x64-musl": {
"version": "1.8.3",
"resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-x64-musl/-/argon2-linux-x64-musl-1.8.3.tgz",
"integrity": "sha512-bDbMuyekIxZaN7NaX+gHVkOyABB8bcMEJYeRPW1vCXKHj3brJns1wiUFSxqeUXreupifNVJlQfPt1Y5B/vFXgQ==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@node-rs/argon2-wasm32-wasi": {
"version": "1.8.3",
"resolved": "https://registry.npmjs.org/@node-rs/argon2-wasm32-wasi/-/argon2-wasm32-wasi-1.8.3.tgz",
"integrity": "sha512-NBf2cMCDbNKMzp13Pog8ZPmI0M9U4Ak5b95EUjkp17kdKZFds12dwW67EMnj7Zy+pRqby2QLECaWebDYfNENTg==",
"cpu": [
"wasm32"
],
"optional": true,
"dependencies": {
"@napi-rs/wasm-runtime": "^0.2.3"
},
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/@node-rs/argon2-win32-arm64-msvc": {
"version": "1.8.3",
"resolved": "https://registry.npmjs.org/@node-rs/argon2-win32-arm64-msvc/-/argon2-win32-arm64-msvc-1.8.3.tgz",
"integrity": "sha512-AHpPo7UbdW5WWjwreVpgFSY0o1RY4A7cUFaqDXZB2OqEuyrhMxBdZct9PX7PQKI18D85pLsODnR+gvVuTwJ6rQ==",
"cpu": [
"arm64"
],
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@node-rs/argon2-win32-ia32-msvc": {
"version": "1.8.3",
"resolved": "https://registry.npmjs.org/@node-rs/argon2-win32-ia32-msvc/-/argon2-win32-ia32-msvc-1.8.3.tgz",
"integrity": "sha512-bqzn2rcQkEwCINefhm69ttBVVkgHJb/V03DdBKsPFtiX6H47axXKz62d1imi26zFXhOEYxhKbu3js03GobJOLw==",
"cpu": [
"ia32"
],
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@node-rs/argon2-win32-x64-msvc": {
"version": "1.8.3",
"resolved": "https://registry.npmjs.org/@node-rs/argon2-win32-x64-msvc/-/argon2-win32-x64-msvc-1.8.3.tgz",
"integrity": "sha512-ILlrRThdbp5xNR5gwYM2ic1n/vG5rJ8dQZ+YMRqksl+lnTJ/6FDe5BOyIhiPtiDwlCiCtUA+1NxpDB9KlUCAIA==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@node-rs/bcrypt": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/@node-rs/bcrypt/-/bcrypt-1.9.0.tgz",
"integrity": "sha512-u2OlIxW264bFUfvbFqDz9HZKFjwe8FHFtn7T/U8mYjPZ7DWYpbUB+/dkW/QgYfMSfR0ejkyuWaBBe0coW7/7ig==",
"engines": {
"node": ">= 10"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/Brooooooklyn"
},
"optionalDependencies": {
"@node-rs/bcrypt-android-arm-eabi": "1.9.0",
"@node-rs/bcrypt-android-arm64": "1.9.0",
"@node-rs/bcrypt-darwin-arm64": "1.9.0",
"@node-rs/bcrypt-darwin-x64": "1.9.0",
"@node-rs/bcrypt-freebsd-x64": "1.9.0",
"@node-rs/bcrypt-linux-arm-gnueabihf": "1.9.0",
"@node-rs/bcrypt-linux-arm64-gnu": "1.9.0",
"@node-rs/bcrypt-linux-arm64-musl": "1.9.0",
"@node-rs/bcrypt-linux-x64-gnu": "1.9.0",
"@node-rs/bcrypt-linux-x64-musl": "1.9.0",
"@node-rs/bcrypt-wasm32-wasi": "1.9.0",
"@node-rs/bcrypt-win32-arm64-msvc": "1.9.0",
"@node-rs/bcrypt-win32-ia32-msvc": "1.9.0",
"@node-rs/bcrypt-win32-x64-msvc": "1.9.0"
}
},
"node_modules/@node-rs/bcrypt-android-arm-eabi": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/@node-rs/bcrypt-android-arm-eabi/-/bcrypt-android-arm-eabi-1.9.0.tgz",
"integrity": "sha512-nOCFISGtnodGHNiLrG0WYLWr81qQzZKYfmwHc7muUeq+KY0sQXyHOwZk9OuNQAWv/lnntmtbwkwT0QNEmOyLvA==",
"cpu": [
"arm"
],
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@node-rs/bcrypt-android-arm64": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/@node-rs/bcrypt-android-arm64/-/bcrypt-android-arm64-1.9.0.tgz",
"integrity": "sha512-+ZrIAtigVmjYkqZQTThHVlz0+TG6D+GDHWhVKvR2DifjtqJ0i+mb9gjo++hN+fWEQdWNGxKCiBBjwgT4EcXd6A==",
"cpu": [
"arm64"
],
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@node-rs/bcrypt-darwin-arm64": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/@node-rs/bcrypt-darwin-arm64/-/bcrypt-darwin-arm64-1.9.0.tgz",
"integrity": "sha512-CQiS+F9Pa0XozvkXR1g7uXE9QvBOPOplDg0iCCPRYTN9PqA5qYxhwe48G3o+v2UeQceNRrbnEtWuANm7JRqIhw==",
"cpu": [
"arm64"
],
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@node-rs/bcrypt-darwin-x64": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/@node-rs/bcrypt-darwin-x64/-/bcrypt-darwin-x64-1.9.0.tgz",
"integrity": "sha512-4pTKGawYd7sNEjdJ7R/R67uwQH1VvwPZ0SSUMmeNHbxD5QlwAPXdDH11q22uzVXsvNFZ6nGQBg8No5OUGpx6Ug==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@node-rs/bcrypt-freebsd-x64": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/@node-rs/bcrypt-freebsd-x64/-/bcrypt-freebsd-x64-1.9.0.tgz",
"integrity": "sha512-UmWzySX4BJhT/B8xmTru6iFif3h0Rpx3TqxRLCcbgmH43r7k5/9QuhpiyzpvKGpKHJCFNm4F3rC2wghvw5FCIg==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@node-rs/bcrypt-linux-arm-gnueabihf": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-arm-gnueabihf/-/bcrypt-linux-arm-gnueabihf-1.9.0.tgz",
"integrity": "sha512-8qoX4PgBND2cVwsbajoAWo3NwdfJPEXgpCsZQZURz42oMjbGyhhSYbovBCskGU3EBLoC8RA2B1jFWooeYVn5BA==",
"cpu": [
"arm"
],
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@node-rs/bcrypt-linux-arm64-gnu": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-arm64-gnu/-/bcrypt-linux-arm64-gnu-1.9.0.tgz",
"integrity": "sha512-TuAC6kx0SbcIA4mSEWPi+OCcDjTQUMl213v5gMNlttF+D4ieIZx6pPDGTaMO6M2PDHTeCG0CBzZl0Lu+9b0c7Q==",
"cpu": [
"arm64"
],
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@node-rs/bcrypt-linux-arm64-musl": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-arm64-musl/-/bcrypt-linux-arm64-musl-1.9.0.tgz",
"integrity": "sha512-/sIvKDABOI8QOEnLD7hIj02BVaNOuCIWBKvxcJOt8+TuwJ6zmY1UI5kSv9d99WbiHjTp97wtAUbZQwauU4b9ew==",
"cpu": [
"arm64"
],
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@node-rs/bcrypt-linux-x64-gnu": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-x64-gnu/-/bcrypt-linux-x64-gnu-1.9.0.tgz",
"integrity": "sha512-DyyhDHDsLBsCKz1tZ1hLvUZSc1DK0FU0v52jK6IBQxrj24WscSU9zZe7ie/V9kdmA4Ep57BfpWX8Dsa2JxGdgQ==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@node-rs/bcrypt-linux-x64-musl": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/@node-rs/bcrypt-linux-x64-musl/-/bcrypt-linux-x64-musl-1.9.0.tgz",
"integrity": "sha512-duIiuqQ+Lew8ASSAYm6ZRqcmfBGWwsi81XLUwz86a2HR7Qv6V4yc3ZAUQovAikhjCsIqe8C11JlAZSK6+PlXYg==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@node-rs/bcrypt-wasm32-wasi": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/@node-rs/bcrypt-wasm32-wasi/-/bcrypt-wasm32-wasi-1.9.0.tgz",
"integrity": "sha512-ylaGmn9Wjwv/D5lxtawttx3H6Uu2WTTR7lWlRHGT6Ga/MB1Vj4OjSGUW8G8zIVnKuXpGbZ92pgHlt4HUpSLctw==",
"cpu": [
"wasm32"
],
"optional": true,
"dependencies": {
"@emnapi/core": "^0.45.0",
"@emnapi/runtime": "^0.45.0",
"@tybys/wasm-util": "^0.8.1",
"memfs-browser": "^3.4.13000"
},
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/@node-rs/bcrypt-win32-arm64-msvc": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/@node-rs/bcrypt-win32-arm64-msvc/-/bcrypt-win32-arm64-msvc-1.9.0.tgz",
"integrity": "sha512-2h86gF7QFyEzODuDFml/Dp1MSJoZjxJ4yyT2Erf4NkwsiA5MqowUhUsorRwZhX6+2CtlGa7orbwi13AKMsYndw==",
"cpu": [
"arm64"
],
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@node-rs/bcrypt-win32-ia32-msvc": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/@node-rs/bcrypt-win32-ia32-msvc/-/bcrypt-win32-ia32-msvc-1.9.0.tgz",
"integrity": "sha512-kqxalCvhs4FkN0+gWWfa4Bdy2NQAkfiqq/CEf6mNXC13RSV673Ev9V8sRlQyNpCHCNkeXfOT9pgoBdJmMs9muA==",
"cpu": [
"ia32"
],
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@node-rs/bcrypt-win32-x64-msvc": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/@node-rs/bcrypt-win32-x64-msvc/-/bcrypt-win32-x64-msvc-1.9.0.tgz",
"integrity": "sha512-2y0Tuo6ZAT2Cz8V7DHulSlv1Bip3zbzeXyeur+uR25IRNYXKvI/P99Zl85Fbuu/zzYAZRLLlGTRe6/9IHofe/w==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@nodelib/fs.scandir": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@ -5274,6 +5828,15 @@
"tslib": "^2.4.0"
}
},
"node_modules/@tybys/wasm-util": {
"version": "0.8.3",
"resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.8.3.tgz",
"integrity": "sha512-Z96T/L6dUFFxgFJ+pQtkPpne9q7i6kIPYCFnQBHSgSPV9idTsKfIhCss0h5iM9irweZCatkrdeP8yi5uM1eX6Q==",
"optional": true,
"dependencies": {
"tslib": "^2.4.0"
}
},
"node_modules/@types/better-sqlite3": {
"version": "7.6.11",
"resolved": "https://registry.npmjs.org/@types/better-sqlite3/-/better-sqlite3-7.6.11.tgz",
@ -9254,6 +9817,12 @@
"node": "^14.17.0 || ^16.13.0 || >=18.0.0"
}
},
"node_modules/fs-monkey": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.6.tgz",
"integrity": "sha512-b1FMfwetIKymC0eioW7mTywihSQE4oLzQn1dB6rZB5fx/3NpNEdAWeCSMB+60/AeT0TCXsxzAlcYVEFCTAksWg==",
"optional": true
},
"node_modules/fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
@ -11099,6 +11668,14 @@
"integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
"devOptional": true
},
"node_modules/lucia": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/lucia/-/lucia-3.2.0.tgz",
"integrity": "sha512-eXMxXwk6hqtjRTj4W/x3EnTUtAztLPm0p2N2TEBMDEbakDLXiYnDQ9z/qahjPdPdhPguQc+vwO0/88zIWxlpuw==",
"dependencies": {
"oslo": "1.2.0"
}
},
"node_modules/make-dir": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz",
@ -11183,6 +11760,27 @@
"node": ">= 0.6"
}
},
"node_modules/memfs": {
"version": "3.5.3",
"resolved": "https://registry.npmjs.org/memfs/-/memfs-3.5.3.tgz",
"integrity": "sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==",
"optional": true,
"dependencies": {
"fs-monkey": "^1.0.4"
},
"engines": {
"node": ">= 4.0.0"
}
},
"node_modules/memfs-browser": {
"version": "3.5.10302",
"resolved": "https://registry.npmjs.org/memfs-browser/-/memfs-browser-3.5.10302.tgz",
"integrity": "sha512-JJTc/nh3ig05O0gBBGZjTCPOyydaTxNF0uHYBrcc1gHNnO+KIHIvo0Y1FKCJsaei6FCl8C6xfQomXqu+cuzkIw==",
"optional": true,
"dependencies": {
"memfs": "3.5.3"
}
},
"node_modules/memory-cache": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/memory-cache/-/memory-cache-0.2.0.tgz",
@ -12095,6 +12693,252 @@
"os-tmpdir": "^1.0.0"
}
},
"node_modules/oslo": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/oslo/-/oslo-1.2.0.tgz",
"integrity": "sha512-OoFX6rDsNcOQVAD2gQD/z03u4vEjWZLzJtwkmgfRF+KpQUXwdgEXErD7zNhyowmHwHefP+PM9Pw13pgpHMRlzw==",
"dependencies": {
"@node-rs/argon2": "1.7.0",
"@node-rs/bcrypt": "1.9.0"
}
},
"node_modules/oslo/node_modules/@node-rs/argon2": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/@node-rs/argon2/-/argon2-1.7.0.tgz",
"integrity": "sha512-zfULc+/tmcWcxn+nHkbyY8vP3+MpEqKORbszt4UkpqZgBgDAAIYvuDN/zukfTgdmo6tmJKKVfzigZOPk4LlIog==",
"engines": {
"node": ">= 10"
},
"optionalDependencies": {
"@node-rs/argon2-android-arm-eabi": "1.7.0",
"@node-rs/argon2-android-arm64": "1.7.0",
"@node-rs/argon2-darwin-arm64": "1.7.0",
"@node-rs/argon2-darwin-x64": "1.7.0",
"@node-rs/argon2-freebsd-x64": "1.7.0",
"@node-rs/argon2-linux-arm-gnueabihf": "1.7.0",
"@node-rs/argon2-linux-arm64-gnu": "1.7.0",
"@node-rs/argon2-linux-arm64-musl": "1.7.0",
"@node-rs/argon2-linux-x64-gnu": "1.7.0",
"@node-rs/argon2-linux-x64-musl": "1.7.0",
"@node-rs/argon2-wasm32-wasi": "1.7.0",
"@node-rs/argon2-win32-arm64-msvc": "1.7.0",
"@node-rs/argon2-win32-ia32-msvc": "1.7.0",
"@node-rs/argon2-win32-x64-msvc": "1.7.0"
}
},
"node_modules/oslo/node_modules/@node-rs/argon2-android-arm-eabi": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/@node-rs/argon2-android-arm-eabi/-/argon2-android-arm-eabi-1.7.0.tgz",
"integrity": "sha512-udDqkr5P9E+wYX1SZwAVPdyfYvaF4ry9Tm+R9LkfSHbzWH0uhU6zjIwNRp7m+n4gx691rk+lqqDAIP8RLKwbhg==",
"cpu": [
"arm"
],
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/oslo/node_modules/@node-rs/argon2-android-arm64": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/@node-rs/argon2-android-arm64/-/argon2-android-arm64-1.7.0.tgz",
"integrity": "sha512-s9j/G30xKUx8WU50WIhF0fIl1EdhBGq0RQ06lEhZ0Gi0ap8lhqbE2Bn5h3/G2D1k0Dx+yjeVVNmt/xOQIRG38A==",
"cpu": [
"arm64"
],
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/oslo/node_modules/@node-rs/argon2-darwin-arm64": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/@node-rs/argon2-darwin-arm64/-/argon2-darwin-arm64-1.7.0.tgz",
"integrity": "sha512-ZIz4L6HGOB9U1kW23g+m7anGNuTZ0RuTw0vNp3o+2DWpb8u8rODq6A8tH4JRL79S+Co/Nq608m9uackN2pe0Rw==",
"cpu": [
"arm64"
],
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/oslo/node_modules/@node-rs/argon2-darwin-x64": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/@node-rs/argon2-darwin-x64/-/argon2-darwin-x64-1.7.0.tgz",
"integrity": "sha512-5oi/pxqVhODW/pj1+3zElMTn/YukQeywPHHYDbcAW3KsojFjKySfhcJMd1DjKTc+CHQI+4lOxZzSUzK7mI14Hw==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/oslo/node_modules/@node-rs/argon2-freebsd-x64": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/@node-rs/argon2-freebsd-x64/-/argon2-freebsd-x64-1.7.0.tgz",
"integrity": "sha512-Ify08683hA4QVXYoIm5SUWOY5DPIT/CMB0CQT+IdxQAg/F+qp342+lUkeAtD5bvStQuCx/dFO3bnnzoe2clMhA==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/oslo/node_modules/@node-rs/argon2-linux-arm-gnueabihf": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-arm-gnueabihf/-/argon2-linux-arm-gnueabihf-1.7.0.tgz",
"integrity": "sha512-7DjDZ1h5AUHAtRNjD19RnQatbhL+uuxBASuuXIBu4/w6Dx8n7YPxwTP4MXfsvuRgKuMWiOb/Ub/HJ3kXVCXRkg==",
"cpu": [
"arm"
],
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/oslo/node_modules/@node-rs/argon2-linux-arm64-gnu": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-arm64-gnu/-/argon2-linux-arm64-gnu-1.7.0.tgz",
"integrity": "sha512-nJDoMP4Y3YcqGswE4DvP080w6O24RmnFEDnL0emdI8Nou17kNYBzP2546Nasx9GCyLzRcYQwZOUjrtUuQ+od2g==",
"cpu": [
"arm64"
],
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/oslo/node_modules/@node-rs/argon2-linux-arm64-musl": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-arm64-musl/-/argon2-linux-arm64-musl-1.7.0.tgz",
"integrity": "sha512-BKWS8iVconhE3jrb9mj6t1J9vwUqQPpzCbUKxfTGJfc+kNL58F1SXHBoe2cDYGnHrFEHTY0YochzXoAfm4Dm/A==",
"cpu": [
"arm64"
],
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/oslo/node_modules/@node-rs/argon2-linux-x64-gnu": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-x64-gnu/-/argon2-linux-x64-gnu-1.7.0.tgz",
"integrity": "sha512-EmgqZOlf4Jurk/szW1iTsVISx25bKksVC5uttJDUloTgsAgIGReCpUUO1R24pBhu9ESJa47iv8NSf3yAfGv6jQ==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/oslo/node_modules/@node-rs/argon2-linux-x64-musl": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/@node-rs/argon2-linux-x64-musl/-/argon2-linux-x64-musl-1.7.0.tgz",
"integrity": "sha512-/o1efYCYIxjfuoRYyBTi2Iy+1iFfhqHCvvVsnjNSgO1xWiWrX0Rrt/xXW5Zsl7vS2Y+yu8PL8KFWRzZhaVxfKA==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/oslo/node_modules/@node-rs/argon2-wasm32-wasi": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/@node-rs/argon2-wasm32-wasi/-/argon2-wasm32-wasi-1.7.0.tgz",
"integrity": "sha512-Evmk9VcxqnuwQftfAfYEr6YZYSPLzmKUsbFIMep5nTt9PT4XYRFAERj7wNYp+rOcBenF3X4xoB+LhwcOMTNE5w==",
"cpu": [
"wasm32"
],
"optional": true,
"dependencies": {
"@emnapi/core": "^0.45.0",
"@emnapi/runtime": "^0.45.0",
"@tybys/wasm-util": "^0.8.1",
"memfs-browser": "^3.4.13000"
},
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/oslo/node_modules/@node-rs/argon2-win32-arm64-msvc": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/@node-rs/argon2-win32-arm64-msvc/-/argon2-win32-arm64-msvc-1.7.0.tgz",
"integrity": "sha512-qgsU7T004COWWpSA0tppDqDxbPLgg8FaU09krIJ7FBl71Sz8SFO40h7fDIjfbTT5w7u6mcaINMQ5bSHu75PCaA==",
"cpu": [
"arm64"
],
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/oslo/node_modules/@node-rs/argon2-win32-ia32-msvc": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/@node-rs/argon2-win32-ia32-msvc/-/argon2-win32-ia32-msvc-1.7.0.tgz",
"integrity": "sha512-JGafwWYQ/HpZ3XSwP4adQ6W41pRvhcdXvpzIWtKvX+17+xEXAe2nmGWM6s27pVkg1iV2ZtoYLRDkOUoGqZkCcg==",
"cpu": [
"ia32"
],
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/oslo/node_modules/@node-rs/argon2-win32-x64-msvc": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/@node-rs/argon2-win32-x64-msvc/-/argon2-win32-x64-msvc-1.7.0.tgz",
"integrity": "sha512-9oq4ShyFakw8AG3mRls0AoCpxBFcimYx7+jvXeAf2OqKNO+mSA6eZ9z7KQeVCi0+SOEUYxMGf5UiGiDb9R6+9Q==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/p-finally": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",

View file

@ -12,12 +12,16 @@
"start": "ENVIRONMENT=prod node dist/server/index.js"
},
"dependencies": {
"@lucia-auth/adapter-drizzle": "1.1.0",
"@node-rs/argon2": "1.8.3",
"axios": "1.7.7",
"better-sqlite3": "11.3.0",
"cors": "2.8.5",
"drizzle-orm": "0.33.0",
"express": "4.21.0",
"helmet": "7.1.0",
"http-errors": "2.0.0",
"lucia": "3.2.0",
"next": "14.2.13",
"react": "^18",
"react-dom": "^18",

50
server/auth/index.ts Normal file
View file

@ -0,0 +1,50 @@
import { Lucia, TimeSpan } from "lucia";
import { DrizzleSQLiteAdapter } from "@lucia-auth/adapter-drizzle";
import db from "@server/db";
import { sessions, users } from "@server/db/schema";
import environment from "@server/environment";
const adapter = new DrizzleSQLiteAdapter(db, sessions, users);
export const lucia = new Lucia(adapter, {
getUserAttributes: (attributes) => {
return {
username: attributes.username,
};
},
// getSessionAttributes: (attributes) => {
// return {
// country: attributes.country,
// };
// },
sessionCookie: {
name: "session",
expires: false, // session cookies have very long lifespan (2 years)
attributes: {
secure: environment.ENVIRONMENT === "prod",
sameSite: "strict",
// domain: "example.com"
},
},
sessionExpiresIn: new TimeSpan(2, "w"),
});
export default lucia;
// IMPORTANT!
declare module "lucia" {
interface Register {
Lucia: typeof lucia;
DatabaseUserAttributes: DatabaseUserAttributes;
DatabaseSessionAttributes: DatabaseSessionAttributes;
}
}
interface DatabaseUserAttributes {
username: string;
passwordHash: string;
}
interface DatabaseSessionAttributes {
// country: string;
}

78
server/auth/login.ts Normal file
View file

@ -0,0 +1,78 @@
import { verify } from "@node-rs/argon2";
import lucia from "@server/auth";
import db from "@server/db";
import { users } from "@server/db/schema";
import HttpCode from "@server/types/HttpCode";
import response from "@server/utils/response";
import { eq } from "drizzle-orm";
import { NextFunction, Request, Response } from "express";
import createHttpError from "http-errors";
import { z } from "zod";
import { fromError } from "zod-validation-error";
export const loginBodySchema = z.object({
email: z.string().email(),
password: z.string(),
});
export async function login(req: Request, res: Response, next: NextFunction) {
const parsedBody = loginBodySchema.safeParse(req.body);
if (!parsedBody.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
fromError(parsedBody.error).toString(),
),
);
}
const { email, password } = parsedBody.data;
const existingUserRes = await db
.select()
.from(users)
.where(eq(users.email, email));
if (!existingUserRes || !existingUserRes.length) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
"A user with that email address does not exist",
),
);
}
const existingUser = existingUserRes[0];
const validPassword = await verify(existingUser.passwordHash, password, {
memoryCost: 19456,
timeCost: 2,
outputLen: 32,
parallelism: 1,
});
if (!validPassword) {
await new Promise((resolve) => setTimeout(resolve, 500)); // delay to prevent brute force attacks
return next(
createHttpError(
HttpCode.BAD_REQUEST,
"The password you entered is incorrect",
),
);
}
const session = await lucia.createSession(existingUser.id, {});
res.appendHeader(
"Set-Cookie",
lucia.createSessionCookie(session.id).serialize(),
);
return res.status(HttpCode.OK).send(
response<null>({
data: null,
success: true,
error: false,
message: "Logged in successfully",
status: HttpCode.OK,
}),
);
}

93
server/auth/signup.ts Normal file
View file

@ -0,0 +1,93 @@
import { NextFunction, Request, Response } from "express";
import db from "@server/db";
import { hash } from "@node-rs/argon2";
import HttpCode from "@server/types/HttpCode";
import { z } from "zod";
import { generateId } from "lucia";
import { users } from "@server/db/schema";
import lucia from "@server/auth";
import { fromError } from "zod-validation-error";
import createHttpError from "http-errors";
import response from "@server/utils/response";
import { SqliteError } from "better-sqlite3";
export const signupBodySchema = z.object({
email: z.string().email(),
password: z
.string()
.min(8, { message: "Password must be at least 8 characters long" })
.max(31, { message: "Password must be at most 31 characters long" })
.regex(/^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[#?!@$%^&*-]).*$/, {
message: `Your password must meet the following conditions:
- At least one uppercase English letter.
- At least one lowercase English letter.
- At least one digit.
- At least one special character.`,
}),
});
export type SignUpBody = z.infer<typeof signupBodySchema>;
export async function signup(req: Request, res: Response, next: NextFunction) {
const parsedBody = signupBodySchema.safeParse(req.body);
if (!parsedBody.success) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
fromError(parsedBody.error).toString(),
),
);
}
const { email, password } = parsedBody.data;
const passwordHash = await hash(password, {
// recommended minimum parameters
memoryCost: 19456,
timeCost: 2,
outputLen: 32,
parallelism: 1,
});
const userId = generateId(15);
try {
await db.insert(users).values({
id: userId,
email: email,
passwordHash,
});
const session = await lucia.createSession(userId, {});
res.appendHeader(
"Set-Cookie",
lucia.createSessionCookie(session.id).serialize(),
);
return res.status(HttpCode.OK).send(
response<null>({
data: null,
success: true,
error: false,
message: "User created successfully",
status: HttpCode.OK,
}),
);
} catch (e) {
if (e instanceof SqliteError && e.code === "SQLITE_CONSTRAINT_UNIQUE") {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
"A user with that email address already exists",
),
);
} else {
return next(
createHttpError(
HttpCode.INTERNAL_SERVER_ERROR,
"Failed to create user",
),
);
}
}
}

View file

@ -8,32 +8,29 @@ export const orgs = sqliteTable("orgs", {
domain: text("domain").notNull(),
});
// Users table
export const users = sqliteTable("users", {
userId: integer("userId").primaryKey({ autoIncrement: true }),
orgId: integer("orgId").references(() => orgs.orgId, { onDelete: "cascade" }),
name: text("name").notNull(),
email: text("email").notNull(),
groups: text("groups"),
});
// Sites table
export const sites = sqliteTable("sites", {
siteId: integer("siteId").primaryKey({ autoIncrement: true }),
orgId: integer("orgId").references(() => orgs.orgId, { onDelete: "cascade" }),
exitNode: integer("exitNode").references(() => exitNodes.exitNodeId, { onDelete: "set null" }),
orgId: integer("orgId").references(() => orgs.orgId, {
onDelete: "cascade",
}),
exitNode: integer("exitNode").references(() => exitNodes.exitNodeId, {
onDelete: "set null",
}),
name: text("name").notNull(),
subdomain: text("subdomain"),
pubKey: text("pubKey"),
subnet: text("subnet"),
megabytesIn: integer("bytesIn"),
megabytesOut: integer("bytesOut")
megabytesOut: integer("bytesOut"),
});
// Resources table
export const resources = sqliteTable("resources", {
resourceId: text("resourceId", { length: 2048 }).primaryKey(),
siteId: integer("siteId").references(() => sites.siteId, { onDelete: "cascade" }),
siteId: integer("siteId").references(() => sites.siteId, {
onDelete: "cascade",
}),
name: text("name").notNull(),
subdomain: text("subdomain"),
});
@ -41,7 +38,9 @@ export const resources = sqliteTable("resources", {
// Targets table
export const targets = sqliteTable("targets", {
targetId: integer("targetId").primaryKey({ autoIncrement: true }),
resourceId: text("resourceId").references(() => resources.resourceId, { onDelete: "cascade" }),
resourceId: text("resourceId").references(() => resources.resourceId, {
onDelete: "cascade",
}),
ip: text("ip").notNull(),
method: text("method").notNull(),
port: integer("port").notNull(),
@ -61,10 +60,28 @@ export const exitNodes = sqliteTable("exitNodes", {
// Routes table
export const routes = sqliteTable("routes", {
routeId: integer("routeId").primaryKey({ autoIncrement: true }),
exitNodeId: integer("exitNodeId").references(() => exitNodes.exitNodeId, { onDelete: "cascade" }),
exitNodeId: integer("exitNodeId").references(() => exitNodes.exitNodeId, {
onDelete: "cascade",
}),
subnet: text("subnet").notNull(),
});
// Users table
export const users = sqliteTable("user", {
id: text("id").primaryKey(), // has to be id not userId for lucia
email: text("email").notNull().unique(),
passwordHash: text("passwordHash").notNull(),
});
// Sessions table
export const sessions = sqliteTable("session", {
id: text("id").primaryKey(), // has to be id not sessionId for lucia
userId: text("userId")
.notNull()
.references(() => users.id),
expiresAt: integer("expiresAt").notNull(),
});
// Define the model types for type inference
export type Org = InferSelectModel<typeof orgs>;
export type User = InferSelectModel<typeof users>;
@ -73,3 +90,4 @@ export type Resource = InferSelectModel<typeof resources>;
export type ExitNode = InferSelectModel<typeof exitNodes>;
export type Route = InferSelectModel<typeof routes>;
export type Target = InferSelectModel<typeof targets>;
export type Session = InferSelectModel<typeof sessions>;

View file

@ -7,6 +7,8 @@ import helmet from "helmet";
import cors from "cors";
import internal from "@server/routers/internal";
import external from "@server/routers/external";
import notFoundMiddleware from "./middlewares/notFound";
import { errorHandlerMiddleware } from "./middlewares/formatError";
const dev = environment.ENVIRONMENT !== "prod";
const app = next({ dev });
@ -34,6 +36,9 @@ app.prepare().then(() => {
logger.info(`Main server is running on http://localhost:${mainPort}`);
});
mainServer.use(notFoundMiddleware);
mainServer.use(errorHandlerMiddleware);
// Internal server
const internalServer = express();
internalServer.use(helmet());

View file

@ -0,0 +1,23 @@
import { ErrorRequestHandler, NextFunction, Response } from "express";
import ErrorResponse from "@server/types/ErrorResponse";
import HttpCode from "@server/types/HttpCode";
import logger from "@server/logger";
import environment from "@server/environment";
export const errorHandlerMiddleware: ErrorRequestHandler = (
error,
req,
res: Response<ErrorResponse>,
next: NextFunction,
) => {
logger.error(error);
const statusCode = error.statusCode || HttpCode.INTERNAL_SERVER_ERROR;
res?.status(statusCode).send({
data: null,
success: false,
error: true,
message: error.message || "Internal Server Error",
status: statusCode,
stack: environment.ENVIRONMENT === "prod" ? null : error.stack,
});
};

View file

@ -0,0 +1,14 @@
import { NextFunction, Request, Response } from "express";
import createHttpError from "http-errors";
import HttpCode from "@server/types/HttpCode";
export function notFoundMiddleware(
req: Request,
res: Response,
next: NextFunction,
) {
const message = `The requests url is not found - ${req.originalUrl}`;
return next(createHttpError(HttpCode.NOT_FOUND, message));
}
export default notFoundMiddleware;

View file

@ -27,6 +27,13 @@ CREATE TABLE `routes` (
FOREIGN KEY (`exitNodeId`) REFERENCES `exitNodes`(`exitNodeId`) ON UPDATE no action ON DELETE cascade
);
--> statement-breakpoint
CREATE TABLE `session` (
`id` text PRIMARY KEY NOT NULL,
`userId` text NOT NULL,
`expiresAt` integer NOT NULL,
FOREIGN KEY (`userId`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE no action
);
--> statement-breakpoint
CREATE TABLE `sites` (
`siteId` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
`orgId` integer,
@ -52,11 +59,10 @@ CREATE TABLE `targets` (
FOREIGN KEY (`resourceId`) REFERENCES `resources`(`resourceId`) ON UPDATE no action ON DELETE cascade
);
--> statement-breakpoint
CREATE TABLE `users` (
`userId` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
`orgId` integer,
`name` text NOT NULL,
CREATE TABLE `user` (
`id` text PRIMARY KEY NOT NULL,
`email` text NOT NULL,
`groups` text,
FOREIGN KEY (`orgId`) REFERENCES `orgs`(`orgId`) ON UPDATE no action ON DELETE cascade
`passwordHash` text NOT NULL
);
--> statement-breakpoint
CREATE UNIQUE INDEX `user_email_unique` ON `user` (`email`);

View file

@ -1,7 +1,7 @@
{
"version": "6",
"dialect": "sqlite",
"id": "db8ede7f-7ece-463c-be9c-da36b2b20db6",
"id": "fb7ff7a8-20e6-4602-b096-2f2284ad751e",
"prevId": "00000000-0000-0000-0000-000000000000",
"tables": {
"exitNodes": {
@ -173,6 +173,50 @@
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"session": {
"name": "session",
"columns": {
"id": {
"name": "id",
"type": "text",
"primaryKey": true,
"notNull": true,
"autoincrement": false
},
"userId": {
"name": "userId",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"expiresAt": {
"name": "expiresAt",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {
"session_userId_user_id_fk": {
"name": "session_userId_user_id_fk",
"tableFrom": "session",
"tableTo": "user",
"columnsFrom": [
"userId"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"sites": {
"name": "sites",
"columns": {
@ -345,27 +389,13 @@
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"users": {
"name": "users",
"user": {
"name": "user",
"columns": {
"userId": {
"name": "userId",
"type": "integer",
"primaryKey": true,
"notNull": true,
"autoincrement": true
},
"orgId": {
"name": "orgId",
"type": "integer",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"name": {
"name": "name",
"id": {
"name": "id",
"type": "text",
"primaryKey": false,
"primaryKey": true,
"notNull": true,
"autoincrement": false
},
@ -376,30 +406,24 @@
"notNull": true,
"autoincrement": false
},
"groups": {
"name": "groups",
"passwordHash": {
"name": "passwordHash",
"type": "text",
"primaryKey": false,
"notNull": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {
"users_orgId_orgs_orgId_fk": {
"name": "users_orgId_orgs_orgId_fk",
"tableFrom": "users",
"tableTo": "orgs",
"columnsFrom": [
"orgId"
"indexes": {
"user_email_unique": {
"name": "user_email_unique",
"columns": [
"email"
],
"columnsTo": [
"orgId"
],
"onDelete": "cascade",
"onUpdate": "no action"
"isUnique": true
}
},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
}

View file

@ -5,8 +5,8 @@
{
"idx": 0,
"version": "6",
"when": 1727582003591,
"tag": "0000_ancient_blob",
"when": 1727750917675,
"tag": "0000_faithful_katie_power",
"breakpoints": true
}
]

View file

@ -1,6 +1,7 @@
import { Router } from "express";
import gerbil from "./gerbil/gerbil";
import pangolin from "./pangolin/pangolin";
import global from "./global/global";
const unauth = Router();
@ -11,4 +12,6 @@ unauth.get("/", (_, res) => {
unauth.use("/newt", gerbil);
unauth.use("/pangolin", pangolin);
unauth.use("/", global)
export default unauth;

View file

@ -1,5 +1,7 @@
import { Router } from "express";
import { createSite } from "./createSite";
import { signup } from "@server/auth/signup";
import { login } from "@server/auth/login";
const global = Router();
@ -9,4 +11,8 @@ global.get("/", (_, res) => {
global.get("/createSite", createSite);
// auth
global.post("/signup", signup);
global.post("/login", login);
export default global;

View file

@ -4,16 +4,19 @@ import * as schema from "@server/db/schema";
import { DynamicTraefikConfig } from "./configSchema";
import { and, like, eq } from "drizzle-orm";
import logger from "@server/logger";
import HttpCode from "@server/types/HttpCode";
export async function traefikConfigProvider(_: Request, res: Response) {
try {
const targets = await getAllTargets();
const traefikConfig = buildTraefikConfig(targets);
// logger.debug("Built traefik config");
res.status(200).send(traefikConfig);
res.status(HttpCode.OK).json(traefikConfig);
} catch (e) {
logger.error(`Failed to build traefik config: ${e}`);
res.status(500).send({ message: "Failed to build traefik config" });
res.status(HttpCode.INTERNAL_SERVER_ERROR).json({
error: "Failed to build traefik config",
});
}
}

View file

@ -0,0 +1,7 @@
import MessageResponse from "./MessageResponse";
export interface ErrorResponse extends MessageResponse<null> {
stack?: string;
}
export default ErrorResponse;

65
server/types/HttpCode.ts Normal file
View file

@ -0,0 +1,65 @@
export enum HttpCode {
CONTINUE = 100,
SWITCHING_PROTOCOLS = 101,
PROCESSING = 102,
EARLY_HINTS = 103,
OK = 200,
CREATED = 201,
ACCEPTED = 202,
NON_AUTHORITATIVE_INFORMATION = 203,
NO_CONTENT = 204,
RESET_CONTENT = 205,
PARTIAL_CONTENT = 206,
MULTI_STATUS = 207,
ALREADY_REPORTED = 208,
IM_USED = 226,
MULTIPLE_CHOICES = 300,
MOVED_PERMANENTLY = 301,
FOUND = 302,
SEE_OTHER = 303,
NOT_MODIFIED = 304,
TEMPORARY_REDIRECT = 307,
PERMANENT_REDIRECT = 308,
BAD_REQUEST = 400,
UNAUTHORIZED = 401,
PAYMENT_REQUIRED = 402,
FORBIDDEN = 403,
NOT_FOUND = 404,
METHOD_NOT_ALLOWED = 405,
NOT_ACCEPTABLE = 406,
PROXY_AUTHENTICATION_REQUIRED = 407,
REQUEST_TIMEOUT = 408,
CONFLICT = 409,
GONE = 410,
LENGTH_REQUIRED = 411,
PRECONDITION_FAILED = 412,
CONTENT_TOO_LARGE = 413,
URI_TOO_LONG = 414,
UNSUPPORTED_MEDIA_TYPE = 415,
RANGE_NOT_SATISFIABLE = 416,
EXPECTATION_FAILED = 417,
IM_A_TEAPOT = 418,
MISDIRECTED_REQUEST = 421,
UNPROCESSABLE_CONTENT = 422,
LOCKED = 423,
FAILED_DEPENDENCY = 424,
TOO_EARLY = 425,
UPGRADE_REQUIRED = 426,
PRECONDITION_REQUIRED = 428,
TOO_MANY_REQUESTS = 429,
REQUEST_HEADER_FIELDS_TOO_LARGE = 431,
UNAVAILABLE_FOR_LEGAL_REASONS = 451,
INTERNAL_SERVER_ERROR = 500,
NOT_IMPLEMENTED = 501,
BAD_GATEWAY = 502,
SERVICE_UNAVAILABLE = 503,
GATEWAY_TIMEOUT = 504,
HTTP_VERSION_NOT_SUPPORTED = 505,
VARIANT_ALSO_NEGOTIATES = 506,
INSUFFICIENT_STORAGE = 507,
LOOP_DETECTED = 508,
NOT_EXTENDED = 510,
NETWORK_AUTHENTICATION_REQUIRED = 511,
}
export default HttpCode;

View file

@ -0,0 +1,9 @@
export interface ResponseT<T> {
data: T | null;
success: boolean;
error: boolean;
message: string;
status: number;
}
export default ResponseT;

9
server/types/Response.ts Normal file
View file

@ -0,0 +1,9 @@
export interface ResponseT<T> {
data: T | null;
success: boolean;
error: boolean;
message: string;
status: number;
}
export default ResponseT;

19
server/utils/response.ts Normal file
View file

@ -0,0 +1,19 @@
import { ResponseT } from "@server/types/Response";
export const response = <T>({
data,
success,
error,
message,
status,
}: ResponseT<T>) => {
return {
data,
success,
error,
message,
status,
};
};
export default response;